From ebcc6fa72439004a23103f68f9acd908981e7f21 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 24 Nov 2025 18:22:52 +0800 Subject: [PATCH 001/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0exp=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/user/BukkitServerPlayer.java | 33 ++++++++-- .../src/main/resources/translations/en.yml | 1 + .../src/main/resources/translations/zh_cn.yml | 4 +- .../core/entity/player/Player.java | 10 +++ .../plugin/context/event/EventFunctions.java | 1 + .../context/function/CommonFunctions.java | 1 + .../plugin/context/function/ExpFunction.java | 65 +++++++++++++++++++ 7 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ExpFunction.java 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 af6789fab..5384d3146 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 @@ -35,10 +35,7 @@ import net.momirealms.craftengine.core.plugin.locale.TranslationManager; 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.util.*; import net.momirealms.craftengine.core.world.*; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.chunk.ChunkStatus; @@ -1253,4 +1250,32 @@ public class BukkitServerPlayer extends Player { platformPlayer().getPersistentDataContainer().remove(KeyUtils.toNamespacedKey(SELECTED_LOCALE_KEY)); } } + + @Override + public void giveExperiencePoints(int xpPoints) { + platformPlayer().giveExp(xpPoints); + } + + @Override + public void giveExperienceLevels(int levels) { + platformPlayer().giveExpLevels(levels); + } + + @Override + public int getXpNeededForNextLevel() { + return platformPlayer().getExperiencePointsNeededForNextLevel(); + } + + @Override + public void setExperiencePoints(int experiencePoints) { + float xpNeededForNextLevel = this.getXpNeededForNextLevel(); + float maxProgressThreshold = (xpNeededForNextLevel - 1.0F) / xpNeededForNextLevel; + float experienceProgress = MiscUtils.clamp(experiencePoints / xpNeededForNextLevel, 0.0F, maxProgressThreshold); + platformPlayer().setExp(experienceProgress); + } + + @Override + public void setExperienceLevels(int level) { + platformPlayer().setLevel(level); + } } diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 761e46f38..e8fbebaaf 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -492,6 +492,7 @@ warning.config.function.if_else.missing_rules: "Issue found in file Issue found in file - The config '' is missing the required 'properties' argument for 'update_block_property' function." warning.config.function.transform_block.missing_block: "Issue found in file - The config '' is missing the required 'block' argument for 'transform_block' function." warning.config.function.cycle_block_property.missing_property: "Issue found in file - The config '' is missing the required 'property' argument for 'cycle_block_property' function." +warning.config.function.exp.missing_count: "Issue found in file - The config '' is missing the required 'count' argument for 'exp' function." 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 ''." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 3de669d54..2264fc523 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -388,6 +388,7 @@ warning.config.loot_table.function.apply_bonus.missing_enchantment: "在 warning.config.loot_table.function.apply_bonus.missing_formula: "在文件 发现问题 - '' 的战利品表配置错误 'apply_bonus' 函数缺少必需的 'formula' 参数" warning.config.loot_table.function.drop_exp.missing_count: "在文件 发现问题 - '' 的战利品表配置错误 'drop_exp' 函数缺少必需的 'count' 参数" warning.config.loot_table.function.set_count.missing_count: "在文件 发现问题 - '' 的战利品表配置错误 'set_count' 函数缺少必需的 'count' 参数" +warning.config.loot_table.function.apply_data.missing_data: "在文件 发现问题 - '' 的战利品表配置错误 'apply_data' 函数缺少必需的 'data' 参数" warning.config.loot_table.entry.missing_type: "在文件 发现问题 - '' 的战利品表配置错误 某个条目缺少必需的 'type' 参数" warning.config.loot_table.entry.invalid_type: "在文件 发现问题 - '' 的战利品表配置错误 某个条目使用了无效的条目类型 ''" warning.config.loot_table.entry.exp.missing_count: "在文件 发现问题 - '' 的战利品表配置错误 'exp' 条目缺少必需的 'count' 参数" @@ -490,6 +491,7 @@ warning.config.function.when.missing_source: "在文件 发现 warning.config.function.if_else.missing_rules: "在文件 发现问题 - 配置项 '' 缺少 'if_else' 函数所需的 'rules' 参数" warning.config.function.update_block_property.missing_properties: "在文件 发现问题 - 配置项 '' 缺少 'update_block_property' 函数所需的 'properties' 参数" warning.config.function.transform_block.missing_block: "在文件 发现问题 - 配置项 '' 缺少 'transform_block' 函数所需的 'block' 参数" +warning.config.function.exp.missing_count: "在文件 发现问题 - 配置项 '' 缺少 'exp' 函数所需的 'count' 参数" warning.config.function.cycle_block_property.missing_property: "在文件 发现问题 - 配置项 '' 缺少 'cycle_block_property' 函数所需的 'property' 参数" warning.config.selector.missing_type: "在文件 发现问题 - 配置项 '' 缺少选择器必需的 'type' 参数" warning.config.selector.invalid_type: "在文件 发现问题 - 配置项 '' 使用了无效的选择器类型 ''" @@ -509,4 +511,4 @@ warning.config.resource_pack.invalid_overlay_format: "在 config.yml 的 warning.config.equipment.duplicate: "在文件 发现问题 - 重复的装备配置 ''. 请检查其他文件中是否存在相同配置" warning.config.equipment.missing_type: "在文件 发现问题 - 装备 '' 缺少必需的 'type' 参数" warning.config.equipment.invalid_type: "在文件 发现问题 - 装备 '' 使用了无效的 'type' 参数" -warning.config.equipment.invalid_sacrificed_armor: "在 config.yml 的 'equipment.sacrificed-vanilla-armor' 处发现问题 - 无效的原版盔甲类型 ''" +warning.config.equipment.invalid_sacrificed_armor: "在 config.yml 的 'equipment.sacrificed-vanilla-armor' 处发现问题 - 无效的原版盔甲类型 ''" \ No newline at end of file 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 c8088ef4f..507def94d 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 @@ -184,6 +184,16 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void setSelectedLocale(@Nullable Locale locale); + public abstract void giveExperiencePoints(int xpPoints); + + public abstract void giveExperienceLevels(int levels); + + public abstract int getXpNeededForNextLevel(); + + public abstract void setExperiencePoints(int experiencePoints); + + public abstract void setExperienceLevels(int level); + @Override public void remove() { } 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 447ce05f0..15a23bcd1 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 @@ -55,6 +55,7 @@ public class EventFunctions { register(CommonFunctions.WHEN, new WhenFunction.FactoryImpl<>(EventConditions::fromMap, EventFunctions::fromMap)); register(CommonFunctions.DAMAGE_ITEM, new DamageItemFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.CYCLE_BLOCK_PROPERTY, new CycleBlockPropertyFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.EXP, new ExpFunction.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/CommonFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java index bc638a23d..a8e0ee201 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 @@ -46,4 +46,5 @@ public final class CommonFunctions { public static final Key DUMMY = Key.of("craftengine:dummy"); public static final Key DAMAGE_ITEM = Key.of("craftengine:damage_item"); public static final Key CYCLE_BLOCK_PROPERTY = Key.of("craftengine:cycle_block_property"); + public static final Key EXP = Key.of("craftengine:exp"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ExpFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ExpFunction.java new file mode 100644 index 000000000..663d4618f --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ExpFunction.java @@ -0,0 +1,65 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +public class ExpFunction extends AbstractConditionalFunction { + private final PlayerSelector selector; + private final NumberProvider count; + private final BiConsumer operation; + + public ExpFunction(List> predicates, PlayerSelector selector, NumberProvider count, BiConsumer operation) { + super(predicates); + this.selector = selector; + this.count = count; + this.operation = operation; + } + + @Override + protected void runInternal(CTX ctx) { + for (Player player : this.selector.get(ctx)) { + this.operation.accept(player, this.count.getInt(ctx)); + } + } + + @Override + public Key type() { + return CommonFunctions.EXP; + } + + public static class FactoryImpl extends AbstractFactory { + private static final BiConsumer ADD_POINTS = Player::giveExperiencePoints; + private static final BiConsumer ADD_LEVELS = Player::giveExperienceLevels; + private static final BiConsumer SET_POINTS = (player, experience) -> { + if (experience < player.getXpNeededForNextLevel()) { + player.setExperiencePoints(experience); + } + }; + private static final BiConsumer SET_LEVELS = Player::setExperienceLevels; + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + PlayerSelector selector = PlayerSelectors.fromObject(arguments.getOrDefault("target", "self"), conditionFactory()); + Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("count"), "warning.config.function.exp.missing_count"); + boolean set = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("set", false), "set"); + boolean level = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("level", false), "level"); + BiConsumer operation = level ? (set ? SET_LEVELS : ADD_LEVELS) : (set ? SET_POINTS : ADD_POINTS); + return new ExpFunction<>(getPredicates(arguments), selector, NumberProviders.fromObject(value), operation); + } + } +} From a2334adbc6d257684caf137e85bfe867353acdab Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 24 Nov 2025 19:07:18 +0800 Subject: [PATCH 002/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A01.21.9=E7=9A=84?= =?UTF-8?q?=E9=93=9C=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../internal/configuration/mappings.yml | 276 ++++++++++++++++++ 1 file changed, 276 insertions(+) diff --git a/common-files/src/main/resources/resources/internal/configuration/mappings.yml b/common-files/src/main/resources/resources/internal/configuration/mappings.yml index 3ed3c4522..09d445d22 100644 --- a/common-files/src/main/resources/resources/internal/configuration/mappings.yml +++ b/common-files/src/main/resources/resources/internal/configuration/mappings.yml @@ -4324,6 +4324,282 @@ block-state-mappings: heavy_weighted_pressure_plate[power=14]: heavy_weighted_pressure_plate[power=1] heavy_weighted_pressure_plate[power=15]: heavy_weighted_pressure_plate[power=1] + #### Lightning Rod #### + $$>=1.21.9#lightning_rod: + exposed_lightning_rod[facing=down,powered=false,waterlogged=false]: waxed_exposed_lightning_rod[facing=down,powered=false,waterlogged=false] + exposed_lightning_rod[facing=down,powered=false,waterlogged=true]: waxed_exposed_lightning_rod[facing=down,powered=false,waterlogged=true] + exposed_lightning_rod[facing=down,powered=true,waterlogged=false]: waxed_exposed_lightning_rod[facing=down,powered=true,waterlogged=false] + exposed_lightning_rod[facing=down,powered=true,waterlogged=true]: waxed_exposed_lightning_rod[facing=down,powered=true,waterlogged=true] + exposed_lightning_rod[facing=east,powered=false,waterlogged=false]: waxed_exposed_lightning_rod[facing=east,powered=false,waterlogged=false] + exposed_lightning_rod[facing=east,powered=false,waterlogged=true]: waxed_exposed_lightning_rod[facing=east,powered=false,waterlogged=true] + exposed_lightning_rod[facing=east,powered=true,waterlogged=false]: waxed_exposed_lightning_rod[facing=east,powered=true,waterlogged=false] + exposed_lightning_rod[facing=east,powered=true,waterlogged=true]: waxed_exposed_lightning_rod[facing=east,powered=true,waterlogged=true] + exposed_lightning_rod[facing=north,powered=false,waterlogged=false]: waxed_exposed_lightning_rod[facing=north,powered=false,waterlogged=false] + exposed_lightning_rod[facing=north,powered=false,waterlogged=true]: waxed_exposed_lightning_rod[facing=north,powered=false,waterlogged=true] + exposed_lightning_rod[facing=north,powered=true,waterlogged=false]: waxed_exposed_lightning_rod[facing=north,powered=true,waterlogged=false] + exposed_lightning_rod[facing=north,powered=true,waterlogged=true]: waxed_exposed_lightning_rod[facing=north,powered=true,waterlogged=true] + exposed_lightning_rod[facing=south,powered=false,waterlogged=false]: waxed_exposed_lightning_rod[facing=south,powered=false,waterlogged=false] + exposed_lightning_rod[facing=south,powered=false,waterlogged=true]: waxed_exposed_lightning_rod[facing=south,powered=false,waterlogged=true] + exposed_lightning_rod[facing=south,powered=true,waterlogged=false]: waxed_exposed_lightning_rod[facing=south,powered=true,waterlogged=false] + exposed_lightning_rod[facing=south,powered=true,waterlogged=true]: waxed_exposed_lightning_rod[facing=south,powered=true,waterlogged=true] + exposed_lightning_rod[facing=up,powered=false,waterlogged=false]: waxed_exposed_lightning_rod[facing=up,powered=false,waterlogged=false] + exposed_lightning_rod[facing=up,powered=false,waterlogged=true]: waxed_exposed_lightning_rod[facing=up,powered=false,waterlogged=true] + exposed_lightning_rod[facing=up,powered=true,waterlogged=false]: waxed_exposed_lightning_rod[facing=up,powered=true,waterlogged=false] + exposed_lightning_rod[facing=up,powered=true,waterlogged=true]: waxed_exposed_lightning_rod[facing=up,powered=true,waterlogged=true] + exposed_lightning_rod[facing=west,powered=false,waterlogged=false]: waxed_exposed_lightning_rod[facing=west,powered=false,waterlogged=false] + exposed_lightning_rod[facing=west,powered=false,waterlogged=true]: waxed_exposed_lightning_rod[facing=west,powered=false,waterlogged=true] + exposed_lightning_rod[facing=west,powered=true,waterlogged=false]: waxed_exposed_lightning_rod[facing=west,powered=true,waterlogged=false] + exposed_lightning_rod[facing=west,powered=true,waterlogged=true]: waxed_exposed_lightning_rod[facing=west,powered=true,waterlogged=true] + lightning_rod[facing=down,powered=false,waterlogged=false]: waxed_lightning_rod[facing=down,powered=false,waterlogged=false] + lightning_rod[facing=down,powered=false,waterlogged=true]: waxed_lightning_rod[facing=down,powered=false,waterlogged=true] + lightning_rod[facing=down,powered=true,waterlogged=false]: waxed_lightning_rod[facing=down,powered=true,waterlogged=false] + lightning_rod[facing=down,powered=true,waterlogged=true]: waxed_lightning_rod[facing=down,powered=true,waterlogged=true] + lightning_rod[facing=east,powered=false,waterlogged=false]: waxed_lightning_rod[facing=east,powered=false,waterlogged=false] + lightning_rod[facing=east,powered=false,waterlogged=true]: waxed_lightning_rod[facing=east,powered=false,waterlogged=true] + lightning_rod[facing=east,powered=true,waterlogged=false]: waxed_lightning_rod[facing=east,powered=true,waterlogged=false] + lightning_rod[facing=east,powered=true,waterlogged=true]: waxed_lightning_rod[facing=east,powered=true,waterlogged=true] + lightning_rod[facing=north,powered=false,waterlogged=false]: waxed_lightning_rod[facing=north,powered=false,waterlogged=false] + lightning_rod[facing=north,powered=false,waterlogged=true]: waxed_lightning_rod[facing=north,powered=false,waterlogged=true] + lightning_rod[facing=north,powered=true,waterlogged=false]: waxed_lightning_rod[facing=north,powered=true,waterlogged=false] + lightning_rod[facing=north,powered=true,waterlogged=true]: waxed_lightning_rod[facing=north,powered=true,waterlogged=true] + lightning_rod[facing=south,powered=false,waterlogged=false]: waxed_lightning_rod[facing=south,powered=false,waterlogged=false] + lightning_rod[facing=south,powered=false,waterlogged=true]: waxed_lightning_rod[facing=south,powered=false,waterlogged=true] + lightning_rod[facing=south,powered=true,waterlogged=false]: waxed_lightning_rod[facing=south,powered=true,waterlogged=false] + lightning_rod[facing=south,powered=true,waterlogged=true]: waxed_lightning_rod[facing=south,powered=true,waterlogged=true] + lightning_rod[facing=up,powered=false,waterlogged=false]: waxed_lightning_rod[facing=up,powered=false,waterlogged=false] + lightning_rod[facing=up,powered=false,waterlogged=true]: waxed_lightning_rod[facing=up,powered=false,waterlogged=true] + lightning_rod[facing=up,powered=true,waterlogged=false]: waxed_lightning_rod[facing=up,powered=true,waterlogged=false] + lightning_rod[facing=up,powered=true,waterlogged=true]: waxed_lightning_rod[facing=up,powered=true,waterlogged=true] + lightning_rod[facing=west,powered=false,waterlogged=false]: waxed_lightning_rod[facing=west,powered=false,waterlogged=false] + lightning_rod[facing=west,powered=false,waterlogged=true]: waxed_lightning_rod[facing=west,powered=false,waterlogged=true] + lightning_rod[facing=west,powered=true,waterlogged=false]: waxed_lightning_rod[facing=west,powered=true,waterlogged=false] + lightning_rod[facing=west,powered=true,waterlogged=true]: waxed_lightning_rod[facing=west,powered=true,waterlogged=true] + oxidized_lightning_rod[facing=down,powered=false,waterlogged=false]: waxed_oxidized_lightning_rod[facing=down,powered=false,waterlogged=false] + oxidized_lightning_rod[facing=down,powered=false,waterlogged=true]: waxed_oxidized_lightning_rod[facing=down,powered=false,waterlogged=true] + oxidized_lightning_rod[facing=down,powered=true,waterlogged=false]: waxed_oxidized_lightning_rod[facing=down,powered=true,waterlogged=false] + oxidized_lightning_rod[facing=down,powered=true,waterlogged=true]: waxed_oxidized_lightning_rod[facing=down,powered=true,waterlogged=true] + oxidized_lightning_rod[facing=east,powered=false,waterlogged=false]: waxed_oxidized_lightning_rod[facing=east,powered=false,waterlogged=false] + oxidized_lightning_rod[facing=east,powered=false,waterlogged=true]: waxed_oxidized_lightning_rod[facing=east,powered=false,waterlogged=true] + oxidized_lightning_rod[facing=east,powered=true,waterlogged=false]: waxed_oxidized_lightning_rod[facing=east,powered=true,waterlogged=false] + oxidized_lightning_rod[facing=east,powered=true,waterlogged=true]: waxed_oxidized_lightning_rod[facing=east,powered=true,waterlogged=true] + oxidized_lightning_rod[facing=north,powered=false,waterlogged=false]: waxed_oxidized_lightning_rod[facing=north,powered=false,waterlogged=false] + oxidized_lightning_rod[facing=north,powered=false,waterlogged=true]: waxed_oxidized_lightning_rod[facing=north,powered=false,waterlogged=true] + oxidized_lightning_rod[facing=north,powered=true,waterlogged=false]: waxed_oxidized_lightning_rod[facing=north,powered=true,waterlogged=false] + oxidized_lightning_rod[facing=north,powered=true,waterlogged=true]: waxed_oxidized_lightning_rod[facing=north,powered=true,waterlogged=true] + oxidized_lightning_rod[facing=south,powered=false,waterlogged=false]: waxed_oxidized_lightning_rod[facing=south,powered=false,waterlogged=false] + oxidized_lightning_rod[facing=south,powered=false,waterlogged=true]: waxed_oxidized_lightning_rod[facing=south,powered=false,waterlogged=true] + oxidized_lightning_rod[facing=south,powered=true,waterlogged=false]: waxed_oxidized_lightning_rod[facing=south,powered=true,waterlogged=false] + oxidized_lightning_rod[facing=south,powered=true,waterlogged=true]: waxed_oxidized_lightning_rod[facing=south,powered=true,waterlogged=true] + oxidized_lightning_rod[facing=up,powered=false,waterlogged=false]: waxed_oxidized_lightning_rod[facing=up,powered=false,waterlogged=false] + oxidized_lightning_rod[facing=up,powered=false,waterlogged=true]: waxed_oxidized_lightning_rod[facing=up,powered=false,waterlogged=true] + oxidized_lightning_rod[facing=up,powered=true,waterlogged=false]: waxed_oxidized_lightning_rod[facing=up,powered=true,waterlogged=false] + oxidized_lightning_rod[facing=up,powered=true,waterlogged=true]: waxed_oxidized_lightning_rod[facing=up,powered=true,waterlogged=true] + oxidized_lightning_rod[facing=west,powered=false,waterlogged=false]: waxed_oxidized_lightning_rod[facing=west,powered=false,waterlogged=false] + oxidized_lightning_rod[facing=west,powered=false,waterlogged=true]: waxed_oxidized_lightning_rod[facing=west,powered=false,waterlogged=true] + oxidized_lightning_rod[facing=west,powered=true,waterlogged=false]: waxed_oxidized_lightning_rod[facing=west,powered=true,waterlogged=false] + oxidized_lightning_rod[facing=west,powered=true,waterlogged=true]: waxed_oxidized_lightning_rod[facing=west,powered=true,waterlogged=true] + weathered_lightning_rod[facing=down,powered=false,waterlogged=false]: waxed_weathered_lightning_rod[facing=down,powered=false,waterlogged=false] + weathered_lightning_rod[facing=down,powered=false,waterlogged=true]: waxed_weathered_lightning_rod[facing=down,powered=false,waterlogged=true] + weathered_lightning_rod[facing=down,powered=true,waterlogged=false]: waxed_weathered_lightning_rod[facing=down,powered=true,waterlogged=false] + weathered_lightning_rod[facing=down,powered=true,waterlogged=true]: waxed_weathered_lightning_rod[facing=down,powered=true,waterlogged=true] + weathered_lightning_rod[facing=east,powered=false,waterlogged=false]: waxed_weathered_lightning_rod[facing=east,powered=false,waterlogged=false] + weathered_lightning_rod[facing=east,powered=false,waterlogged=true]: waxed_weathered_lightning_rod[facing=east,powered=false,waterlogged=true] + weathered_lightning_rod[facing=east,powered=true,waterlogged=false]: waxed_weathered_lightning_rod[facing=east,powered=true,waterlogged=false] + weathered_lightning_rod[facing=east,powered=true,waterlogged=true]: waxed_weathered_lightning_rod[facing=east,powered=true,waterlogged=true] + weathered_lightning_rod[facing=north,powered=false,waterlogged=false]: waxed_weathered_lightning_rod[facing=north,powered=false,waterlogged=false] + weathered_lightning_rod[facing=north,powered=false,waterlogged=true]: waxed_weathered_lightning_rod[facing=north,powered=false,waterlogged=true] + weathered_lightning_rod[facing=north,powered=true,waterlogged=false]: waxed_weathered_lightning_rod[facing=north,powered=true,waterlogged=false] + weathered_lightning_rod[facing=north,powered=true,waterlogged=true]: waxed_weathered_lightning_rod[facing=north,powered=true,waterlogged=true] + weathered_lightning_rod[facing=south,powered=false,waterlogged=false]: waxed_weathered_lightning_rod[facing=south,powered=false,waterlogged=false] + weathered_lightning_rod[facing=south,powered=false,waterlogged=true]: waxed_weathered_lightning_rod[facing=south,powered=false,waterlogged=true] + weathered_lightning_rod[facing=south,powered=true,waterlogged=false]: waxed_weathered_lightning_rod[facing=south,powered=true,waterlogged=false] + weathered_lightning_rod[facing=south,powered=true,waterlogged=true]: waxed_weathered_lightning_rod[facing=south,powered=true,waterlogged=true] + weathered_lightning_rod[facing=up,powered=false,waterlogged=false]: waxed_weathered_lightning_rod[facing=up,powered=false,waterlogged=false] + weathered_lightning_rod[facing=up,powered=false,waterlogged=true]: waxed_weathered_lightning_rod[facing=up,powered=false,waterlogged=true] + weathered_lightning_rod[facing=up,powered=true,waterlogged=false]: waxed_weathered_lightning_rod[facing=up,powered=true,waterlogged=false] + weathered_lightning_rod[facing=up,powered=true,waterlogged=true]: waxed_weathered_lightning_rod[facing=up,powered=true,waterlogged=true] + weathered_lightning_rod[facing=west,powered=false,waterlogged=false]: waxed_weathered_lightning_rod[facing=west,powered=false,waterlogged=false] + weathered_lightning_rod[facing=west,powered=false,waterlogged=true]: waxed_weathered_lightning_rod[facing=west,powered=false,waterlogged=true] + weathered_lightning_rod[facing=west,powered=true,waterlogged=false]: waxed_weathered_lightning_rod[facing=west,powered=true,waterlogged=false] + weathered_lightning_rod[facing=west,powered=true,waterlogged=true]: waxed_weathered_lightning_rod[facing=west,powered=true,waterlogged=true] + + #### Copper Bars #### + $$>=1.21.9#copper_bars: + copper_bars[east=false,north=false,south=false,west=false,waterlogged=false]: waxed_copper_bars[east=false,north=false,south=false,west=false,waterlogged=false] + copper_bars[east=false,north=false,south=false,west=false,waterlogged=true]: waxed_copper_bars[east=false,north=false,south=false,west=false,waterlogged=true] + copper_bars[east=false,north=false,south=false,west=true,waterlogged=false]: waxed_copper_bars[east=false,north=false,south=false,west=true,waterlogged=false] + copper_bars[east=false,north=false,south=false,west=true,waterlogged=true]: waxed_copper_bars[east=false,north=false,south=false,west=true,waterlogged=true] + copper_bars[east=false,north=false,south=true,west=false,waterlogged=false]: waxed_copper_bars[east=false,north=false,south=true,west=false,waterlogged=false] + copper_bars[east=false,north=false,south=true,west=false,waterlogged=true]: waxed_copper_bars[east=false,north=false,south=true,west=false,waterlogged=true] + copper_bars[east=false,north=false,south=true,west=true,waterlogged=false]: waxed_copper_bars[east=false,north=false,south=true,west=true,waterlogged=false] + copper_bars[east=false,north=false,south=true,west=true,waterlogged=true]: waxed_copper_bars[east=false,north=false,south=true,west=true,waterlogged=true] + copper_bars[east=false,north=true,south=false,west=false,waterlogged=false]: waxed_copper_bars[east=false,north=true,south=false,west=false,waterlogged=false] + copper_bars[east=false,north=true,south=false,west=false,waterlogged=true]: waxed_copper_bars[east=false,north=true,south=false,west=false,waterlogged=true] + copper_bars[east=false,north=true,south=false,west=true,waterlogged=false]: waxed_copper_bars[east=false,north=true,south=false,west=true,waterlogged=false] + copper_bars[east=false,north=true,south=false,west=true,waterlogged=true]: waxed_copper_bars[east=false,north=true,south=false,west=true,waterlogged=true] + copper_bars[east=false,north=true,south=true,west=false,waterlogged=false]: waxed_copper_bars[east=false,north=true,south=true,west=false,waterlogged=false] + copper_bars[east=false,north=true,south=true,west=false,waterlogged=true]: waxed_copper_bars[east=false,north=true,south=true,west=false,waterlogged=true] + copper_bars[east=false,north=true,south=true,west=true,waterlogged=false]: waxed_copper_bars[east=false,north=true,south=true,west=true,waterlogged=false] + copper_bars[east=false,north=true,south=true,west=true,waterlogged=true]: waxed_copper_bars[east=false,north=true,south=true,west=true,waterlogged=true] + copper_bars[east=true,north=false,south=false,west=false,waterlogged=false]: waxed_copper_bars[east=true,north=false,south=false,west=false,waterlogged=false] + copper_bars[east=true,north=false,south=false,west=false,waterlogged=true]: waxed_copper_bars[east=true,north=false,south=false,west=false,waterlogged=true] + copper_bars[east=true,north=false,south=false,west=true,waterlogged=false]: waxed_copper_bars[east=true,north=false,south=false,west=true,waterlogged=false] + copper_bars[east=true,north=false,south=false,west=true,waterlogged=true]: waxed_copper_bars[east=true,north=false,south=false,west=true,waterlogged=true] + copper_bars[east=true,north=false,south=true,west=false,waterlogged=false]: waxed_copper_bars[east=true,north=false,south=true,west=false,waterlogged=false] + copper_bars[east=true,north=false,south=true,west=false,waterlogged=true]: waxed_copper_bars[east=true,north=false,south=true,west=false,waterlogged=true] + copper_bars[east=true,north=false,south=true,west=true,waterlogged=false]: waxed_copper_bars[east=true,north=false,south=true,west=true,waterlogged=false] + copper_bars[east=true,north=false,south=true,west=true,waterlogged=true]: waxed_copper_bars[east=true,north=false,south=true,west=true,waterlogged=true] + copper_bars[east=true,north=true,south=false,west=false,waterlogged=false]: waxed_copper_bars[east=true,north=true,south=false,west=false,waterlogged=false] + copper_bars[east=true,north=true,south=false,west=false,waterlogged=true]: waxed_copper_bars[east=true,north=true,south=false,west=false,waterlogged=true] + copper_bars[east=true,north=true,south=false,west=true,waterlogged=false]: waxed_copper_bars[east=true,north=true,south=false,west=true,waterlogged=false] + copper_bars[east=true,north=true,south=false,west=true,waterlogged=true]: waxed_copper_bars[east=true,north=true,south=false,west=true,waterlogged=true] + copper_bars[east=true,north=true,south=true,west=false,waterlogged=false]: waxed_copper_bars[east=true,north=true,south=true,west=false,waterlogged=false] + copper_bars[east=true,north=true,south=true,west=false,waterlogged=true]: waxed_copper_bars[east=true,north=true,south=true,west=false,waterlogged=true] + copper_bars[east=true,north=true,south=true,west=true,waterlogged=false]: waxed_copper_bars[east=true,north=true,south=true,west=true,waterlogged=false] + copper_bars[east=true,north=true,south=true,west=true,waterlogged=true]: waxed_copper_bars[east=true,north=true,south=true,west=true,waterlogged=true] + exposed_copper_bars[east=false,north=false,south=false,west=false,waterlogged=false]: waxed_exposed_copper_bars[east=false,north=false,south=false,west=false,waterlogged=false] + exposed_copper_bars[east=false,north=false,south=false,west=false,waterlogged=true]: waxed_exposed_copper_bars[east=false,north=false,south=false,west=false,waterlogged=true] + exposed_copper_bars[east=false,north=false,south=false,west=true,waterlogged=false]: waxed_exposed_copper_bars[east=false,north=false,south=false,west=true,waterlogged=false] + exposed_copper_bars[east=false,north=false,south=false,west=true,waterlogged=true]: waxed_exposed_copper_bars[east=false,north=false,south=false,west=true,waterlogged=true] + exposed_copper_bars[east=false,north=false,south=true,west=false,waterlogged=false]: waxed_exposed_copper_bars[east=false,north=false,south=true,west=false,waterlogged=false] + exposed_copper_bars[east=false,north=false,south=true,west=false,waterlogged=true]: waxed_exposed_copper_bars[east=false,north=false,south=true,west=false,waterlogged=true] + exposed_copper_bars[east=false,north=false,south=true,west=true,waterlogged=false]: waxed_exposed_copper_bars[east=false,north=false,south=true,west=true,waterlogged=false] + exposed_copper_bars[east=false,north=false,south=true,west=true,waterlogged=true]: waxed_exposed_copper_bars[east=false,north=false,south=true,west=true,waterlogged=true] + exposed_copper_bars[east=false,north=true,south=false,west=false,waterlogged=false]: waxed_exposed_copper_bars[east=false,north=true,south=false,west=false,waterlogged=false] + exposed_copper_bars[east=false,north=true,south=false,west=false,waterlogged=true]: waxed_exposed_copper_bars[east=false,north=true,south=false,west=false,waterlogged=true] + exposed_copper_bars[east=false,north=true,south=false,west=true,waterlogged=false]: waxed_exposed_copper_bars[east=false,north=true,south=false,west=true,waterlogged=false] + exposed_copper_bars[east=false,north=true,south=false,west=true,waterlogged=true]: waxed_exposed_copper_bars[east=false,north=true,south=false,west=true,waterlogged=true] + exposed_copper_bars[east=false,north=true,south=true,west=false,waterlogged=false]: waxed_exposed_copper_bars[east=false,north=true,south=true,west=false,waterlogged=false] + exposed_copper_bars[east=false,north=true,south=true,west=false,waterlogged=true]: waxed_exposed_copper_bars[east=false,north=true,south=true,west=false,waterlogged=true] + exposed_copper_bars[east=false,north=true,south=true,west=true,waterlogged=false]: waxed_exposed_copper_bars[east=false,north=true,south=true,west=true,waterlogged=false] + exposed_copper_bars[east=false,north=true,south=true,west=true,waterlogged=true]: waxed_exposed_copper_bars[east=false,north=true,south=true,west=true,waterlogged=true] + exposed_copper_bars[east=true,north=false,south=false,west=false,waterlogged=false]: waxed_exposed_copper_bars[east=true,north=false,south=false,west=false,waterlogged=false] + exposed_copper_bars[east=true,north=false,south=false,west=false,waterlogged=true]: waxed_exposed_copper_bars[east=true,north=false,south=false,west=false,waterlogged=true] + exposed_copper_bars[east=true,north=false,south=false,west=true,waterlogged=false]: waxed_exposed_copper_bars[east=true,north=false,south=false,west=true,waterlogged=false] + exposed_copper_bars[east=true,north=false,south=false,west=true,waterlogged=true]: waxed_exposed_copper_bars[east=true,north=false,south=false,west=true,waterlogged=true] + exposed_copper_bars[east=true,north=false,south=true,west=false,waterlogged=false]: waxed_exposed_copper_bars[east=true,north=false,south=true,west=false,waterlogged=false] + exposed_copper_bars[east=true,north=false,south=true,west=false,waterlogged=true]: waxed_exposed_copper_bars[east=true,north=false,south=true,west=false,waterlogged=true] + exposed_copper_bars[east=true,north=false,south=true,west=true,waterlogged=false]: waxed_exposed_copper_bars[east=true,north=false,south=true,west=true,waterlogged=false] + exposed_copper_bars[east=true,north=false,south=true,west=true,waterlogged=true]: waxed_exposed_copper_bars[east=true,north=false,south=true,west=true,waterlogged=true] + exposed_copper_bars[east=true,north=true,south=false,west=false,waterlogged=false]: waxed_exposed_copper_bars[east=true,north=true,south=false,west=false,waterlogged=false] + exposed_copper_bars[east=true,north=true,south=false,west=false,waterlogged=true]: waxed_exposed_copper_bars[east=true,north=true,south=false,west=false,waterlogged=true] + exposed_copper_bars[east=true,north=true,south=false,west=true,waterlogged=false]: waxed_exposed_copper_bars[east=true,north=true,south=false,west=true,waterlogged=false] + exposed_copper_bars[east=true,north=true,south=false,west=true,waterlogged=true]: waxed_exposed_copper_bars[east=true,north=true,south=false,west=true,waterlogged=true] + exposed_copper_bars[east=true,north=true,south=true,west=false,waterlogged=false]: waxed_exposed_copper_bars[east=true,north=true,south=true,west=false,waterlogged=false] + exposed_copper_bars[east=true,north=true,south=true,west=false,waterlogged=true]: waxed_exposed_copper_bars[east=true,north=true,south=true,west=false,waterlogged=true] + exposed_copper_bars[east=true,north=true,south=true,west=true,waterlogged=false]: waxed_exposed_copper_bars[east=true,north=true,south=true,west=true,waterlogged=false] + exposed_copper_bars[east=true,north=true,south=true,west=true,waterlogged=true]: waxed_exposed_copper_bars[east=true,north=true,south=true,west=true,waterlogged=true] + oxidized_copper_bars[east=false,north=false,south=false,west=false,waterlogged=false]: waxed_oxidized_copper_bars[east=false,north=false,south=false,west=false,waterlogged=false] + oxidized_copper_bars[east=false,north=false,south=false,west=false,waterlogged=true]: waxed_oxidized_copper_bars[east=false,north=false,south=false,west=false,waterlogged=true] + oxidized_copper_bars[east=false,north=false,south=false,west=true,waterlogged=false]: waxed_oxidized_copper_bars[east=false,north=false,south=false,west=true,waterlogged=false] + oxidized_copper_bars[east=false,north=false,south=false,west=true,waterlogged=true]: waxed_oxidized_copper_bars[east=false,north=false,south=false,west=true,waterlogged=true] + oxidized_copper_bars[east=false,north=false,south=true,west=false,waterlogged=false]: waxed_oxidized_copper_bars[east=false,north=false,south=true,west=false,waterlogged=false] + oxidized_copper_bars[east=false,north=false,south=true,west=false,waterlogged=true]: waxed_oxidized_copper_bars[east=false,north=false,south=true,west=false,waterlogged=true] + oxidized_copper_bars[east=false,north=false,south=true,west=true,waterlogged=false]: waxed_oxidized_copper_bars[east=false,north=false,south=true,west=true,waterlogged=false] + oxidized_copper_bars[east=false,north=false,south=true,west=true,waterlogged=true]: waxed_oxidized_copper_bars[east=false,north=false,south=true,west=true,waterlogged=true] + oxidized_copper_bars[east=false,north=true,south=false,west=false,waterlogged=false]: waxed_oxidized_copper_bars[east=false,north=true,south=false,west=false,waterlogged=false] + oxidized_copper_bars[east=false,north=true,south=false,west=false,waterlogged=true]: waxed_oxidized_copper_bars[east=false,north=true,south=false,west=false,waterlogged=true] + oxidized_copper_bars[east=false,north=true,south=false,west=true,waterlogged=false]: waxed_oxidized_copper_bars[east=false,north=true,south=false,west=true,waterlogged=false] + oxidized_copper_bars[east=false,north=true,south=false,west=true,waterlogged=true]: waxed_oxidized_copper_bars[east=false,north=true,south=false,west=true,waterlogged=true] + oxidized_copper_bars[east=false,north=true,south=true,west=false,waterlogged=false]: waxed_oxidized_copper_bars[east=false,north=true,south=true,west=false,waterlogged=false] + oxidized_copper_bars[east=false,north=true,south=true,west=false,waterlogged=true]: waxed_oxidized_copper_bars[east=false,north=true,south=true,west=false,waterlogged=true] + oxidized_copper_bars[east=false,north=true,south=true,west=true,waterlogged=false]: waxed_oxidized_copper_bars[east=false,north=true,south=true,west=true,waterlogged=false] + oxidized_copper_bars[east=false,north=true,south=true,west=true,waterlogged=true]: waxed_oxidized_copper_bars[east=false,north=true,south=true,west=true,waterlogged=true] + oxidized_copper_bars[east=true,north=false,south=false,west=false,waterlogged=false]: waxed_oxidized_copper_bars[east=true,north=false,south=false,west=false,waterlogged=false] + oxidized_copper_bars[east=true,north=false,south=false,west=false,waterlogged=true]: waxed_oxidized_copper_bars[east=true,north=false,south=false,west=false,waterlogged=true] + oxidized_copper_bars[east=true,north=false,south=false,west=true,waterlogged=false]: waxed_oxidized_copper_bars[east=true,north=false,south=false,west=true,waterlogged=false] + oxidized_copper_bars[east=true,north=false,south=false,west=true,waterlogged=true]: waxed_oxidized_copper_bars[east=true,north=false,south=false,west=true,waterlogged=true] + oxidized_copper_bars[east=true,north=false,south=true,west=false,waterlogged=false]: waxed_oxidized_copper_bars[east=true,north=false,south=true,west=false,waterlogged=false] + oxidized_copper_bars[east=true,north=false,south=true,west=false,waterlogged=true]: waxed_oxidized_copper_bars[east=true,north=false,south=true,west=false,waterlogged=true] + oxidized_copper_bars[east=true,north=false,south=true,west=true,waterlogged=false]: waxed_oxidized_copper_bars[east=true,north=false,south=true,west=true,waterlogged=false] + oxidized_copper_bars[east=true,north=false,south=true,west=true,waterlogged=true]: waxed_oxidized_copper_bars[east=true,north=false,south=true,west=true,waterlogged=true] + oxidized_copper_bars[east=true,north=true,south=false,west=false,waterlogged=false]: waxed_oxidized_copper_bars[east=true,north=true,south=false,west=false,waterlogged=false] + oxidized_copper_bars[east=true,north=true,south=false,west=false,waterlogged=true]: waxed_oxidized_copper_bars[east=true,north=true,south=false,west=false,waterlogged=true] + oxidized_copper_bars[east=true,north=true,south=false,west=true,waterlogged=false]: waxed_oxidized_copper_bars[east=true,north=true,south=false,west=true,waterlogged=false] + oxidized_copper_bars[east=true,north=true,south=false,west=true,waterlogged=true]: waxed_oxidized_copper_bars[east=true,north=true,south=false,west=true,waterlogged=true] + oxidized_copper_bars[east=true,north=true,south=true,west=false,waterlogged=false]: waxed_oxidized_copper_bars[east=true,north=true,south=true,west=false,waterlogged=false] + oxidized_copper_bars[east=true,north=true,south=true,west=false,waterlogged=true]: waxed_oxidized_copper_bars[east=true,north=true,south=true,west=false,waterlogged=true] + oxidized_copper_bars[east=true,north=true,south=true,west=true,waterlogged=false]: waxed_oxidized_copper_bars[east=true,north=true,south=true,west=true,waterlogged=false] + oxidized_copper_bars[east=true,north=true,south=true,west=true,waterlogged=true]: waxed_oxidized_copper_bars[east=true,north=true,south=true,west=true,waterlogged=true] + weathered_copper_bars[east=false,north=false,south=false,west=false,waterlogged=false]: waxed_weathered_copper_bars[east=false,north=false,south=false,west=false,waterlogged=false] + weathered_copper_bars[east=false,north=false,south=false,west=false,waterlogged=true]: waxed_weathered_copper_bars[east=false,north=false,south=false,west=false,waterlogged=true] + weathered_copper_bars[east=false,north=false,south=false,west=true,waterlogged=false]: waxed_weathered_copper_bars[east=false,north=false,south=false,west=true,waterlogged=false] + weathered_copper_bars[east=false,north=false,south=false,west=true,waterlogged=true]: waxed_weathered_copper_bars[east=false,north=false,south=false,west=true,waterlogged=true] + weathered_copper_bars[east=false,north=false,south=true,west=false,waterlogged=false]: waxed_weathered_copper_bars[east=false,north=false,south=true,west=false,waterlogged=false] + weathered_copper_bars[east=false,north=false,south=true,west=false,waterlogged=true]: waxed_weathered_copper_bars[east=false,north=false,south=true,west=false,waterlogged=true] + weathered_copper_bars[east=false,north=false,south=true,west=true,waterlogged=false]: waxed_weathered_copper_bars[east=false,north=false,south=true,west=true,waterlogged=false] + weathered_copper_bars[east=false,north=false,south=true,west=true,waterlogged=true]: waxed_weathered_copper_bars[east=false,north=false,south=true,west=true,waterlogged=true] + weathered_copper_bars[east=false,north=true,south=false,west=false,waterlogged=false]: waxed_weathered_copper_bars[east=false,north=true,south=false,west=false,waterlogged=false] + weathered_copper_bars[east=false,north=true,south=false,west=false,waterlogged=true]: waxed_weathered_copper_bars[east=false,north=true,south=false,west=false,waterlogged=true] + weathered_copper_bars[east=false,north=true,south=false,west=true,waterlogged=false]: waxed_weathered_copper_bars[east=false,north=true,south=false,west=true,waterlogged=false] + weathered_copper_bars[east=false,north=true,south=false,west=true,waterlogged=true]: waxed_weathered_copper_bars[east=false,north=true,south=false,west=true,waterlogged=true] + weathered_copper_bars[east=false,north=true,south=true,west=false,waterlogged=false]: waxed_weathered_copper_bars[east=false,north=true,south=true,west=false,waterlogged=false] + weathered_copper_bars[east=false,north=true,south=true,west=false,waterlogged=true]: waxed_weathered_copper_bars[east=false,north=true,south=true,west=false,waterlogged=true] + weathered_copper_bars[east=false,north=true,south=true,west=true,waterlogged=false]: waxed_weathered_copper_bars[east=false,north=true,south=true,west=true,waterlogged=false] + weathered_copper_bars[east=false,north=true,south=true,west=true,waterlogged=true]: waxed_weathered_copper_bars[east=false,north=true,south=true,west=true,waterlogged=true] + weathered_copper_bars[east=true,north=false,south=false,west=false,waterlogged=false]: waxed_weathered_copper_bars[east=true,north=false,south=false,west=false,waterlogged=false] + weathered_copper_bars[east=true,north=false,south=false,west=false,waterlogged=true]: waxed_weathered_copper_bars[east=true,north=false,south=false,west=false,waterlogged=true] + weathered_copper_bars[east=true,north=false,south=false,west=true,waterlogged=false]: waxed_weathered_copper_bars[east=true,north=false,south=false,west=true,waterlogged=false] + weathered_copper_bars[east=true,north=false,south=false,west=true,waterlogged=true]: waxed_weathered_copper_bars[east=true,north=false,south=false,west=true,waterlogged=true] + weathered_copper_bars[east=true,north=false,south=true,west=false,waterlogged=false]: waxed_weathered_copper_bars[east=true,north=false,south=true,west=false,waterlogged=false] + weathered_copper_bars[east=true,north=false,south=true,west=false,waterlogged=true]: waxed_weathered_copper_bars[east=true,north=false,south=true,west=false,waterlogged=true] + weathered_copper_bars[east=true,north=false,south=true,west=true,waterlogged=false]: waxed_weathered_copper_bars[east=true,north=false,south=true,west=true,waterlogged=false] + weathered_copper_bars[east=true,north=false,south=true,west=true,waterlogged=true]: waxed_weathered_copper_bars[east=true,north=false,south=true,west=true,waterlogged=true] + weathered_copper_bars[east=true,north=true,south=false,west=false,waterlogged=false]: waxed_weathered_copper_bars[east=true,north=true,south=false,west=false,waterlogged=false] + weathered_copper_bars[east=true,north=true,south=false,west=false,waterlogged=true]: waxed_weathered_copper_bars[east=true,north=true,south=false,west=false,waterlogged=true] + weathered_copper_bars[east=true,north=true,south=false,west=true,waterlogged=false]: waxed_weathered_copper_bars[east=true,north=true,south=false,west=true,waterlogged=false] + weathered_copper_bars[east=true,north=true,south=false,west=true,waterlogged=true]: waxed_weathered_copper_bars[east=true,north=true,south=false,west=true,waterlogged=true] + weathered_copper_bars[east=true,north=true,south=true,west=false,waterlogged=false]: waxed_weathered_copper_bars[east=true,north=true,south=true,west=false,waterlogged=false] + weathered_copper_bars[east=true,north=true,south=true,west=false,waterlogged=true]: waxed_weathered_copper_bars[east=true,north=true,south=true,west=false,waterlogged=true] + weathered_copper_bars[east=true,north=true,south=true,west=true,waterlogged=false]: waxed_weathered_copper_bars[east=true,north=true,south=true,west=true,waterlogged=false] + weathered_copper_bars[east=true,north=true,south=true,west=true,waterlogged=true]: waxed_weathered_copper_bars[east=true,north=true,south=true,west=true,waterlogged=true] + + #### Copper Chain #### + $$>=1.21.9#copper_chain: + copper_chain[axis=x,waterlogged=false]: waxed_copper_chain[axis=x,waterlogged=false] + copper_chain[axis=x,waterlogged=true]: waxed_copper_chain[axis=x,waterlogged=true] + copper_chain[axis=y,waterlogged=false]: waxed_copper_chain[axis=y,waterlogged=false] + copper_chain[axis=y,waterlogged=true]: waxed_copper_chain[axis=y,waterlogged=true] + copper_chain[axis=z,waterlogged=false]: waxed_copper_chain[axis=z,waterlogged=false] + copper_chain[axis=z,waterlogged=true]: waxed_copper_chain[axis=z,waterlogged=true] + exposed_copper_chain[axis=x,waterlogged=false]: waxed_exposed_copper_chain[axis=x,waterlogged=false] + exposed_copper_chain[axis=x,waterlogged=true]: waxed_exposed_copper_chain[axis=x,waterlogged=true] + exposed_copper_chain[axis=y,waterlogged=false]: waxed_exposed_copper_chain[axis=y,waterlogged=false] + exposed_copper_chain[axis=y,waterlogged=true]: waxed_exposed_copper_chain[axis=y,waterlogged=true] + exposed_copper_chain[axis=z,waterlogged=false]: waxed_exposed_copper_chain[axis=z,waterlogged=false] + exposed_copper_chain[axis=z,waterlogged=true]: waxed_exposed_copper_chain[axis=z,waterlogged=true] + oxidized_copper_chain[axis=x,waterlogged=false]: waxed_oxidized_copper_chain[axis=x,waterlogged=false] + oxidized_copper_chain[axis=x,waterlogged=true]: waxed_oxidized_copper_chain[axis=x,waterlogged=true] + oxidized_copper_chain[axis=y,waterlogged=false]: waxed_oxidized_copper_chain[axis=y,waterlogged=false] + oxidized_copper_chain[axis=y,waterlogged=true]: waxed_oxidized_copper_chain[axis=y,waterlogged=true] + oxidized_copper_chain[axis=z,waterlogged=false]: waxed_oxidized_copper_chain[axis=z,waterlogged=false] + oxidized_copper_chain[axis=z,waterlogged=true]: waxed_oxidized_copper_chain[axis=z,waterlogged=true] + weathered_copper_chain[axis=x,waterlogged=false]: waxed_weathered_copper_chain[axis=x,waterlogged=false] + weathered_copper_chain[axis=x,waterlogged=true]: waxed_weathered_copper_chain[axis=x,waterlogged=true] + weathered_copper_chain[axis=y,waterlogged=false]: waxed_weathered_copper_chain[axis=y,waterlogged=false] + weathered_copper_chain[axis=y,waterlogged=true]: waxed_weathered_copper_chain[axis=y,waterlogged=true] + weathered_copper_chain[axis=z,waterlogged=false]: waxed_weathered_copper_chain[axis=z,waterlogged=false] + weathered_copper_chain[axis=z,waterlogged=true]: waxed_weathered_copper_chain[axis=z,waterlogged=true] + + #### Copper Lantern #### + $$>=1.21.9#copper_lantern: + copper_lantern[hanging=false,waterlogged=false]: waxed_copper_lantern[hanging=false,waterlogged=false] + copper_lantern[hanging=false,waterlogged=true]: waxed_copper_lantern[hanging=false,waterlogged=true] + copper_lantern[hanging=true,waterlogged=false]: waxed_copper_lantern[hanging=true,waterlogged=false] + copper_lantern[hanging=true,waterlogged=true]: waxed_copper_lantern[hanging=true,waterlogged=true] + exposed_copper_lantern[hanging=false,waterlogged=false]: waxed_exposed_copper_lantern[hanging=false,waterlogged=false] + exposed_copper_lantern[hanging=false,waterlogged=true]: waxed_exposed_copper_lantern[hanging=false,waterlogged=true] + exposed_copper_lantern[hanging=true,waterlogged=false]: waxed_exposed_copper_lantern[hanging=true,waterlogged=false] + exposed_copper_lantern[hanging=true,waterlogged=true]: waxed_exposed_copper_lantern[hanging=true,waterlogged=true] + oxidized_copper_lantern[hanging=false,waterlogged=false]: waxed_oxidized_copper_lantern[hanging=false,waterlogged=false] + oxidized_copper_lantern[hanging=false,waterlogged=true]: waxed_oxidized_copper_lantern[hanging=false,waterlogged=true] + oxidized_copper_lantern[hanging=true,waterlogged=false]: waxed_oxidized_copper_lantern[hanging=true,waterlogged=false] + oxidized_copper_lantern[hanging=true,waterlogged=true]: waxed_oxidized_copper_lantern[hanging=true,waterlogged=true] + weathered_copper_lantern[hanging=false,waterlogged=false]: waxed_weathered_copper_lantern[hanging=false,waterlogged=false] + weathered_copper_lantern[hanging=false,waterlogged=true]: waxed_weathered_copper_lantern[hanging=false,waterlogged=true] + weathered_copper_lantern[hanging=true,waterlogged=false]: waxed_weathered_copper_lantern[hanging=true,waterlogged=false] + weathered_copper_lantern[hanging=true,waterlogged=true]: waxed_weathered_copper_lantern[hanging=true,waterlogged=true] + #### Corals #### # Coral blocks are ideal for creating water blocks or wall-mounted blocks. But you have to sacrifice its dry appearance. # dead_brain_coral[waterlogged=false]: brain_coral[waterlogged=false] From d4b7d0a332ef20bed9f833bffb80adc0a6f5a25e Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 24 Nov 2025 20:58:56 +0800 Subject: [PATCH 003/135] =?UTF-8?q?=E5=88=87=E5=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/context/event/EventFunctions.java | 3 +- .../context/function/CommonFunctions.java | 3 +- .../{ExpFunction.java => SetExpFunction.java} | 14 ++--- .../context/function/SetLevelFunction.java | 57 +++++++++++++++++++ 4 files changed, 66 insertions(+), 11 deletions(-) rename core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/{ExpFunction.java => SetExpFunction.java} (70%) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetLevelFunction.java 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 15a23bcd1..1b94eb8c6 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 @@ -55,7 +55,8 @@ public class EventFunctions { register(CommonFunctions.WHEN, new WhenFunction.FactoryImpl<>(EventConditions::fromMap, EventFunctions::fromMap)); register(CommonFunctions.DAMAGE_ITEM, new DamageItemFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.CYCLE_BLOCK_PROPERTY, new CycleBlockPropertyFunction.FactoryImpl<>(EventConditions::fromMap)); - register(CommonFunctions.EXP, new ExpFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.SET_EXP, new SetExpFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.SET_LEVEL, new SetLevelFunction.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/CommonFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java index a8e0ee201..a49c1f6e9 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 @@ -46,5 +46,6 @@ public final class CommonFunctions { public static final Key DUMMY = Key.of("craftengine:dummy"); public static final Key DAMAGE_ITEM = Key.of("craftengine:damage_item"); public static final Key CYCLE_BLOCK_PROPERTY = Key.of("craftengine:cycle_block_property"); - public static final Key EXP = Key.of("craftengine:exp"); + public static final Key SET_EXP = Key.of("craftengine:set_exp"); + public static final Key SET_LEVEL = Key.of("craftengine:set_level"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ExpFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetExpFunction.java similarity index 70% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ExpFunction.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetExpFunction.java index 663d4618f..b812016d2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ExpFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetExpFunction.java @@ -14,12 +14,12 @@ import java.util.List; import java.util.Map; import java.util.function.BiConsumer; -public class ExpFunction extends AbstractConditionalFunction { +public class SetExpFunction extends AbstractConditionalFunction { private final PlayerSelector selector; private final NumberProvider count; private final BiConsumer operation; - public ExpFunction(List> predicates, PlayerSelector selector, NumberProvider count, BiConsumer operation) { + public SetExpFunction(List> predicates, PlayerSelector selector, NumberProvider count, BiConsumer operation) { super(predicates); this.selector = selector; this.count = count; @@ -35,18 +35,16 @@ public class ExpFunction extends AbstractConditionalFunctio @Override public Key type() { - return CommonFunctions.EXP; + return CommonFunctions.SET_EXP; } public static class FactoryImpl extends AbstractFactory { private static final BiConsumer ADD_POINTS = Player::giveExperiencePoints; - private static final BiConsumer ADD_LEVELS = Player::giveExperienceLevels; private static final BiConsumer SET_POINTS = (player, experience) -> { if (experience < player.getXpNeededForNextLevel()) { player.setExperiencePoints(experience); } }; - private static final BiConsumer SET_LEVELS = Player::setExperienceLevels; public FactoryImpl(java.util.function.Function, Condition> factory) { super(factory); @@ -56,10 +54,8 @@ public class ExpFunction extends AbstractConditionalFunctio public Function create(Map arguments) { PlayerSelector selector = PlayerSelectors.fromObject(arguments.getOrDefault("target", "self"), conditionFactory()); Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("count"), "warning.config.function.exp.missing_count"); - boolean set = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("set", false), "set"); - boolean level = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("level", false), "level"); - BiConsumer operation = level ? (set ? SET_LEVELS : ADD_LEVELS) : (set ? SET_POINTS : ADD_POINTS); - return new ExpFunction<>(getPredicates(arguments), selector, NumberProviders.fromObject(value), operation); + boolean add = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("add", false), "add"); + return new SetExpFunction<>(getPredicates(arguments), selector, NumberProviders.fromObject(value), add ? ADD_POINTS : SET_POINTS); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetLevelFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetLevelFunction.java new file mode 100644 index 000000000..c553bc283 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetLevelFunction.java @@ -0,0 +1,57 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +public class SetLevelFunction extends AbstractConditionalFunction { + private final PlayerSelector selector; + private final NumberProvider count; + private final BiConsumer operation; + + public SetLevelFunction(List> predicates, PlayerSelector selector, NumberProvider count, BiConsumer operation) { + super(predicates); + this.selector = selector; + this.count = count; + this.operation = operation; + } + + @Override + protected void runInternal(CTX ctx) { + for (Player player : this.selector.get(ctx)) { + this.operation.accept(player, this.count.getInt(ctx)); + } + } + + @Override + public Key type() { + return CommonFunctions.SET_LEVEL; + } + + public static class FactoryImpl extends AbstractFactory { + private static final BiConsumer ADD_LEVELS = Player::giveExperienceLevels; + private static final BiConsumer SET_LEVELS = Player::setExperienceLevels; + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + PlayerSelector selector = PlayerSelectors.fromObject(arguments.getOrDefault("target", "self"), conditionFactory()); + Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("count"), "warning.config.function.exp.missing_count"); + boolean add = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("add", false), "add"); + return new SetLevelFunction<>(getPredicates(arguments), selector, NumberProviders.fromObject(value), add ? ADD_LEVELS : SET_LEVELS); + } + } +} From 13acd6f86e00d5c431d453fe0e0312b666032009 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 24 Nov 2025 21:02:33 +0800 Subject: [PATCH 004/135] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/translations/en.yml | 3 ++- common-files/src/main/resources/translations/zh_cn.yml | 3 ++- .../core/plugin/context/function/SetExpFunction.java | 2 +- .../core/plugin/context/function/SetLevelFunction.java | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index e8fbebaaf..8b92bd343 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -492,7 +492,8 @@ warning.config.function.if_else.missing_rules: "Issue found in file Issue found in file - The config '' is missing the required 'properties' argument for 'update_block_property' function." warning.config.function.transform_block.missing_block: "Issue found in file - The config '' is missing the required 'block' argument for 'transform_block' function." warning.config.function.cycle_block_property.missing_property: "Issue found in file - The config '' is missing the required 'property' argument for 'cycle_block_property' function." -warning.config.function.exp.missing_count: "Issue found in file - The config '' is missing the required 'count' argument for 'exp' function." +warning.config.function.set_exp.missing_count: "Issue found in file - The config '' is missing the required 'count' argument for 'set_exp' function." +warning.config.function.set_level.missing_count: "Issue found in file - The config '' is missing the required 'count' argument for 'set_level' function." 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 ''." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 2264fc523..da06eeff2 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -491,8 +491,9 @@ warning.config.function.when.missing_source: "在文件 发现 warning.config.function.if_else.missing_rules: "在文件 发现问题 - 配置项 '' 缺少 'if_else' 函数所需的 'rules' 参数" warning.config.function.update_block_property.missing_properties: "在文件 发现问题 - 配置项 '' 缺少 'update_block_property' 函数所需的 'properties' 参数" warning.config.function.transform_block.missing_block: "在文件 发现问题 - 配置项 '' 缺少 'transform_block' 函数所需的 'block' 参数" -warning.config.function.exp.missing_count: "在文件 发现问题 - 配置项 '' 缺少 'exp' 函数所需的 'count' 参数" warning.config.function.cycle_block_property.missing_property: "在文件 发现问题 - 配置项 '' 缺少 'cycle_block_property' 函数所需的 'property' 参数" +warning.config.function.set_exp.missing_count: "在文件 发现问题 - 配置项 '' 缺少 'set_exp' 函数所需的 'count' 参数" +warning.config.function.set_level.missing_count: "在文件 发现问题 - 配置项 '' 缺少 'set_level' 函数所需的 'count' 参数" warning.config.selector.missing_type: "在文件 发现问题 - 配置项 '' 缺少选择器必需的 'type' 参数" warning.config.selector.invalid_type: "在文件 发现问题 - 配置项 '' 使用了无效的选择器类型 ''" warning.config.selector.invalid_target: "在文件 发现问题 - 配置项 '' 使用了无效的选择器目标 ''" diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetExpFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetExpFunction.java index b812016d2..5e07b336a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetExpFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetExpFunction.java @@ -53,7 +53,7 @@ public class SetExpFunction extends AbstractConditionalFunc @Override public Function create(Map arguments) { PlayerSelector selector = PlayerSelectors.fromObject(arguments.getOrDefault("target", "self"), conditionFactory()); - Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("count"), "warning.config.function.exp.missing_count"); + Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("count"), "warning.config.function.set_exp.missing_count"); boolean add = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("add", false), "add"); return new SetExpFunction<>(getPredicates(arguments), selector, NumberProviders.fromObject(value), add ? ADD_POINTS : SET_POINTS); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetLevelFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetLevelFunction.java index c553bc283..e95194529 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetLevelFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetLevelFunction.java @@ -49,7 +49,7 @@ public class SetLevelFunction extends AbstractConditionalFu @Override public Function create(Map arguments) { PlayerSelector selector = PlayerSelectors.fromObject(arguments.getOrDefault("target", "self"), conditionFactory()); - Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("count"), "warning.config.function.exp.missing_count"); + Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("count"), "warning.config.function.set_level.missing_count"); boolean add = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("add", false), "add"); return new SetLevelFunction<>(getPredicates(arguments), selector, NumberProviders.fromObject(value), add ? ADD_LEVELS : SET_LEVELS); } From 23e416ae6a2baec11ca7f7ffa8bcef0300d26248 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Tue, 25 Nov 2025 20:36:07 +0800 Subject: [PATCH 005/135] =?UTF-8?q?=E4=BC=98=E5=8C=96key=20equals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/net/momirealms/craftengine/core/util/Key.java | 6 ++---- gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Key.java b/core/src/main/java/net/momirealms/craftengine/core/util/Key.java index a09969dd3..f580210ac 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Key.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Key.java @@ -39,9 +39,7 @@ public record Key(String namespace, String value) { @Override public int hashCode() { - int result = this.namespace.hashCode(); - result = 31 * result + this.value.hashCode(); - return result; + return 31 * this.namespace.hashCode() + this.value.hashCode(); } @Override @@ -54,7 +52,7 @@ public record Key(String namespace, String value) { } if (!(obj instanceof Key key)) return false; // 先比value命中率高 - return this.value.equals(key.value()) && this.namespace.equals(key.namespace()); + return this.value.equals(key.value) && this.namespace.equals(key.namespace); } @Override diff --git a/gradle.properties b/gradle.properties index 5ad74eca6..a3efe51fd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings -project_version=0.0.65.12.2 +project_version=0.0.65.13 config_version=58 lang_version=40 project_group=net.momirealms @@ -38,7 +38,7 @@ zstd_version=1.5.7-6 commons_io_version=2.21.0 commons_lang3_version=3.20.0 sparrow_nbt_version=0.10.6 -sparrow_util_version=0.65 +sparrow_util_version=0.66 fastutil_version=8.5.18 netty_version=4.1.128.Final joml_version=1.10.8 From 759bbb2271022eefc874f4a3bcdc572614c2690f Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Tue, 25 Nov 2025 20:54:21 +0800 Subject: [PATCH 006/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=8F=82=E6=95=B0=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/plugin/config/template/TemplateArguments.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java index 82fd38d45..e45c45cb6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java @@ -32,11 +32,11 @@ public class TemplateArguments { } public static TemplateArgument fromMap(Map map) { - String type = (String) map.get("type"); - if (type == null) { + Object type = map.get("type"); + if (!(type instanceof String type0) || map.containsKey("__skip_template_argument__")) { return new MapTemplateArgument(map); } else { - Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); + Key key = Key.withDefaultNamespace(type0, Key.DEFAULT_NAMESPACE); TemplateArgumentFactory factory = BuiltInRegistries.TEMPLATE_ARGUMENT_FACTORY.getValue(key); if (factory == null) { throw new IllegalArgumentException("Unknown argument type: " + type); From d897a5587c1ba4ee87b5ceec0c47e85b24b848d3 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Tue, 25 Nov 2025 21:52:34 +0800 Subject: [PATCH 007/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=9B=BE=E8=85=BE?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/user/BukkitServerPlayer.java | 6 + .../craftengine/bukkit/util/PlayerUtils.java | 2 +- .../core/entity/player/Player.java | 2 + .../plugin/context/event/EventFunctions.java | 1 + .../context/function/CommonFunctions.java | 1 + .../function/PlayTotemAnimationFunction.java | 103 ++++++++++++++++++ 6 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlayTotemAnimationFunction.java 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 5384d3146..434f60d70 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 @@ -34,6 +34,7 @@ import net.momirealms.craftengine.core.plugin.context.CooldownData; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.network.ConnectionState; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; +import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.sound.SoundSource; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.*; @@ -1278,4 +1279,9 @@ public class BukkitServerPlayer extends Player { public void setExperienceLevels(int level) { platformPlayer().setLevel(level); } + + @Override + public void sendTotemAnimation(Item totem, @Nullable SoundData sound, boolean removeSound) { + PlayerUtils.sendTotemAnimation(this, totem, sound, removeSound); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java index 82b9b8113..c20d195b9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java @@ -57,7 +57,7 @@ public final class PlayerUtils { } } - public static void sendTotemAnimation(Player player, Item totem, @Nullable SoundData sound, boolean removeSound) { + public static void sendTotemAnimation(Player player, Item totem, @Nullable SoundData sound, boolean removeSound) { List packets = new ArrayList<>(); try { Object totemItem = totem.getLiteralObject(); 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 507def94d..260b935f5 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 @@ -194,6 +194,8 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void setExperienceLevels(int level); + public abstract void sendTotemAnimation(Item totem, @Nullable SoundData sound, boolean removeSound); + @Override public void remove() { } 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 1b94eb8c6..fbc8a4e69 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 @@ -57,6 +57,7 @@ public class EventFunctions { register(CommonFunctions.CYCLE_BLOCK_PROPERTY, new CycleBlockPropertyFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.SET_EXP, new SetExpFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.SET_LEVEL, new SetLevelFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.PLAY_TOTEM_ANIMATION, new PlayTotemAnimationFunction.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/CommonFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java index a49c1f6e9..937534105 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 @@ -48,4 +48,5 @@ public final class CommonFunctions { public static final Key CYCLE_BLOCK_PROPERTY = Key.of("craftengine:cycle_block_property"); public static final Key SET_EXP = Key.of("craftengine:set_exp"); public static final Key SET_LEVEL = Key.of("craftengine:set_level"); + public static final Key PLAY_TOTEM_ANIMATION = Key.of("craftengine:play_totem_animation"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlayTotemAnimationFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlayTotemAnimationFunction.java new file mode 100644 index 000000000..4d167a9d5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlayTotemAnimationFunction.java @@ -0,0 +1,103 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.CustomItem; +import net.momirealms.craftengine.core.item.DataComponentKeys; +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.selector.PlayerSelector; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; +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 org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class PlayTotemAnimationFunction extends AbstractConditionalFunction { + private final PlayerSelector selector; + private final Key item; + @Nullable + private final Key sound; + private final float volume; + private final float pitch; + private final float minVolume; + private final float minPitch; + private final boolean noSound; + + public PlayTotemAnimationFunction( + List> predicates, + PlayerSelector selector, + Key item, + @Nullable Key sound, + float volume, + float pitch, + float minVolume, + float minPitch, + boolean noSound + ) { + super(predicates); + this.selector = selector; + this.item = item; + this.sound = sound; + this.volume = volume; + this.pitch = pitch; + this.minVolume = minVolume; + this.minPitch = minPitch; + this.noSound = noSound; + } + + @Override + protected void runInternal(CTX ctx) { + CustomItem customItem = CraftEngine.instance().itemManager().getCustomItem(this.item).orElse(null); + if (customItem == null) { + return; + } + SoundData soundData = null; + if (this.sound != null) { + soundData = SoundData.of( + this.sound, + SoundData.SoundValue.ranged(this.minVolume, this.volume), + SoundData.SoundValue.ranged(this.minPitch, this.pitch) + ); + } + for (Player player : this.selector.get(ctx)) { + Item buildItem = customItem.buildItem(player); + if (VersionHelper.isOrAbove1_21_2()) { + buildItem.setJavaComponent(DataComponentKeys.DEATH_PROTECTION, Map.of()); + } + player.sendTotemAnimation(buildItem, soundData, this.noSound); + } + } + + @Override + public Key type() { + return CommonFunctions.PLAY_TOTEM_ANIMATION; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + PlayerSelector selector = PlayerSelectors.fromObject(arguments.getOrDefault("target", "self"), conditionFactory()); + Key item = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.function.play_totem_animation.missing_item")); + @Nullable Key sound = Optional.ofNullable(arguments.get("sound")).map(String::valueOf).map(Key::of).orElse(null); + float volume = Math.max(ResourceConfigUtils.getAsFloat(arguments.getOrDefault("volume", 1f), "volume"), 0f); + float pitch = MiscUtils.clamp(ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 1f), "pitch"), 0f, 2f); + float minVolume = Math.max(ResourceConfigUtils.getAsFloat(arguments.getOrDefault("min-volume", 1f), "min-volume"), 0f); + float minPitch = MiscUtils.clamp(ResourceConfigUtils.getAsFloat(arguments.getOrDefault("min-pitch", 1f), "min-pitch"), 0f, 2f); + boolean noSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("no-sound", false), "no-sound"); + return new PlayTotemAnimationFunction<>(getPredicates(arguments), selector, item, sound, volume, pitch, minVolume, minPitch, noSound); + } + } +} From 276c49ea3f73e46c41d043d22658c1f11aa770fb Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Tue, 25 Nov 2025 22:31:53 +0800 Subject: [PATCH 008/135] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../function/PlayTotemAnimationFunction.java | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlayTotemAnimationFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlayTotemAnimationFunction.java index 4d167a9d5..564ed7c21 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlayTotemAnimationFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlayTotemAnimationFunction.java @@ -7,6 +7,8 @@ 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.selector.PlayerSelector; import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; import net.momirealms.craftengine.core.sound.SoundData; @@ -25,22 +27,22 @@ public class PlayTotemAnimationFunction extends AbstractCon private final Key item; @Nullable private final Key sound; - private final float volume; - private final float pitch; - private final float minVolume; - private final float minPitch; - private final boolean noSound; + private final NumberProvider volume; + private final NumberProvider pitch; + private final NumberProvider minVolume; + private final NumberProvider minPitch; + private final boolean silent; public PlayTotemAnimationFunction( List> predicates, PlayerSelector selector, Key item, @Nullable Key sound, - float volume, - float pitch, - float minVolume, - float minPitch, - boolean noSound + NumberProvider volume, + NumberProvider pitch, + NumberProvider minVolume, + NumberProvider minPitch, + boolean silent ) { super(predicates); this.selector = selector; @@ -50,7 +52,7 @@ public class PlayTotemAnimationFunction extends AbstractCon this.pitch = pitch; this.minVolume = minVolume; this.minPitch = minPitch; - this.noSound = noSound; + this.silent = silent; } @Override @@ -63,8 +65,8 @@ public class PlayTotemAnimationFunction extends AbstractCon if (this.sound != null) { soundData = SoundData.of( this.sound, - SoundData.SoundValue.ranged(this.minVolume, this.volume), - SoundData.SoundValue.ranged(this.minPitch, this.pitch) + SoundData.SoundValue.ranged(Math.max(this.minVolume.getFloat(ctx), 0f), Math.max(this.volume.getFloat(ctx), 0f)), + SoundData.SoundValue.ranged(MiscUtils.clamp(this.minPitch.getFloat(ctx), 0f, 2f), MiscUtils.clamp(this.pitch.getFloat(ctx), 0f, 2f)) ); } for (Player player : this.selector.get(ctx)) { @@ -72,7 +74,7 @@ public class PlayTotemAnimationFunction extends AbstractCon if (VersionHelper.isOrAbove1_21_2()) { buildItem.setJavaComponent(DataComponentKeys.DEATH_PROTECTION, Map.of()); } - player.sendTotemAnimation(buildItem, soundData, this.noSound); + player.sendTotemAnimation(buildItem, soundData, this.silent); } } @@ -92,12 +94,12 @@ public class PlayTotemAnimationFunction extends AbstractCon PlayerSelector selector = PlayerSelectors.fromObject(arguments.getOrDefault("target", "self"), conditionFactory()); Key item = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.function.play_totem_animation.missing_item")); @Nullable Key sound = Optional.ofNullable(arguments.get("sound")).map(String::valueOf).map(Key::of).orElse(null); - float volume = Math.max(ResourceConfigUtils.getAsFloat(arguments.getOrDefault("volume", 1f), "volume"), 0f); - float pitch = MiscUtils.clamp(ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 1f), "pitch"), 0f, 2f); - float minVolume = Math.max(ResourceConfigUtils.getAsFloat(arguments.getOrDefault("min-volume", 1f), "min-volume"), 0f); - float minPitch = MiscUtils.clamp(ResourceConfigUtils.getAsFloat(arguments.getOrDefault("min-pitch", 1f), "min-pitch"), 0f, 2f); - boolean noSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("no-sound", false), "no-sound"); - return new PlayTotemAnimationFunction<>(getPredicates(arguments), selector, item, sound, volume, pitch, minVolume, minPitch, noSound); + NumberProvider volume = NumberProviders.fromObject(arguments.getOrDefault("volume", 1f)); + NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", 1f)); + NumberProvider minVolume = NumberProviders.fromObject(arguments.getOrDefault("min-volume", 1f)); + NumberProvider minPitch = NumberProviders.fromObject(arguments.getOrDefault("min-pitch", 1f)); + boolean silent = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("silent", false), "silent"); + return new PlayTotemAnimationFunction<>(getPredicates(arguments), selector, item, sound, volume, pitch, minVolume, minPitch, silent); } } } From c1883648efc400ff9ebc2b0c5484d771b6607c5f Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Tue, 25 Nov 2025 22:34:31 +0800 Subject: [PATCH 009/135] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../function/PlayTotemAnimationFunction.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlayTotemAnimationFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlayTotemAnimationFunction.java index 564ed7c21..0b1eb11b7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlayTotemAnimationFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlayTotemAnimationFunction.java @@ -29,8 +29,6 @@ public class PlayTotemAnimationFunction extends AbstractCon private final Key sound; private final NumberProvider volume; private final NumberProvider pitch; - private final NumberProvider minVolume; - private final NumberProvider minPitch; private final boolean silent; public PlayTotemAnimationFunction( @@ -40,8 +38,6 @@ public class PlayTotemAnimationFunction extends AbstractCon @Nullable Key sound, NumberProvider volume, NumberProvider pitch, - NumberProvider minVolume, - NumberProvider minPitch, boolean silent ) { super(predicates); @@ -50,8 +46,6 @@ public class PlayTotemAnimationFunction extends AbstractCon this.sound = sound; this.volume = volume; this.pitch = pitch; - this.minVolume = minVolume; - this.minPitch = minPitch; this.silent = silent; } @@ -65,8 +59,8 @@ public class PlayTotemAnimationFunction extends AbstractCon if (this.sound != null) { soundData = SoundData.of( this.sound, - SoundData.SoundValue.ranged(Math.max(this.minVolume.getFloat(ctx), 0f), Math.max(this.volume.getFloat(ctx), 0f)), - SoundData.SoundValue.ranged(MiscUtils.clamp(this.minPitch.getFloat(ctx), 0f, 2f), MiscUtils.clamp(this.pitch.getFloat(ctx), 0f, 2f)) + SoundData.SoundValue.fixed(Math.max(this.volume.getFloat(ctx), 0f)), + SoundData.SoundValue.fixed(MiscUtils.clamp(this.pitch.getFloat(ctx), 0f, 2f)) ); } for (Player player : this.selector.get(ctx)) { @@ -96,10 +90,8 @@ public class PlayTotemAnimationFunction extends AbstractCon @Nullable Key sound = Optional.ofNullable(arguments.get("sound")).map(String::valueOf).map(Key::of).orElse(null); NumberProvider volume = NumberProviders.fromObject(arguments.getOrDefault("volume", 1f)); NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", 1f)); - NumberProvider minVolume = NumberProviders.fromObject(arguments.getOrDefault("min-volume", 1f)); - NumberProvider minPitch = NumberProviders.fromObject(arguments.getOrDefault("min-pitch", 1f)); boolean silent = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("silent", false), "silent"); - return new PlayTotemAnimationFunction<>(getPredicates(arguments), selector, item, sound, volume, pitch, minVolume, minPitch, silent); + return new PlayTotemAnimationFunction<>(getPredicates(arguments), selector, item, sound, volume, pitch, silent); } } } From c00feb7a3d99983b8ca6b339bb93f1e3cd52fc37 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Tue, 25 Nov 2025 22:42:00 +0800 Subject: [PATCH 010/135] =?UTF-8?q?=E6=94=B9=E6=88=90silent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/bukkit/plugin/user/BukkitServerPlayer.java | 4 ++-- .../net/momirealms/craftengine/bukkit/util/PlayerUtils.java | 4 ++-- .../net/momirealms/craftengine/core/entity/player/Player.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) 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 434f60d70..ca013aee3 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 @@ -1281,7 +1281,7 @@ public class BukkitServerPlayer extends Player { } @Override - public void sendTotemAnimation(Item totem, @Nullable SoundData sound, boolean removeSound) { - PlayerUtils.sendTotemAnimation(this, totem, sound, removeSound); + public void sendTotemAnimation(Item totem, @Nullable SoundData sound, boolean silent) { + PlayerUtils.sendTotemAnimation(this, totem, sound, silent); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java index c20d195b9..ee138d4d8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java @@ -57,7 +57,7 @@ public final class PlayerUtils { } } - public static void sendTotemAnimation(Player player, Item totem, @Nullable SoundData sound, boolean removeSound) { + public static void sendTotemAnimation(Player player, Item totem, @Nullable SoundData sound, boolean silent) { List packets = new ArrayList<>(); try { Object totemItem = totem.getLiteralObject(); @@ -86,7 +86,7 @@ public final class PlayerUtils { packets.add(NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance( player.entityID(), List.of(Pair.of(CoreReflections.instance$EquipmentSlot$OFFHAND, previousOffHandItem)) )); - if (sound != null || removeSound) { + if (sound != null || silent) { packets.add(NetworkReflections.constructor$ClientboundStopSoundPacket.newInstance( FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "item.totem.use"), CoreReflections.instance$SoundSource$PLAYERS 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 260b935f5..c211bdd9f 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 @@ -194,7 +194,7 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void setExperienceLevels(int level); - public abstract void sendTotemAnimation(Item totem, @Nullable SoundData sound, boolean removeSound); + public abstract void sendTotemAnimation(Item totem, @Nullable SoundData sound, boolean silent); @Override public void remove() { From 60f79e5caeb182a1764b93fae6322d2be7cb83f3 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Tue, 25 Nov 2025 23:14:45 +0800 Subject: [PATCH 011/135] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=87=A0=E7=A7=8D?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=8F=82=E6=95=B0=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configuration/templates/block_states.yml | 6 ++- .../src/main/resources/translations/en.yml | 4 ++ .../template/ConditionTemplateArgument.java | 35 ++++++++++++++++ .../SelfIncreaseIntTemplateArgument.java | 33 +++++++++++++-- .../config/template/TemplateArguments.java | 17 ++++++++ .../template/ToLowerCaseTemplateArgument.java | 42 +++++++++++++++++++ .../template/ToUpperCaseTemplateArgument.java | 42 +++++++++++++++++++ 7 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ConditionTemplateArgument.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ToLowerCaseTemplateArgument.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ToUpperCaseTemplateArgument.java diff --git a/common-files/src/main/resources/resources/default/configuration/templates/block_states.yml b/common-files/src/main/resources/resources/default/configuration/templates/block_states.yml index ed493907e..50b0f8de5 100644 --- a/common-files/src/main/resources/resources/default/configuration/templates/block_states.yml +++ b/common-files/src/main/resources/resources/default/configuration/templates/block_states.yml @@ -85,7 +85,11 @@ templates: template: default:block_state/__leaves__ arguments: auto_state: leaves - leaves_base_model: leaves + leaves_base_model: + type: condition + condition: ${tintable:-false} + on-true: leaves + on-false: cube_all # tintable leaves block default:block_state/tintable_leaves: template: default:block_state/__leaves__ diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 8b92bd343..b3ce311aa 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -172,6 +172,10 @@ warning.config.translation.unknown_locale: "Issue found in file warning.config.template.duplicate: "Issue found in file - Duplicated template ''. Please check if there is the same configuration in other files." warning.config.template.invalid: "Issue found in file - The config '' is using an invalid template ''." warning.config.template.argument.self_increase_int.invalid_range: "Issue found in file - The template '' is using a 'from' '' larger than 'to' '' in 'self_increase_int' argument." +warning.config.template.argument.to_upper_case.invalid_locale: "Issue found in file - The template '' is using an invalid locale '' in 'to_upper_case' argument." +warning.config.template.argument.to_upper_case.missing_value: "Issue found in file - The template '' is missing the required 'value' argument for 'to_upper_case' argument." +warning.config.template.argument.to_lower_case.invalid_locale: "Issue found in file - The template '' is using an invalid locale '' in 'to_lower_case' argument." +warning.config.template.argument.to_lower_case.missing_value: "Issue found in file - The template '' is missing the required 'value' argument for 'to_upper_case' argument." warning.config.template.argument.list.invalid_type: "Issue found in file - The template '' is using a 'list' argument which expects a 'List' as argument while the input argument is a(n) ''." warning.config.template.argument.missing_value: "Issue found in file - The config '' is missing the template argument for ''. Please use the arguments option to configure or set a default value for this parameter." warning.config.vanilla_loot.missing_type: "Issue found in file - The vanilla loot '' is missing the required 'type' argument." diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ConditionTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ConditionTemplateArgument.java new file mode 100644 index 000000000..ebd5df786 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ConditionTemplateArgument.java @@ -0,0 +1,35 @@ +package net.momirealms.craftengine.core.plugin.config.template; + +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Map; + +public class ConditionTemplateArgument implements TemplateArgument { + public static final Factory FACTORY = new Factory(); + private final TemplateArgument result; + + private ConditionTemplateArgument(TemplateArgument result) { + this.result = result; + } + + @Override + public Key type() { + return TemplateArguments.CONDITION; + } + + @Override + public Object get(Map arguments) { + return this.result.get(arguments); + } + + public static class Factory implements TemplateArgumentFactory { + + @Override + public TemplateArgument create(Map arguments) { + TemplateArgument onTrue = TemplateArguments.fromObject(ResourceConfigUtils.get(arguments, "on-true", "on_true")); + TemplateArgument onFalse = TemplateArguments.fromObject(ResourceConfigUtils.get(arguments, "on-false", "on_false")); + return new ConditionTemplateArgument(ResourceConfigUtils.getAsBoolean(arguments.get("condition"), "condition") ? onTrue : onFalse); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/SelfIncreaseIntTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/SelfIncreaseIntTemplateArgument.java index d01b87124..d607f4358 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/SelfIncreaseIntTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/SelfIncreaseIntTemplateArgument.java @@ -11,17 +11,30 @@ public class SelfIncreaseIntTemplateArgument implements TemplateArgument { private final int min; private final int max; private int current; + private final int step; + private final int stepInterval; + private int callCount; - public SelfIncreaseIntTemplateArgument(int min, int max) { + public SelfIncreaseIntTemplateArgument(int min, int max, int step, int stepInterval) { this.min = min; this.max = max; this.current = min; + this.step = step; + this.stepInterval = stepInterval; + this.callCount = 0; } @Override public String get(Map arguments) { String value = String.valueOf(this.current); - if (this.current < this.max) this.current += 1; + this.callCount++; + if (this.stepInterval <= 0 || this.callCount % this.stepInterval == 0) { + if (this.current + this.step <= this.max) { + this.current += this.step; + } else { + this.current = this.max; + } + } return value; } @@ -42,13 +55,27 @@ public class SelfIncreaseIntTemplateArgument implements TemplateArgument { return this.max; } + public int step() { + return this.step; + } + + public int stepInterval() { + return this.stepInterval; + } + + public int callCount() { + return this.callCount; + } + public static class Factory implements TemplateArgumentFactory { @Override public TemplateArgument create(Map arguments) { int from = ResourceConfigUtils.getAsInt(arguments.get("from"), "from"); int to = ResourceConfigUtils.getAsInt(arguments.get("to"), "to"); + int step = ResourceConfigUtils.getAsInt(arguments.getOrDefault("step", 1), "step"); + int stepInterval = ResourceConfigUtils.getAsInt(arguments.getOrDefault("step-interval", 1), "step-interval"); if (from > to) throw new LocalizedResourceConfigException("warning.config.template.argument.self_increase_int.invalid_range", String.valueOf(from), String.valueOf(to)); - return new SelfIncreaseIntTemplateArgument(from, to); + return new SelfIncreaseIntTemplateArgument(from, to, step, stepInterval); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java index e45c45cb6..b49caec1c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java @@ -6,6 +6,7 @@ import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceKey; +import java.util.List; import java.util.Map; public class TemplateArguments { @@ -14,8 +15,11 @@ public class TemplateArguments { public static final Key MAP = Key.of("craftengine:map"); public static final Key LIST = Key.of("craftengine:list"); public static final Key NULL = Key.of("craftengine:null"); + public static final Key CONDITION = Key.of("craftengine:condition"); public static final Key EXPRESSION = Key.of("craftengine:expression"); public static final Key OBJECT = Key.of("craftengine:object"); // No Factory, internal use + public static final Key TO_UPPER_CASE = Key.of("craftengine:to_upper_case"); + public static final Key TO_LOWER_CASE = Key.of("craftengine:to_lower_case"); public static void register(Key key, TemplateArgumentFactory factory) { ((WritableRegistry) BuiltInRegistries.TEMPLATE_ARGUMENT_FACTORY) @@ -29,6 +33,19 @@ public class TemplateArguments { register(LIST, ListTemplateArgument.FACTORY); register(NULL, NullTemplateArgument.FACTORY); register(EXPRESSION, ExpressionTemplateArgument.FACTORY); + register(CONDITION, ConditionTemplateArgument.FACTORY); + register(TO_UPPER_CASE, ToUpperCaseTemplateArgument.FACTORY); + register(TO_LOWER_CASE, ToLowerCaseTemplateArgument.FACTORY); + } + + @SuppressWarnings("unchecked") + public static TemplateArgument fromObject(Object object) { + return switch (object) { + case null -> NullTemplateArgument.INSTANCE; + case List list -> new ListTemplateArgument((List) list); + case Map map -> fromMap((Map) map); + default -> new ObjectTemplateArgument(object); + }; } public static TemplateArgument fromMap(Map map) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ToLowerCaseTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ToLowerCaseTemplateArgument.java new file mode 100644 index 000000000..e00dc0f97 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ToLowerCaseTemplateArgument.java @@ -0,0 +1,42 @@ +package net.momirealms.craftengine.core.plugin.config.template; + +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Locale; +import java.util.Map; + +public class ToLowerCaseTemplateArgument implements TemplateArgument { + public static final Factory FACTORY = new Factory(); + private final String result; + + private ToLowerCaseTemplateArgument(String result) { + this.result = result; + } + + @Override + public Key type() { + return TemplateArguments.TO_LOWER_CASE; + } + + @Override + public Object get(Map arguments) { + return this.result; + } + + public static class Factory implements TemplateArgumentFactory { + + @Override + public TemplateArgument create(Map arguments) { + String text = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("value"), "warning.config.template.argument.to_lower_case.missing_value"); + String localeName = arguments.containsKey("locale") ? arguments.get("locale").toString() : null; + Locale locale = localeName != null ? TranslationManager.parseLocale(localeName) : Locale.ROOT; + if (locale == null) { + throw new LocalizedResourceConfigException("warning.config.template.argument.to_lower_case.invalid_locale", localeName); + } + return new ToLowerCaseTemplateArgument(text.toLowerCase(locale)); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ToUpperCaseTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ToUpperCaseTemplateArgument.java new file mode 100644 index 000000000..33f67f6f3 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ToUpperCaseTemplateArgument.java @@ -0,0 +1,42 @@ +package net.momirealms.craftengine.core.plugin.config.template; + +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Locale; +import java.util.Map; + +public class ToUpperCaseTemplateArgument implements TemplateArgument { + public static final Factory FACTORY = new Factory(); + private final String result; + + private ToUpperCaseTemplateArgument(String result) { + this.result = result; + } + + @Override + public Key type() { + return TemplateArguments.TO_UPPER_CASE; + } + + @Override + public Object get(Map arguments) { + return this.result; + } + + public static class Factory implements TemplateArgumentFactory { + + @Override + public TemplateArgument create(Map arguments) { + String text = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("value"), "warning.config.template.argument.to_upper_case.missing_value"); + String localeName = arguments.containsKey("locale") ? arguments.get("locale").toString() : null; + Locale locale = localeName != null ? TranslationManager.parseLocale(localeName) : Locale.ROOT; + if (locale == null) { + throw new LocalizedResourceConfigException("warning.config.template.argument.to_upper_case.invalid_locale", localeName); + } + return new ToUpperCaseTemplateArgument(text.toUpperCase(locale)); + } + } +} From 431aea5385548839c30a46845f06621355742d90 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Tue, 25 Nov 2025 23:36:43 +0800 Subject: [PATCH 012/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0when=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configuration/templates/block_states.yml | 2 +- .../config/template/TemplateArguments.java | 2 + .../config/template/WhenTemplateArgument.java | 40 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/WhenTemplateArgument.java diff --git a/common-files/src/main/resources/resources/default/configuration/templates/block_states.yml b/common-files/src/main/resources/resources/default/configuration/templates/block_states.yml index 50b0f8de5..3550304a2 100644 --- a/common-files/src/main/resources/resources/default/configuration/templates/block_states.yml +++ b/common-files/src/main/resources/resources/default/configuration/templates/block_states.yml @@ -1,5 +1,5 @@ templates: - # pillar/log blocks + # axis based blocks default:block_state/pillar: properties: axis: diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java index b49caec1c..0538d98bf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java @@ -20,6 +20,7 @@ public class TemplateArguments { public static final Key OBJECT = Key.of("craftengine:object"); // No Factory, internal use public static final Key TO_UPPER_CASE = Key.of("craftengine:to_upper_case"); public static final Key TO_LOWER_CASE = Key.of("craftengine:to_lower_case"); + public static final Key WHEN = Key.of("craftengine:when"); public static void register(Key key, TemplateArgumentFactory factory) { ((WritableRegistry) BuiltInRegistries.TEMPLATE_ARGUMENT_FACTORY) @@ -36,6 +37,7 @@ public class TemplateArguments { register(CONDITION, ConditionTemplateArgument.FACTORY); register(TO_UPPER_CASE, ToUpperCaseTemplateArgument.FACTORY); register(TO_LOWER_CASE, ToLowerCaseTemplateArgument.FACTORY); + register(WHEN, WhenTemplateArgument.FACTORY); } @SuppressWarnings("unchecked") diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/WhenTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/WhenTemplateArgument.java new file mode 100644 index 000000000..e91f0cc9f --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/WhenTemplateArgument.java @@ -0,0 +1,40 @@ +package net.momirealms.craftengine.core.plugin.config.template; + +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Map; + +public class WhenTemplateArgument implements TemplateArgument { + public static final Factory FACTORY = new Factory(); + private final TemplateArgument result; + + private WhenTemplateArgument(TemplateArgument result) { + this.result = result; + } + + @Override + public Key type() { + return TemplateArguments.WHEN; + } + + @Override + public Object get(Map arguments) { + return this.result.get(arguments); + } + + public static class Factory implements TemplateArgumentFactory { + + @Override + public TemplateArgument create(Map arguments) { + String source = ResourceConfigUtils.getAsStringOrNull(arguments.get("source")); + TemplateArgument fallback = TemplateArguments.fromObject(arguments.get("fallback")); + if (source == null) { + return new WhenTemplateArgument(fallback); + } + Map whenMap = ResourceConfigUtils.getAsMap(arguments.get("when"), "when"); + TemplateArgument value = whenMap.containsKey(source) ? TemplateArguments.fromObject(whenMap.get(source)) : fallback; + return new WhenTemplateArgument(value); + } + } +} From 6e20de126f112a28fffec4da2f7ff918824e4328 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 26 Nov 2025 00:58:12 +0800 Subject: [PATCH 013/135] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=80=BC=E5=B5=8C=E5=A5=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/core/plugin/CraftEngine.java | 2 +- .../config/template/ArgumentString.java | 303 ++++++++++++++++++ .../config/template/TemplateManager.java | 295 +---------------- .../config/template/TemplateManagerImpl.java | 25 +- .../ConditionTemplateArgument.java | 2 +- .../ExpressionTemplateArgument.java | 8 +- .../{ => argument}/ListTemplateArgument.java | 2 +- .../{ => argument}/MapTemplateArgument.java | 2 +- .../{ => argument}/NullTemplateArgument.java | 2 +- .../ObjectTemplateArgument.java | 2 +- .../PlainStringTemplateArgument.java | 2 +- .../SelfIncreaseIntTemplateArgument.java | 2 +- .../{ => argument}/TemplateArgument.java | 2 +- .../TemplateArgumentFactory.java | 2 +- .../{ => argument}/TemplateArguments.java | 2 +- .../ToLowerCaseTemplateArgument.java | 2 +- .../ToUpperCaseTemplateArgument.java | 2 +- .../{ => argument}/WhenTemplateArgument.java | 2 +- .../core/registry/BuiltInRegistries.java | 2 +- .../craftengine/core/registry/Registries.java | 2 +- gradle.properties | 2 +- 21 files changed, 341 insertions(+), 324 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java rename core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/{ => argument}/ConditionTemplateArgument.java (94%) rename core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/{ => argument}/ExpressionTemplateArgument.java (86%) rename core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/{ => argument}/ListTemplateArgument.java (95%) rename core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/{ => argument}/MapTemplateArgument.java (92%) rename core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/{ => argument}/NullTemplateArgument.java (91%) rename core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/{ => argument}/ObjectTemplateArgument.java (88%) rename core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/{ => argument}/PlainStringTemplateArgument.java (92%) rename core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/{ => argument}/SelfIncreaseIntTemplateArgument.java (97%) rename core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/{ => argument}/TemplateArgument.java (71%) rename core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/{ => argument}/TemplateArgumentFactory.java (64%) rename core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/{ => argument}/TemplateArguments.java (97%) rename core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/{ => argument}/ToLowerCaseTemplateArgument.java (95%) rename core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/{ => argument}/ToUpperCaseTemplateArgument.java (95%) rename core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/{ => argument}/WhenTemplateArgument.java (94%) 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 8bbfb01c0..58c1ac8cd 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 @@ -114,7 +114,7 @@ public abstract class CraftEngine implements Plugin { LegacyRecipeTypes.init(); // 初始化模板管理器 - this.templateManager = new TemplateManagerImpl(); + this.templateManager = TemplateManager.INSTANCE; // 初始化全局变量管理器 this.globalVariableManager = new GlobalVariableManager(); // 初始化物品浏览器 diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java new file mode 100644 index 000000000..f3846da86 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java @@ -0,0 +1,303 @@ +package net.momirealms.craftengine.core.plugin.config.template; + +import net.momirealms.craftengine.core.plugin.config.template.argument.TemplateArgument; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.SNBTReader; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public interface ArgumentString { + String rawValue(); + + Object get(Map arguments); + + final class Literal implements ArgumentString { + private final String value; + + public Literal(String value) { + this.value = value; + } + + public static Literal literal(String value) { + return new Literal(value); + } + + @Override + public String rawValue() { + return this.value; + } + + @Override + public Object get(Map arguments) { + return this.value; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Literal literal)) return false; + return this.value.equals(literal.value); + } + + @Override + public int hashCode() { + return this.value.hashCode(); + } + + @Override + public String toString() { + return "Literal(" + this.value + ")"; + } + } + + final class Placeholder implements ArgumentString { + private final String placeholder; + private final String rawText; + private final Object defaultValue; + private final boolean hasDefaultValue; + + public Placeholder(String placeholderContent) { + this.rawText = "${" + placeholderContent + "}"; + int separatorIndex = placeholderContent.indexOf(":-"); + if (separatorIndex == -1) { + this.placeholder = placeholderContent; + this.defaultValue = null; + this.hasDefaultValue = false; + } else { + this.placeholder = placeholderContent.substring(0, separatorIndex); + String defaultValueString = placeholderContent.substring(separatorIndex + 2); + try { + this.defaultValue = ((TemplateManagerImpl) TemplateManager.INSTANCE).preprocessUnknownValue(new SNBTReader(defaultValueString).deserializeAsJava()); + } catch (LocalizedResourceConfigException e) { + e.appendTailArgument(this.placeholder); + throw e; + } + this.hasDefaultValue = true; + } + } + + public static Placeholder placeholder(String placeholder) { + return new Placeholder(placeholder); + } + + @Override + public Object get(Map arguments) { + TemplateArgument replacement = arguments.get(this.placeholder); + if (replacement != null) { + return replacement.get(arguments); + } + if (this.hasDefaultValue) { + if (this.defaultValue == null) { + return null; + } + return ((TemplateManagerImpl) TemplateManager.INSTANCE).processUnknownValue(this.defaultValue, arguments); + } + throw new LocalizedResourceConfigException("warning.config.template.argument.missing_value", this.rawText); + } + + @Override + public String rawValue() { + return this.rawText; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Placeholder that)) return false; + return this.placeholder.equals(that.placeholder); + } + + @Override + public int hashCode() { + return this.placeholder.hashCode(); + } + + @Override + public String toString() { + return "Placeholder(" + this.placeholder + ")"; + } + } + + final class Complex2 implements ArgumentString { + private final String rawText; + private final ArgumentString arg1; + private final ArgumentString arg2; + + public Complex2(String rawText, ArgumentString arg1, ArgumentString arg2) { + this.arg1 = arg1; + this.arg2 = arg2; + this.rawText = rawText; + } + + @Override + public Object get(Map arguments) { + Object arg1 = this.arg1.get(arguments); + Object arg2 = this.arg2.get(arguments); + if (arg1 == null && arg2 == null) return null; + if (arg1 == null) return String.valueOf(arg2); + if (arg2 == null) return String.valueOf(arg1); + return String.valueOf(arg1) + arg2; + } + + @Override + public String rawValue() { + return this.rawText; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Complex that)) return false; + return this.rawText.equals(that.rawText); + } + + @Override + public int hashCode() { + return this.rawText.hashCode(); + } + + @Override + public String toString() { + return "Complex2(" + this.rawText + ")"; + } + } + + final class Complex implements ArgumentString { + private final List parts; + private final String rawText; + + public Complex(String rawText, List parts) { + this.parts = parts; + this.rawText = rawText; + } + + @Override + public Object get(Map arguments) { + StringBuilder result = new StringBuilder(); + boolean hasValue = false; + for (ArgumentString part : this.parts) { + Object arg = part.get(arguments); + if (arg != null) { + result.append(arg); + hasValue = true; + } + } + if (!hasValue) return null; + return result.toString(); + } + + @Override + public String rawValue() { + return this.rawText; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Complex that)) return false; + return this.rawText.equals(that.rawText); + } + + @Override + public int hashCode() { + return this.rawText.hashCode(); + } + + @Override + public String toString() { + return "Complex(" + this.rawText + ")"; + } + } + + static ArgumentString preParse(String input) { + if (input == null || input.isEmpty()) { + return Literal.literal(""); + } + + List arguments = new ArrayList<>(); + StringBuilder currentLiteral = new StringBuilder(); + final int n = input.length(); + int i = 0; + + while (i < n) { + char c = input.charAt(i); + + // --- 1. 优先检测占位符触发器 --- + if (c == '$' && i + 1 < n && input.charAt(i + 1) == '{') { + + // a. 提交之前的普通文本 + if (!currentLiteral.isEmpty()) { + arguments.add(Literal.literal(currentLiteral.toString())); + currentLiteral.setLength(0); + } + + // b. 解析占位符内部,此处的逻辑拥有自己的转义规则 + int contentStartIndex = i + 2; + StringBuilder keyBuilder = new StringBuilder(); + int depth = 1; + int j = contentStartIndex; + boolean foundMatch = false; + + while (j < n) { + char innerChar = input.charAt(j); + + // --- 占位符内部的转义逻辑 --- + if (innerChar == '\\') { + if (j + 1 < n && (input.charAt(j + 1) == '{' || input.charAt(j + 1) == '}')) { + keyBuilder.append(input.charAt(j + 1)); + j += 2; + } else { + // 在占位符内部,一个无法识别的转义\依旧被当作普通\处理 + keyBuilder.append(innerChar); + j++; + } + } else if (innerChar == '{') { + depth++; + keyBuilder.append(innerChar); + j++; + } else if (innerChar == '}') { + depth--; + if (depth == 0) { // 找到匹配的闭合括号 + arguments.add(Placeholder.placeholder(keyBuilder.toString())); + i = j + 1; + foundMatch = true; + break; + } + keyBuilder.append(innerChar); + j++; + } else { + keyBuilder.append(innerChar); + j++; + } + } + + if (foundMatch) { + continue; + } else { + // 未找到闭合括号,将 '$' 视为普通字符 + currentLiteral.append(c); + i++; + } + } + // --- 2. 其次,只处理对触发器'$'的转义 --- + else if (c == '\\' && i + 1 < n && input.charAt(i + 1) == '$') { + currentLiteral.append('$'); // 直接添加 '$' + i += 2; // 跳过 '\' 和 '$' + } + // --- 3. 处理所有其他字符(包括独立的'\'和'{')为普通文本 --- + else { + currentLiteral.append(c); + i++; + } + } + + if (!currentLiteral.isEmpty()) { + arguments.add(Literal.literal(currentLiteral.toString())); + } + + return switch (arguments.size()) { + case 0 -> Literal.literal(""); + case 1 -> arguments.getFirst(); + case 2 -> new Complex2(input, arguments.get(0), arguments.get(1)); + default -> new Complex(input, arguments); + }; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java index 86840c238..79b942023 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.plugin.config.template; import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.config.template.argument.TemplateArgument; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.SNBTReader; @@ -12,299 +13,9 @@ import java.util.Map; public interface TemplateManager extends Manageable { + TemplateManager INSTANCE = new TemplateManagerImpl(); + ConfigParser parser(); Object applyTemplates(Key id, Object input); - - interface ArgumentString { - - String rawValue(); - - Object get(Map arguments); - } - - final class Literal implements ArgumentString { - private final String value; - - public Literal(String value) { - this.value = value; - } - - public static Literal literal(String value) { - return new Literal(value); - } - - @Override - public String rawValue() { - return this.value; - } - - @Override - public Object get(Map arguments) { - return this.value; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Literal literal)) return false; - return this.value.equals(literal.value); - } - - @Override - public int hashCode() { - return this.value.hashCode(); - } - - @Override - public String toString() { - return "Literal(" + this.value + ")"; - } - } - - final class Placeholder implements ArgumentString { - private final String placeholder; - private final String rawText; - private final Object defaultValue; - private final boolean hasDefaultValue; - - public Placeholder(String placeholderContent) { - this.rawText = "${" + placeholderContent + "}"; - int separatorIndex = placeholderContent.indexOf(":-"); - if (separatorIndex == -1) { - this.placeholder = placeholderContent; - this.defaultValue = null; - this.hasDefaultValue = false; - } else { - this.placeholder = placeholderContent.substring(0, separatorIndex); - String defaultValueString = placeholderContent.substring(separatorIndex + 2); - try { - this.defaultValue = new SNBTReader(defaultValueString).deserializeAsJava(); - } catch (LocalizedResourceConfigException e) { - e.appendTailArgument(this.placeholder); - throw e; - } - this.hasDefaultValue = true; - } - } - - public static Placeholder placeholder(String placeholder) { - return new Placeholder(placeholder); - } - - @Override - public Object get(Map arguments) { - TemplateArgument replacement = arguments.get(this.placeholder); - if (replacement != null) { - return replacement.get(arguments); - } - if (this.hasDefaultValue) { - return this.defaultValue; - } - throw new LocalizedResourceConfigException("warning.config.template.argument.missing_value", this.rawText); - } - - @Override - public String rawValue() { - return this.rawText; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Placeholder that)) return false; - return this.placeholder.equals(that.placeholder); - } - - @Override - public int hashCode() { - return this.placeholder.hashCode(); - } - - @Override - public String toString() { - return "Placeholder(" + this.placeholder + ")"; - } - } - - final class Complex2 implements ArgumentString { - private final String rawText; - private final ArgumentString arg1; - private final ArgumentString arg2; - - public Complex2(String rawText, ArgumentString arg1, ArgumentString arg2) { - this.arg1 = arg1; - this.arg2 = arg2; - this.rawText = rawText; - } - - @Override - public Object get(Map arguments) { - Object arg1 = this.arg1.get(arguments); - Object arg2 = this.arg2.get(arguments); - if (arg1 == null && arg2 == null) return null; - if (arg1 == null) return String.valueOf(arg2); - if (arg2 == null) return String.valueOf(arg1); - return String.valueOf(arg1) + arg2; - } - - @Override - public String rawValue() { - return this.rawText; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Complex that)) return false; - return this.rawText.equals(that.rawText); - } - - @Override - public int hashCode() { - return this.rawText.hashCode(); - } - - @Override - public String toString() { - return "Complex2(" + this.rawText + ")"; - } - } - - final class Complex implements ArgumentString { - private final List parts; - private final String rawText; - - public Complex(String rawText, List parts) { - this.parts = parts; - this.rawText = rawText; - } - - @Override - public Object get(Map arguments) { - StringBuilder result = new StringBuilder(); - boolean hasValue = false; - for (ArgumentString part : this.parts) { - Object arg = part.get(arguments); - if (arg != null) { - result.append(arg); - hasValue = true; - } - } - if (!hasValue) return null; - return result.toString(); - } - - @Override - public String rawValue() { - return this.rawText; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Complex that)) return false; - return this.rawText.equals(that.rawText); - } - - @Override - public int hashCode() { - return this.rawText.hashCode(); - } - - @Override - public String toString() { - return "Complex(" + this.rawText + ")"; - } - } - - static ArgumentString preParse(String input) { - if (input == null || input.isEmpty()) { - return Literal.literal(""); - } - - List arguments = new ArrayList<>(); - StringBuilder currentLiteral = new StringBuilder(); - final int n = input.length(); - int i = 0; - - while (i < n) { - char c = input.charAt(i); - - // --- 1. 优先检测占位符触发器 --- - if (c == '$' && i + 1 < n && input.charAt(i + 1) == '{') { - - // a. 提交之前的普通文本 - if (!currentLiteral.isEmpty()) { - arguments.add(Literal.literal(currentLiteral.toString())); - currentLiteral.setLength(0); - } - - // b. 解析占位符内部,此处的逻辑拥有自己的转义规则 - int contentStartIndex = i + 2; - StringBuilder keyBuilder = new StringBuilder(); - int depth = 1; - int j = contentStartIndex; - boolean foundMatch = false; - - while (j < n) { - char innerChar = input.charAt(j); - - // --- 占位符内部的转义逻辑 --- - if (innerChar == '\\') { - if (j + 1 < n && (input.charAt(j + 1) == '{' || input.charAt(j + 1) == '}')) { - keyBuilder.append(input.charAt(j + 1)); - j += 2; - } else { - // 在占位符内部,一个无法识别的转义\依旧被当作普通\处理 - keyBuilder.append(innerChar); - j++; - } - } else if (innerChar == '{') { - depth++; - keyBuilder.append(innerChar); - j++; - } else if (innerChar == '}') { - depth--; - if (depth == 0) { // 找到匹配的闭合括号 - arguments.add(Placeholder.placeholder(keyBuilder.toString())); - i = j + 1; - foundMatch = true; - break; - } - keyBuilder.append(innerChar); - j++; - } else { - keyBuilder.append(innerChar); - j++; - } - } - - if (foundMatch) { - continue; - } else { - // 未找到闭合括号,将 '$' 视为普通字符 - currentLiteral.append(c); - i++; - } - } - // --- 2. 其次,只处理对触发器'$'的转义 --- - else if (c == '\\' && i + 1 < n && input.charAt(i + 1) == '$') { - currentLiteral.append('$'); // 直接添加 '$' - i += 2; // 跳过 '\' 和 '$' - } - // --- 3. 处理所有其他字符(包括独立的'\'和'{')为普通文本 --- - else { - currentLiteral.append(c); - i++; - } - } - - if (!currentLiteral.isEmpty()) { - arguments.add(Literal.literal(currentLiteral.toString())); - } - - return switch (arguments.size()) { - case 0 -> Literal.literal(""); - case 1 -> arguments.getFirst(); - case 2 -> new Complex2(input, arguments.get(0), arguments.get(1)); - default -> new Complex(input, arguments); - }; - } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java index 0d411d956..3b0502db5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.config.IdObjectConfigParser; +import net.momirealms.craftengine.core.plugin.config.template.argument.*; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; @@ -14,16 +15,16 @@ import java.util.*; @SuppressWarnings("DuplicatedCode") public class TemplateManagerImpl implements TemplateManager { - private static final ArgumentString TEMPLATE = Literal.literal("template"); - private static final ArgumentString OVERRIDES = Literal.literal("overrides"); - private static final ArgumentString ARGUMENTS = Literal.literal("arguments"); - private static final ArgumentString MERGES = Literal.literal("merges"); + private static final ArgumentString TEMPLATE = ArgumentString.Literal.literal("template"); + private static final ArgumentString OVERRIDES = ArgumentString.Literal.literal("overrides"); + private static final ArgumentString ARGUMENTS = ArgumentString.Literal.literal("arguments"); + private static final ArgumentString MERGES = ArgumentString.Literal.literal("merges"); private final static Set NON_TEMPLATE_ARGUMENTS = new HashSet<>(Set.of(TEMPLATE, ARGUMENTS, OVERRIDES, MERGES)); private final Map templates = new HashMap<>(); private final TemplateParser templateParser; - public TemplateManagerImpl() { + protected TemplateManagerImpl() { this.templateParser = new TemplateParser(); } @@ -69,13 +70,13 @@ public class TemplateManagerImpl implements TemplateManager { )); } - private Object preprocessUnknownValue(Object value) { + public Object preprocessUnknownValue(Object value) { switch (value) { case Map map -> { Map in = MiscUtils.castToMap(map, false); Map out = new LinkedHashMap<>(map.size()); for (Map.Entry entry : in.entrySet()) { - out.put(TemplateManager.preParse(entry.getKey()), preprocessUnknownValue(entry.getValue())); + out.put(ArgumentString.preParse(entry.getKey()), preprocessUnknownValue(entry.getValue())); } return out; } @@ -87,7 +88,7 @@ public class TemplateManagerImpl implements TemplateManager { return objList; } case String string -> { - return TemplateManager.preParse(string); + return ArgumentString.preParse(string); } case null, default -> { return value; @@ -188,8 +189,8 @@ public class TemplateManagerImpl implements TemplateManager { // 处理一个类型未知的值,本方法只管将member处理好后,传递回调用者a @SuppressWarnings("unchecked") - private Object processUnknownValue(Object value, - Map arguments) { + public Object processUnknownValue(Object value, + Map arguments) { switch (value) { case Map innerMap -> // map下面还是个map吗?这并不一定 @@ -237,7 +238,7 @@ public class TemplateManagerImpl implements TemplateManager { // 如果模板id被用了参数,则应先应用参数后再查询模板 Object actualTemplate = templateId.get(parentArguments); if (actualTemplate == null) continue; // 忽略被null掉的模板 - Object template = Optional.ofNullable(this.templates.get(Key.of(actualTemplate.toString()))) + Object template = Optional.ofNullable(((TemplateManagerImpl) INSTANCE).templates.get(Key.of(actualTemplate.toString()))) .orElseThrow(() -> new LocalizedResourceConfigException("warning.config.template.invalid", actualTemplate.toString())); Object processedTemplate = processUnknownValue(template, arguments); if (processedTemplate != null) templateList.add(processedTemplate); @@ -294,7 +295,7 @@ public class TemplateManagerImpl implements TemplateManager { // 合并参数 @SuppressWarnings("unchecked") private Map mergeArguments(@NotNull Map childArguments, - @NotNull Map parentArguments) { + @NotNull Map parentArguments) { Map result = new LinkedHashMap<>(parentArguments); for (Map.Entry argumentEntry : childArguments.entrySet()) { Object placeholderObj = argumentEntry.getKey().get(result); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ConditionTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ConditionTemplateArgument.java similarity index 94% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ConditionTemplateArgument.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ConditionTemplateArgument.java index ebd5df786..f8f3c72be 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ConditionTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ConditionTemplateArgument.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.plugin.config.template; +package net.momirealms.craftengine.core.plugin.config.template.argument; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ExpressionTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java similarity index 86% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ExpressionTemplateArgument.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java index a92d6fdf2..d25f7d041 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ExpressionTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java @@ -1,7 +1,9 @@ -package net.momirealms.craftengine.core.plugin.config.template; +package net.momirealms.craftengine.core.plugin.config.template.argument; import com.ezylang.evalex.Expression; import com.ezylang.evalex.data.EvaluationValue; +import net.momirealms.craftengine.core.plugin.config.template.ArgumentString; +import net.momirealms.craftengine.core.plugin.config.template.TemplateManager; import net.momirealms.craftengine.core.util.Key; import java.util.Locale; @@ -11,11 +13,11 @@ import java.util.function.Function; public class ExpressionTemplateArgument implements TemplateArgument { public static final Factory FACTORY = new Factory(); - private final TemplateManager.ArgumentString expression; + private final ArgumentString expression; private final ValueType valueType; protected ExpressionTemplateArgument(String expression, ValueType valueType) { - this.expression = TemplateManager.preParse(expression); + this.expression = ArgumentString.preParse(expression); this.valueType = valueType; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ListTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ListTemplateArgument.java similarity index 95% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ListTemplateArgument.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ListTemplateArgument.java index eae376ac0..3863b7841 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ListTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ListTemplateArgument.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.plugin.config.template; +package net.momirealms.craftengine.core.plugin.config.template.argument; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/MapTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/MapTemplateArgument.java similarity index 92% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/MapTemplateArgument.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/MapTemplateArgument.java index 9df939320..b77c47bb0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/MapTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/MapTemplateArgument.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.plugin.config.template; +package net.momirealms.craftengine.core.plugin.config.template.argument; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/NullTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/NullTemplateArgument.java similarity index 91% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/NullTemplateArgument.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/NullTemplateArgument.java index 8a568d2af..51a0af576 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/NullTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/NullTemplateArgument.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.plugin.config.template; +package net.momirealms.craftengine.core.plugin.config.template.argument; import net.momirealms.craftengine.core.util.Key; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ObjectTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ObjectTemplateArgument.java similarity index 88% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ObjectTemplateArgument.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ObjectTemplateArgument.java index e78af6b66..980eadef6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ObjectTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ObjectTemplateArgument.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.plugin.config.template; +package net.momirealms.craftengine.core.plugin.config.template.argument; import net.momirealms.craftengine.core.util.Key; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/PlainStringTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/PlainStringTemplateArgument.java similarity index 92% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/PlainStringTemplateArgument.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/PlainStringTemplateArgument.java index a9ef8d6ae..3305ac5c5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/PlainStringTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/PlainStringTemplateArgument.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.plugin.config.template; +package net.momirealms.craftengine.core.plugin.config.template.argument; import net.momirealms.craftengine.core.util.Key; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/SelfIncreaseIntTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/SelfIncreaseIntTemplateArgument.java similarity index 97% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/SelfIncreaseIntTemplateArgument.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/SelfIncreaseIntTemplateArgument.java index d607f4358..b117622f4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/SelfIncreaseIntTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/SelfIncreaseIntTemplateArgument.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.plugin.config.template; +package net.momirealms.craftengine.core.plugin.config.template.argument; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArgument.java similarity index 71% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArgument.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArgument.java index 9816cfebc..6e029e88c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArgument.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.plugin.config.template; +package net.momirealms.craftengine.core.plugin.config.template.argument; import net.momirealms.craftengine.core.util.Key; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArgumentFactory.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArgumentFactory.java similarity index 64% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArgumentFactory.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArgumentFactory.java index 63812d722..a5976017a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArgumentFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArgumentFactory.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.plugin.config.template; +package net.momirealms.craftengine.core.plugin.config.template.argument; import java.util.Map; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java similarity index 97% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java index 0538d98bf..9b1a8ef91 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateArguments.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.plugin.config.template; +package net.momirealms.craftengine.core.plugin.config.template.argument; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Registries; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ToLowerCaseTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ToLowerCaseTemplateArgument.java similarity index 95% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ToLowerCaseTemplateArgument.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ToLowerCaseTemplateArgument.java index e00dc0f97..83e2c8dae 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ToLowerCaseTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ToLowerCaseTemplateArgument.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.plugin.config.template; +package net.momirealms.craftengine.core.plugin.config.template.argument; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ToUpperCaseTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ToUpperCaseTemplateArgument.java similarity index 95% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ToUpperCaseTemplateArgument.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ToUpperCaseTemplateArgument.java index 33f67f6f3..d3600a5eb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ToUpperCaseTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ToUpperCaseTemplateArgument.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.plugin.config.template; +package net.momirealms.craftengine.core.plugin.config.template.argument; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/WhenTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/WhenTemplateArgument.java similarity index 94% rename from core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/WhenTemplateArgument.java rename to core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/WhenTemplateArgument.java index e91f0cc9f..98dfcb7a0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/WhenTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/WhenTemplateArgument.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.plugin.config.template; +package net.momirealms.craftengine.core.plugin.config.template.argument; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; 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 312550c23..f09e3a159 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 @@ -37,7 +37,7 @@ import net.momirealms.craftengine.core.pack.model.special.SpecialModelFactory; import net.momirealms.craftengine.core.pack.model.special.SpecialModelReader; import net.momirealms.craftengine.core.pack.model.tint.TintFactory; import net.momirealms.craftengine.core.pack.model.tint.TintReader; -import net.momirealms.craftengine.core.plugin.config.template.TemplateArgumentFactory; +import net.momirealms.craftengine.core.plugin.config.template.argument.TemplateArgumentFactory; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory; import net.momirealms.craftengine.core.plugin.context.function.FunctionFactory; 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 866abd070..33a34a9d9 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 @@ -37,7 +37,7 @@ import net.momirealms.craftengine.core.pack.model.special.SpecialModelFactory; import net.momirealms.craftengine.core.pack.model.special.SpecialModelReader; import net.momirealms.craftengine.core.pack.model.tint.TintFactory; import net.momirealms.craftengine.core.pack.model.tint.TintReader; -import net.momirealms.craftengine.core.plugin.config.template.TemplateArgumentFactory; +import net.momirealms.craftengine.core.plugin.config.template.argument.TemplateArgumentFactory; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory; import net.momirealms.craftengine.core.plugin.context.function.FunctionFactory; diff --git a/gradle.properties b/gradle.properties index a3efe51fd..102014893 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings -project_version=0.0.65.13 +project_version=0.0.65.13.1 config_version=58 lang_version=40 project_group=net.momirealms From f91bf47a7460a24a723d34656552c9064acd4763 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 26 Nov 2025 01:43:06 +0800 Subject: [PATCH 014/135] =?UTF-8?q?=E4=BC=98=E5=8C=96expression=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/template/argument/ExpressionTemplateArgument.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java index d25f7d041..d809c4af9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java @@ -43,6 +43,7 @@ public class ExpressionTemplateArgument implements TemplateArgument { SHORT(e -> e.getNumberValue().shortValueExact()), DOUBLE(e -> e.getNumberValue().doubleValue()), FLOAT(e -> e.getNumberValue().floatValue()), + BYTE(e -> e.getNumberValue().byteValue()), BOOLEAN(EvaluationValue::getBooleanValue),; private final Function formatter; @@ -61,7 +62,7 @@ public class ExpressionTemplateArgument implements TemplateArgument { public TemplateArgument create(Map arguments) { return new ExpressionTemplateArgument( arguments.getOrDefault("expression", "").toString(), - ValueType.valueOf(arguments.getOrDefault("value-type", "double").toString().toUpperCase(Locale.ENGLISH)) + ValueType.valueOf(arguments.getOrDefault("value-type", "double").toString().toUpperCase(Locale.ROOT)) ); } } From 8b7e257e6a6f1d9d7e176b0757da984571a5f480 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 26 Nov 2025 15:45:00 +0800 Subject: [PATCH 015/135] Update ExpressionTemplateArgument.java --- .../config/template/argument/ExpressionTemplateArgument.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java index d809c4af9..667b28db2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java @@ -40,7 +40,7 @@ public class ExpressionTemplateArgument implements TemplateArgument { protected enum ValueType { INT(e -> e.getNumberValue().intValue()), LONG(e -> e.getNumberValue().longValue()), - SHORT(e -> e.getNumberValue().shortValueExact()), + SHORT(e -> e.getNumberValue().shortValue()), DOUBLE(e -> e.getNumberValue().doubleValue()), FLOAT(e -> e.getNumberValue().floatValue()), BYTE(e -> e.getNumberValue().byteValue()), From 89dce806f8a4f84c094a9952d91df6de268e0288 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 26 Nov 2025 16:16:08 +0800 Subject: [PATCH 016/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dconsume=20replacement?= =?UTF-8?q?=E5=B1=AF=E7=89=A9=E5=93=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../item/listener/ItemEventListener.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) 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 9f38f16d0..b5c2cbac0 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 @@ -426,7 +426,7 @@ public class ItemEventListener implements Listener { } } - @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onConsumeItem(PlayerItemConsumeEvent event) { ItemStack consumedItem = event.getItem(); if (ItemStackUtils.isEmpty(consumedItem)) return; @@ -435,9 +435,11 @@ public class ItemEventListener implements Listener { if (optionalCustomItem.isEmpty()) { return; } + Player player = event.getPlayer(); + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled); CustomItem customItem = optionalCustomItem.get(); - PlayerOptionalContext context = PlayerOptionalContext.of(BukkitAdaptors.adapt(event.getPlayer()), ContextHolder.builder() + PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder() .withParameter(DirectContextParameters.ITEM_IN_HAND, wrapped) .withParameter(DirectContextParameters.EVENT, cancellable) .withParameter(DirectContextParameters.HAND, event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND) @@ -448,11 +450,19 @@ public class ItemEventListener implements Listener { } if (event.getPlayer().getGameMode() != GameMode.CREATIVE) { Key replacement = customItem.settings().consumeReplacement(); - if (replacement == null) { - event.setReplacement(null); + if (wrapped.count() == 1) { + if (replacement == null) { + event.setReplacement(null); + } else { + ItemStack replacementItem = this.plugin.itemManager().buildItemStack(replacement, serverPlayer); + event.setReplacement(replacementItem); + } } else { - ItemStack replacementItem = this.plugin.itemManager().buildItemStack(replacement, BukkitAdaptors.adapt(event.getPlayer())); - event.setReplacement(replacementItem); + event.setReplacement(null); + Item replacementItem = this.plugin.itemManager().createWrappedItem(replacement, serverPlayer); + if (replacementItem != null) { + PlayerUtils.giveItem(serverPlayer, 1, replacementItem); + } } } } From d598cbc934816f45f3ec4c76458881b3def19ade Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 26 Nov 2025 16:19:16 +0800 Subject: [PATCH 017/135] Update ItemEventListener.java --- .../craftengine/bukkit/item/listener/ItemEventListener.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 b5c2cbac0..3c61cdf90 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 @@ -451,14 +451,12 @@ public class ItemEventListener implements Listener { if (event.getPlayer().getGameMode() != GameMode.CREATIVE) { Key replacement = customItem.settings().consumeReplacement(); if (wrapped.count() == 1) { - if (replacement == null) { - event.setReplacement(null); - } else { + if (replacement != null) { ItemStack replacementItem = this.plugin.itemManager().buildItemStack(replacement, serverPlayer); event.setReplacement(replacementItem); } } else { - event.setReplacement(null); + // fixme 如何取消堆叠数量>1的物品的默认replacement Item replacementItem = this.plugin.itemManager().createWrappedItem(replacement, serverPlayer); if (replacementItem != null) { PlayerUtils.giveItem(serverPlayer, 1, replacementItem); From d8eb4dfa17a42292bb4bae3dd412c4bbaa71c3ab Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 26 Nov 2025 16:31:58 +0800 Subject: [PATCH 018/135] Update AutoStateGroup.java --- .../momirealms/craftengine/core/block/AutoStateGroup.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/AutoStateGroup.java b/core/src/main/java/net/momirealms/craftengine/core/block/AutoStateGroup.java index 22bcf2b81..17e3aa1d6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/AutoStateGroup.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/AutoStateGroup.java @@ -10,21 +10,21 @@ import java.util.function.Predicate; public enum AutoStateGroup { NON_TINTABLE_LEAVES(List.of("no_tint_leaves", "leaves_no_tint", "non_tintable_leaves"), - Set.of(BlockKeys.AZALEA_LEAVES, BlockKeys.FLOWERING_AZALEA_LEAVES, BlockKeys.CHERRY_LEAVES, BlockKeys.PALE_OAK_LEAVES), + Set.of(BlockKeys.AZALEA_LEAVES, BlockKeys.FLOWERING_AZALEA_LEAVES, BlockKeys.CHERRY_LEAVES, BlockKeys.PALE_OAK_LEAVES, BlockKeys.SPRUCE_LEAVES, BlockKeys.BIRCH_LEAVES), (w) -> !(boolean) w.getProperty("waterlogged") ), WATERLOGGED_NON_TINTABLE_LEAVES( List.of("waterlogged_no_tint_leaves", "waterlogged_leaves_no_tint", "waterlogged_non_tintable_leaves"), - Set.of(BlockKeys.AZALEA_LEAVES, BlockKeys.FLOWERING_AZALEA_LEAVES, BlockKeys.CHERRY_LEAVES, BlockKeys.PALE_OAK_LEAVES), + Set.of(BlockKeys.AZALEA_LEAVES, BlockKeys.FLOWERING_AZALEA_LEAVES, BlockKeys.CHERRY_LEAVES, BlockKeys.PALE_OAK_LEAVES, BlockKeys.SPRUCE_LEAVES, BlockKeys.BIRCH_LEAVES), (w) -> w.getProperty("waterlogged") ), TINTABLE_LEAVES("tintable_leaves", - Set.of(BlockKeys.OAK_LEAVES, BlockKeys.SPRUCE_LEAVES, BlockKeys.BIRCH_LEAVES, BlockKeys.JUNGLE_LEAVES, BlockKeys.ACACIA_LEAVES, BlockKeys.DARK_OAK_LEAVES, BlockKeys.MANGROVE_LEAVES), + Set.of(BlockKeys.OAK_LEAVES, BlockKeys.JUNGLE_LEAVES, BlockKeys.ACACIA_LEAVES, BlockKeys.DARK_OAK_LEAVES, BlockKeys.MANGROVE_LEAVES), (w) -> !(boolean) w.getProperty("waterlogged") ), WATERLOGGED_TINTABLE_LEAVES( "waterlogged_tintable_leaves", - Set.of(BlockKeys.OAK_LEAVES, BlockKeys.SPRUCE_LEAVES, BlockKeys.BIRCH_LEAVES, BlockKeys.JUNGLE_LEAVES, BlockKeys.ACACIA_LEAVES, BlockKeys.DARK_OAK_LEAVES, BlockKeys.MANGROVE_LEAVES), + Set.of(BlockKeys.OAK_LEAVES, BlockKeys.JUNGLE_LEAVES, BlockKeys.ACACIA_LEAVES, BlockKeys.DARK_OAK_LEAVES, BlockKeys.MANGROVE_LEAVES), (w) -> w.getProperty("waterlogged") ), LEAVES("leaves", From 809ef571b1003f6f5b07706a58b67575a9f3aefe Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 26 Nov 2025 16:35:12 +0800 Subject: [PATCH 019/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=B4=AB=E9=A2=82?= =?UTF-8?q?=E6=A4=8D=E6=A0=AA=E8=87=AA=E5=8A=A8=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/momirealms/craftengine/core/block/AutoStateGroup.java | 1 + .../java/net/momirealms/craftengine/core/block/BlockKeys.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/AutoStateGroup.java b/core/src/main/java/net/momirealms/craftengine/core/block/AutoStateGroup.java index 17e3aa1d6..48edcfb58 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/AutoStateGroup.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/AutoStateGroup.java @@ -51,6 +51,7 @@ public enum AutoStateGroup { WEEPING_VINES(List.of("weeping_vines", "weeping_vine"), Set.of(BlockKeys.WEEPING_VINES), (w) -> true), TWISTING_VINES(List.of("twisting_vines", "twisting_vine"), Set.of(BlockKeys.TWISTING_VINES), (w) -> true), KELP("kelp", Set.of(BlockKeys.KELP), (w) -> true), + CHORUS("chorus", Set.of(BlockKeys.CHORUS_PLANT), (w) -> true), PRESSURE_PLATE("pressure_plate", Set.of(BlockKeys.LIGHT_WEIGHTED_PRESSURE_PLATE, BlockKeys.HEAVY_WEIGHTED_PRESSURE_PLATE), (w) -> true), SAPLING("sapling", Set.of(BlockKeys.OAK_SAPLING, BlockKeys.SPRUCE_SAPLING, BlockKeys.BIRCH_SAPLING, BlockKeys.JUNGLE_SAPLING, BlockKeys.ACACIA_SAPLING, BlockKeys.DARK_OAK_SAPLING, BlockKeys.CHERRY_SAPLING, BlockKeys.PALE_OAK_SAPLING), (w) -> true), MUSHROOM("mushroom", Set.of(BlockKeys.BROWN_MUSHROOM_BLOCK, BlockKeys.RED_MUSHROOM_BLOCK, BlockKeys.MUSHROOM_STEM), (w) -> true), 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 6679672ae..7f308db32 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 @@ -14,7 +14,6 @@ public final class BlockKeys { public static final Key TRIPWIRE = Key.of("minecraft:tripwire"); public static final Key CACTUS = Key.of("minecraft:cactus"); public static final Key POWDER_SNOW = Key.of("minecraft:powder_snow"); - // 功能方块 public static final Key CRAFTING_TABLE = Key.of("minecraft:crafting_table"); public static final Key STONECUTTER = Key.of("minecraft:stonecutter"); public static final Key CARTOGRAPHY_TABLE = Key.of("minecraft:cartography_table"); @@ -49,6 +48,7 @@ public final class BlockKeys { public static final Key WEEPING_VINES = Key.of("minecraft:weeping_vines"); public static final Key TWISTING_VINES = Key.of("minecraft:twisting_vines"); public static final Key KELP = Key.of("minecraft:kelp"); + public static final Key CHORUS_PLANT = Key.of("minecraft:chorus_plant"); public static final Key CHEST = Key.of("minecraft:chest"); public static final Key BARREL = Key.of("minecraft:barrel"); From 3f623eff1b5b935d9567a07350f5ba7dc35c6e91 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 26 Nov 2025 17:27:41 +0800 Subject: [PATCH 020/135] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E8=AF=AD=E8=A8=80=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/network/BukkitNetworkManager.java | 22 +++++++++++++ .../minecraft/NetworkReflections.java | 31 +++++++++++++------ .../plugin/user/BukkitServerPlayer.java | 20 ++++++++++-- .../core/entity/player/Player.java | 2 ++ 4 files changed, 63 insertions(+), 12 deletions(-) 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 4d7761189..ba62c7b36 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 @@ -358,6 +358,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerNMSPacketConsumer(new FinishConfigurationListener(), NetworkReflections.clazz$ClientboundFinishConfigurationPacket); registerNMSPacketConsumer(new LoginFinishedListener(), NetworkReflections.clazz$ClientboundLoginFinishedPacket); registerNMSPacketConsumer(new UpdateTagsListener(), NetworkReflections.clazz$ClientboundUpdateTagsPacket); + registerNMSPacketConsumer(new ClientInformationListener(), VersionHelper.isOrAbove1_20_2() ? NetworkReflections.clazz$ServerboundClientInformationPacket1 : NetworkReflections.clazz$ServerboundClientInformationPacket0); registerNMSPacketConsumer(new ContainerClickListener1_21_5(), VersionHelper.isOrAbove1_21_5() ? NetworkReflections.clazz$ServerboundContainerClickPacket : null); registerS2CGamePacketListener(new ForgetLevelChunkListener(), this.packetIds.clientboundForgetLevelChunkPacket(), "ClientboundForgetLevelChunkPacket"); registerS2CGamePacketListener(new SetScoreListener1_20_3(), VersionHelper.isOrAbove1_20_3() ? this.packetIds.clientboundSetScorePacket() : -1, "ClientboundSetScorePacket"); @@ -1288,6 +1289,27 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } } + public static class ClientInformationListener implements NMSPacketListener { + + @Override + public void onPacketReceive(NetWorkUser user, NMSPacketEvent event, Object packet) { + try { + if (VersionHelper.isOrAbove1_20_2()) { + Object clientInfo = NetworkReflections.field$ServerboundClientInformationPacket$information.get(packet); + if (clientInfo == null) return; + String locale = (String) CoreReflections.field$ClientInformation$language.get(clientInfo); + if (locale == null) return; + ((BukkitServerPlayer) user).setClientLocale(TranslationManager.parseLocale(locale)); + } else { + String locale = (String) NetworkReflections.field$ServerboundClientInformationPacket$language.get(packet); + if (locale == null) return; + ((BukkitServerPlayer) user).setClientLocale(TranslationManager.parseLocale(locale)); + } + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundClientInformationPacket", e); + } + } + } public static class SetCreativeSlotListener implements NMSPacketListener { 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 a8f62e3ac..9b1e00511 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 @@ -1087,20 +1087,31 @@ public final class NetworkReflections { ReflectionUtils.getDeclaredConstructor(clazz$ClientboundMoveEntityPacket$Pos, int.class, short.class, short.class, short.class, boolean.class) ); - // 1.20.2+ - public static final Class clazz$ServerboundClientInformationPacket = - ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundClientInformationPacket")); + // 1.20.1 + public static final Class clazz$ServerboundClientInformationPacket0 = + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.protocol.game.PacketPlayInSettings", + "network.protocol.game.ServerboundClientInformationPacket" + ); + + // 1.20.1 + public static final Field field$ServerboundClientInformationPacket$language = MiscUtils.requireNonNullIf(Optional.ofNullable(clazz$ServerboundClientInformationPacket0) + .map(it -> ReflectionUtils.getDeclaredField(it, String.class, 0)) + .orElse(null), !VersionHelper.isOrAbove1_20_2()); // 1.20.2+ - public static final Constructor constructor$ServerboundClientInformationPacket = Optional.ofNullable(clazz$ServerboundClientInformationPacket) - .map(it -> ReflectionUtils.getConstructor(it, 1)) - .orElse(null); + public static final Class clazz$ServerboundClientInformationPacket1 = MiscUtils.requireNonNullIf( + ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundClientInformationPacket")), + VersionHelper.isOrAbove1_20_2() + ); // 1.20.2+ - public static final Field field$ServerboundClientInformationPacket$information = Optional.ofNullable(clazz$ServerboundClientInformationPacket) - .map(it -> ReflectionUtils.getDeclaredField(it, 0)) - .orElse(null); - + public static final Field field$ServerboundClientInformationPacket$information = MiscUtils.requireNonNullIf( + Optional.ofNullable(clazz$ServerboundClientInformationPacket1) + .map(it -> ReflectionUtils.getDeclaredField(it, CoreReflections.clazz$ClientInformation, 0)) + .orElse(null), + VersionHelper.isOrAbove1_20_2() + ); public static final Class clazz$ClientboundSetTitleTextPacket = requireNonNull( ReflectionUtils.getClazz( 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 ca013aee3..60ed0d9e8 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 @@ -127,9 +127,11 @@ public class BukkitServerPlayer extends Player { private ConcurrentLong2ReferenceChainedHashTable trackedChunks; // entity view private Map entityTypeView; - // selected client locale + // 通过指令或api设定的语言 @Nullable private Locale selectedLocale; + // 客户端选择的语言 + private Locale clientLocale; // 存储客户端在发送停止破坏包前正在破坏的最后一个方块 private BlockPos lastStopMiningPos; // 修复连续挖掘的标志位 @@ -1233,7 +1235,21 @@ public class BukkitServerPlayer extends Player { @Override public Locale locale() { - return this.platformPlayer().locale(); + if (this.clientLocale != null) { + return this.clientLocale; + } else { + org.bukkit.entity.Player player = this.platformPlayer(); + if (player != null) { + return player.locale(); + } else { + return Locale.US; + } + } + } + + @Override + public void setClientLocale(Locale clientLocale) { + this.clientLocale = clientLocale; } @Override 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 c211bdd9f..034502a96 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 @@ -180,6 +180,8 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract Locale locale(); + public abstract void setClientLocale(Locale clientLocale); + public abstract Locale selectedLocale(); public abstract void setSelectedLocale(@Nullable Locale locale); From e00a803533c15690637013e214c13205af19eaeb Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 26 Nov 2025 19:46:59 +0800 Subject: [PATCH 021/135] =?UTF-8?q?=E9=81=BF=E5=85=8D=E5=9B=A0=E4=B8=BA?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E9=94=99=E8=AF=AF=E8=80=8C=E9=99=A4=E4=BB=A5?= =?UTF-8?q?0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/item/recipe/RecipeEventListener.java | 6 +++++- .../craftengine/core/item/ItemSettings.java | 11 ++++------- 2 files changed, 9 insertions(+), 8 deletions(-) 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 6e2035f84..82467401f 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 @@ -407,8 +407,12 @@ public class RecipeEventListener implements Listener { } boolean hasResult = true; - + int realDurabilityPerItem = (int) (repairItem.amount() + repairItem.percent() * maxDamage); + if (realDurabilityPerItem == 0) { + return; + } + int consumeMaxAmount = damage / realDurabilityPerItem + 1; int actualConsumedAmount = Math.min(consumeMaxAmount, wrappedSecond.count()); int actualRepairAmount = actualConsumedAmount * realDurabilityPerItem; 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 08e4310c5..186588e32 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 @@ -109,7 +109,7 @@ public class ItemSettings { newSettings.dyeColor = settings.dyeColor; newSettings.fireworkColor = settings.fireworkColor; newSettings.ingredientSubstitutes = settings.ingredientSubstitutes; - newSettings.customData = settings.customData; + newSettings.customData = new IdentityHashMap<>(settings.customData); return newSettings; } @@ -360,14 +360,11 @@ public class ItemSettings { return settings -> settings.renameable(bool); })); registerFactory("anvil-repair-item", (value -> { - @SuppressWarnings("unchecked") - List> materials = (List>) value; - List anvilRepairItemList = new ArrayList<>(); - for (Map material : materials) { + List anvilRepairItemList = ResourceConfigUtils.parseConfigAsList(value, material -> { int amount = ResourceConfigUtils.getAsInt(material.getOrDefault("amount", 0), "amount"); double percent = ResourceConfigUtils.getAsDouble(material.getOrDefault("percent", 0), "percent"); - anvilRepairItemList.add(new AnvilRepairItem(MiscUtils.getAsStringList(material.get("target")), amount, percent)); - } + return new AnvilRepairItem(MiscUtils.getAsStringList(material.get("target")), amount, percent); + }); return settings -> settings.repairItems(anvilRepairItemList); })); registerFactory("fuel-time", (value -> { From 56d57a286a67d6898744c036e7d6c442d4043c78 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Thu, 27 Nov 2025 15:54:14 +0800 Subject: [PATCH 022/135] =?UTF-8?q?=E7=BB=99=E5=88=86=E7=B1=BB=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=9D=83=E9=99=90=E6=8E=A7=E5=88=B6=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E5=8F=AF=E8=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/translations/en.yml | 1 + .../src/main/resources/translations/zh_cn.yml | 1 + .../core/plugin/gui/category/Category.java | 10 +++++++++- .../plugin/gui/category/ItemBrowserManagerImpl.java | 13 ++++++++++--- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index b3ce311aa..4f6dcab23 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -498,6 +498,7 @@ warning.config.function.transform_block.missing_block: "Issue found in f warning.config.function.cycle_block_property.missing_property: "Issue found in file - The config '' is missing the required 'property' argument for 'cycle_block_property' function." warning.config.function.set_exp.missing_count: "Issue found in file - The config '' is missing the required 'count' argument for 'set_exp' function." warning.config.function.set_level.missing_count: "Issue found in file - The config '' is missing the required 'count' argument for 'set_level' function." +warning.config.function.play_totem_animation.missing_item: "Issue found in file - The config '' is missing the required 'item' argument for 'play_totem_animation' function." 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 ''." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index da06eeff2..f175cdcd8 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -494,6 +494,7 @@ warning.config.function.transform_block.missing_block: "在文件 在文件 发现问题 - 配置项 '' 缺少 'cycle_block_property' 函数所需的 'property' 参数" warning.config.function.set_exp.missing_count: "在文件 发现问题 - 配置项 '' 缺少 'set_exp' 函数所需的 'count' 参数" warning.config.function.set_level.missing_count: "在文件 发现问题 - 配置项 '' 缺少 'set_level' 函数所需的 'count' 参数" +warning.config.function.play_totem_animation.missing_item: "在文件 发现问题 - 配置项 '' 缺少 'play_totem_animation' 函数所需的 'item' 参数" warning.config.selector.missing_type: "在文件 发现问题 - 配置项 '' 缺少选择器必需的 'type' 参数" warning.config.selector.invalid_type: "在文件 发现问题 - 配置项 '' 使用了无效的选择器类型 ''" warning.config.selector.invalid_target: "在文件 发现问题 - 配置项 '' 使用了无效的选择器目标 ''" 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 c4c18bb80..deb353317 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 @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.plugin.gui.category; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -14,8 +15,9 @@ public class Category implements Comparable { private final List members; private final int priority; private final boolean hidden; + private final String permission; - public Category(Key id, String displayName, List displayLore, Key icon, List members, int priority, boolean hidden) { + public Category(Key id, String displayName, List displayLore, Key icon, List members, int priority, boolean hidden, String permission) { this.id = id; this.displayName = displayName; this.members = new ArrayList<>(members); @@ -23,6 +25,7 @@ public class Category implements Comparable { this.priority = priority; this.displayLore = new ArrayList<>(displayLore); this.hidden = hidden; + this.permission = permission; } public void addMember(String member) { @@ -45,6 +48,11 @@ public class Category implements Comparable { return hidden; } + @Nullable + public String permission() { + return permission; + } + public List displayLore() { return displayLore; } 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 b4d6d30f4..ee60fa95a 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 @@ -115,8 +115,10 @@ 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, new ArrayList<>(members), priority, - ResourceConfigUtils.getAsBoolean(section.getOrDefault("hidden", false), "hidden")); + List lore = MiscUtils.getAsStringList(section.getOrDefault("lore", List.of())); + boolean hidden = ResourceConfigUtils.getAsBoolean(section.getOrDefault("hidden", false), "hidden"); + String permission = ResourceConfigUtils.getAsStringOrNull(section.get("permission")); + Category category = new Category(id, name, lore, icon, new ArrayList<>(members), priority, hidden, permission); if (ItemBrowserManagerImpl.this.byId.containsKey(id)) { ItemBrowserManagerImpl.this.byId.get(id).merge(category); } else { @@ -157,6 +159,9 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { ); List iconList = this.categoryOnMainPage.stream().map(it -> { + if (it.permission() != null && !player.hasPermission(it.permission())) { + return null; + } Item item = this.plugin.itemManager().createWrappedItem(it.icon(), player); if (ItemUtils.isEmpty(item)) { this.plugin.logger().warn("Can't not find item " + it.icon() + " for category icon"); @@ -245,6 +250,8 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { 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 if (subCategory.permission() != null && !player.hasPermission(subCategory.permission())) { + return null; } else { item = this.plugin.itemManager().createWrappedItem(subCategory.icon(), player); if (ItemUtils.isEmpty(item)) { @@ -317,7 +324,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }); } - }).toList(); + }).filter(Objects::nonNull).toList(); PagedGui.builder() .addIngredients(itemList) From 367b689cc5c46fe89686d1a25630e60a70d47377 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 27 Nov 2025 16:08:01 +0800 Subject: [PATCH 023/135] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=BF=98=E5=8E=9F?= =?UTF-8?q?=E5=8E=9F=E7=89=88=E6=96=B9=E5=9D=97=E7=9B=AE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/block/BukkitBlockManager.java | 9 +++++-- .../block/BukkitCustomBlockStateWrapper.java | 2 +- .../block/behavior/BukkitBlockBehavior.java | 2 +- .../block/behavior/CropBlockBehavior.java | 4 +-- .../behavior/FenceGateBlockBehavior.java | 2 +- .../block/behavior/GrassBlockBehavior.java | 4 +-- .../block/behavior/SaplingBlockBehavior.java | 4 +-- .../block/behavior/TrapDoorBlockBehavior.java | 2 +- .../bukkit/item/behavior/AxeItemBehavior.java | 2 +- .../item/behavior/BlockItemBehavior.java | 2 +- .../behavior/FlintAndSteelItemBehavior.java | 4 +-- .../item/listener/ItemEventListener.java | 6 ++--- .../feature/DebugCleanCacheCommand.java | 2 +- .../plugin/injector/WorldStorageInjector.java | 6 ++--- .../plugin/network/BukkitNetworkManager.java | 4 +-- .../protocol/VisualBlockStatePacket.java | 2 +- .../plugin/user/BukkitServerPlayer.java | 4 +-- .../bukkit/world/BukkitWorldManager.java | 10 +++++--- .../core/block/AbstractBlockManager.java | 6 ++--- .../craftengine/core/block/BlockKeys.java | 1 + .../core/block/ImmutableBlockState.java | 25 ++++++++++++++++--- 21 files changed, 65 insertions(+), 38 deletions(-) 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 10c77c589..ce09c5bff 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 @@ -223,7 +223,7 @@ public final class BukkitBlockManager extends AbstractBlockManager { protected void applyPlatformSettings(ImmutableBlockState state) { DelegatingBlockState nmsState = (DelegatingBlockState) state.customBlockState().literalObject(); nmsState.setBlockState(state); - Object nmsVisualState = state.vanillaBlockState().literalObject(); + Object nmsVisualState = state.visualBlockState().literalObject(); BlockSettings settings = state.settings(); try { @@ -291,9 +291,14 @@ public final class BukkitBlockManager extends AbstractBlockManager { this.burnableBlocks.add(nmsBlock); } - Key vanillaBlockId = state.vanillaBlockState().ownerId(); + Key vanillaBlockId = state.visualBlockState().ownerId(); BlockGenerator.field$CraftEngineBlock$isNoteBlock().set(nmsBlock, vanillaBlockId.equals(BlockKeys.NOTE_BLOCK)); BlockGenerator.field$CraftEngineBlock$isTripwire().set(nmsBlock, vanillaBlockId.equals(BlockKeys.TRIPWIRE)); + if (vanillaBlockId.equals(BlockKeys.BARRIER)) { + state.setRestoreBlockState(createBlockState("minecraft:glass")); + } else { + state.setRestoreBlockState(state.visualBlockState()); + } } catch (ReflectiveOperationException e) { this.plugin.logger().warn("Failed to apply platform block settings for block state " + state, e); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java index 53d9cbfb0..907fa26c6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java @@ -20,7 +20,7 @@ public class BukkitCustomBlockStateWrapper extends AbstractBlockStateWrapper imp @Override public BlockStateWrapper visualBlockState() { - return getImmutableBlockState().map(ImmutableBlockState::vanillaBlockState).orElse(null); + return getImmutableBlockState().map(ImmutableBlockState::visualBlockState).orElse(null); } @Override 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 ea36861b8..9bc29f2da 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 @@ -180,7 +180,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior { public boolean isPathFindable(Object thisBlock, Object[] args, Callable superMethod) throws Exception { Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(args[0]); if (optionalCustomState.isEmpty()) return false; - BlockStateWrapper vanillaState = optionalCustomState.get().vanillaBlockState(); + BlockStateWrapper vanillaState = optionalCustomState.get().visualBlockState(); if (vanillaState == null) return false; 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/CropBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java index 7ae23af7f..066a13178 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 @@ -135,7 +135,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior { if (isMaxAge(state)) return InteractionResult.PASS; boolean sendSwing = false; - Object visualState = state.vanillaBlockState().literalObject(); + Object visualState = state.visualBlockState().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); @@ -158,7 +158,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior { } ImmutableBlockState customState = optionalCustomState.get(); boolean sendParticles = false; - Object visualState = customState.vanillaBlockState().literalObject(); + Object visualState = customState.visualBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, pos, visualState); 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 27ffdea31..697b84624 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 @@ -147,7 +147,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior implements IsPat Player player = context.getPlayer(); if (player == null) return; this.toggle(state, context.getLevel(), context.getClickedPos(), player); - if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().literalObject()), context.getHitResult(), (Item) context.getItem())) { + if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.visualBlockState().literalObject()), context.getHitResult(), (Item) context.getItem())) { player.swingHand(context.getHand()); } } 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 a53af5459..359ff4a9c 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 @@ -60,7 +60,7 @@ public class GrassBlockBehavior extends BukkitBlockBehavior { } boolean sendParticles = false; ImmutableBlockState customState = optionalCustomState.get(); - Object visualState = customState.vanillaBlockState().literalObject(); + Object visualState = customState.visualBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, blockPos, visualState); @@ -93,7 +93,7 @@ public class GrassBlockBehavior extends BukkitBlockBehavior { if (!block.isEmpty()) return InteractionResult.PASS; boolean sendSwing = false; - Object visualState = state.vanillaBlockState().literalObject(); + Object visualState = state.visualBlockState().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/SaplingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java index 9f13df44c..3d53ce579 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 @@ -115,7 +115,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior { } ImmutableBlockState customState = optionalCustomState.get(); boolean sendParticles = false; - Object visualState = customState.vanillaBlockState().literalObject(); + Object visualState = customState.visualBlockState().literalObject(); Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState); if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) { boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, blockPos, visualState); @@ -153,7 +153,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior { if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || player == null || player.isAdventureMode()) return InteractionResult.PASS; boolean sendSwing = false; - Object visualState = state.vanillaBlockState().literalObject(); + Object visualState = state.visualBlockState().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/TrapDoorBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/TrapDoorBlockBehavior.java index c297d3289..4339bf882 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 @@ -120,7 +120,7 @@ public class TrapDoorBlockBehavior extends BukkitBlockBehavior implements IsPath Player player = context.getPlayer(); if (player == null) return; this.toggle(state, context.getLevel(), context.getClickedPos(), player); - if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().literalObject()), context.getHitResult(), (Item) context.getItem())) { + if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.visualBlockState().literalObject()), context.getHitResult(), (Item) context.getItem())) { player.swingHand(context.getHand()); } } 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 699fb7975..619567244 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 @@ -101,7 +101,7 @@ public class AxeItemBehavior extends ItemBehavior { // resend swing if it's not interactable on client side if (!InteractUtils.isInteractable( - bukkitPlayer, BlockStateUtils.fromBlockData(customState.vanillaBlockState().literalObject()), + bukkitPlayer, BlockStateUtils.fromBlockData(customState.visualBlockState().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 add1679d3..6d0e7871d 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 @@ -112,7 +112,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().literalObject() : againstBlockState)) { + if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, Config.simplifyAdventurePlaceCheck() ? customState.visualBlockState().literalObject() : againstBlockState)) { return InteractionResult.FAIL; } } 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 b734543f3..70ac737e6 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 @@ -77,10 +77,10 @@ public class FlintAndSteelItemBehavior extends ItemBehavior { // 点击对象为自定义方块 ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId); // 原版外观也可燃 - if (BlockStateUtils.isBurnable(immutableBlockState.vanillaBlockState().literalObject())) { + if (BlockStateUtils.isBurnable(immutableBlockState.visualBlockState().literalObject())) { return InteractionResult.PASS; } - BlockData vanillaBlockState = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject()); + BlockData vanillaBlockState = BlockStateUtils.fromBlockData(immutableBlockState.visualBlockState().literalObject()); // 点击的是方块上面,则只需要判断shift和可交互 if (direction == Direction.UP) { // 客户端层面必须可交互 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 3c61cdf90..88f5173de 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 @@ -167,7 +167,7 @@ public class ItemEventListener implements Listener { // fix client side issues if (action.isRightClick() && hitResult != null && - InteractUtils.canPlaceVisualBlock(player, BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject()), hitResult, itemInHand)) { + InteractUtils.canPlaceVisualBlock(player, BlockStateUtils.fromBlockData(immutableBlockState.visualBlockState().literalObject()), hitResult, itemInHand)) { player.updateInventory(); } @@ -272,13 +272,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().literalObject()); + BlockData craftBlockData = BlockStateUtils.fromBlockData(immutableBlockState.visualBlockState().literalObject()); if (InteractUtils.isInteractable(player, craftBlockData, hitResult, itemInHand)) { if (!serverPlayer.isSecondaryUseActive()) { serverPlayer.setResendSound(); } } else { - if (BlockStateUtils.isReplaceable(immutableBlockState.customBlockState().literalObject()) && !BlockStateUtils.isReplaceable(immutableBlockState.vanillaBlockState().literalObject())) { + if (BlockStateUtils.isReplaceable(immutableBlockState.customBlockState().literalObject()) && !BlockStateUtils.isReplaceable(immutableBlockState.visualBlockState().literalObject())) { serverPlayer.setResendSwing(); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugCleanCacheCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugCleanCacheCommand.java index 126e06e3e..910ecf5c2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugCleanCacheCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugCleanCacheCommand.java @@ -78,7 +78,7 @@ public class DebugCleanCacheCommand extends BukkitCommandFeature Set ids = new HashSet<>(); for (CustomBlock customBlock : instance.loadedBlocks().values()) { for (ImmutableBlockState state : customBlock.variantProvider().states()) { - ids.add(state.vanillaBlockState()); + ids.add(state.visualBlockState()); } } VisualBlockStateAllocator visualBlockStateAllocator = instance.blockParser().visualBlockStateAllocator(); 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 0d90bceef..ed69ec3a4 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 @@ -283,10 +283,10 @@ public final class WorldStorageInjector { if (Config.enableLightSystem()) { if (previousImmutableBlockState.isEmpty()) { // 原版块到自定义块,只需要判断新块是否和客户端视觉一致 - updateLight(holder, newImmutableBlockState.vanillaBlockState().literalObject(), newState, x, y, z); + updateLight(holder, newImmutableBlockState.visualBlockState().literalObject(), newState, x, y, z); } else { // 自定义块到自定义块 - updateLight$complex(holder, newImmutableBlockState.vanillaBlockState().literalObject(), newState, previousState, x, y, z); + updateLight$complex(holder, newImmutableBlockState.visualBlockState().literalObject(), newState, previousState, x, y, z); } } } else { @@ -311,7 +311,7 @@ public final class WorldStorageInjector { } if (Config.enableLightSystem()) { // 自定义块到原版块,只需要判断旧块是否和客户端一直 - BlockStateWrapper wrapper = previous.vanillaBlockState(); + BlockStateWrapper wrapper = previous.visualBlockState(); if (wrapper != null) { updateLight(holder, wrapper.literalObject(), previousState, x, y, z); } 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 ba62c7b36..563a5a6e4 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 @@ -1096,7 +1096,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes if (player.isAdventureMode()) { if (Config.simplifyAdventureBreakCheck()) { ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId); - if (!player.canBreak(pos, state.vanillaBlockState().literalObject())) { + if (!player.canBreak(pos, state.visualBlockState().literalObject())) { player.preventMiningBlock(); return; } @@ -1358,7 +1358,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes Key itemId = state.settings().itemId(); // no item available if (itemId == null) return; - Object vanillaBlock = FastNMS.INSTANCE.method$BlockState$getBlock(state.vanillaBlockState().literalObject()); + Object vanillaBlock = FastNMS.INSTANCE.method$BlockState$getBlock(state.visualBlockState().literalObject()); Object vanillaBlockItem = FastNMS.INSTANCE.method$Block$asItem(vanillaBlock); if (vanillaBlockItem == null) return; Key addItemId = KeyUtils.namespacedKey2Key(item.getType().getKey()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/VisualBlockStatePacket.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/VisualBlockStatePacket.java index bed116f3e..19d48a0bf 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/VisualBlockStatePacket.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/VisualBlockStatePacket.java @@ -103,7 +103,7 @@ public record VisualBlockStatePacket(int[] data) implements ModPacket { for (int i = 0; i < serverSideBlockCount; i++) { ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(i + vanillaBlockStateCount); if (state.isEmpty()) continue; - mappings[state.customBlockState().registryId() - vanillaBlockStateCount] = state.vanillaBlockState().registryId(); + mappings[state.customBlockState().registryId() - vanillaBlockStateCount] = state.visualBlockState().registryId(); } return new VisualBlockStatePacket(mappings); } 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 60ed0d9e8..d6bb5a6f8 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 @@ -646,7 +646,7 @@ public class BukkitServerPlayer extends Player { // instant break boolean custom = immutableBlockState != null; if (custom && getDestroyProgress(state, pos) >= 1f) { - BlockStateWrapper vanillaBlockState = immutableBlockState.vanillaBlockState(); + BlockStateWrapper vanillaBlockState = immutableBlockState.visualBlockState(); // if it's not an instant break on client side, we should resend level event if (vanillaBlockState != null && getDestroyProgress(vanillaBlockState.literalObject(), pos) < 1f) { Object levelEventPacket = FastNMS.INSTANCE.constructor$ClientboundLevelEventPacket( @@ -811,7 +811,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().literalObject())) { + if (canBreak(hitPos, customState.visualBlockState().literalObject())) { // Error might occur so we use try here try { FastNMS.INSTANCE.field$Player$mayBuild(serverPlayer, true); 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 082ae0d6c..c54cb0d55 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 @@ -6,6 +6,7 @@ import net.momirealms.craftengine.bukkit.plugin.injector.WorldStorageInjector; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.block.BlockStateWrapper; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; @@ -296,9 +297,12 @@ public class BukkitWorldManager implements WorldManager, Listener { for (int z = 0; z < 16; z++) { for (int y = 0; y < 16; y++) { ImmutableBlockState customState = ceSection.getBlockState(x, y, z); - if (!customState.isEmpty() && customState.vanillaBlockState() != null) { - FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, customState.vanillaBlockState().literalObject(), false); - unsaved = true; + if (!customState.isEmpty()) { + BlockStateWrapper wrapper = customState.restoreBlockState(); + if (wrapper != null) { + FastNMS.INSTANCE.method$LevelChunkSection$setBlockState(section, x, y, z, wrapper.literalObject(), false); + unsaved = true; + } } } } 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 66abb09a2..748d27ea8 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 @@ -631,7 +631,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem continue; } for (ImmutableBlockState possibleState : possibleStates) { - possibleState.setVanillaBlockState(appearance.blockState()); + possibleState.setVisualBlockState(appearance.blockState()); appearance.blockEntityRenderer().ifPresent(possibleState::setConstantRenderers); } } @@ -650,11 +650,11 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem } state.setBehavior(blockBehavior); int internalId = state.customBlockState().registryId(); - BlockStateWrapper visualState = state.vanillaBlockState(); + BlockStateWrapper visualState = state.visualBlockState(); // 校验,为未绑定外观的强行添加外观 if (visualState == null) { visualState = anyAppearance.blockState(); - state.setVanillaBlockState(visualState); + state.setVisualBlockState(visualState); anyAppearance.blockEntityRenderer().ifPresent(state::setConstantRenderers); } int appearanceId = visualState.registryId(); 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 7f308db32..59ea7c2a0 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 @@ -49,6 +49,7 @@ public final class BlockKeys { public static final Key TWISTING_VINES = Key.of("minecraft:twisting_vines"); public static final Key KELP = Key.of("minecraft:kelp"); public static final Key CHORUS_PLANT = Key.of("minecraft:chorus_plant"); + public static final Key BARRIER = Key.of("minecraft:barrier"); public static final Key CHEST = Key.of("minecraft:chest"); public static final Key BARREL = Key.of("minecraft:barrel"); 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 2c837d796..8e4430e2e 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 @@ -20,6 +20,7 @@ import net.momirealms.craftengine.core.world.World; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.NBT; import net.momirealms.sparrow.nbt.Tag; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,7 +34,9 @@ public final class ImmutableBlockState { private CompoundTag tag; private BlockStateWrapper customBlockState; - private BlockStateWrapper vanillaBlockState; + private BlockStateWrapper visualBlockState; + // 安全的,在卸载ce后还原的方块 + private BlockStateWrapper restoreBlockState; private BlockBehavior behavior; private BlockSettings settings; private BlockEntityType blockEntityType; @@ -96,16 +99,30 @@ public final class ImmutableBlockState { return this.customBlockState; } + @Deprecated public BlockStateWrapper vanillaBlockState() { - return this.vanillaBlockState; + return this.visualBlockState; + } + + public BlockStateWrapper visualBlockState() { + return this.visualBlockState; + } + + @ApiStatus.Internal + public BlockStateWrapper restoreBlockState() { + return this.restoreBlockState; } public void setCustomBlockState(@NotNull BlockStateWrapper customBlockState) { this.customBlockState = customBlockState; } - public void setVanillaBlockState(@NotNull BlockStateWrapper vanillaBlockState) { - this.vanillaBlockState = vanillaBlockState; + public void setVisualBlockState(@NotNull BlockStateWrapper visualBlockState) { + this.visualBlockState = visualBlockState; + } + + public void setRestoreBlockState(BlockStateWrapper restoreBlockState) { + this.restoreBlockState = restoreBlockState; } public CompoundTag getNbtToSave() { From c691f6a06466ed1072f9bb3a9706d52e6944b3f7 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 27 Nov 2025 16:13:39 +0800 Subject: [PATCH 024/135] =?UTF-8?q?=E5=BA=94=E9=81=BF=E5=85=8D=E5=AE=9E?= =?UTF-8?q?=E6=97=B6=E8=8E=B7=E5=8F=96property?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/block/behavior/SeatBlockBehavior.java | 2 +- .../craftengine/bukkit/block/entity/SeatBlockEntity.java | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SeatBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SeatBlockBehavior.java index c54f6eb66..e1c68747b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SeatBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SeatBlockBehavior.java @@ -33,7 +33,7 @@ public class SeatBlockBehavior extends BukkitBlockBehavior implements EntityBloc @Override public BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state) { - return new SeatBlockEntity(pos, state, this.seats); + return new SeatBlockEntity(pos, state, this.seats, this.directionProperty); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SeatBlockEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SeatBlockEntity.java index 421e8f94e..5ff7a8812 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SeatBlockEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SeatBlockEntity.java @@ -15,10 +15,12 @@ import net.momirealms.sparrow.nbt.CompoundTag; public class SeatBlockEntity extends BlockEntity implements SeatOwner { private final Seat[] seats; + private final Property facing; @SuppressWarnings("unchecked") - public SeatBlockEntity(BlockPos pos, ImmutableBlockState blockState, SeatConfig[] seats) { + public SeatBlockEntity(BlockPos pos, ImmutableBlockState blockState, SeatConfig[] seats, Property directionProperty) { super(BukkitBlockEntityTypes.SEAT, pos, blockState); + this.facing = directionProperty; this.seats = new Seat[seats.length]; for (int i = 0; i < seats.length; i++) { this.seats[i] = new BukkitSeat<>(this, seats[i]); @@ -38,10 +40,9 @@ public class SeatBlockEntity extends BlockEntity implements SeatOwner { } public boolean spawnSeat(Player player) { - Property facing = super.blockState.owner().value().getProperty("facing"); int yRot = 0; - if (facing != null && facing.valueClass() == HorizontalDirection.class) { - HorizontalDirection direction = (HorizontalDirection) super.blockState.get(facing); + if (this.facing != null) { + HorizontalDirection direction = super.blockState.get(facing); yRot = switch (direction) { case NORTH -> 0; case SOUTH -> 180; From 88fea6efd5b2a0fcbb235c73020e2831b3140071 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Thu, 27 Nov 2025 16:34:23 +0800 Subject: [PATCH 025/135] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=88=90=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/plugin/gui/category/Category.java | 12 +++++++----- .../gui/category/ItemBrowserManagerImpl.java | 18 +++++++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) 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 deb353317..639ccbbfc 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 @@ -1,5 +1,7 @@ package net.momirealms.craftengine.core.plugin.gui.category; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -15,9 +17,9 @@ public class Category implements Comparable { private final List members; private final int priority; private final boolean hidden; - private final String permission; + private final Condition condition; - public Category(Key id, String displayName, List displayLore, Key icon, List members, int priority, boolean hidden, String permission) { + public Category(Key id, String displayName, List displayLore, Key icon, List members, int priority, boolean hidden, Condition condition) { this.id = id; this.displayName = displayName; this.members = new ArrayList<>(members); @@ -25,7 +27,7 @@ public class Category implements Comparable { this.priority = priority; this.displayLore = new ArrayList<>(displayLore); this.hidden = hidden; - this.permission = permission; + this.condition = condition; } public void addMember(String member) { @@ -49,8 +51,8 @@ public class Category implements Comparable { } @Nullable - public String permission() { - return permission; + public Condition condition() { + return condition; } public List displayLore() { 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 ee60fa95a..4b1446046 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 @@ -13,8 +13,12 @@ import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.condition.AllOfCondition; +import net.momirealms.craftengine.core.plugin.context.event.EventConditions; import net.momirealms.craftengine.core.plugin.gui.*; import net.momirealms.craftengine.core.plugin.gui.Ingredient; import net.momirealms.craftengine.core.util.*; @@ -117,8 +121,9 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { int priority = ResourceConfigUtils.getAsInt(section.getOrDefault("priority", 0), "priority"); List lore = MiscUtils.getAsStringList(section.getOrDefault("lore", List.of())); boolean hidden = ResourceConfigUtils.getAsBoolean(section.getOrDefault("hidden", false), "hidden"); - String permission = ResourceConfigUtils.getAsStringOrNull(section.get("permission")); - Category category = new Category(id, name, lore, icon, new ArrayList<>(members), priority, hidden, permission); + List> conditionList = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(section, "conditions", "condition"), EventConditions::fromMap); + Condition conditions = conditionList.isEmpty() ? null : conditionList.size() == 1 ? conditionList.getFirst() : new AllOfCondition<>(conditionList); + Category category = new Category(id, name, lore, icon, new ArrayList<>(members), priority, hidden, conditions); if (ItemBrowserManagerImpl.this.byId.containsKey(id)) { ItemBrowserManagerImpl.this.byId.get(id).merge(category); } else { @@ -159,7 +164,8 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { ); List iconList = this.categoryOnMainPage.stream().map(it -> { - if (it.permission() != null && !player.hasPermission(it.permission())) { + Condition condition = it.condition(); + if (condition != null && !condition.test(PlayerOptionalContext.of(player))) { return null; } Item item = this.plugin.itemManager().createWrappedItem(it.icon(), player); @@ -250,9 +256,11 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { 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 if (subCategory.permission() != null && !player.hasPermission(subCategory.permission())) { - return null; } else { + Condition condition = subCategory.condition(); + if (condition != null && !condition.test(PlayerOptionalContext.of(player))) { + return null; + } item = this.plugin.itemManager().createWrappedItem(subCategory.icon(), player); if (ItemUtils.isEmpty(item)) { if (!subCategory.icon().equals(ItemKeys.AIR)) { From 37bc06c80dbd592fded09d90cdd182437dbf6cc0 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Thu, 27 Nov 2025 16:57:08 +0800 Subject: [PATCH 026/135] =?UTF-8?q?=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/translations/en.yml | 2 +- common-files/src/main/resources/translations/zh_cn.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 4f6dcab23..71c690f2d 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -175,7 +175,7 @@ warning.config.template.argument.self_increase_int.invalid_range: "Issue warning.config.template.argument.to_upper_case.invalid_locale: "Issue found in file - The template '' is using an invalid locale '' in 'to_upper_case' argument." warning.config.template.argument.to_upper_case.missing_value: "Issue found in file - The template '' is missing the required 'value' argument for 'to_upper_case' argument." warning.config.template.argument.to_lower_case.invalid_locale: "Issue found in file - The template '' is using an invalid locale '' in 'to_lower_case' argument." -warning.config.template.argument.to_lower_case.missing_value: "Issue found in file - The template '' is missing the required 'value' argument for 'to_upper_case' argument." +warning.config.template.argument.to_lower_case.missing_value: "Issue found in file - The template '' is missing the required 'value' argument for 'to_lower_case' argument." warning.config.template.argument.list.invalid_type: "Issue found in file - The template '' is using a 'list' argument which expects a 'List' as argument while the input argument is a(n) ''." warning.config.template.argument.missing_value: "Issue found in file - The config '' is missing the template argument for ''. Please use the arguments option to configure or set a default value for this parameter." warning.config.vanilla_loot.missing_type: "Issue found in file - The vanilla loot '' is missing the required 'type' argument." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index f175cdcd8..ab537a422 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -172,6 +172,10 @@ warning.config.translation.unknown_locale: "在文件 发现问 warning.config.template.duplicate: "在文件 发现问题 - 重复的模板 '' 请检查其他文件中是否存在相同配置" warning.config.template.invalid: "在文件 发现问题 - 配置项 '' 使用了无效的模板 ''" warning.config.template.argument.self_increase_int.invalid_range: "在文件 发现问题 - 模板 '' 在 'self_increase_int' 参数中使用了一个起始值 '' 大于终止值 ''" +warning.config.template.argument.to_upper_case.invalid_locale: "在文件 发现问题 - 模板 '' 在 'to_upper_case' 参数中使用了无效的区域设置 ''" +warning.config.template.argument.to_upper_case.missing_value: "在文件 发现问题 - 模板 '' 缺少 'to_upper_case' 参数所需的 'value' 参数" +warning.config.template.argument.to_lower_case.invalid_locale: "在文件 发现问题 - 模板 '' 在 'to_lower_case' 参数中使用了无效的区域设置 ''" +warning.config.template.argument.to_lower_case.missing_value: "在文件 发现问题 - 模板 '' 缺少 'to_lower_case' 参数所需的 'value' 参数" warning.config.template.argument.list.invalid_type: "在文件 发现问题 - 模板 '' 的 'list' 参数需要列表类型 但输入参数类型为 ''" warning.config.template.argument.missing_value: "在文件 发现问题 - 配置项 '' 缺少了 '' 必要的模板参数值. 请使用 arguments 选项进行配置或为此参数设定默认值" warning.config.vanilla_loot.missing_type: "在文件 发现问题 - 原版战利品 '' 缺少必需的 'type' 参数" From af989b4099aafb92165fc5857a697783a0b06082 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 27 Nov 2025 17:05:14 +0800 Subject: [PATCH 027/135] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E7=AA=92=E6=81=AF?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E9=BB=98=E8=AE=A4=E5=80=BC=EF=BC=8C=E4=BB=8E?= =?UTF-8?q?=E8=80=8C=E9=97=B4=E6=8E=A5=E4=BF=AE=E5=A4=8Dview=20blocking?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/block/BukkitBlockManager.java | 30 ++++++++++++++++++- .../reflection/minecraft/CoreReflections.java | 22 ++++++++++++++ .../craftengine/bukkit/world/BukkitWorld.java | 5 +--- .../core/block/AbstractBlockManager.java | 7 +++++ .../core/block/BlockRegistryMirror.java | 4 +++ 5 files changed, 63 insertions(+), 5 deletions(-) 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 ce09c5bff..c250782e5 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 @@ -42,6 +42,7 @@ import java.util.*; public final class BukkitBlockManager extends AbstractBlockManager { public static final Set CLIENT_SIDE_NOTE_BLOCKS = new HashSet<>(2048, 0.6f); + private static final Object BLOCK_POS$ZERO = LocationUtils.toBlockPos(0,0,0); private static final Object ALWAYS_FALSE = FastNMS.INSTANCE.method$StatePredicate$always(false); private static final Object ALWAYS_TRUE = FastNMS.INSTANCE.method$StatePredicate$always(true); private static BukkitBlockManager instance; @@ -80,10 +81,12 @@ public final class BukkitBlockManager extends AbstractBlockManager { @Override public void init() { + super.init(); this.initMirrorRegistry(); this.initFireBlock(); this.deceiveBukkitRegistry(); this.markVanillaNoteBlocks(); + this.findViewBlockingVanillaBlocks(); Arrays.fill(this.immutableBlockStates, EmptyBlock.INSTANCE.defaultState()); this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings); // 一定要预先初始化一次,预防id超出上限 } @@ -240,8 +243,15 @@ public final class BukkitBlockManager extends AbstractBlockManager { boolean useShapeForLightOcclusion = settings.useShapeForLightOcclusion() == Tristate.UNDEFINED ? CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.getBoolean(nmsVisualState) : settings.useShapeForLightOcclusion().asBoolean(); CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.set(nmsState, useShapeForLightOcclusion); CoreReflections.field$BlockStateBase$isRedstoneConductor.set(nmsState, settings.isRedstoneConductor().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE); + + boolean suffocating = settings.isSuffocating() == Tristate.UNDEFINED ? (canBlockView(state.visualBlockState())) : (settings.isSuffocating().asBoolean()); + CoreReflections.field$BlockStateBase$isSuffocating.set(nmsState, suffocating ? ALWAYS_TRUE : ALWAYS_FALSE); CoreReflections.field$BlockStateBase$isSuffocating.set(nmsState, settings.isSuffocating().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE); - CoreReflections.field$BlockStateBase$isViewBlocking.set(nmsState, settings.isViewBlocking() == Tristate.UNDEFINED ? settings.isSuffocating().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE : (settings.isViewBlocking().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE)); + CoreReflections.field$BlockStateBase$isViewBlocking.set(nmsState, + settings.isViewBlocking() == Tristate.UNDEFINED ? + (suffocating ? ALWAYS_TRUE : ALWAYS_FALSE) : + (settings.isViewBlocking().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE) + ); DelegatingBlock nmsBlock = (DelegatingBlock) BlockStateUtils.getBlockOwner(nmsState); ObjectHolder shapeHolder = nmsBlock.shapeDelegate(); @@ -299,6 +309,8 @@ public final class BukkitBlockManager extends AbstractBlockManager { } else { state.setRestoreBlockState(state.visualBlockState()); } + // 根据客户端的状态决定其是否阻挡视线 + super.viewBlockingBlocks[state.customBlockState().registryId()] = canBlockView(state.visualBlockState()); } catch (ReflectiveOperationException e) { this.plugin.logger().warn("Failed to apply platform block settings for block state " + state, e); } @@ -384,6 +396,22 @@ public final class BukkitBlockManager extends AbstractBlockManager { } } + public boolean canBlockView(BlockStateWrapper blockState) { + if (!BlockStateUtils.isOcclude(blockState)) { + return false; + } + return FastNMS.INSTANCE.method$BlockStateBase$isCollisionShapeFullBlock(blockState, CoreReflections.instance$EmptyBlockGetter$INSTANCE, BLOCK_POS$ZERO); + } + + private void findViewBlockingVanillaBlocks() { + for (int i = 0; i < this.vanillaBlockStateCount; i++) { + BlockStateWrapper blockState = BlockRegistryMirror.byId(i); + if (canBlockView(blockState)) { + this.viewBlockingBlocks[i] = true; + } + } + } + @Override protected void setVanillaBlockTags(Key id, List tags) { Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(id)); 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 4bef1277b..f92d2c2cd 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 @@ -4572,4 +4572,26 @@ public final class CoreReflections { "world.level.levelgen.feature.Feature" ) ); + + public static final Class clazz$EmptyBlockGetter = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.level.BlockAccessAir", + "world.level.EmptyBlockGetter" + ) + ); + + public static final Method method$EmptyBlockGetter$values = requireNonNull( + ReflectionUtils.getStaticMethod(clazz$EmptyBlockGetter, clazz$EmptyBlockGetter.arrayType()) + ); + + public static final Object instance$EmptyBlockGetter$INSTANCE; + + static { + try { + Object[] values = (Object[]) method$EmptyBlockGetter$values.invoke(null); + instance$EmptyBlockGetter$INSTANCE = values[0]; + } catch (ReflectiveOperationException e) { + throw new ReflectionInitException("Failed to init EmptyBlockGetter$INSTANCE", e); + } + } } 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 66df48ee3..91d168666 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 @@ -24,10 +24,7 @@ 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; +import java.util.*; public class BukkitWorld implements World { private final WeakReference world; 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 748d27ea8..c0f427de8 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 @@ -85,6 +85,8 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem // 临时存储哪些视觉方块被使用了 protected final Set tempVisualBlockStatesInUse = new HashSet<>(); protected final Set tempVisualBlocksInUse = new HashSet<>(); + // 能遮挡视线的方块 + protected final boolean[] viewBlockingBlocks; // 声音映射表,和使用了哪些视觉方块有关 protected Map soundReplacements = Map.of(); // 是否使用了透明方块模型 @@ -102,6 +104,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem this.tempVanillaBlockStateModels = new JsonElement[vanillaBlockStateCount]; this.blockParser = new BlockParser(this.autoVisualBlockStateCandidates); this.blockStateMappingParser = new BlockStateMappingParser(); + this.viewBlockingBlocks = new boolean[vanillaBlockStateCount + customBlockCount]; Arrays.fill(this.blockStateMappings, -1); } @@ -232,6 +235,10 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem public abstract BlockBehavior createBlockBehavior(CustomBlock customBlock, List> behaviorConfig); + public boolean isViewBlockingBlock(int stateId) { + return this.viewBlockingBlocks[stateId]; + } + protected abstract void updateTags(); protected abstract boolean isVanillaBlock(Key id); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockRegistryMirror.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockRegistryMirror.java index ccc19d42e..0011324f1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockRegistryMirror.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockRegistryMirror.java @@ -22,4 +22,8 @@ public final class BlockRegistryMirror { public static BlockStateWrapper stoneState() { return stoneState; } + + public static BlockStateWrapper[] blockStates() { + return blockStates; + } } From 9bce13475f6361da20c595459c12cd170f177c23 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 27 Nov 2025 17:07:46 +0800 Subject: [PATCH 028/135] Update BukkitBlockManager.java --- .../craftengine/bukkit/block/BukkitBlockManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 c250782e5..30237a4c2 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 @@ -396,7 +396,8 @@ public final class BukkitBlockManager extends AbstractBlockManager { } } - public boolean canBlockView(BlockStateWrapper blockState) { + public boolean canBlockView(BlockStateWrapper wrapper) { + Object blockState = wrapper.literalObject(); if (!BlockStateUtils.isOcclude(blockState)) { return false; } From bced7bfda6bcbcb32fb68a3ca7560f49403ef96e Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 28 Nov 2025 02:57:59 +0800 Subject: [PATCH 029/135] =?UTF-8?q?=E5=AE=9E=E4=BD=93=E5=89=94=E9=99=A4?= =?UTF-8?q?=E5=89=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/block/BukkitBlockManager.java | 4 +- .../plugin/network/BukkitNetworkManager.java | 97 ++++++++++++++++--- .../plugin/user/BukkitServerPlayer.java | 53 ++++++---- .../craftengine/bukkit/world/BukkitWorld.java | 5 +- common-files/src/main/resources/config.yml | 9 +- .../src/main/resources/translations/en.yml | 1 + .../core/block/AbstractBlockManager.java | 8 +- .../core/block/BlockStateAppearance.java | 5 +- .../core/block/ImmutableBlockState.java | 10 ++ .../render/ConstantBlockEntityRenderer.java | 13 ++- .../core/entity/player/Player.java | 16 ++- .../craftengine/core/plugin/CraftEngine.java | 1 - .../core/plugin/config/Config.java | 9 ++ .../config/template/TemplateManager.java | 7 -- .../argument/ExpressionTemplateArgument.java | 1 - .../core/plugin/network/NetWorkUser.java | 11 +-- .../core/util/ResourceConfigUtils.java | 49 ++++++++++ .../craftengine/core/world/Cullable.java | 13 +++ .../craftengine/core/world/Vec3i.java | 21 +--- .../core/world/chunk/ArrayPalette.java | 13 ++- .../core/world/chunk/BiMapPalette.java | 12 ++- .../craftengine/core/world/chunk/CEChunk.java | 21 ++-- .../core/world/chunk/ChunkStatus.java | 7 -- .../core/world/chunk/IdListPalette.java | 5 + .../craftengine/core/world/chunk/Palette.java | 2 + .../core/world/chunk/SingularPalette.java | 9 ++ .../core/world/chunk/client/ClientChunk.java | 33 +++++++ .../world/chunk/client/ClientSection.java | 34 +++++++ .../client/ClientSectionOcclusionStorage.java | 6 ++ .../chunk/client/PackedOcclusionStorage.java | 37 +++++++ .../client/SingularOcclusionStorage.java | 14 +++ .../chunk/client/VirtualCullableObject.java | 31 ++++++ .../serialization/DefaultChunkSerializer.java | 2 +- .../core/world/collision/AABB.java | 11 +++ 34 files changed, 470 insertions(+), 100 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/Cullable.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/chunk/ChunkStatus.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientSection.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientSectionOcclusionStorage.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/PackedOcclusionStorage.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/SingularOcclusionStorage.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java 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 30237a4c2..bb379b71f 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 @@ -88,7 +88,7 @@ public final class BukkitBlockManager extends AbstractBlockManager { this.markVanillaNoteBlocks(); this.findViewBlockingVanillaBlocks(); Arrays.fill(this.immutableBlockStates, EmptyBlock.INSTANCE.defaultState()); - this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings); // 一定要预先初始化一次,预防id超出上限 + this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings, this::isViewBlockingBlock); // 一定要预先初始化一次,预防id超出上限 } public static BukkitBlockManager instance() { @@ -128,7 +128,7 @@ public final class BukkitBlockManager extends AbstractBlockManager { @Override public void delayedLoad() { - this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings); // 重置方块映射表 + this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings, this::isViewBlockingBlock); // 重置方块映射表 super.delayedLoad(); this.cachedVisualBlockStatePacket = VisualBlockStatePacket.create(); for (BukkitServerPlayer player : BukkitNetworkManager.instance().onlineUsers()) { 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 563a5a6e4..0d962ccb9 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 @@ -22,6 +22,7 @@ import net.kyori.adventure.nbt.api.BinaryTagHolder; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.DataComponentValue; import net.kyori.adventure.text.event.HoverEvent; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; import net.momirealms.craftengine.bukkit.api.event.FurnitureAttemptBreakEvent; @@ -90,9 +91,12 @@ import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.*; import net.momirealms.craftengine.core.world.chunk.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.client.ClientChunk; +import net.momirealms.craftengine.core.world.chunk.client.ClientSection; +import net.momirealms.craftengine.core.world.chunk.client.PackedOcclusionStorage; +import net.momirealms.craftengine.core.world.chunk.client.SingularOcclusionStorage; import net.momirealms.craftengine.core.world.chunk.packet.BlockEntityData; import net.momirealms.craftengine.core.world.chunk.packet.MCSection; import net.momirealms.sparrow.nbt.CompoundTag; @@ -125,6 +129,7 @@ import java.time.Instant; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiFunction; +import java.util.function.Predicate; public class BukkitNetworkManager implements NetworkManager, Listener, PluginMessageListener { private static BukkitNetworkManager instance; @@ -288,7 +293,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } } - public void registerBlockStatePacketListeners(int[] blockStateMappings) { + public void registerBlockStatePacketListeners(int[] blockStateMappings, Predicate occlusionPredicate) { int stoneId = BlockStateUtils.blockStateToId(MBlocks.STONE$defaultState); int vanillaBlocks = BlockStateUtils.vanillaBlockStateCount(); int[] newMappings = new int[blockStateMappings.length]; @@ -318,7 +323,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes newMappings, newMappingsMOD, newMappings.length, - RegistryUtils.currentBiomeRegistrySize() + RegistryUtils.currentBiomeRegistrySize(), + occlusionPredicate ), this.packetIds.clientboundLevelChunkWithLightPacket(), "ClientboundLevelChunkWithLightPacket"); registerS2CGamePacketListener(new SectionBlockUpdateListener(newMappings, newMappingsMOD), this.packetIds.clientboundSectionBlocksUpdatePacket(), "ClientboundSectionBlocksUpdatePacket"); registerS2CGamePacketListener(new BlockUpdateListener(newMappings, newMappingsMOD), this.packetIds.clientboundBlockUpdatePacket(), "ClientboundBlockUpdatePacket"); @@ -1433,9 +1439,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes Object location = FastNMS.INSTANCE.field$ResourceKey$location(dimensionKey); World world = Bukkit.getWorld(Objects.requireNonNull(NamespacedKey.fromString(location.toString()))); if (world != null) { - int sectionCount = (world.getMaxHeight() - world.getMinHeight()) / 16; - player.setClientSideSectionCount(sectionCount); - player.setClientSideDimension(Key.of(location.toString())); + player.setClientSideWorld(BukkitAdaptors.adapt(world)); } else { CraftEngine.instance().logger().warn("Failed to handle ClientboundLoginPacket: World " + location + " does not exist"); } @@ -1463,10 +1467,9 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes Object location = FastNMS.INSTANCE.field$ResourceKey$location(dimensionKey); World world = Bukkit.getWorld(Objects.requireNonNull(NamespacedKey.fromString(location.toString()))); if (world != null) { - int sectionCount = (world.getMaxHeight() - world.getMinHeight()) / 16; - player.setClientSideSectionCount(sectionCount); - player.setClientSideDimension(Key.of(location.toString())); + player.setClientSideWorld(BukkitAdaptors.adapt(world)); player.clearTrackedChunks(); + player.clearTrackedBlockEntities(); } else { CraftEngine.instance().logger().warn("Failed to handle ClientboundRespawnPacket: World " + location + " does not exist"); } @@ -1979,13 +1982,15 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes private final IntIdentityList biomeList; private final IntIdentityList blockList; private final boolean needsDowngrade; + private final Predicate occlusionPredicate; - public LevelChunkWithLightListener(int[] blockStateMapper, int[] modBlockStateMapper, int blockRegistrySize, int biomeRegistrySize) { + public LevelChunkWithLightListener(int[] blockStateMapper, int[] modBlockStateMapper, int blockRegistrySize, int biomeRegistrySize, Predicate occlusionPredicate) { this.blockStateMapper = blockStateMapper; this.modBlockStateMapper = modBlockStateMapper; this.biomeList = new IntIdentityList(biomeRegistrySize); this.blockList = new IntIdentityList(blockRegistrySize); this.needsDowngrade = MiscUtils.ceilLog2(BlockStateUtils.vanillaBlockStateCount()) != MiscUtils.ceilLog2(blockRegistrySize); + this.occlusionPredicate = occlusionPredicate; } public int remapBlockState(int stateId, boolean enableMod) { @@ -2022,36 +2027,92 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes buf.readBytes(chunkDataBytes); // 客户端侧section数量很重要,不能读取此时玩家所在的真实世界,包具有滞后性 - int count = player.clientSideSectionCount(); + net.momirealms.craftengine.core.world.World clientSideWorld = player.clientSideWorld(); + WorldHeight worldHeight = clientSideWorld.worldHeight(); + int count = worldHeight.getSectionsCount(); MCSection[] sections = new MCSection[count]; FriendlyByteBuf chunkDataByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(chunkDataBytes)); boolean hasChangedAnyBlock = false; boolean hasGlobalPalette = false; + // 创建客户端侧世界(只在开启实体情况下创建) + ClientSection[] clientSections = Config.enableEntityCulling() ? new ClientSection[count] : null; + for (int i = 0; i < count; i++) { MCSection mcSection = new MCSection(user.clientBlockList(), this.blockList, this.biomeList); mcSection.readPacket(chunkDataByteBuf); + PalettedContainer container = mcSection.blockStateContainer(); + // 重定向生物群系 if (remapBiomes(user, mcSection.biomeContainer())) { hasChangedAnyBlock = true; } + Palette palette = container.data().palette(); if (palette.canRemap()) { + + // 重定向方块 if (palette.remapAndCheck(s -> remapBlockState(s, user.clientModEnabled()))) { hasChangedAnyBlock = true; } + + // 处理客户端侧哪些方块有阻挡 + if (clientSections != null) { + int size = palette.getSize(); + // 单个元素的情况下,使用优化的存储方案 + if (size == 1) { + clientSections[i] = new ClientSection(new SingularOcclusionStorage(this.occlusionPredicate.test(palette.get(0)))); + } else { + boolean hasOcclusions = false; + boolean hasNoOcclusions = false; + for (int h = 0; h < size; h++) { + int entry = palette.get(h); + if (this.occlusionPredicate.test(entry)) { + hasOcclusions = true; + } else { + hasNoOcclusions = true; + } + } + // 两种情况都有,那么需要一个个遍历处理视线遮挡数据 + if (hasOcclusions && hasNoOcclusions) { + PackedOcclusionStorage storage = new PackedOcclusionStorage(false); + for (int j = 0; j < 4096; j++) { + int state = container.get(j); + storage.set(j, this.occlusionPredicate.test(state)); + } + } + // 全遮蔽或全透视则使用优化存储方案 + else { + clientSections[i] = new ClientSection(new SingularOcclusionStorage(hasOcclusions)); + } + } + } } else { hasGlobalPalette = true; + + PackedOcclusionStorage storage = null; + if (clientSections != null) { + storage = new PackedOcclusionStorage(false); + } + for (int j = 0; j < 4096; j++) { int state = container.get(j); + + // 重定向方块 int newState = remapBlockState(state, user.clientModEnabled()); if (newState != state) { container.set(j, newState); hasChangedAnyBlock = true; } + + // 写入视线遮挡数据 + if (storage != null) { + storage.set(j, this.occlusionPredicate.test(state)); + } } } + sections[i] = mcSection; } @@ -2116,13 +2177,17 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } // 记录加载的区块 - player.addTrackedChunk(chunkPos.longKey, new ChunkStatus()); + player.addTrackedChunk(chunkPos.longKey, new ClientChunk(clientSections, worldHeight)); // 生成方块实体 - CEWorld ceWorld = BukkitWorldManager.instance().getWorld(player.world().uuid()); - CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(chunkPos.longKey); - if (ceChunk != null) { - ceChunk.spawnBlockEntities(player); + CEWorld ceWorld = clientSideWorld.storageWorld(); + // 世界可能被卸载,因为包滞后 + if (ceWorld != null) { + CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(chunkPos.longKey); + if (ceChunk != null) { + // 生成方块实体 + ceChunk.spawnBlockEntities(player); + } } } } 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 d6bb5a6f8..842be8e1d 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 @@ -23,6 +23,7 @@ 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.block.entity.render.ConstantBlockEntityRenderer; import net.momirealms.craftengine.core.entity.data.EntityData; import net.momirealms.craftengine.core.entity.player.GameMode; import net.momirealms.craftengine.core.entity.player.InteractionHand; @@ -39,7 +40,8 @@ import net.momirealms.craftengine.core.sound.SoundSource; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.*; import net.momirealms.craftengine.core.world.World; -import net.momirealms.craftengine.core.world.chunk.ChunkStatus; +import net.momirealms.craftengine.core.world.chunk.client.ClientChunk; +import net.momirealms.craftengine.core.world.chunk.client.VirtualCullableObject; import net.momirealms.craftengine.core.world.collision.AABB; import org.bukkit.*; import org.bukkit.attribute.Attribute; @@ -85,8 +87,7 @@ public class BukkitServerPlayer extends Player { private Reference playerRef; private Reference serverPlayerRef; // client side dimension info - private int sectionCount; - private Key clientSideDimension; + private World clientSideWorld; // check main hand/offhand interaction private int lastSuccessfulInteraction; // to prevent duplicated events @@ -124,7 +125,7 @@ public class BukkitServerPlayer extends Player { // cooldown data private CooldownData cooldownData; // tracked chunks - private ConcurrentLong2ReferenceChainedHashTable trackedChunks; + private ConcurrentLong2ReferenceChainedHashTable trackedChunks; // entity view private Map entityTypeView; // 通过指令或api设定的语言 @@ -138,6 +139,8 @@ public class BukkitServerPlayer extends Player { private boolean isHackedBreak; // 上一次停止挖掘包发出的时间 private int lastStopMiningTick; + // 跟踪到的方块实体渲染器 + private final Map trackedBlockEntityRenderers = new ConcurrentHashMap<>(); public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) { this.channel = channel; @@ -477,21 +480,13 @@ public class BukkitServerPlayer extends Player { } @Override - public int clientSideSectionCount() { - return sectionCount; - } - - public void setClientSideSectionCount(int sectionCount) { - this.sectionCount = sectionCount; + public World clientSideWorld() { + return this.clientSideWorld; } @Override - public Key clientSideDimension() { - return clientSideDimension; - } - - public void setClientSideDimension(Key clientSideDimension) { - this.clientSideDimension = clientSideDimension; + public void setClientSideWorld(World world) { + this.clientSideWorld = world; } public void setConnectionState(ConnectionState connectionState) { @@ -1180,12 +1175,12 @@ public class BukkitServerPlayer extends Player { } @Override - public ChunkStatus getTrackedChunk(long chunkPos) { + public ClientChunk getTrackedChunk(long chunkPos) { return this.trackedChunks.get(chunkPos); } @Override - public void addTrackedChunk(long chunkPos, ChunkStatus chunkStatus) { + public void addTrackedChunk(long chunkPos, ClientChunk chunkStatus) { this.trackedChunks.put(chunkPos, chunkStatus); } @@ -1300,4 +1295,26 @@ public class BukkitServerPlayer extends Player { public void sendTotemAnimation(Item totem, @Nullable SoundData sound, boolean silent) { PlayerUtils.sendTotemAnimation(this, totem, sound, silent); } + + @Override + public void addTrackedBlockEntities(Map renders) { + for (Map.Entry entry : renders.entrySet()) { + this.trackedBlockEntityRenderers.put(entry.getKey(), new VirtualCullableObject(entry.getValue())); + } + } + + @Override + public void removeTrackedBlockEntities(Collection renders) { + for (BlockPos render : renders) { + VirtualCullableObject remove = this.trackedBlockEntityRenderers.remove(render); + if (remove != null && remove.isShown()) { + remove.cullable().hide(this); + } + } + } + + @Override + public void clearTrackedBlockEntities() { + this.trackedBlockEntityRenderers.clear(); + } } 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 91d168666..66df48ee3 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 @@ -24,7 +24,10 @@ import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; import java.lang.ref.WeakReference; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; public class BukkitWorld implements World { private final WeakReference world; diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index c5c00829e..0d6efafb4 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -552,11 +552,10 @@ chunk-system: remove: [] convert: {} -#client-optimization: -# # Using server-side ray tracing algorithms to hide certain entities and reduce client-side rendering pressure. -# entity-culling: -# enable: false -# whitelist-entities: [] +client-optimization: + # Using server-side ray tracing algorithms to hide certain entities and reduce client-side rendering pressure. + entity-culling: + enable: false # Enables or disables debug mode debug: diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index b3ce311aa..cd0751c7b 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -87,6 +87,7 @@ warning.config.type.quaternionf: "Issue found in file - Failed t 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.aabb: "Issue found in file - Failed to load '': Cannot cast '' to AABB 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." warning.config.number.invalid_type: "Issue found in file - The config '' is using an invalid number argument type ''." diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java index c0f427de8..1b0b09185 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 @@ -600,7 +600,11 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem this.arrangeModelForStateAndVerify(visualBlockState, parseBlockModel(modelConfig)); } } - BlockStateAppearance blockStateAppearance = new BlockStateAppearance(visualBlockState, parseBlockEntityRender(appearanceSection.get("entity-renderer"))); + BlockStateAppearance blockStateAppearance = new BlockStateAppearance( + visualBlockState, + parseBlockEntityRender(appearanceSection.get("entity-renderer")), + ResourceConfigUtils.getAsAABB(appearanceSection.getOrDefault("aabb", 1), "aabb") + ); appearances.put(appearanceName, blockStateAppearance); if (anyAppearance == null) { anyAppearance = blockStateAppearance; @@ -639,6 +643,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem } for (ImmutableBlockState possibleState : possibleStates) { possibleState.setVisualBlockState(appearance.blockState()); + possibleState.setEstimatedBoundingBox(appearance.estimateAABB()); appearance.blockEntityRenderer().ifPresent(possibleState::setConstantRenderers); } } @@ -662,6 +667,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem if (visualState == null) { visualState = anyAppearance.blockState(); state.setVisualBlockState(visualState); + state.setEstimatedBoundingBox(anyAppearance.estimateAABB()); anyAppearance.blockEntityRenderer().ifPresent(state::setConstantRenderers); } int appearanceId = visualState.registryId(); 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 index 75e39df37..d2b8d0189 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateAppearance.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateAppearance.java @@ -2,8 +2,11 @@ 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 net.momirealms.craftengine.core.world.collision.AABB; import java.util.Optional; -public record BlockStateAppearance(BlockStateWrapper blockState, Optional[]> blockEntityRenderer) { +public record BlockStateAppearance(BlockStateWrapper blockState, + Optional[]> blockEntityRenderer, + AABB estimateAABB) { } 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 8e4430e2e..9a4010ef8 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 @@ -17,6 +17,7 @@ import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.collision.AABB; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.NBT; import net.momirealms.sparrow.nbt.Tag; @@ -42,6 +43,7 @@ public final class ImmutableBlockState { private BlockEntityType blockEntityType; @Nullable private BlockEntityElementConfig[] renderers; + private AABB estimatedBoundingBox; ImmutableBlockState( Holder.Reference owner, @@ -87,6 +89,14 @@ public final class ImmutableBlockState { this.renderers = renderers; } + public void setEstimatedBoundingBox(AABB aabb) { + this.estimatedBoundingBox = aabb; + } + + public AABB estimatedBoundingBox() { + return estimatedBoundingBox; + } + public boolean hasBlockEntity() { return this.blockEntityType != null; } 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 index 4b45900a0..48245a05d 100644 --- 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 @@ -2,14 +2,18 @@ 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 net.momirealms.craftengine.core.world.Cullable; +import net.momirealms.craftengine.core.world.collision.AABB; import org.jetbrains.annotations.ApiStatus; @ApiStatus.Experimental -public class ConstantBlockEntityRenderer { +public class ConstantBlockEntityRenderer implements Cullable { private final BlockEntityElement[] elements; + public final AABB aabb; - public ConstantBlockEntityRenderer(BlockEntityElement[] elements) { + public ConstantBlockEntityRenderer(BlockEntityElement[] elements, AABB aabb) { this.elements = elements; + this.aabb = aabb; } public void show(Player player) { @@ -47,4 +51,9 @@ public class ConstantBlockEntityRenderer { public BlockEntityElement[] elements() { return this.elements; } + + @Override + public AABB aabb() { + return this.aabb; + } } 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 034502a96..d4dc6cddc 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 @@ -2,6 +2,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.block.entity.render.ConstantBlockEntityRenderer; import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.CooldownData; @@ -9,14 +10,13 @@ import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.sound.SoundSource; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.BlockPos; -import net.momirealms.craftengine.core.world.Position; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.Locale; +import java.util.Map; public abstract class Player extends AbstractEntity implements NetWorkUser { private static final Key TYPE = Key.of("minecraft:player"); @@ -35,6 +35,8 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { @Override public abstract Object serverPlayer(); + public abstract void setClientSideWorld(World world); + public abstract float getDestroyProgress(Object blockState, BlockPos pos); public abstract void setClientSideCanBreakBlock(boolean canBreak); @@ -198,6 +200,12 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void sendTotemAnimation(Item totem, @Nullable SoundData sound, boolean silent); + public abstract void addTrackedBlockEntities(Map renders); + + public abstract void removeTrackedBlockEntities(Collection renders); + + public abstract void clearTrackedBlockEntities(); + @Override public void remove() { } 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 58c1ac8cd..2c32adc8b 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 @@ -21,7 +21,6 @@ import net.momirealms.craftengine.core.plugin.compatibility.CompatibilityManager import net.momirealms.craftengine.core.plugin.compatibility.PluginTaskRegistry; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.template.TemplateManager; -import net.momirealms.craftengine.core.plugin.config.template.TemplateManagerImpl; import net.momirealms.craftengine.core.plugin.context.GlobalVariableManager; import net.momirealms.craftengine.core.plugin.dependency.Dependencies; import net.momirealms.craftengine.core.plugin.dependency.Dependency; 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 c75eaa370..3720a4268 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 @@ -203,6 +203,8 @@ public class Config { protected boolean emoji$contexts$sign; protected int emoji$max_emojis_per_parse; + protected boolean client_optimization$entity_culling$enable; + public Config(CraftEngine plugin) { this.plugin = plugin; this.configVersion = PluginProperties.getValue("config"); @@ -562,6 +564,9 @@ public class Config { emoji$contexts$sign = config.getBoolean("emoji.contexts.sign", true); emoji$max_emojis_per_parse = config.getInt("emoji.max-emojis-per-parse", 32); + // client optimization + client_optimization$entity_culling$enable = config.getBoolean("client-optimization.entity-culling.enable", false); + firstTime = false; } @@ -1152,6 +1157,10 @@ public class Config { return instance.resource_pack$optimization$texture$zopfli_iterations; } + public static boolean enableEntityCulling() { + return instance.client_optimization$entity_culling$enable; + } + public YamlDocument loadOrCreateYamlData(String fileName) { Path path = this.plugin.dataFolderPath().resolve(fileName); if (!Files.exists(path)) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java index 79b942023..bed0110ef 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java @@ -2,14 +2,7 @@ package net.momirealms.craftengine.core.plugin.config.template; import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.ConfigParser; -import net.momirealms.craftengine.core.plugin.config.template.argument.TemplateArgument; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.SNBTReader; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; public interface TemplateManager extends Manageable { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java index 667b28db2..cf02eeb46 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java @@ -3,7 +3,6 @@ package net.momirealms.craftengine.core.plugin.config.template.argument; import com.ezylang.evalex.Expression; import com.ezylang.evalex.data.EvaluationValue; import net.momirealms.craftengine.core.plugin.config.template.ArgumentString; -import net.momirealms.craftengine.core.plugin.config.template.TemplateManager; import net.momirealms.craftengine.core.util.Key; import java.util.Locale; 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 501200a8a..ab1d0d520 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 @@ -6,7 +6,8 @@ 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 net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.chunk.client.ClientChunk; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -65,9 +66,7 @@ public interface NetWorkUser { @ApiStatus.Internal ConnectionState encoderState(); - int clientSideSectionCount(); - - Key clientSideDimension(); + World clientSideWorld(); Object serverPlayer(); @@ -89,9 +88,9 @@ public interface NetWorkUser { boolean isChunkTracked(long chunkPos); - ChunkStatus getTrackedChunk(long chunkPos); + ClientChunk getTrackedChunk(long chunkPos); - void addTrackedChunk(long chunkPos, ChunkStatus chunkStatus); + void addTrackedChunk(long chunkPos, ClientChunk chunkStatus); void clearTrackedChunks(); 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 415893a3a..1a98323d1 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 @@ -6,6 +6,7 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.collision.AABB; import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; @@ -341,4 +342,52 @@ public final class ResourceConfigUtils { } TranslationManager.instance().log(e.node(), e.arguments()); } + + public static AABB getAsAABB(Object o, String option) { + if (o == null) { + throw new LocalizedResourceConfigException("warning.config.type.aabb", "null", option); + } + if (o instanceof Number number) { + double min = -(number.doubleValue() / 2); + double max = number.doubleValue() / 2; + return new AABB(min, min, min, max, max, max); + } else { + double[] args; + if (o instanceof List list) { + args = new double[list.size()]; + for (int i = 0; i < args.length; i++) { + if (list.get(i) instanceof Number number) { + args[i] = number.doubleValue(); + } else { + try { + args[i] = Double.parseDouble(list.get(i).toString()); + } catch (NumberFormatException e) { + throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); + } + } + } + } else { + String[] split = o.toString().split(","); + args = new double[split.length]; + for (int i = 0; i < args.length; i++) { + try { + args[i] = Double.parseDouble(split[i]); + } catch (NumberFormatException e) { + throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); + } + } + } + if (args.length == 1) { + return new AABB(-args[0]/2, -args[0]/2, -args[0]/2, args[0]/2, args[0]/2, args[0]/2); + } else if (args.length == 2) { + return new AABB(-args[0]/2, -args[1]/2, -args[0]/2, args[0]/2, args[1]/2, args[0]/2); + } else if (args.length == 3) { + return new AABB(-args[0]/2, -args[1]/2, -args[2]/2, args[0]/2, args[1]/2, args[2]/2); + } else if (args.length == 6) { + return new AABB(args[0], args[1], args[2], args[3], args[4], args[5]); + } else { + throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); + } + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/Cullable.java b/core/src/main/java/net/momirealms/craftengine/core/world/Cullable.java new file mode 100644 index 000000000..700442fb3 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/Cullable.java @@ -0,0 +1,13 @@ +package net.momirealms.craftengine.core.world; + +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.world.collision.AABB; + +public interface Cullable { + + AABB aabb(); + + void show(Player player); + + void hide(Player player); +} 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 82b2e4bbb..bd4a707e6 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 @@ -4,9 +4,9 @@ import net.momirealms.craftengine.core.util.Direction; public class Vec3i implements Comparable { public static final Vec3i ZERO = new Vec3i(0, 0, 0); - protected int x; - protected int y; - protected int z; + public final int x; + public final int y; + public final int z; public Vec3i(int x, int y, int z) { this.x = x; @@ -30,21 +30,6 @@ public class Vec3i implements Comparable { return x == 0 && y == 0 && z == 0 ? this : new Vec3i(this.x() + x, this.y() + y, this.z() + z); } - protected Vec3i setX(int x) { - this.x = x; - return this; - } - - protected Vec3i setY(int y) { - this.y = y; - return this; - } - - protected Vec3i setZ(int z) { - this.z = z; - return this; - } - @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; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ArrayPalette.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ArrayPalette.java index 0ace21c98..f7926684a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ArrayPalette.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ArrayPalette.java @@ -77,15 +77,24 @@ public class ArrayPalette implements Palette { @Override public boolean hasAny(Predicate predicate) { - for(int i = 0; i < this.size; ++i) { + for (int i = 0; i < this.size; ++i) { if (predicate.test(this.array[i])) { return true; } } - return false; } + @Override + public boolean allMatch(Predicate predicate) { + for (int i = 0; i < this.size; ++i) { + if (!predicate.test(this.array[i])) { + return false; + } + } + return true; + } + @Override public T get(int id) { if (id >= 0 && id < this.size) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/BiMapPalette.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/BiMapPalette.java index 02d0b190d..ae9be1758 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/BiMapPalette.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/BiMapPalette.java @@ -67,7 +67,7 @@ public class BiMapPalette implements Palette { @Override public boolean hasAny(Predicate predicate) { - for(int i = 0; i < this.getSize(); ++i) { + for (int i = 0; i < this.getSize(); ++i) { if (predicate.test(this.map.get(i))) { return true; } @@ -75,6 +75,16 @@ public class BiMapPalette implements Palette { return false; } + @Override + public boolean allMatch(Predicate predicate) { + for (int i = 0; i < this.getSize(); ++i) { + if (!predicate.test(this.map.get(i))) { + return false; + } + } + return true; + } + @Override public T get(int id) { T object = this.map.get(id); 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 a0babe284..f12123ae2 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 @@ -11,6 +11,7 @@ import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityEl 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.config.Config; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.world.*; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntityRendererSerializer; @@ -91,8 +92,12 @@ public class CEChunk { public void spawnBlockEntities(Player player) { try { this.renderLock.readLock().lock(); - for (ConstantBlockEntityRenderer renderer : this.constantBlockEntityRenderers.values()) { - renderer.show(player); + if (Config.enableEntityCulling()) { + player.addTrackedBlockEntities(this.constantBlockEntityRenderers); + } else { + for (ConstantBlockEntityRenderer renderer : this.constantBlockEntityRenderers.values()) { + renderer.show(player); + } } for (DynamicBlockEntityRenderer renderer : this.dynamicBlockEntityRenderers.values()) { renderer.show(player); @@ -105,8 +110,12 @@ public class CEChunk { public void despawnBlockEntities(Player player) { try { this.renderLock.readLock().lock(); - for (ConstantBlockEntityRenderer renderer : this.constantBlockEntityRenderers.values()) { - renderer.hide(player); + if (Config.enableEntityCulling()) { + player.removeTrackedBlockEntities(this.constantBlockEntityRenderers.keySet()); + } else { + for (ConstantBlockEntityRenderer renderer : this.constantBlockEntityRenderers.values()) { + renderer.hide(player); + } } for (DynamicBlockEntityRenderer renderer : this.dynamicBlockEntityRenderers.values()) { renderer.hide(player); @@ -129,7 +138,7 @@ public class CEChunk { BlockEntityElementConfig[] renderers = state.constantRenderers(); if (renderers != null && renderers.length > 0) { BlockEntityElement[] elements = new BlockEntityElement[renderers.length]; - ConstantBlockEntityRenderer renderer = new ConstantBlockEntityRenderer(elements); + ConstantBlockEntityRenderer renderer = new ConstantBlockEntityRenderer(elements, state.estimatedBoundingBox().move(pos)); World wrappedWorld = this.world.world(); List trackedBy = getTrackedBy(); boolean hasTrackedBy = trackedBy != null && !trackedBy.isEmpty(); @@ -439,7 +448,7 @@ public class CEChunk { return Collections.unmodifiableCollection(this.blockEntities.values()); } - public List constantBlockEntityRenderers() { + public List constantBlockEntityRendererPositions() { try { this.renderLock.readLock().lock(); return new ArrayList<>(this.constantBlockEntityRenderers.keySet()); 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 deleted file mode 100644 index 744068aa6..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ChunkStatus.java +++ /dev/null @@ -1,7 +0,0 @@ -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/IdListPalette.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/IdListPalette.java index 51ee07710..1fd1f1155 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/IdListPalette.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/IdListPalette.java @@ -29,6 +29,11 @@ public class IdListPalette implements Palette { return true; } + @Override + public boolean allMatch(Predicate predicate) { + return true; + } + @Override public T get(int id) { T object = this.idList.get(id); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/Palette.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/Palette.java index f190afaee..73d6523ab 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/Palette.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/Palette.java @@ -13,6 +13,8 @@ public interface Palette { boolean hasAny(Predicate predicate); + boolean allMatch(Predicate predicate); + T get(int id); int getSize(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/SingularPalette.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/SingularPalette.java index afcfe09be..b52a6308c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/SingularPalette.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/SingularPalette.java @@ -45,6 +45,15 @@ public class SingularPalette implements Palette { } } + @Override + public boolean allMatch(Predicate predicate) { + if (this.entry == null) { + throw new IllegalStateException("Use of an uninitialized palette"); + } else { + return predicate.test(this.entry); + } + } + @Override public T get(int id) { if (this.entry != null && id == 0) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java new file mode 100644 index 000000000..e37e1ac31 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java @@ -0,0 +1,33 @@ +package net.momirealms.craftengine.core.world.chunk.client; + +import net.momirealms.craftengine.core.world.SectionPos; +import net.momirealms.craftengine.core.world.WorldHeight; +import org.jetbrains.annotations.Nullable; + +public class ClientChunk { + @Nullable + public final ClientSection[] sections; + private final WorldHeight worldHeight; + + public ClientChunk(ClientSection[] sections, WorldHeight worldHeight) { + this.sections = sections; + this.worldHeight = worldHeight; + } + + @Nullable + public ClientSection[] sections() { + return sections; + } + + public boolean isOccluding(int x, int y, int z) { + if (this.sections == null) return false; + int index = sectionIndex(SectionPos.blockToSectionCoord(y)); + ClientSection section = this.sections[index]; + if (section == null) return false; + return section.isOccluding((y & 15) << 8 | (z & 15) << 4 | x & 15); + } + + public int sectionIndex(int sectionId) { + return sectionId - this.worldHeight.getMinSection(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientSection.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientSection.java new file mode 100644 index 000000000..af3f71749 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientSection.java @@ -0,0 +1,34 @@ +package net.momirealms.craftengine.core.world.chunk.client; + +public class ClientSection { + private ClientSectionOcclusionStorage storage; + + public ClientSection(ClientSectionOcclusionStorage storage) { + this.storage = storage; + } + + boolean isOccluding(int x, int y, int z) { + return isOccluding((y << 4 | z) << 4 | x); + } + + boolean isOccluding(int index) { + return this.storage.isOccluding(index); + } + + void setOccluding(int x, int y, int z, boolean value) { + this.setOccluding((y << 4 | z) << 4 | x, value); + } + + void setOccluding(int index, boolean value) { + boolean wasOccluding = this.storage.isOccluding(index); + if (wasOccluding != value) { + if (this.storage instanceof PackedOcclusionStorage arrayStorage) { + arrayStorage.set(index, value); + } else { + PackedOcclusionStorage newStorage = new PackedOcclusionStorage(wasOccluding); + newStorage.set(index, value); + this.storage = newStorage; + } + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientSectionOcclusionStorage.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientSectionOcclusionStorage.java new file mode 100644 index 000000000..0a53ede3d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientSectionOcclusionStorage.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.core.world.chunk.client; + +public interface ClientSectionOcclusionStorage { + + boolean isOccluding(int index); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/PackedOcclusionStorage.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/PackedOcclusionStorage.java new file mode 100644 index 000000000..10e3b08dd --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/PackedOcclusionStorage.java @@ -0,0 +1,37 @@ +package net.momirealms.craftengine.core.world.chunk.client; + +import java.util.Arrays; + +public class PackedOcclusionStorage implements ClientSectionOcclusionStorage { + private static final int SIZE = 4096; + private static final int LONGS = SIZE / 64; + private final long[] data; + + public PackedOcclusionStorage() { + this.data = new long[LONGS]; + } + + public PackedOcclusionStorage(boolean defaultValue) { + this.data = new long[LONGS]; + if (defaultValue) { + Arrays.fill(this.data, -1L); // 所有位设为1 + } + } + + @Override + public boolean isOccluding(int index) { + int arrayIndex = index >>> 6; // index / 64 + int bitIndex = index & 0x3F; // index % 64 + return (this.data[arrayIndex] & (1L << bitIndex)) != 0; + } + + public void set(int index, boolean occlusion) { + int arrayIndex = index >>> 6; // index / 64 + int bitIndex = index & 0x3F; // index % 64 + if (occlusion) { + this.data[arrayIndex] |= (1L << bitIndex); + } else { + this.data[arrayIndex] &= ~(1L << bitIndex); + } + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/SingularOcclusionStorage.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/SingularOcclusionStorage.java new file mode 100644 index 000000000..7da13ba23 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/SingularOcclusionStorage.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.core.world.chunk.client; + +public class SingularOcclusionStorage implements ClientSectionOcclusionStorage { + private final boolean isOccluding; + + public SingularOcclusionStorage(boolean isOccluding) { + this.isOccluding = isOccluding; + } + + @Override + public boolean isOccluding(int index) { + return this.isOccluding; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java new file mode 100644 index 000000000..127b8b09e --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java @@ -0,0 +1,31 @@ +package net.momirealms.craftengine.core.world.chunk.client; + +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.world.Cullable; + +public class VirtualCullableObject { + private final Cullable cullable; + private boolean isShown; + + public VirtualCullableObject(Cullable cullable) { + this.cullable = cullable; + this.isShown = false; + } + + public Cullable cullable() { + return cullable; + } + + public boolean isShown() { + return isShown; + } + + public void setShown(Player player, boolean shown) { + this.isShown = shown; + if (shown) { + this.cullable.show(player); + } else { + this.cullable.hide(player); + } + } +} 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 ef1357235..2f851fe49 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 @@ -32,7 +32,7 @@ public final class DefaultChunkSerializer { if (!blockEntities.isEmpty()) { chunkNbt.put("block_entities", blockEntities); } - ListTag blockEntityRenders = DefaultBlockEntityRendererSerializer.serialize(chunk.constantBlockEntityRenderers()); + ListTag blockEntityRenders = DefaultBlockEntityRendererSerializer.serialize(chunk.constantBlockEntityRendererPositions()); if (!blockEntityRenders.isEmpty()) { chunkNbt.put("block_entity_renderers", blockEntityRenders); } 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 9a837a003..c5a725f59 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 @@ -35,6 +35,17 @@ public class AABB { this.maxZ = Math.max(pos1.z, pos2.z); } + public AABB move(BlockPos pos) { + return new AABB( + this.minX + pos.x + 0.5, + this.minY + pos.y + 0.5, + this.minZ + pos.z + 0.5, + this.maxX + pos.x + 0.5, + this.maxY + pos.y + 0.5, + this.maxZ + pos.z + 0.5 + ); + } + public AABB(BlockPos pos) { this(pos.x(), pos.y(), pos.z(), pos.x() + 1, pos.y() + 1, pos.z() + 1); } From 425e52c479c945b63907489dd392d37953da8037 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 28 Nov 2025 03:49:39 +0800 Subject: [PATCH 030/135] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E4=BE=A7=E9=81=AE=E8=94=BD=E4=B8=96=E7=95=8C=E6=9E=84?= =?UTF-8?q?=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/network/BukkitNetworkManager.java | 101 +++++++++--------- .../core/plugin/context/ContextHolder.java | 7 +- .../craftengine/core/world/SectionPos.java | 35 ++++++ .../core/world/chunk/client/ClientChunk.java | 20 ++++ .../world/chunk/client/ClientSection.java | 8 +- 5 files changed, 115 insertions(+), 56 deletions(-) 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 0d962ccb9..b0c928641 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 @@ -326,8 +326,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes RegistryUtils.currentBiomeRegistrySize(), occlusionPredicate ), this.packetIds.clientboundLevelChunkWithLightPacket(), "ClientboundLevelChunkWithLightPacket"); - registerS2CGamePacketListener(new SectionBlockUpdateListener(newMappings, newMappingsMOD), this.packetIds.clientboundSectionBlocksUpdatePacket(), "ClientboundSectionBlocksUpdatePacket"); - registerS2CGamePacketListener(new BlockUpdateListener(newMappings, newMappingsMOD), this.packetIds.clientboundBlockUpdatePacket(), "ClientboundBlockUpdatePacket"); + registerS2CGamePacketListener(new SectionBlockUpdateListener(newMappings, newMappingsMOD, occlusionPredicate), this.packetIds.clientboundSectionBlocksUpdatePacket(), "ClientboundSectionBlocksUpdatePacket"); + registerS2CGamePacketListener(new BlockUpdateListener(newMappings, newMappingsMOD, occlusionPredicate), this.packetIds.clientboundBlockUpdatePacket(), "ClientboundBlockUpdatePacket"); registerS2CGamePacketListener( VersionHelper.isOrAbove1_21_4() ? new LevelParticleListener1_21_4(newMappings, newMappingsMOD) : @@ -1993,10 +1993,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes this.occlusionPredicate = occlusionPredicate; } - public int remapBlockState(int stateId, boolean enableMod) { - return enableMod ? this.modBlockStateMapper[stateId] : this.blockStateMapper[stateId]; - } - @Override public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { BukkitServerPlayer player = (BukkitServerPlayer) user; @@ -2006,6 +2002,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); boolean named = !VersionHelper.isOrAbove1_20_2(); + int[] remapper = user.clientModEnabled() ? this.modBlockStateMapper : this.blockStateMapper; + // 读取区块数据 int heightmapsCount = 0; Map heightmapsMap = null; @@ -2053,7 +2051,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes if (palette.canRemap()) { // 重定向方块 - if (palette.remapAndCheck(s -> remapBlockState(s, user.clientModEnabled()))) { + if (palette.remapAndCheck(s -> remapper[s])) { hasChangedAnyBlock = true; } @@ -2100,7 +2098,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes int state = container.get(j); // 重定向方块 - int newState = remapBlockState(state, user.clientModEnabled()); + int newState = remapper[state]; if (newState != state) { container.set(j, newState); hasChangedAnyBlock = true; @@ -2195,63 +2193,64 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes public static class SectionBlockUpdateListener implements ByteBufferPacketListener { private final int[] blockStateMapper; private final int[] modBlockStateMapper; + private final Predicate occlusionPredicate; - public SectionBlockUpdateListener(int[] blockStateMapper, int[] modBlockStateMapper) { + public SectionBlockUpdateListener(int[] blockStateMapper, int[] modBlockStateMapper, Predicate occlusionPredicate) { this.blockStateMapper = blockStateMapper; this.modBlockStateMapper = modBlockStateMapper; + this.occlusionPredicate = occlusionPredicate; } @Override public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) { - if (user.clientModEnabled()) { - FriendlyByteBuf buf = event.getBuffer(); - long pos = buf.readLong(); - int blocks = buf.readVarInt(); - short[] positions = new short[blocks]; - int[] states = new int[blocks]; - for (int i = 0; i < blocks; i++) { - long k = buf.readVarLong(); - positions[i] = (short) ((int) (k & 4095L)); - states[i] = modBlockStateMapper[((int) (k >>> 12))]; - } - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeLong(pos); - buf.writeVarInt(blocks); - for (int i = 0; i < blocks; i++) { - buf.writeVarLong((long) states[i] << 12 | positions[i]); - } - event.setChanged(true); - } else { - FriendlyByteBuf buf = event.getBuffer(); - long pos = buf.readLong(); - int blocks = buf.readVarInt(); - short[] positions = new short[blocks]; - int[] states = new int[blocks]; - for (int i = 0; i < blocks; i++) { - long k = buf.readVarLong(); - positions[i] = (short) ((int) (k & 4095L)); - states[i] = blockStateMapper[((int) (k >>> 12))]; - } - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeLong(pos); - buf.writeVarInt(blocks); - for (int i = 0; i < blocks; i++) { - buf.writeVarLong((long) states[i] << 12 | positions[i]); - } - event.setChanged(true); + int[] remapper = user.clientModEnabled() ? this.modBlockStateMapper : this.blockStateMapper; + FriendlyByteBuf buf = event.getBuffer(); + long sPos = buf.readLong(); + int blocks = buf.readVarInt(); + short[] positions = new short[blocks]; + int[] states = new int[blocks]; + + // 获取客户端侧区域 + ClientSection clientSection = null; + if (Config.enableEntityCulling()) { + SectionPos sectionPos = SectionPos.of(sPos); + ClientChunk trackedChunk = user.getTrackedChunk(sectionPos.asChunkPos().longKey); + clientSection = trackedChunk.sectionById(sectionPos.y); } + + for (int i = 0; i < blocks; i++) { + long k = buf.readVarLong(); + short posIndex = (short) ((int) (k & 4095L)); + positions[i] = posIndex; + int beforeState = ((int) (k >>> 12)); + states[i] = remapper[beforeState]; + if (clientSection != null) { + // 设置遮蔽状态 + BlockPos pos = SectionPos.unpackSectionRelativePos(posIndex); + clientSection.setOccluding(pos.x, pos.y, pos.z, this.occlusionPredicate.test(beforeState)); + } + } + + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeLong(sPos); + buf.writeVarInt(blocks); + for (int i = 0; i < blocks; i++) { + buf.writeVarLong((long) states[i] << 12 | positions[i]); + } + event.setChanged(true); } } public static class BlockUpdateListener implements ByteBufferPacketListener { private final int[] blockStateMapper; private final int[] modBlockStateMapper; + private final Predicate occlusionPredicate; - public BlockUpdateListener(int[] blockStateMapper, int[] modBlockStateMapper) { + public BlockUpdateListener(int[] blockStateMapper, int[] modBlockStateMapper, Predicate occlusionPredicate) { this.blockStateMapper = blockStateMapper; this.modBlockStateMapper = modBlockStateMapper; + this.occlusionPredicate = occlusionPredicate; } @Override @@ -2271,6 +2270,12 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes buf.writeVarInt(event.packetID()); buf.writeBlockPos(pos); buf.writeVarInt(state); + if (Config.enableEntityCulling()) { + ClientChunk trackedChunk = user.getTrackedChunk(ChunkPos.asLong(pos.x >> 4, pos.z >> 4)); + if (trackedChunk != null) { + trackedChunk.setOccluding(pos.x, pos.y, pos.z, this.occlusionPredicate.test(state)); + } + } } } 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 59d678797..758f77042 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,13 +1,12 @@ package net.momirealms.craftengine.core.plugin.context; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSortedMap; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Optional; +import java.util.*; import java.util.function.Supplier; public class ContextHolder { 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 fac06da92..48c234da8 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 @@ -17,4 +17,39 @@ public class SectionPos extends Vec3i { public static int sectionRelative(int rel) { return rel & 15; } + + public static SectionPos of(long packed) { + return new SectionPos((int) (packed >> 42), (int) (packed << 44 >> 44), (int) (packed << 22 >> 42)); + } + + public long asLong() { + return ((long) this.x & 4194303L) << 42 | (long) this.y & 1048575L | ((long) this.z & 4194303L) << 20; + } + + public ChunkPos asChunkPos() { + return new ChunkPos(this.x, this.z); + } + + public static short packSectionRelativePos(BlockPos pos) { + return (short) ((pos.x & 15) << 8 | (pos.z & 15) << 4 | pos.y & 15); + } + + public static BlockPos unpackSectionRelativePos(short encoded) { + int x = (encoded >> 8) & 15; + int z = (encoded >> 4) & 15; + int y = encoded & 15; + return new BlockPos(x, y, z); + } + + public final int minBlockX() { + return this.x << 4; + } + + public final int minBlockY() { + return this.y << 4; + } + + public final int minBlockZ() { + return this.z << 4; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java index e37e1ac31..29379fd21 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java @@ -27,7 +27,27 @@ public class ClientChunk { return section.isOccluding((y & 15) << 8 | (z & 15) << 4 | x & 15); } + public void setOccluding(int x, int y, int z, boolean occluding) { + if (this.sections == null) return; + int index = sectionIndex(SectionPos.blockToSectionCoord(y)); + ClientSection section = this.sections[index]; + if (section == null) return; + section.setOccluding((y & 15) << 8 | (z & 15) << 4, occluding); + } + public int sectionIndex(int sectionId) { return sectionId - this.worldHeight.getMinSection(); } + + @Nullable + public ClientSection sectionByIndex(int sectionIndex) { + if (this.sections == null) return null; + return this.sections[sectionIndex]; + } + + @Nullable + public ClientSection sectionById(int sectionId) { + if (this.sections == null) return null; + return this.sections[sectionIndex(sectionId)]; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientSection.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientSection.java index af3f71749..32e668d15 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientSection.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientSection.java @@ -7,19 +7,19 @@ public class ClientSection { this.storage = storage; } - boolean isOccluding(int x, int y, int z) { + public boolean isOccluding(int x, int y, int z) { return isOccluding((y << 4 | z) << 4 | x); } - boolean isOccluding(int index) { + public boolean isOccluding(int index) { return this.storage.isOccluding(index); } - void setOccluding(int x, int y, int z, boolean value) { + public void setOccluding(int x, int y, int z, boolean value) { this.setOccluding((y << 4 | z) << 4 | x, value); } - void setOccluding(int index, boolean value) { + public void setOccluding(int index, boolean value) { boolean wasOccluding = this.storage.isOccluding(index); if (wasOccluding != value) { if (this.storage instanceof PackedOcclusionStorage arrayStorage) { From f4607844608270253e9660b2df3ab3ecdda593e1 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 28 Nov 2025 15:35:50 +0800 Subject: [PATCH 031/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BA=A7=E6=A4=85?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E7=9B=91=E5=90=AC=E5=99=A8=E6=9C=AA=E6=B3=A8?= =?UTF-8?q?=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/bukkit/entity/seat/BukkitSeatManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/seat/BukkitSeatManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/seat/BukkitSeatManager.java index 2e52d5564..e704276ff 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/seat/BukkitSeatManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/seat/BukkitSeatManager.java @@ -61,6 +61,7 @@ public class BukkitSeatManager implements SeatManager, Listener { @Override public void delayedInit() { Bukkit.getPluginManager().registerEvents(this.dismountListener, this.plugin.javaPlugin()); + Bukkit.getPluginManager().registerEvents(this, this.plugin.javaPlugin()); } @Override From 639813a05e9c2e4109e8b1f267fbeebd147188a5 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Fri, 28 Nov 2025 18:46:44 +0800 Subject: [PATCH 032/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=84=8F=E5=A4=96?= =?UTF-8?q?=E7=9A=84=E8=A7=A6=E5=8F=912=E6=AC=A1PlayerSpawnLocationEvent?= =?UTF-8?q?=E5=92=8CAsyncPlayerSpawnLocationEvent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/network/BukkitNetworkManager.java | 31 +++++++++++++------ .../reflection/minecraft/CoreReflections.java | 21 +++++++++++++ 2 files changed, 42 insertions(+), 10 deletions(-) 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 ba62c7b36..f7991d927 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 @@ -14,6 +14,7 @@ import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; +import io.papermc.paper.event.player.AsyncPlayerSpawnLocationEvent; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -124,6 +125,7 @@ import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.BiFunction; public class BukkitNetworkManager implements NetworkManager, Listener, PluginMessageListener { @@ -1802,6 +1804,15 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes public static class FinishConfigurationListener implements NMSPacketListener { + private void returnToWorld(NetWorkUser user, Queue configurationTasks, Object packetListener) { + configurationTasks.add(CoreReflections.instance$JoinWorldTask); + try { + CoreReflections.methodHandle$ServerConfigurationPacketListenerImpl$startNextTask.invokeExact(packetListener); + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Cannot return to world for " + user.name(), e); + } + } + @SuppressWarnings("unchecked") @Override public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { @@ -1852,15 +1863,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes // 请求资源包 ResourcePackHost host = CraftEngine.instance().packManager().resourcePackHost(); host.requestResourcePackDownloadLink(user.uuid()).whenComplete((dataList, t) -> { - if (t != null) { - CraftEngine.instance().logger().warn("Failed to get pack data for player " + user.name(), t); - FastNMS.INSTANCE.method$ServerConfigurationPacketListenerImpl$returnToWorld(packetListener); - return; - } - if (dataList.isEmpty()) { - FastNMS.INSTANCE.method$ServerConfigurationPacketListenerImpl$returnToWorld(packetListener); - return; - } Queue configurationTasks; try { configurationTasks = (Queue) CoreReflections.methodHandle$ServerConfigurationPacketListenerImpl$configurationTasksGetter.invokeExact(packetListener); @@ -1869,13 +1871,22 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes FastNMS.INSTANCE.method$ServerConfigurationPacketListenerImpl$returnToWorld(packetListener); return; } + if (t != null) { + CraftEngine.instance().logger().warn("Failed to get pack data for player " + user.name(), t); + returnToWorld(user, configurationTasks, packetListener); + return; + } + if (dataList.isEmpty()) { + returnToWorld(user, configurationTasks, packetListener); + return; + } // 向配置阶段连接的任务重加入资源包的任务 for (ResourcePackDownloadData data : dataList) { configurationTasks.add(FastNMS.INSTANCE.constructor$ServerResourcePackConfigurationTask(ResourcePackUtils.createServerResourcePackInfo(data.uuid(), data.url(), data.sha1()))); user.addResourcePackUUID(data.uuid()); } // 最后再加入一个 JoinWorldTask 并开始资源包任务 - FastNMS.INSTANCE.method$ServerConfigurationPacketListenerImpl$returnToWorld(packetListener); + returnToWorld(user, configurationTasks, packetListener); }); } } 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 4bef1277b..2d30ab409 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 @@ -3958,6 +3958,27 @@ public final class CoreReflections { } } + // 1.20.2+ + public static final Method method$ServerConfigurationPacketListenerImpl$startNextTask = Optional.ofNullable(clazz$ServerConfigurationPacketListenerImpl) + .map(it -> ReflectionUtils.getDeclaredMethod(it, void.class, VersionHelper.isOrAbove1_20_5() ? new String[]{"startNextTask", "o"} : new String[]{"startNextTask", "p"})) + .orElse(null); + + public static final MethodHandle methodHandle$ServerConfigurationPacketListenerImpl$startNextTask; + + static { + try { + if (VersionHelper.isOrAbove1_20_2()) { + methodHandle$ServerConfigurationPacketListenerImpl$startNextTask = + ReflectionUtils.unreflectMethod(method$ServerConfigurationPacketListenerImpl$startNextTask) + .asType(MethodType.methodType(void.class, Object.class)); + } else { + methodHandle$ServerConfigurationPacketListenerImpl$startNextTask = null; + } + } catch (IllegalAccessException e) { + throw new ReflectionInitException("Failed to initialize reflection", e); + } + } + public static final Class clazz$JoinWorldTask = MiscUtils.requireNonNullIf( ReflectionUtils.getClazz( BukkitReflectionUtils.assembleMCClass("server.network.config.JoinWorldTask") From 7eb4dc1ff8b2110dfa787f40937d5cc6702f2ce7 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 28 Nov 2025 22:06:06 +0800 Subject: [PATCH 033/135] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=89=94=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mythicmobs/MythicItemDrop.java | 2 +- .../plugin/network/BukkitNetworkManager.java | 21 +- .../plugin/user/BukkitServerPlayer.java | 23 + .../craftengine/bukkit/util/EntityUtils.java | 2 +- .../configuration/templates/loot_tables.yml | 2 +- .../render/ConstantBlockEntityRenderer.java | 2 + .../core/entity/player/Player.java | 5 + .../craftengine/core/loot/LootPool.java | 2 +- .../core/plugin/context/ContextHolder.java | 7 +- .../condition/MatchBlockCondition.java | 2 +- .../context/function/BreakBlockFunction.java | 2 +- .../function/CycleBlockPropertyFunction.java | 6 +- .../context/function/PlaceBlockFunction.java | 2 +- .../plugin/context/function/RunFunction.java | 2 +- .../function/TransformBlockFunction.java | 6 +- .../function/UpdateBlockPropertyFunction.java | 6 +- .../parameter/EntityParameterProvider.java | 6 +- .../parameter/PlayerParameterProvider.java | 6 +- .../parameter/PositionParameterProvider.java | 6 +- .../plugin/entityculling/EntityCulling.java | 601 ++++++------------ .../plugin/entityculling/VoxelIterator.java | 216 +++++++ .../craftengine/core/util/Color.java | 2 +- .../craftengine/core/util/MiscUtils.java | 8 +- .../craftengine/core/world/BlockPos.java | 2 +- .../core/world/EntityHitResult.java | 6 +- .../craftengine/core/world/MutableVec3d.java | 6 +- .../craftengine/core/world/Vec3d.java | 2 +- .../craftengine/core/world/chunk/CEChunk.java | 78 ++- .../core/world/chunk/client/ClientChunk.java | 2 +- .../chunk/client/VirtualCullableObject.java | 3 +- 30 files changed, 584 insertions(+), 452 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/VoxelIterator.java 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 9258e4214..cb3ba791a 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 @@ -42,7 +42,7 @@ public class MythicItemDrop extends ItemDrop implements IItemDrop { context = ItemBuildContext.of(player); } } - int amountInt = MiscUtils.fastFloor(amount + 0.5F); + int amountInt = MiscUtils.floor(amount + 0.5F); ItemStack itemStack = this.customItem.buildItemStack(context, amountInt); return adapt(itemStack).amount(amountInt); } 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 b0c928641..c49b423e6 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 @@ -2075,6 +2075,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes // 两种情况都有,那么需要一个个遍历处理视线遮挡数据 if (hasOcclusions && hasNoOcclusions) { PackedOcclusionStorage storage = new PackedOcclusionStorage(false); + clientSections[i] = new ClientSection(storage); for (int j = 0; j < 4096; j++) { int state = container.get(j); storage.set(j, this.occlusionPredicate.test(state)); @@ -2092,6 +2093,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes PackedOcclusionStorage storage = null; if (clientSections != null) { storage = new PackedOcclusionStorage(false); + clientSections[i] = new ClientSection(storage); } for (int j = 0; j < 4096; j++) { @@ -2258,6 +2260,12 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes FriendlyByteBuf buf = event.getBuffer(); BlockPos pos = buf.readBlockPos(); int before = buf.readVarInt(); + if (Config.enableEntityCulling()) { + ClientChunk trackedChunk = user.getTrackedChunk(ChunkPos.asLong(pos.x >> 4, pos.z >> 4)); + if (trackedChunk != null) { + trackedChunk.setOccluding(pos.x, pos.y, pos.z, this.occlusionPredicate.test(before)); + } + } if (user.clientModEnabled() && !BlockStateUtils.isVanillaBlock(before)) { return; } @@ -2270,12 +2278,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes buf.writeVarInt(event.packetID()); buf.writeBlockPos(pos); buf.writeVarInt(state); - if (Config.enableEntityCulling()) { - ClientChunk trackedChunk = user.getTrackedChunk(ChunkPos.asLong(pos.x >> 4, pos.z >> 4)); - if (trackedChunk != null) { - trackedChunk.setOccluding(pos.x, pos.y, pos.z, this.occlusionPredicate.test(state)); - } - } } } @@ -2437,6 +2439,13 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes if (eventId != WorldEvents.BLOCK_BREAK_EFFECT) return; BlockPos blockPos = buf.readBlockPos(); int state = buf.readInt(); + // 移除不透明设置 + if (Config.enableEntityCulling()) { + ClientChunk trackedChunk = user.getTrackedChunk(ChunkPos.asLong(blockPos.x >> 4, blockPos.z >> 4)); + if (trackedChunk != null) { + trackedChunk.setOccluding(blockPos.x, blockPos.y, blockPos.z, false); + } + } boolean global = buf.readBoolean(); int newState = user.clientModEnabled() ? modBlockStateMapper[state] : blockStateMapper[state]; Object blockState = BlockStateUtils.idToBlockState(state); 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 842be8e1d..a08fea4dc 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 @@ -32,6 +32,7 @@ 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.CooldownData; +import net.momirealms.craftengine.core.plugin.entityculling.EntityCulling; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.network.ConnectionState; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; @@ -142,6 +143,8 @@ public class BukkitServerPlayer extends Player { // 跟踪到的方块实体渲染器 private final Map trackedBlockEntityRenderers = new ConcurrentHashMap<>(); + private final EntityCulling culling; + public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) { this.channel = channel; this.plugin = plugin; @@ -154,6 +157,7 @@ public class BukkitServerPlayer extends Player { } } } + this.culling = new EntityCulling(this, 64, 0.5); } public void setPlayer(org.bukkit.entity.Player player) { @@ -559,6 +563,15 @@ public class BukkitServerPlayer extends Player { this.predictNextBlockToMine(); } } + if (Config.enableEntityCulling()) { + long nano1 = System.nanoTime(); + for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { + boolean visible = this.culling.isVisible(cullableObject.cullable.aabb(), LocationUtils.toVec3d(platformPlayer().getEyeLocation())); + cullableObject.setShown(this, visible); + } + long nano2 = System.nanoTime(); + //CraftEngine.instance().logger().info("EntityCulling took " + (nano2 - nano1) / 1_000_000d + "ms"); + } } private void updateGUI() { @@ -1303,6 +1316,16 @@ public class BukkitServerPlayer extends Player { } } + @Override + public void addTrackedBlockEntity(BlockPos blockPos, ConstantBlockEntityRenderer renderer) { + this.trackedBlockEntityRenderers.put(blockPos, new VirtualCullableObject(renderer)); + } + + @Override + public VirtualCullableObject getTrackedBlockEntity(BlockPos blockPos) { + return this.trackedBlockEntityRenderers.get(blockPos); + } + @Override public void removeTrackedBlockEntities(Collection renders) { for (BlockPos render : renders) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java index 05a9600e1..f9cc52975 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java @@ -60,7 +60,7 @@ public final class EntityUtils { Object serverLevel = BukkitAdaptors.adapt(player.getWorld()).serverWorld(); Object serverPlayer = FastNMS.INSTANCE.method$CraftPlayer$getHandle(player); for (Object pose : List.of(CoreReflections.instance$Pose$STANDING, CoreReflections.instance$Pose$CROUCHING, CoreReflections.instance$Pose$SWIMMING)) { - BlockPos pos = new BlockPos(MiscUtils.fastFloor(x), MiscUtils.fastFloor(y), MiscUtils.fastFloor(z)); + BlockPos pos = new BlockPos(MiscUtils.floor(x), MiscUtils.floor(y), MiscUtils.floor(z)); try { double floorHeight = (double) CoreReflections.method$BlockGetter$getBlockFloorHeight.invoke(serverLevel, LocationUtils.toBlockPos(pos)); if (pos.y() + floorHeight > y + 0.75 || !isBlockFloorValid(floorHeight)) { diff --git a/common-files/src/main/resources/resources/default/configuration/templates/loot_tables.yml b/common-files/src/main/resources/resources/default/configuration/templates/loot_tables.yml index 99a366d26..24d3c1878 100644 --- a/common-files/src/main/resources/resources/default/configuration/templates/loot_tables.yml +++ b/common-files/src/main/resources/resources/default/configuration/templates/loot_tables.yml @@ -178,7 +178,7 @@ templates: # template: default:loot_table/ore # arguments: # ore_block: the ore block - # ore_drop: the drops of the ore + # ore_drop: the drops of the ore material # ore_drop_count: the amount of the ore materials # exp: the exp to drop default:loot_table/ore: 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 index 48245a05d..4551ed667 100644 --- 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 @@ -16,6 +16,7 @@ public class ConstantBlockEntityRenderer implements Cullable { this.aabb = aabb; } + @Override public void show(Player player) { for (BlockEntityElement element : this.elements) { if (element != null) { @@ -24,6 +25,7 @@ public class ConstantBlockEntityRenderer implements Cullable { } } + @Override public void hide(Player player) { for (BlockEntityElement element : this.elements) { if (element != null) { 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 d4dc6cddc..36e3fcb76 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 @@ -11,6 +11,7 @@ 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.*; +import net.momirealms.craftengine.core.world.chunk.client.VirtualCullableObject; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -202,6 +203,10 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void addTrackedBlockEntities(Map renders); + public abstract void addTrackedBlockEntity(BlockPos blockPos, ConstantBlockEntityRenderer renderer); + + public abstract VirtualCullableObject getTrackedBlockEntity(BlockPos blockPos); + public abstract void removeTrackedBlockEntities(Collection renders); public abstract void clearTrackedBlockEntities(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/LootPool.java b/core/src/main/java/net/momirealms/craftengine/core/loot/LootPool.java index f07bd5d3c..f82d2a7e9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/LootPool.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/LootPool.java @@ -44,7 +44,7 @@ public class LootPool { } if (this.compositeCondition.test(context)) { Consumer> consumer = LootFunction.decorate(this.compositeFunction, lootConsumer, context); - int i = this.rolls.getInt(context) + MiscUtils.fastFloor(this.bonusRolls.getFloat(context) * context.luck()); + int i = this.rolls.getInt(context) + MiscUtils.floor(this.bonusRolls.getFloat(context) * context.luck()); for (int j = 0; j < i; ++j) { this.addRandomItem(createFunctionApplier(consumer, context), context); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ContextHolder.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/ContextHolder.java index 758f77042..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,12 +1,13 @@ package net.momirealms.craftengine.core.plugin.context; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSortedMap; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; import java.util.function.Supplier; public class ContextHolder { 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 index 9eee31c0c..7f5e977c4 100644 --- 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 @@ -40,7 +40,7 @@ public class MatchBlockCondition implements Condition Optional optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION); if (optionalWorldPosition.isPresent()) { World world = optionalWorldPosition.get().world(); - ExistingBlock blockAt = world.getBlock(MiscUtils.fastFloor(this.x.getDouble(ctx)), MiscUtils.fastFloor(this.y.getDouble(ctx)), MiscUtils.fastFloor(this.z.getDouble(ctx))); + ExistingBlock blockAt = world.getBlock(MiscUtils.floor(this.x.getDouble(ctx)), MiscUtils.floor(this.y.getDouble(ctx)), MiscUtils.floor(this.z.getDouble(ctx))); return MiscUtils.matchRegex(blockAt.id().asString(), this.ids, this.regexMatch); } return false; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/BreakBlockFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/BreakBlockFunction.java index 268f23984..d0b63f852 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/BreakBlockFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/BreakBlockFunction.java @@ -28,7 +28,7 @@ public class BreakBlockFunction extends AbstractConditional @Override public void runInternal(CTX ctx) { Optional optionalPlayer = ctx.getOptionalParameter(DirectContextParameters.PLAYER); - optionalPlayer.ifPresent(player -> player.breakBlock(MiscUtils.fastFloor(x.getDouble(ctx)), MiscUtils.fastFloor(y.getDouble(ctx)), MiscUtils.fastFloor(z.getDouble(ctx)))); + optionalPlayer.ifPresent(player -> player.breakBlock(MiscUtils.floor(x.getDouble(ctx)), MiscUtils.floor(y.getDouble(ctx)), MiscUtils.floor(z.getDouble(ctx)))); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java index 6e1e58bf4..b99ef0e72 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java @@ -55,9 +55,9 @@ public class CycleBlockPropertyFunction extends AbstractCon Optional optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION); if (optionalWorldPosition.isEmpty()) return; World world = optionalWorldPosition.get().world(); - int x = MiscUtils.fastFloor(this.x.getDouble(ctx)); - int y = MiscUtils.fastFloor(this.y.getDouble(ctx)); - int z = MiscUtils.fastFloor(this.z.getDouble(ctx)); + int x = MiscUtils.floor(this.x.getDouble(ctx)); + int y = MiscUtils.floor(this.y.getDouble(ctx)); + int z = MiscUtils.floor(this.z.getDouble(ctx)); BlockStateWrapper wrapper = updateBlockState(world.getBlock(x, y, z).blockState(), ctx); world.setBlockState(x, y, z, wrapper, this.updateFlags.getInt(ctx)); } 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 1b6e8bf72..fedd7639a 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 @@ -40,7 +40,7 @@ public class PlaceBlockFunction extends AbstractConditional Optional optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION); if (optionalWorldPosition.isPresent()) { World world = optionalWorldPosition.get().world(); - world.setBlockState(MiscUtils.fastFloor(this.x.getDouble(ctx)), MiscUtils.fastFloor(this.y.getDouble(ctx)), MiscUtils.fastFloor(this.z.getDouble(ctx)), this.lazyBlockState.get(), this.updateFlags.getInt(ctx)); + world.setBlockState(MiscUtils.floor(this.x.getDouble(ctx)), MiscUtils.floor(this.y.getDouble(ctx)), MiscUtils.floor(this.z.getDouble(ctx)), this.lazyBlockState.get(), this.updateFlags.getInt(ctx)); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RunFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RunFunction.java index 020c44fe7..15f5b0586 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RunFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RunFunction.java @@ -48,7 +48,7 @@ public class RunFunction extends AbstractConditionalFunctio for (Function function : functions) { function.run(ctx); } - }, delay, pos.world().platformWorld(), MiscUtils.fastFloor(pos.x()) >> 4, MiscUtils.fastFloor(pos.z()) >> 4); + }, delay, pos.world().platformWorld(), MiscUtils.floor(pos.x()) >> 4, MiscUtils.floor(pos.z()) >> 4); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TransformBlockFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TransformBlockFunction.java index 09da0dc26..b27f438c4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TransformBlockFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TransformBlockFunction.java @@ -45,9 +45,9 @@ public class TransformBlockFunction extends AbstractConditi Optional optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION); if (optionalWorldPosition.isPresent()) { World world = optionalWorldPosition.get().world(); - int x = MiscUtils.fastFloor(this.x.getDouble(ctx)); - int y = MiscUtils.fastFloor(this.y.getDouble(ctx)); - int z = MiscUtils.fastFloor(this.z.getDouble(ctx)); + int x = MiscUtils.floor(this.x.getDouble(ctx)); + int y = MiscUtils.floor(this.y.getDouble(ctx)); + int z = MiscUtils.floor(this.z.getDouble(ctx)); BlockStateWrapper existingBlockState = world.getBlock(x, y, z).blockState().withProperties(this.properties); CompoundTag newProperties = new CompoundTag(); for (String propertyName : existingBlockState.getPropertyNames()) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/UpdateBlockPropertyFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/UpdateBlockPropertyFunction.java index 693647cf5..b59202282 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/UpdateBlockPropertyFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/UpdateBlockPropertyFunction.java @@ -40,9 +40,9 @@ public class UpdateBlockPropertyFunction extends AbstractCo Optional optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION); if (optionalWorldPosition.isPresent()) { World world = optionalWorldPosition.get().world(); - int x = MiscUtils.fastFloor(this.x.getDouble(ctx)); - int y = MiscUtils.fastFloor(this.y.getDouble(ctx)); - int z = MiscUtils.fastFloor(this.z.getDouble(ctx)); + int x = MiscUtils.floor(this.x.getDouble(ctx)); + int y = MiscUtils.floor(this.y.getDouble(ctx)); + int z = MiscUtils.floor(this.z.getDouble(ctx)); ExistingBlock blockAt = world.getBlock(x, y, z); BlockStateWrapper wrapper = blockAt.blockState().withProperties(this.properties); world.setBlockState(x, y, z, wrapper, this.updateFlags.getInt(ctx)); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java index 628b8d54c..0fd065ee2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java @@ -20,9 +20,9 @@ public class EntityParameterProvider implements ChainParameterProvider { CONTEXT_FUNCTIONS.put(DirectContextParameters.YAW, Entity::xRot); CONTEXT_FUNCTIONS.put(DirectContextParameters.PITCH, Entity::yRot); CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, Entity::position); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MiscUtils.fastFloor(p.x())); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.fastFloor(p.y())); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.fastFloor(p.z())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MiscUtils.floor(p.x())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.floor(p.y())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.floor(p.z())); CONTEXT_FUNCTIONS.put(DirectContextParameters.NAME, Entity::name); CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, Entity::uuid); CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, Entity::world); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PlayerParameterProvider.java index ee6033ccd..668a0c6d5 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 @@ -21,9 +21,9 @@ public class PlayerParameterProvider implements ChainParameterProvider { CONTEXT_FUNCTIONS.put(DirectContextParameters.PITCH, Entity::xRot); CONTEXT_FUNCTIONS.put(DirectContextParameters.YAW, Entity::yRot); CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, Entity::position); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MiscUtils.fastFloor(p.x())); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.fastFloor(p.y())); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.fastFloor(p.z())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MiscUtils.floor(p.x())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.floor(p.y())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.floor(p.z())); CONTEXT_FUNCTIONS.put(DirectContextParameters.FOOD, Player::foodLevel); CONTEXT_FUNCTIONS.put(DirectContextParameters.SATURATION, Player::saturation); CONTEXT_FUNCTIONS.put(DirectContextParameters.NAME, Player::name); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PositionParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PositionParameterProvider.java index c646658fb..3808e3f00 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PositionParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/PositionParameterProvider.java @@ -20,9 +20,9 @@ public class PositionParameterProvider implements ChainParameterProvider MiscUtils.fastFloor(p.x())); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.fastFloor(p.y())); - CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.fastFloor(p.z())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MiscUtils.floor(p.x())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.floor(p.y())); + CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.floor(p.z())); } @SuppressWarnings("unchecked") diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java index 72c6ecdc0..fe190845b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java @@ -1,453 +1,276 @@ package net.momirealms.craftengine.core.plugin.entityculling; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.MutableVec3d; import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.chunk.client.ClientChunk; +import net.momirealms.craftengine.core.world.collision.AABB; import java.util.Arrays; -import java.util.BitSet; -public class EntityCulling { - - // 面掩码常量 - private static final int ON_MIN_X = 0x01; - private static final int ON_MAX_X = 0x02; - private static final int ON_MIN_Y = 0x04; - private static final int ON_MAX_Y = 0x08; - private static final int ON_MIN_Z = 0x10; - private static final int ON_MAX_Z = 0x20; - - private final int reach; +public final class EntityCulling { + private final Player player; + private final int maxDistance; private final double aabbExpansion; - private final DataProvider provider; - private final OcclusionCache cache; + private final boolean[] dotSelectors = new boolean[14]; + private final MutableVec3d[] targetPoints = new MutableVec3d[14]; - // 重用数据结构,减少GC压力 - private final BitSet skipList = new BitSet(); - private final MutableVec3d[] targetPoints = new MutableVec3d[15]; - private final MutableVec3d targetPos = new MutableVec3d(0, 0, 0); - private final int[] cameraPos = new int[3]; - private final boolean[] dotselectors = new boolean[14]; - private final int[] lastHitBlock = new int[3]; - - // 状态标志 - private boolean allowRayChecks = false; - private boolean allowWallClipping = false; - - public EntityCulling(int maxDistance, DataProvider provider) { - this(maxDistance, provider, new ArrayOcclusionCache(maxDistance), 0.5); - } - - public EntityCulling(int maxDistance, DataProvider provider, OcclusionCache cache, double aabbExpansion) { - this.reach = maxDistance; - this.provider = provider; - this.cache = cache; + public EntityCulling(Player player, int maxDistance, double aabbExpansion) { + this.player = player; + this.maxDistance = maxDistance; this.aabbExpansion = aabbExpansion; - // 预先初始化点对象 - for(int i = 0; i < targetPoints.length; i++) { - targetPoints[i] = new MutableVec3d(0, 0, 0); + for (int i = 0; i < this.targetPoints.length; i++) { + this.targetPoints[i] = new MutableVec3d(0,0,0); } } - public boolean isAABBVisible(Vec3d aabbMin, MutableVec3d aabbMax, MutableVec3d viewerPosition) { - try { - // 计算包围盒范围 - int maxX = MiscUtils.fastFloor(aabbMax.x + aabbExpansion); - int maxY = MiscUtils.fastFloor(aabbMax.y + aabbExpansion); - int maxZ = MiscUtils.fastFloor(aabbMax.z + aabbExpansion); - int minX = MiscUtils.fastFloor(aabbMin.x - aabbExpansion); - int minY = MiscUtils.fastFloor(aabbMin.y - aabbExpansion); - int minZ = MiscUtils.fastFloor(aabbMin.z - aabbExpansion); + public boolean isVisible(AABB aabb, Vec3d cameraPos) { + // 根据AABB获取能包裹此AABB的最小长方体 + int minX = MiscUtils.floor(aabb.minX - this.aabbExpansion); + int minY = MiscUtils.floor(aabb.minY - this.aabbExpansion); + int minZ = MiscUtils.floor(aabb.minZ - this.aabbExpansion); + int maxX = MiscUtils.ceil(aabb.maxX + this.aabbExpansion); + int maxY = MiscUtils.ceil(aabb.maxY + this.aabbExpansion); + int maxZ = MiscUtils.ceil(aabb.maxZ + this.aabbExpansion); - cameraPos[0] = MiscUtils.fastFloor(viewerPosition.x); - cameraPos[1] = MiscUtils.fastFloor(viewerPosition.y); - cameraPos[2] = MiscUtils.fastFloor(viewerPosition.z); - - // 判断是否在包围盒内部 - Relative relX = Relative.from(minX, maxX, cameraPos[0]); - Relative relY = Relative.from(minY, maxY, cameraPos[1]); - Relative relZ = Relative.from(minZ, maxZ, cameraPos[2]); - - if(relX == Relative.INSIDE && relY == Relative.INSIDE && relZ == Relative.INSIDE) { - return true; + double cameraX = cameraPos.x; + double cameraY = cameraPos.y; + double cameraZ = cameraPos.z; + + Relative relX = Relative.from(minX, maxX, cameraX); + Relative relY = Relative.from(minY, maxY, cameraY); + Relative relZ = Relative.from(minZ, maxZ, cameraZ); + + // 相机位于实体内部 + if (relX == Relative.INSIDE && relY == Relative.INSIDE && relZ == Relative.INSIDE) { + return true; + } + + // 如果设置了最大距离 + if (this.maxDistance > 0) { + // 计算AABB到相机的最小距离 + double distanceSq = 0.0; + // 计算XYZ轴方向的距离 + distanceSq += distanceSq(minX, maxX, cameraX, relX); + distanceSq += distanceSq(minY, maxY, cameraY, relY); + distanceSq += distanceSq(minZ, maxZ, cameraZ, relZ); + // 检查距离是否超过最大值 + double maxDistanceSq = this.maxDistance * this.maxDistance; + // 超过最大距离,剔除 + if (distanceSq > maxDistanceSq) { + return false; } - - skipList.clear(); - - // 1. 快速检查缓存 - int id = 0; - for (int x = minX; x <= maxX; x++) { - for (int y = minY; y <= maxY; y++) { - for (int z = minZ; z <= maxZ; z++) { - int cachedValue = getCacheValue(x, y, z); - if (cachedValue == 1) return true; // 缓存显示可见 - if (cachedValue != 0) skipList.set(id); // 缓存显示不可见或遮挡 - id++; - } - } - } - - allowRayChecks = false; - id = 0; - - // 2. 遍历体素进行光线投射检查 - for (int x = minX; x <= maxX; x++) { - // 预计算X轴面的可见性和边缘数据 - byte visibleOnFaceX = 0; - byte faceEdgeDataX = 0; - if (x == minX) { faceEdgeDataX |= ON_MIN_X; if (relX == Relative.POSITIVE) visibleOnFaceX |= ON_MIN_X; } - if (x == maxX) { faceEdgeDataX |= ON_MAX_X; if (relX == Relative.NEGATIVE) visibleOnFaceX |= ON_MAX_X; } - - for (int y = minY; y <= maxY; y++) { - byte visibleOnFaceY = visibleOnFaceX; - byte faceEdgeDataY = faceEdgeDataX; - if (y == minY) { faceEdgeDataY |= ON_MIN_Y; if (relY == Relative.POSITIVE) visibleOnFaceY |= ON_MIN_Y; } - if (y == maxY) { faceEdgeDataY |= ON_MAX_Y; if (relY == Relative.NEGATIVE) visibleOnFaceY |= ON_MAX_Y; } - - for (int z = minZ; z <= maxZ; z++) { - // 如果缓存已标记为不可见,跳过 - if(skipList.get(id++)) continue; - - byte visibleOnFace = visibleOnFaceY; - byte faceEdgeData = faceEdgeDataY; - if (z == minZ) { faceEdgeData |= ON_MIN_Z; if (relZ == Relative.POSITIVE) visibleOnFace |= ON_MIN_Z; } - if (z == maxZ) { faceEdgeData |= ON_MAX_Z; if (relZ == Relative.NEGATIVE) visibleOnFace |= ON_MAX_Z; } - - if (visibleOnFace != 0) { - targetPos.set(x, y, z); - // 检查单个体素是否可见 - if (isVoxelVisible(viewerPosition, targetPos, faceEdgeData, visibleOnFace)) { - return true; - } - } - } - } - } - return false; - } catch (Throwable t) { - t.printStackTrace(); - return true; // 发生异常默认可见,防止渲染错误 - } - } - - // 接口定义 - public interface DataProvider { - boolean prepareChunk(int chunkX, int chunkZ); - boolean isOpaqueFullCube(int x, int y, int z); - default void cleanup() {} - default void checkingPosition(MutableVec3d[] targetPoints, int size, MutableVec3d viewerPosition) {} - } - - /** - * 检查单个体素是否对观察者可见 - */ - private boolean isVoxelVisible(MutableVec3d viewerPosition, MutableVec3d position, byte faceData, byte visibleOnFace) { - int targetSize = 0; - Arrays.fill(dotselectors, false); - - // 根据相对位置选择需要检测的关键点(角点和面中心点) - if((visibleOnFace & ON_MIN_X) != 0){ - dotselectors[0] = true; - if((faceData & ~ON_MIN_X) != 0) { dotselectors[1] = dotselectors[4] = dotselectors[5] = true; } - dotselectors[8] = true; - } - if((visibleOnFace & ON_MIN_Y) != 0){ - dotselectors[0] = true; - if((faceData & ~ON_MIN_Y) != 0) { dotselectors[3] = dotselectors[4] = dotselectors[7] = true; } - dotselectors[9] = true; - } - if((visibleOnFace & ON_MIN_Z) != 0){ - dotselectors[0] = true; - if((faceData & ~ON_MIN_Z) != 0) { dotselectors[1] = dotselectors[4] = dotselectors[5] = true; } - dotselectors[10] = true; - } - if((visibleOnFace & ON_MAX_X) != 0){ - dotselectors[4] = true; - if((faceData & ~ON_MAX_X) != 0) { dotselectors[5] = dotselectors[6] = dotselectors[7] = true; } - dotselectors[11] = true; - } - if((visibleOnFace & ON_MAX_Y) != 0){ - dotselectors[1] = true; - if((faceData & ~ON_MAX_Y) != 0) { dotselectors[2] = dotselectors[5] = dotselectors[6] = true; } - dotselectors[12] = true; - } - if((visibleOnFace & ON_MAX_Z) != 0){ - dotselectors[2] = true; - if((faceData & ~ON_MAX_Z) != 0) { dotselectors[3] = dotselectors[6] = dotselectors[7] = true; } - dotselectors[13] = true; } - // 填充目标点,使用偏移量防止Z-Fighting或精度问题 - if (dotselectors[0]) targetPoints[targetSize++].add(position, 0.05, 0.05, 0.05); - if (dotselectors[1]) targetPoints[targetSize++].add(position, 0.05, 0.95, 0.05); - if (dotselectors[2]) targetPoints[targetSize++].add(position, 0.05, 0.95, 0.95); - if (dotselectors[3]) targetPoints[targetSize++].add(position, 0.05, 0.05, 0.95); - if (dotselectors[4]) targetPoints[targetSize++].add(position, 0.95, 0.05, 0.05); - if (dotselectors[5]) targetPoints[targetSize++].add(position, 0.95, 0.95, 0.05); - if (dotselectors[6]) targetPoints[targetSize++].add(position, 0.95, 0.95, 0.95); - if (dotselectors[7]) targetPoints[targetSize++].add(position, 0.95, 0.05, 0.95); + // 清空之前的缓存 + Arrays.fill(this.dotSelectors, false); + if (relX == Relative.POSITIVE) { + this.dotSelectors[0] = this.dotSelectors[2] = this.dotSelectors[4] = this.dotSelectors[6] = this.dotSelectors[10] = true; + } else if (relX == Relative.NEGATIVE) { + this.dotSelectors[1] = this.dotSelectors[3] = this.dotSelectors[5] = this.dotSelectors[7] = this.dotSelectors[11] = true; + } + if (relY == Relative.POSITIVE) { + this.dotSelectors[0] = this.dotSelectors[1] = this.dotSelectors[2] = this.dotSelectors[3] = this.dotSelectors[12] = true; + } else if (relY == Relative.NEGATIVE) { + this.dotSelectors[4] = this.dotSelectors[5] = this.dotSelectors[6] = this.dotSelectors[7] = this.dotSelectors[13] = true; + } + if (relZ == Relative.POSITIVE) { + this.dotSelectors[0] = this.dotSelectors[1] = this.dotSelectors[4] = this.dotSelectors[5] = this.dotSelectors[8] = true; + } else if (relZ == Relative.NEGATIVE) { + this.dotSelectors[2] = this.dotSelectors[3] = this.dotSelectors[6] = this.dotSelectors[7] = this.dotSelectors[9] = true; + } + + int size = 0; + if (this.dotSelectors[0]) targetPoints[size++].set(minX, minY, minZ); + if (this.dotSelectors[1]) targetPoints[size++].set(maxX, minY, minZ); + if (this.dotSelectors[2]) targetPoints[size++].set(minX, minY, maxZ); + if (this.dotSelectors[3]) targetPoints[size++].set(maxX, minY, maxZ); + if (this.dotSelectors[4]) targetPoints[size++].set(minX, maxY, minZ); + if (this.dotSelectors[5]) targetPoints[size++].set(maxX, maxY, minZ); + if (this.dotSelectors[6]) targetPoints[size++].set(minX, maxY, maxZ); + if (this.dotSelectors[7]) targetPoints[size++].set(maxX, maxY, maxZ); // 面中心点 - if (dotselectors[8]) targetPoints[targetSize++].add(position, 0.05, 0.5, 0.5); - if (dotselectors[9]) targetPoints[targetSize++].add(position, 0.5, 0.05, 0.5); - if (dotselectors[10]) targetPoints[targetSize++].add(position, 0.5, 0.5, 0.05); - if (dotselectors[11]) targetPoints[targetSize++].add(position, 0.95, 0.5, 0.5); - if (dotselectors[12]) targetPoints[targetSize++].add(position, 0.5, 0.95, 0.5); - if (dotselectors[13]) targetPoints[targetSize++].add(position, 0.5, 0.5, 0.95); + double averageX = (minX + maxX) / 2.0; + double averageY = (minY + maxY) / 2.0; + double averageZ = (minZ + maxZ) / 2.0; + if (this.dotSelectors[8]) targetPoints[size++].set(averageX, averageY, minZ); + if (this.dotSelectors[9]) targetPoints[size++].set(averageX, averageY, maxZ); + if (this.dotSelectors[10]) targetPoints[size++].set(minX, averageY, averageZ); + if (this.dotSelectors[11]) targetPoints[size++].set(maxX, averageY, averageZ); + if (this.dotSelectors[12]) targetPoints[size++].set(averageX, minY, averageZ); + if (this.dotSelectors[13]) targetPoints[size].set(averageX, maxY, averageZ); - return isVisible(viewerPosition, targetPoints, targetSize); - } - - // 优化:使用基本数据类型代替对象分配 - private boolean rayIntersection(int[] b, MutableVec3d rayOrigin, double dirX, double dirY, double dirZ) { - double invX = 1.0 / dirX; - double invY = 1.0 / dirY; - double invZ = 1.0 / dirZ; - - double t1 = (b[0] - rayOrigin.x) * invX; - double t2 = (b[0] + 1 - rayOrigin.x) * invX; - double t3 = (b[1] - rayOrigin.y) * invY; - double t4 = (b[1] + 1 - rayOrigin.y) * invY; - double t5 = (b[2] - rayOrigin.z) * invZ; - double t6 = (b[2] + 1 - rayOrigin.z) * invZ; - - double tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4)), Math.min(t5, t6)); - double tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4)), Math.max(t5, t6)); - - // tmax > 0: 射线与AABB相交,但AABB在身后 - // tmin > tmax: 射线不相交 - return tmax > 0 && tmin <= tmax; + return isVisible(cameraPos, this.targetPoints, size); } /** - * 基于网格的光线追踪 (DDA算法) + * 使用3D DDA算法检测从起点到多个目标点的视线是否通畅 + * 算法基于数字微分分析,遍历射线路径上的所有方块 */ - private boolean isVisible(MutableVec3d start, MutableVec3d[] targets, int size) { - int startX = cameraPos[0]; - int startY = cameraPos[1]; - int startZ = cameraPos[2]; + private boolean isVisible(Vec3d start, MutableVec3d[] targets, int targetCount) { + // 起点所在方块的整数坐标(世界坐标转换为方块坐标) + int startBlockX = MiscUtils.floor(start.x); + int startBlockY = MiscUtils.floor(start.y); + int startBlockZ = MiscUtils.floor(start.z); - for (int v = 0; v < size; v++) { - MutableVec3d target = targets[v]; + // 遍历所有目标点进行视线检测 + for (int targetIndex = 0; targetIndex < targetCount; targetIndex++) { + MutableVec3d currentTarget = targets[targetIndex]; - double relX = start.x - target.x; - double relY = start.y - target.y; - double relZ = start.z - target.z; + // 计算起点到目标的相对向量(世界坐标差) + double deltaX = start.x - currentTarget.x; + double deltaY = start.y - currentTarget.y; + double deltaZ = start.z - currentTarget.z; - // 优化:避免在此处创建新的Vec3d对象进行归一化 - if(allowRayChecks) { - double len = Math.sqrt(relX * relX + relY * relY + relZ * relZ); - // 传入归一化后的方向分量 - if (rayIntersection(lastHitBlock, start, relX / len, relY / len, relZ / len)) { - continue; - } - } - - double dimAbsX = Math.abs(relX); - double dimAbsY = Math.abs(relY); - double dimAbsZ = Math.abs(relZ); + // 计算相对向量的绝对值,用于确定各方向上的距离 + double absDeltaX = Math.abs(deltaX); + double absDeltaY = Math.abs(deltaY); + double absDeltaZ = Math.abs(deltaZ); - double dimFracX = 1f / dimAbsX; - double dimFracY = 1f / dimAbsY; - double dimFracZ = 1f / dimAbsZ; + // 预计算每单位距离在各方块边界上的步进增量 + // 这些值表示射线穿过一个方块所需的时间分数 + double stepIncrementX = 1.0 / (absDeltaX + 1e-10); // 避免除0 + double stepIncrementY = 1.0 / (absDeltaY + 1e-10); + double stepIncrementZ = 1.0 / (absDeltaZ + 1e-10); - int intersectCount = 1; - int x_inc, y_inc, z_inc; - double t_next_y, t_next_x, t_next_z; + // 射线将穿过的总方块数量(包括起点和终点) + int totalBlocksToCheck = 1; - // 初始化DDA步进参数 - if (dimAbsX == 0f) { - x_inc = 0; t_next_x = dimFracX; - } else if (target.x > start.x) { - x_inc = 1; - intersectCount += MiscUtils.fastFloor(target.x) - startX; - t_next_x = (startX + 1 - start.x) * dimFracX; + // 各方块坐标的步进方向(1: 正向, -1: 反向, 0: 静止) + int stepDirectionX, stepDirectionY, stepDirectionZ; + + // 到下一个方块边界的时间参数(射线参数化表示) + double nextStepTimeX, nextStepTimeY, nextStepTimeZ; + + // X方向步进参数计算 + if (absDeltaX == 0.0) { + // X方向无变化,射线平行于YZ平面 + stepDirectionX = 0; + nextStepTimeX = stepIncrementX; + } else if (currentTarget.x > start.x) { + // 目标在起点右侧,向右步进 + stepDirectionX = 1; + totalBlocksToCheck += MiscUtils.floor(currentTarget.x) - startBlockX; + nextStepTimeX = (startBlockX + 1 - start.x) * stepIncrementX; } else { - x_inc = -1; - intersectCount += startX - MiscUtils.fastFloor(target.x); - t_next_x = (start.x - startX) * dimFracX; + // 目标在起点左侧,向左步进 + stepDirectionX = -1; + totalBlocksToCheck += startBlockX - MiscUtils.floor(currentTarget.x); + nextStepTimeX = (start.x - startBlockX) * stepIncrementX; } - if (dimAbsY == 0f) { - y_inc = 0; t_next_y = dimFracY; - } else if (target.y > start.y) { - y_inc = 1; - intersectCount += MiscUtils.fastFloor(target.y) - startY; - t_next_y = (startY + 1 - start.y) * dimFracY; + // Y方向步进参数计算 + if (absDeltaY == 0.0) { + // Y方向无变化,射线平行于XZ平面 + stepDirectionY = 0; + nextStepTimeY = stepIncrementY; + } else if (currentTarget.y > start.y) { + // 目标在起点上方,向上步进 + stepDirectionY = 1; + totalBlocksToCheck += MiscUtils.floor(currentTarget.y) - startBlockY; + nextStepTimeY = (startBlockY + 1 - start.y) * stepIncrementY; } else { - y_inc = -1; - intersectCount += startY - MiscUtils.fastFloor(target.y); - t_next_y = (start.y - startY) * dimFracY; + // 目标在起点下方,向下步进 + stepDirectionY = -1; + totalBlocksToCheck += startBlockY - MiscUtils.floor(currentTarget.y); + nextStepTimeY = (start.y - startBlockY) * stepIncrementY; } - if (dimAbsZ == 0f) { - z_inc = 0; t_next_z = dimFracZ; - } else if (target.z > start.z) { - z_inc = 1; - intersectCount += MiscUtils.fastFloor(target.z) - startZ; - t_next_z = (startZ + 1 - start.z) * dimFracZ; + // Z方向步进参数计算 + if (absDeltaZ == 0.0) { + // Z方向无变化,射线平行于XY平面 + stepDirectionZ = 0; + nextStepTimeZ = stepIncrementZ; + } else if (currentTarget.z > start.z) { + // 目标在起点前方,向前步进 + stepDirectionZ = 1; + totalBlocksToCheck += MiscUtils.floor(currentTarget.z) - startBlockZ; + nextStepTimeZ = (startBlockZ + 1 - start.z) * stepIncrementZ; } else { - z_inc = -1; - intersectCount += startZ - MiscUtils.fastFloor(target.z); - t_next_z = (start.z - startZ) * dimFracZ; + // 目标在起点后方,向后步进 + stepDirectionZ = -1; + totalBlocksToCheck += startBlockZ - MiscUtils.floor(currentTarget.z); + nextStepTimeZ = (start.z - startBlockZ) * stepIncrementZ; } - boolean finished = stepRay(startX, startY, startZ, - dimFracX, dimFracY, dimFracZ, intersectCount, - x_inc, y_inc, z_inc, - t_next_y, t_next_x, t_next_z); - - provider.cleanup(); - if (finished) { - cacheResult(targets[0], true); + // 执行DDA步进算法,遍历射线路径上的所有方块 + boolean isLineOfSightClear = stepRay( + startBlockX, startBlockY, startBlockZ, + stepIncrementX, stepIncrementY, stepIncrementZ, totalBlocksToCheck, + stepDirectionX, stepDirectionY, stepDirectionZ, + nextStepTimeY, nextStepTimeX, nextStepTimeZ); + + // 如果当前目标点可见立即返回 + if (isLineOfSightClear) { return true; - } else { - allowRayChecks = true; } } - cacheResult(targets[0], false); + return false; } - private boolean stepRay(int currentX, int currentY, int currentZ, - double distInX, double distInY, double distInZ, - int n, int x_inc, int y_inc, int z_inc, - double t_next_y, double t_next_x, double t_next_z) { - - allowWallClipping = true; // 初始允许穿墙直到移出起始方块 + private boolean stepRay(int currentBlockX, int currentBlockY, int currentBlockZ, + double stepSizeX, double stepSizeY, double stepSizeZ, + int remainingSteps, int stepDirectionX, int stepDirectionY, + int stepDirectionZ, double nextStepTimeY, double nextStepTimeX, + double nextStepTimeZ) { - for (; n > 1; n--) { - // 检查缓存状态:2=遮挡 - int cVal = getCacheValue(currentX, currentY, currentZ); - if (cVal == 2 && !allowWallClipping) { - lastHitBlock[0] = currentX; lastHitBlock[1] = currentY; lastHitBlock[2] = currentZ; - return false; + // 遍历射线路径上的所有方块(跳过最后一个目标方块) + for (; remainingSteps > 1; remainingSteps--) { + + // 检查当前方块是否遮挡视线 + if (isOccluding(currentBlockX, currentBlockY, currentBlockZ)) { + return false; // 视线被遮挡,立即返回 } - if (cVal == 0) { - // 未缓存,查询Provider - int chunkX = currentX >> 4; - int chunkZ = currentZ >> 4; - if (!provider.prepareChunk(chunkX, chunkZ)) return false; - - if (provider.isOpaqueFullCube(currentX, currentY, currentZ)) { - if (!allowWallClipping) { - cache.setLastHidden(); - lastHitBlock[0] = currentX; lastHitBlock[1] = currentY; lastHitBlock[2] = currentZ; - return false; - } - } else { - allowWallClipping = false; - cache.setLastVisible(); - } - } else if(cVal == 1) { - allowWallClipping = false; - } - - // DDA算法选择下一个体素 - if (t_next_y < t_next_x && t_next_y < t_next_z) { - currentY += y_inc; - t_next_y += distInY; - } else if (t_next_x < t_next_y && t_next_x < t_next_z) { - currentX += x_inc; - t_next_x += distInX; + // 基于时间参数选择下一个要遍历的方块方向 + // 选择距离最近的方块边界作为下一步 + if (nextStepTimeY < nextStepTimeX && nextStepTimeY < nextStepTimeZ) { + // Y方向边界最近,垂直移动 + currentBlockY += stepDirectionY; + nextStepTimeY += stepSizeY; + } else if (nextStepTimeX < nextStepTimeY && nextStepTimeX < nextStepTimeZ) { + // X方向边界最近,水平移动 + currentBlockX += stepDirectionX; + nextStepTimeX += stepSizeX; } else { - currentZ += z_inc; - t_next_z += distInZ; + // Z方向边界最近,深度移动 + currentBlockZ += stepDirectionZ; + nextStepTimeZ += stepSizeZ; } } + + // 成功遍历所有中间方块,视线通畅 return true; } - // 缓存状态:-1=无效, 0=未检查, 1=可见, 2=遮挡 - private int getCacheValue(int x, int y, int z) { - x -= cameraPos[0]; - y -= cameraPos[1]; - z -= cameraPos[2]; - if (Math.abs(x) > reach - 2 || Math.abs(y) > reach - 2 || Math.abs(z) > reach - 2) { - return -1; + private double distanceSq(int min, int max, double camera, Relative rel) { + if (rel == Relative.NEGATIVE) { + double dx = camera - max; + return dx * dx; + } else if (rel == Relative.POSITIVE) { + double dx = min - camera; + return dx * dx; } - return cache.getState(x + reach, y + reach, z + reach); + return 0d; } - private void cacheResult(MutableVec3d vector, boolean result) { - int cx = MiscUtils.fastFloor(vector.x) - cameraPos[0] + reach; - int cy = MiscUtils.fastFloor(vector.y) - cameraPos[1] + reach; - int cz = MiscUtils.fastFloor(vector.z) - cameraPos[2] + reach; - if (result) cache.setVisible(cx, cy, cz); - else cache.setHidden(cx, cy, cz); - } - - public void resetCache() { - this.cache.resetCache(); + private boolean isOccluding(int x, int y, int z) { + ClientChunk trackedChunk = this.player.getTrackedChunk(ChunkPos.asLong(x >> 4, z >> 4)); + if (trackedChunk == null) { + return false; + } + return trackedChunk.isOccluding(x, y, z); } private enum Relative { INSIDE, POSITIVE, NEGATIVE; - public static Relative from(int min, int max, int pos) { - if (max > pos && min > pos) return POSITIVE; - else if (min < pos && max < pos) return NEGATIVE; + public static Relative from(int min, int max, double pos) { + if (min > pos) return POSITIVE; + else if (max < pos) return NEGATIVE; return INSIDE; } } - - public interface OcclusionCache { - void resetCache(); - void setVisible(int x, int y, int z); - void setHidden(int x, int y, int z); - int getState(int x, int y, int z); - void setLastHidden(); - void setLastVisible(); - } - - // 使用位运算压缩存储状态的缓存实现 - public static class ArrayOcclusionCache implements OcclusionCache { - private final int reachX2; - private final byte[] cache; - private int entry, offset; - - public ArrayOcclusionCache(int reach) { - this.reachX2 = reach * 2; - // 每一个位置占2位 - this.cache = new byte[(reachX2 * reachX2 * reachX2) / 4 + 1]; - } - - @Override - public void resetCache() { - Arrays.fill(cache, (byte) 0); - } - - private void calcIndex(int x, int y, int z) { - int positionKey = x + y * reachX2 + z * reachX2 * reachX2; - entry = positionKey / 4; - offset = (positionKey % 4) * 2; - } - - @Override - public void setVisible(int x, int y, int z) { - calcIndex(x, y, z); - cache[entry] |= 1 << offset; - } - - @Override - public void setHidden(int x, int y, int z) { - calcIndex(x, y, z); - cache[entry] |= 1 << (offset + 1); - } - - @Override - public int getState(int x, int y, int z) { - calcIndex(x, y, z); - return (cache[entry] >> offset) & 3; - } - - @Override - public void setLastVisible() { - cache[entry] |= 1 << offset; - } - - @Override - public void setLastHidden() { - cache[entry] |= 1 << (offset + 1); - } - } -} \ No newline at end of file +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/VoxelIterator.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/VoxelIterator.java new file mode 100644 index 000000000..a77160fa1 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/VoxelIterator.java @@ -0,0 +1,216 @@ +package net.momirealms.craftengine.core.plugin.entityculling; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +// Amanatides, J., & Woo, A. A Fast Voxel Traversal Algorithm for Ray Tracing. http://www.cse.yorku.ca/~amana/research/grid.pdf. +public final class VoxelIterator implements Iterator { + private int x; + private int y; + private int z; + private int stepX; + private int stepY; + private int stepZ; + private double tMax; + private double tMaxX; + private double tMaxY; + private double tMaxZ; + private double tDeltaX; + private double tDeltaY; + private double tDeltaZ; + private int[] ref = new int[3]; // This implementation always returns ref or refSwap to avoid garbage. Can easily be changed if needed. + private int[] refSwap = new int[3]; + private int[] next; + + public VoxelIterator(double startX, double startY, double startZ, double endX, double endY, double endZ) { + initialize(startX, startY, startZ, endX, endY, endZ); + } + + public VoxelIterator(int x, int y, int z, double startX, double startY, double startZ, double endX, double endY, double endZ) { + initialize(x, y, z, startX, startY, startZ, endX, endY, endZ); + } + + public VoxelIterator(double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) { + initialize(startX, startY, startZ, directionX, directionY, directionZ, distance); + } + + public VoxelIterator(int x, int y, int z, double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) { + initialize(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, distance); + } + + public VoxelIterator(double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance, boolean normalized) { + if (normalized) { + initializeNormalized(startX, startY, startZ, directionX, directionY, directionZ, distance); + } else { + initialize(startX, startY, startZ, directionX, directionY, directionZ, distance); + } + } + + public VoxelIterator(int x, int y, int z, double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance, boolean normalized) { + if (normalized) { + initializeNormalized(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, distance); + } else { + initialize(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, distance); + } + } + + public VoxelIterator initialize(double startX, double startY, double startZ, double endX, double endY, double endZ) { + return initialize(floor(startX), floor(startY), floor(startZ), startX, startY, startZ, endX, endY, endZ); + } + + public VoxelIterator initialize(int x, int y, int z, double startX, double startY, double startZ, double endX, double endY, double endZ) { + double directionX = endX - startX; + double directionY = endY - startY; + double directionZ = endZ - startZ; + double distance = Math.sqrt(directionX * directionX + directionY * directionY + directionZ * directionZ); + double fixedDistance = distance == 0. ? Double.NaN : distance; + directionX /= fixedDistance; + directionY /= fixedDistance; + directionZ /= fixedDistance; + return initializeNormalized(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, distance); + } + + public VoxelIterator initialize(double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) { + return initialize(floor(startX), floor(startY), floor(startZ), startX, startY, startZ, directionX, directionY, directionZ, distance); + } + + public VoxelIterator initialize(int x, int y, int z, double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) { + double signum = Math.signum(distance); + directionX *= signum; + directionY *= signum; + directionZ *= signum; + double length = Math.sqrt(directionX * directionX + directionY * directionY + directionZ * directionZ); + + if (length == 0.) { + length = Double.NaN; + } + + directionX /= length; + directionY /= length; + directionZ /= length; + return initializeNormalized(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, Math.abs(distance)); + } + + public VoxelIterator initializeNormalized(double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) { + return initializeNormalized(floor(startX), floor(startY), floor(startZ), startX, startY, startZ, directionX, directionY, directionZ, Math.abs(distance)); + } + + public VoxelIterator initializeNormalized(int x, int y, int z, double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) { + this.x = x; + this.y = y; + this.z = z; + tMax = distance; + stepX = directionX < 0. ? -1 : 1; + stepY = directionY < 0. ? -1 : 1; + stepZ = directionZ < 0. ? -1 : 1; + tMaxX = directionX == 0. ? Double.POSITIVE_INFINITY : (x + (stepX + 1) / 2 - startX) / directionX; + tMaxY = directionY == 0. ? Double.POSITIVE_INFINITY : (y + (stepY + 1) / 2 - startY) / directionY; + tMaxZ = directionZ == 0. ? Double.POSITIVE_INFINITY : (z + (stepZ + 1) / 2 - startZ) / directionZ; + tDeltaX = 1. / Math.abs(directionX); + tDeltaY = 1. / Math.abs(directionY); + tDeltaZ = 1. / Math.abs(directionZ); + next = ref; + ref[0] = x; + ref[1] = y; + ref[2] = z; + return this; + } + + public int[] calculateNext() { + if (tMaxX < tMaxY) { + if (tMaxZ < tMaxX) { + if (tMaxZ <= tMax) { + z += stepZ; + // next = new int[] { x, y, z }; + ref[0] = x; + ref[1] = y; + ref[2] = z; + tMaxZ += tDeltaZ; + } else { + next = null; + } + } else { + if (tMaxX <= tMax) { + if (tMaxZ == tMaxX) { + z += stepZ; + tMaxZ += tDeltaZ; + } + + x += stepX; + // next = new int[] { x, y, z }; + ref[0] = x; + ref[1] = y; + ref[2] = z; + tMaxX += tDeltaX; + } else { + next = null; + } + } + } else if (tMaxY < tMaxZ) { + if (tMaxY <= tMax) { + if (tMaxX == tMaxY) { + x += stepX; + tMaxX += tDeltaX; + } + + y += stepY; + // next = new int[] { x, y, z }; + ref[0] = x; + ref[1] = y; + ref[2] = z; + tMaxY += tDeltaY; + } else { + next = null; + } + } else { + if (tMaxZ <= tMax) { + if (tMaxX == tMaxZ) { + x += stepX; + tMaxX += tDeltaX; + } + + if (tMaxY == tMaxZ) { + y += stepY; + tMaxY += tDeltaY; + } + + z += stepZ; + // next = new int[] { x, y, z }; + ref[0] = x; + ref[1] = y; + ref[2] = z; + tMaxZ += tDeltaZ; + } else { + next = null; + } + } + + return next; + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public int[] next() { + int[] next = this.next; + + if (next == null) { + throw new NoSuchElementException(); + } + + int[] temp = ref; + ref = refSwap; + refSwap = temp; + this.next = ref; + calculateNext(); + return next; + } + + private static int floor(double value) { + int i = (int) value; + return value < (double) i ? i - 1 : i; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Color.java b/core/src/main/java/net/momirealms/craftengine/core/util/Color.java index 5f37d42b0..38299e43b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Color.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Color.java @@ -38,7 +38,7 @@ public class Color { } public static Color fromVector3f(Vector3f vec) { - return new Color(0 << 24 /*不可省略*/ | MiscUtils.fastFloor(vec.x) << 16 | MiscUtils.fastFloor(vec.y) << 8 | MiscUtils.fastFloor(vec.z)); + return new Color(0 << 24 /*不可省略*/ | MiscUtils.floor(vec.x) << 16 | MiscUtils.floor(vec.y) << 8 | MiscUtils.floor(vec.z)); } public static int opaque(int color) { 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 2e28001c2..523bbc364 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 @@ -21,19 +21,19 @@ public class MiscUtils { } }); - public static int fastFloor(double value) { + public static int floor(double value) { int truncated = (int) value; return value < (double) truncated ? truncated - 1 : truncated; } - public static int fastFloor(float value) { + public static int floor(float value) { int truncated = (int) value; return value < (double) truncated ? truncated - 1 : truncated; } public static int lerpDiscrete(float delta, int start, int end) { int i = end - start; - return start + fastFloor(delta * (float) (i - 1)) + (delta > 0.0F ? 1 : 0); + return start + floor(delta * (float) (i - 1)) + (delta > 0.0F ? 1 : 0); } public static int murmurHash3Mixer(int value) { @@ -270,7 +270,7 @@ public class MiscUtils { } public static byte packDegrees(float degrees) { - return (byte) fastFloor(degrees * 256.0F / 360.0F); + return (byte) floor(degrees * 256.0F / 360.0F); } public static float unpackDegrees(byte degrees) { 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 38a9acf20..71838e8c3 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 @@ -23,7 +23,7 @@ public class BlockPos extends Vec3i { } public static BlockPos fromVec3d(Vec3d vec) { - return new BlockPos(MiscUtils.fastFloor(vec.x), MiscUtils.fastFloor(vec.y), MiscUtils.fastFloor(vec.z)); + return new BlockPos(MiscUtils.floor(vec.x), MiscUtils.floor(vec.y), MiscUtils.floor(vec.z)); } public static BlockPos of(long packedPos) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/EntityHitResult.java b/core/src/main/java/net/momirealms/craftengine/core/world/EntityHitResult.java index 6944337ed..fed33e417 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/EntityHitResult.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/EntityHitResult.java @@ -23,9 +23,9 @@ public class EntityHitResult { } private BlockPos getBlockPos() { - int x = MiscUtils.fastFloor(this.position.x); - int y = MiscUtils.fastFloor(this.position.y); - int z = MiscUtils.fastFloor(this.position.z); + int x = MiscUtils.floor(this.position.x); + int y = MiscUtils.floor(this.position.y); + int z = MiscUtils.floor(this.position.z); if (this.direction == Direction.UP) { if (this.position.y % 1 == 0) { y--; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/MutableVec3d.java b/core/src/main/java/net/momirealms/craftengine/core/world/MutableVec3d.java index 1f4a989ac..dd469e7ec 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/MutableVec3d.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/MutableVec3d.java @@ -14,9 +14,9 @@ public class MutableVec3d implements Position { } public MutableVec3d toCenter() { - this.x = MiscUtils.fastFloor(x) + 0.5; - this.y = MiscUtils.fastFloor(y) + 0.5; - this.z = MiscUtils.fastFloor(z) + 0.5; + this.x = MiscUtils.floor(x) + 0.5; + this.y = MiscUtils.floor(y) + 0.5; + this.z = MiscUtils.floor(z) + 0.5; return this; } 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 7165596a0..fe3cb6938 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 @@ -15,7 +15,7 @@ public class Vec3d implements Position { } public Vec3d toCenter() { - return new Vec3d(MiscUtils.fastFloor(x) + 0.5, MiscUtils.fastFloor(y) + 0.5, MiscUtils.fastFloor(z) + 0.5); + return new Vec3d(MiscUtils.floor(x) + 0.5, MiscUtils.floor(y) + 0.5, MiscUtils.floor(z) + 0.5); } public Vec3d add(Vec3d vec) { 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 f12123ae2..4ca2fe5df 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 @@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.world.*; +import net.momirealms.craftengine.core.world.chunk.client.VirtualCullableObject; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntityRendererSerializer; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer; import net.momirealms.sparrow.nbt.ListTag; @@ -155,8 +156,20 @@ public class CEChunk { if (element != null) { elements[0] = element; if (hasTrackedBy) { - for (Player player : trackedBy) { - element.transform(player); + // 如果启用实体剔除,那么只对已经渲染的进行变换 + if (Config.enableEntityCulling()) { + for (Player player : trackedBy) { + VirtualCullableObject trackedBlockEntity = player.getTrackedBlockEntity(pos); + if (trackedBlockEntity == null || trackedBlockEntity.isShown()) { + element.transform(player); + } + } + } + // 否则直接变换 + else { + for (Player player : trackedBy) { + element.transform(player); + } } } break outer; @@ -165,9 +178,18 @@ public class CEChunk { BlockEntityElement element = config.create(wrappedWorld, pos); elements[0] = element; if (hasTrackedBy) { - for (Player player : trackedBy) { - previousElement.hide(player); - element.show(player); + // 如果启用实体剔除,那么只添加记录 + if (Config.enableEntityCulling()) { + for (Player player : trackedBy) { + player.addTrackedBlockEntity(pos, renderer); + } + } + // 否则直接显示 + else { + for (Player player : trackedBy) { + previousElement.hide(player); + element.show(player); + } } } } @@ -182,8 +204,20 @@ public class CEChunk { previousElements[j] = null; elements[i] = newElement; if (hasTrackedBy) { - for (Player player : trackedBy) { - newElement.transform(player); + // 如果启用实体剔除,那么只对已经渲染的进行变换 + if (Config.enableEntityCulling()) { + for (Player player : trackedBy) { + VirtualCullableObject trackedBlockEntity = player.getTrackedBlockEntity(pos); + if (trackedBlockEntity == null || trackedBlockEntity.isShown()) { + newElement.transform(player); + } + } + } + // 否则直接变换 + else { + for (Player player : trackedBy) { + newElement.transform(player); + } } } continue outer; @@ -193,8 +227,14 @@ public class CEChunk { BlockEntityElement newElement = config.create(wrappedWorld, pos); elements[i] = newElement; if (hasTrackedBy) { - for (Player player : trackedBy) { - newElement.show(player); + if (Config.enableEntityCulling()) { + for (Player player : trackedBy) { + player.addTrackedBlockEntity(pos, renderer); + } + } else { + for (Player player : trackedBy) { + newElement.show(player); + } } } } @@ -214,8 +254,14 @@ public class CEChunk { elements[i] = renderers[i].create(wrappedWorld, pos); } if (hasTrackedBy) { - for (Player player : trackedBy) { - renderer.show(player); + if (Config.enableEntityCulling()) { + for (Player player : trackedBy) { + player.addTrackedBlockEntity(pos, renderer); + } + } else { + for (Player player : trackedBy) { + renderer.show(player); + } } } } @@ -242,8 +288,14 @@ public class CEChunk { ConstantBlockEntityRenderer removed = this.constantBlockEntityRenderers.remove(pos); if (removed != null) { if (hide) { - for (Player player : getTrackedBy()) { - removed.hide(player); + if (Config.enableEntityCulling()) { + for (Player player : getTrackedBy()) { + player.removeTrackedBlockEntities(List.of(pos)); + } + } else { + for (Player player : getTrackedBy()) { + removed.hide(player); + } } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java index 29379fd21..03e0ac6ad 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java @@ -32,7 +32,7 @@ public class ClientChunk { int index = sectionIndex(SectionPos.blockToSectionCoord(y)); ClientSection section = this.sections[index]; if (section == null) return; - section.setOccluding((y & 15) << 8 | (z & 15) << 4, occluding); + section.setOccluding((y & 15) << 8 | (z & 15) << 4 | x & 15, occluding); } public int sectionIndex(int sectionId) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java index 127b8b09e..80b6f9ef2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java @@ -4,7 +4,7 @@ import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.world.Cullable; public class VirtualCullableObject { - private final Cullable cullable; + public final Cullable cullable; private boolean isShown; public VirtualCullableObject(Cullable cullable) { @@ -21,6 +21,7 @@ public class VirtualCullableObject { } public void setShown(Player player, boolean shown) { + if (this.isShown == shown) return; this.isShown = shown; if (shown) { this.cullable.show(player); From 399a01cd1b827beb3a732a1369d7da264c8318fc Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 28 Nov 2025 22:49:35 +0800 Subject: [PATCH 034/135] =?UTF-8?q?=E5=AF=B9=E4=BA=8E=E9=A2=91=E7=B9=81?= =?UTF-8?q?=E5=91=BD=E4=B8=AD=E5=AF=B9=E8=B1=A1=E8=BF=9B=E8=A1=8C=E8=AE=BF?= =?UTF-8?q?=E9=97=AE=E9=A2=91=E6=AC=A1=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/entityculling/EntityCulling.java | 98 +++++++++++++++++-- 1 file changed, 92 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java index fe190845b..2b16695bf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java @@ -11,22 +11,33 @@ import net.momirealms.craftengine.core.world.collision.AABB; import java.util.Arrays; public final class EntityCulling { + public static final int MAX_SAMPLES = 14; private final Player player; private final int maxDistance; private final double aabbExpansion; - private final boolean[] dotSelectors = new boolean[14]; - private final MutableVec3d[] targetPoints = new MutableVec3d[14]; - + private final boolean[] dotSelectors = new boolean[MAX_SAMPLES]; + private final MutableVec3d[] targetPoints = new MutableVec3d[MAX_SAMPLES]; + private final int[] lastHitBlock = new int[MAX_SAMPLES * 3]; + private final boolean[] canCheckLastHitBlock = new boolean[MAX_SAMPLES]; + private int hitBlockCount = 0; + private int lastVisitChunkX = Integer.MAX_VALUE; + private int lastVisitChunkZ = Integer.MAX_VALUE; + private ClientChunk lastVisitChunk = null; + public EntityCulling(Player player, int maxDistance, double aabbExpansion) { this.player = player; this.maxDistance = maxDistance; this.aabbExpansion = aabbExpansion; - for (int i = 0; i < this.targetPoints.length; i++) { + for (int i = 0; i < MAX_SAMPLES; i++) { this.targetPoints[i] = new MutableVec3d(0,0,0); } } public boolean isVisible(AABB aabb, Vec3d cameraPos) { + // 情空标志位 + Arrays.fill(this.canCheckLastHitBlock, false); + this.hitBlockCount = 0; + // 根据AABB获取能包裹此AABB的最小长方体 int minX = MiscUtils.floor(aabb.minX - this.aabbExpansion); int minY = MiscUtils.floor(aabb.minY - this.aabbExpansion); @@ -105,18 +116,59 @@ public final class EntityCulling { return isVisible(cameraPos, this.targetPoints, size); } + /** + * 检测射线与轴对齐边界框(AABB)是否相交 + * 使用slab方法进行射线-AABB相交检测 + */ + private boolean rayIntersection(int x, int y, int z, Vec3d rayOrigin, MutableVec3d rayDirection) { + // 计算射线方向的倒数,避免除法运算 + // 这对于处理射线方向分量为0的情况很重要 + MutableVec3d inverseRayDirection = new MutableVec3d(1, 1, 1).divide(rayDirection); + + // 计算射线与边界框各对面(slab)的相交参数 + // 对于每个轴,计算射线进入和退出该轴对应两个平面的时间 + double tMinX = (x - rayOrigin.x) * inverseRayDirection.x; + double tMaxX = (x + 1 - rayOrigin.x) * inverseRayDirection.x; + double tMinY = (y - rayOrigin.y) * inverseRayDirection.y; + double tMaxY = (y + 1 - rayOrigin.y) * inverseRayDirection.y; + double tMinZ = (z - rayOrigin.z) * inverseRayDirection.z; + double tMaxZ = (z + 1 - rayOrigin.z) * inverseRayDirection.z; + + // 计算射线进入边界框的最大时间(最近进入点) + // 需要取各轴进入时间的最大值,因为射线必须进入所有轴的范围内 + double tEntry = Math.max(Math.max(Math.min(tMinX, tMaxX), Math.min(tMinY, tMaxY)), Math.min(tMinZ, tMaxZ)); + + // 计算射线退出边界框的最短时间(最早退出点) + // 需要取各轴退出时间的最小值,因为射线一旦退出任一轴的范围就离开了边界框 + double tExit = Math.min(Math.min(Math.max(tMinX, tMaxX), Math.max(tMinY, tMaxY)), Math.max(tMinZ, tMaxZ)); + + // 如果最早退出时间大于0,说明整个边界框在射线起点后面 + // 这种情况我们视为不相交,因为通常我们只关心射线前方的相交 + if (tExit > 0) { + return false; + } + + // 如果进入时间大于退出时间,说明没有有效的相交区间 + // 这发生在射线完全错过边界框的情况下 + // 满足以下条件说明射线与边界框相交: + // 1. 进入时间 <= 退出时间(存在有效相交区间) + // 2. 退出时间 <= 0(边界框至少有一部分在射线起点前方或包含起点) + return tEntry <= tExit; + } + /** * 使用3D DDA算法检测从起点到多个目标点的视线是否通畅 * 算法基于数字微分分析,遍历射线路径上的所有方块 */ private boolean isVisible(Vec3d start, MutableVec3d[] targets, int targetCount) { + // 起点所在方块的整数坐标(世界坐标转换为方块坐标) int startBlockX = MiscUtils.floor(start.x); int startBlockY = MiscUtils.floor(start.y); int startBlockZ = MiscUtils.floor(start.z); // 遍历所有目标点进行视线检测 - for (int targetIndex = 0; targetIndex < targetCount; targetIndex++) { + outer: for (int targetIndex = 0; targetIndex < targetCount; targetIndex++) { MutableVec3d currentTarget = targets[targetIndex]; // 计算起点到目标的相对向量(世界坐标差) @@ -124,6 +176,18 @@ public final class EntityCulling { double deltaY = start.y - currentTarget.y; double deltaZ = start.z - currentTarget.z; + // 检查之前命中的方块,大概率还是命中 + for (int i = 0; i < MAX_SAMPLES; i++) { + if (this.canCheckLastHitBlock[i]) { + int offset = i * 3; + if (rayIntersection(this.lastHitBlock[offset], this.lastHitBlock[offset + 1], this.lastHitBlock[offset + 2], start, new MutableVec3d(deltaX, deltaY, deltaZ).normalize())) { + continue outer; + } + } else { + break; + } + } + // 计算相对向量的绝对值,用于确定各方向上的距离 double absDeltaX = Math.abs(deltaX); double absDeltaY = Math.abs(deltaY); @@ -205,6 +269,8 @@ public final class EntityCulling { // 如果当前目标点可见立即返回 if (isLineOfSightClear) { return true; + } else { + this.canCheckLastHitBlock[this.hitBlockCount++] = true; } } @@ -222,6 +288,9 @@ public final class EntityCulling { // 检查当前方块是否遮挡视线 if (isOccluding(currentBlockX, currentBlockY, currentBlockZ)) { + this.lastHitBlock[this.hitBlockCount * 3] = currentBlockX; + this.lastHitBlock[this.hitBlockCount * 3 + 1] = currentBlockY; + this.lastHitBlock[this.hitBlockCount * 3 + 2] = currentBlockZ; return false; // 视线被遮挡,立即返回 } @@ -258,13 +327,30 @@ public final class EntityCulling { } private boolean isOccluding(int x, int y, int z) { - ClientChunk trackedChunk = this.player.getTrackedChunk(ChunkPos.asLong(x >> 4, z >> 4)); + int chunkX = x >> 4; + int chunkZ = z >> 4; + ClientChunk trackedChunk; + // 使用上次记录的值,比每次走hash都更快 + if (chunkX == this.lastVisitChunkX && chunkZ == this.lastVisitChunkZ) { + trackedChunk = this.lastVisitChunk; + } else { + trackedChunk = this.player.getTrackedChunk(ChunkPos.asLong(chunkX, chunkZ)); + this.lastVisitChunk = trackedChunk; + this.lastVisitChunkX = chunkX; + this.lastVisitChunkZ = chunkZ; + } if (trackedChunk == null) { return false; } return trackedChunk.isOccluding(x, y, z); } + public void removeLastVisitChunkIfMatches(int chunkX, int chunkZ) { + if (this.lastVisitChunk != null && this.lastVisitChunkX == chunkX && this.lastVisitChunkZ == chunkZ) { + this.lastVisitChunk = null; + } + } + private enum Relative { INSIDE, POSITIVE, NEGATIVE; public static Relative from(int min, int max, double pos) { From c59478e891745d246829cd55481ad46aa9326a5c Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 29 Nov 2025 01:46:31 +0800 Subject: [PATCH 035/135] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AE=9E=E4=BD=93?= =?UTF-8?q?=E5=89=94=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/network/BukkitNetworkManager.java | 9 ++ .../plugin/user/BukkitServerPlayer.java | 47 +++++++- common-files/src/main/resources/config.yml | 5 +- .../core/block/AbstractBlockManager.java | 24 +++- .../core/block/BlockStateAppearance.java | 5 +- .../core/block/ImmutableBlockState.java | 14 ++- .../render/ConstantBlockEntityRenderer.java | 17 ++- .../core/entity/player/Player.java | 2 +- .../core/plugin/config/Config.java | 6 + .../plugin/entityculling/CullingData.java | 27 +++++ .../plugin/entityculling/EntityCulling.java | 112 +++++++++++++++--- .../scheduler/AbstractJavaScheduler.java | 8 ++ .../core/plugin/scheduler/LazyAsyncTask.java | 21 ++++ .../plugin/scheduler/SchedulerAdapter.java | 3 + .../craftengine/core/world/Cullable.java | 8 +- .../craftengine/core/world/chunk/CEChunk.java | 12 +- 16 files changed, 270 insertions(+), 50 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/CullingData.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/LazyAsyncTask.java 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 c49b423e6..ba75b5f5e 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 @@ -128,6 +128,7 @@ import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Predicate; @@ -461,6 +462,14 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes player.getScheduler().runAtFixedRate(plugin.javaPlugin(), (t) -> user.tick(), () -> {}, 1, 1); } + // 安排异步tick任务 + this.plugin.scheduler().asyncRepeating(t -> { + if (!user.isOnline()) { + t.cancel(); + return; + } + user.asyncTick(); + }, 50, 50, TimeUnit.MILLISECONDS); user.sendPacket(TotemAnimationCommand.FIX_TOTEM_SOUND_PACKET, false); } } 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 a08fea4dc..e1b19a350 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 @@ -32,6 +32,7 @@ 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.CooldownData; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.plugin.entityculling.EntityCulling; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.network.ConnectionState; @@ -157,7 +158,7 @@ public class BukkitServerPlayer extends Player { } } } - this.culling = new EntityCulling(this, 64, 0.5); + this.culling = new EntityCulling(this); } public void setPlayer(org.bukkit.entity.Player player) { @@ -506,6 +507,9 @@ public class BukkitServerPlayer extends Player { this.encoderState = encoderState; } + private final Queue recentDurations = new LinkedList<>(); + private static final int SAMPLE_SIZE = 100; + @Override public void tick() { // not fully online @@ -563,14 +567,42 @@ public class BukkitServerPlayer extends Player { this.predictNextBlockToMine(); } } + } + + public void asyncTick() { if (Config.enableEntityCulling()) { long nano1 = System.nanoTime(); for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { - boolean visible = this.culling.isVisible(cullableObject.cullable.aabb(), LocationUtils.toVec3d(platformPlayer().getEyeLocation())); - cullableObject.setShown(this, visible); + CullingData cullingData = cullableObject.cullable.cullingData(); + if (cullingData != null) { + Vec3d vec3d = LocationUtils.toVec3d(platformPlayer().getEyeLocation()); + boolean visible = this.culling.isVisible(cullingData, vec3d, vec3d); + cullableObject.setShown(this, visible); + } else { + cullableObject.setShown(this, true); + } + this.culling.resetCache(); } long nano2 = System.nanoTime(); - //CraftEngine.instance().logger().info("EntityCulling took " + (nano2 - nano1) / 1_000_000d + "ms"); + + long duration = nano2 - nano1; + + // 添加到队列 + recentDurations.offer(duration); + + // 保持队列大小为100 + if (recentDurations.size() > SAMPLE_SIZE) { + recentDurations.poll(); + } + + // 每100次输出一次平均耗时 + if (recentDurations.size() == SAMPLE_SIZE) { + double averageMs = recentDurations.stream() + .mapToLong(Long::longValue) + .average() + .orElse(0) / 1_000_000d; + CraftEngine.instance().logger().info("EntityCulling 最近100次平均耗时: " + averageMs + "ms"); + } } } @@ -1200,6 +1232,9 @@ public class BukkitServerPlayer extends Player { @Override public void removeTrackedChunk(long chunkPos) { this.trackedChunks.remove(chunkPos); + if (Config.enableEntityCulling()) { + this.culling.removeLastVisitChunkIfMatches((int) chunkPos, (int) (chunkPos >> 32)); + } } @Override @@ -1317,8 +1352,8 @@ public class BukkitServerPlayer extends Player { } @Override - public void addTrackedBlockEntity(BlockPos blockPos, ConstantBlockEntityRenderer renderer) { - this.trackedBlockEntityRenderers.put(blockPos, new VirtualCullableObject(renderer)); + public VirtualCullableObject addTrackedBlockEntity(BlockPos blockPos, ConstantBlockEntityRenderer renderer) { + return this.trackedBlockEntityRenderers.put(blockPos, new VirtualCullableObject(renderer)); } @Override diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 0d6efafb4..d2a376c04 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -553,9 +553,10 @@ chunk-system: convert: {} client-optimization: - # Using server-side ray tracing algorithms to hide certain entities and reduce client-side rendering pressure. + # Using server-side ray tracing algorithms to hide block entities/furniture and reduce client-side rendering pressure. entity-culling: - enable: false + enable: true + view-distance: 64 # Enables or disables debug mode debug: 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 1b0b09185..0570149e7 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 @@ -31,6 +31,7 @@ import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.function.Function; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.logger.Debugger; @@ -38,6 +39,7 @@ import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.*; +import net.momirealms.craftengine.core.world.collision.AABB; import net.momirealms.sparrow.nbt.CompoundTag; import org.incendo.cloud.suggestion.Suggestion; import org.jetbrains.annotations.NotNull; @@ -52,6 +54,7 @@ import java.util.concurrent.ExecutionException; public abstract class AbstractBlockManager extends AbstractModelGenerator implements BlockManager { private static final JsonElement EMPTY_VARIANT_MODEL = MiscUtils.init(new JsonObject(), o -> o.addProperty("model", "minecraft:block/empty")); + private static final AABB DEFAULT_BLOCK_ENTITY_AABB = new AABB(-.5, -.5, -.5, .5, .5, .5); protected final BlockParser blockParser; protected final BlockStateMappingParser blockStateMappingParser; // 根据id获取自定义方块 @@ -603,7 +606,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem BlockStateAppearance blockStateAppearance = new BlockStateAppearance( visualBlockState, parseBlockEntityRender(appearanceSection.get("entity-renderer")), - ResourceConfigUtils.getAsAABB(appearanceSection.getOrDefault("aabb", 1), "aabb") + parseCullingData(appearanceSection.get("entity-culling")) ); appearances.put(appearanceName, blockStateAppearance); if (anyAppearance == null) { @@ -643,7 +646,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem } for (ImmutableBlockState possibleState : possibleStates) { possibleState.setVisualBlockState(appearance.blockState()); - possibleState.setEstimatedBoundingBox(appearance.estimateAABB()); + possibleState.setCullingData(appearance.cullingData()); appearance.blockEntityRenderer().ifPresent(possibleState::setConstantRenderers); } } @@ -667,7 +670,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem if (visualState == null) { visualState = anyAppearance.blockState(); state.setVisualBlockState(visualState); - state.setEstimatedBoundingBox(anyAppearance.estimateAABB()); + state.setCullingData(anyAppearance.cullingData()); anyAppearance.blockEntityRenderer().ifPresent(state::setConstantRenderers); } int appearanceId = visualState.registryId(); @@ -707,6 +710,21 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem }, () -> GsonHelper.get().toJson(section))); } + private CullingData parseCullingData(Object arguments) { + if (arguments instanceof Boolean b && !b) { + return null; + } + if (!(arguments instanceof Map)) { + return new CullingData(DEFAULT_BLOCK_ENTITY_AABB, Config.entityCullingViewDistance(), 0.5); + } + Map argumentsMap = ResourceConfigUtils.getAsMap(arguments, "entity-culling"); + return new CullingData( + ResourceConfigUtils.getAsAABB(argumentsMap.getOrDefault("aabb", 1), "aabb"), + ResourceConfigUtils.getAsInt(argumentsMap.getOrDefault("view-distance", Config.entityCullingViewDistance()), "view-distance"), + ResourceConfigUtils.getAsDouble(argumentsMap.getOrDefault("aabb-expansion", 0.5), "aabb-expansion") + ); + } + @SuppressWarnings("unchecked") private Optional[]> parseBlockEntityRender(Object arguments) { if (arguments == null) return Optional.empty(); 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 index d2b8d0189..d3e7f8c85 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateAppearance.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateAppearance.java @@ -2,11 +2,12 @@ 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 net.momirealms.craftengine.core.world.collision.AABB; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; +import org.jetbrains.annotations.Nullable; import java.util.Optional; public record BlockStateAppearance(BlockStateWrapper blockState, Optional[]> blockEntityRenderer, - AABB estimateAABB) { + @Nullable CullingData cullingData) { } 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 9a4010ef8..a112628f5 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 @@ -13,11 +13,11 @@ import net.momirealms.craftengine.core.item.Item; 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.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.World; -import net.momirealms.craftengine.core.world.collision.AABB; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.NBT; import net.momirealms.sparrow.nbt.Tag; @@ -43,7 +43,8 @@ public final class ImmutableBlockState { private BlockEntityType blockEntityType; @Nullable private BlockEntityElementConfig[] renderers; - private AABB estimatedBoundingBox; + @Nullable + private CullingData cullingData; ImmutableBlockState( Holder.Reference owner, @@ -89,12 +90,13 @@ public final class ImmutableBlockState { this.renderers = renderers; } - public void setEstimatedBoundingBox(AABB aabb) { - this.estimatedBoundingBox = aabb; + @Nullable + public CullingData cullingData() { + return cullingData; } - public AABB estimatedBoundingBox() { - return estimatedBoundingBox; + public void setCullingData(@Nullable CullingData cullingData) { + this.cullingData = cullingData; } public boolean hasBlockEntity() { 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 index 4551ed667..461331e96 100644 --- 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 @@ -2,18 +2,19 @@ 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 net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.world.Cullable; -import net.momirealms.craftengine.core.world.collision.AABB; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; @ApiStatus.Experimental public class ConstantBlockEntityRenderer implements Cullable { private final BlockEntityElement[] elements; - public final AABB aabb; + public final CullingData cullingData; - public ConstantBlockEntityRenderer(BlockEntityElement[] elements, AABB aabb) { + public ConstantBlockEntityRenderer(BlockEntityElement[] elements, @Nullable CullingData cullingData) { this.elements = elements; - this.aabb = aabb; + this.cullingData = cullingData; } @Override @@ -55,7 +56,11 @@ public class ConstantBlockEntityRenderer implements Cullable { } @Override - public AABB aabb() { - return this.aabb; + public CullingData cullingData() { + return this.cullingData; + } + + public boolean canCull() { + return this.cullingData != null; } } 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 36e3fcb76..25b098bb1 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 @@ -203,7 +203,7 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void addTrackedBlockEntities(Map renders); - public abstract void addTrackedBlockEntity(BlockPos blockPos, ConstantBlockEntityRenderer renderer); + public abstract VirtualCullableObject addTrackedBlockEntity(BlockPos blockPos, ConstantBlockEntityRenderer renderer); public abstract VirtualCullableObject getTrackedBlockEntity(BlockPos blockPos); 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 3720a4268..d0b501b11 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 @@ -204,6 +204,7 @@ public class Config { protected int emoji$max_emojis_per_parse; protected boolean client_optimization$entity_culling$enable; + protected int client_optimization$entity_culling$view_distance; public Config(CraftEngine plugin) { this.plugin = plugin; @@ -566,6 +567,7 @@ public class Config { // client optimization client_optimization$entity_culling$enable = config.getBoolean("client-optimization.entity-culling.enable", false); + client_optimization$entity_culling$view_distance = config.getInt("client-optimization.entity-culling.view-distance", 64); firstTime = false; } @@ -1161,6 +1163,10 @@ public class Config { return instance.client_optimization$entity_culling$enable; } + public static int entityCullingViewDistance() { + return instance.client_optimization$entity_culling$view_distance; + } + public YamlDocument loadOrCreateYamlData(String fileName) { Path path = this.plugin.dataFolderPath().resolve(fileName); if (!Files.exists(path)) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/CullingData.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/CullingData.java new file mode 100644 index 000000000..8d67a1349 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/CullingData.java @@ -0,0 +1,27 @@ +package net.momirealms.craftengine.core.plugin.entityculling; + +import net.momirealms.craftengine.core.world.collision.AABB; + +public final class CullingData { + public final AABB aabb; + public final int maxDistance; + public final double aabbExpansion; + + public CullingData(AABB aabb, int maxDistance, double aabbExpansion) { + this.aabb = aabb; + this.maxDistance = maxDistance; + this.aabbExpansion = aabbExpansion; + } + + public AABB aabb() { + return aabb; + } + + public int maxDistance() { + return maxDistance; + } + + public double aabbExpansion() { + return aabbExpansion; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java index 2b16695bf..b9e7d87b9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java @@ -13,38 +13,42 @@ import java.util.Arrays; public final class EntityCulling { public static final int MAX_SAMPLES = 14; private final Player player; - private final int maxDistance; - private final double aabbExpansion; private final boolean[] dotSelectors = new boolean[MAX_SAMPLES]; private final MutableVec3d[] targetPoints = new MutableVec3d[MAX_SAMPLES]; private final int[] lastHitBlock = new int[MAX_SAMPLES * 3]; private final boolean[] canCheckLastHitBlock = new boolean[MAX_SAMPLES]; + // 相机附近的点位大概率会多次重复获取 + private final long[] occlusionCache = new long[32 * 32 * 32 / 32]; private int hitBlockCount = 0; private int lastVisitChunkX = Integer.MAX_VALUE; private int lastVisitChunkZ = Integer.MAX_VALUE; private ClientChunk lastVisitChunk = null; - public EntityCulling(Player player, int maxDistance, double aabbExpansion) { + public EntityCulling(Player player) { this.player = player; - this.maxDistance = maxDistance; - this.aabbExpansion = aabbExpansion; for (int i = 0; i < MAX_SAMPLES; i++) { this.targetPoints[i] = new MutableVec3d(0,0,0); } } - public boolean isVisible(AABB aabb, Vec3d cameraPos) { + public void resetCache() { + Arrays.fill(this.occlusionCache, 0); + } + + public boolean isVisible(CullingData cullable, Vec3d cameraPos, Vec3d playerHeadPos) { // 情空标志位 Arrays.fill(this.canCheckLastHitBlock, false); this.hitBlockCount = 0; + AABB aabb = cullable.aabb; + double aabbExpansion = cullable.aabbExpansion; // 根据AABB获取能包裹此AABB的最小长方体 - int minX = MiscUtils.floor(aabb.minX - this.aabbExpansion); - int minY = MiscUtils.floor(aabb.minY - this.aabbExpansion); - int minZ = MiscUtils.floor(aabb.minZ - this.aabbExpansion); - int maxX = MiscUtils.ceil(aabb.maxX + this.aabbExpansion); - int maxY = MiscUtils.ceil(aabb.maxY + this.aabbExpansion); - int maxZ = MiscUtils.ceil(aabb.maxZ + this.aabbExpansion); + int minX = MiscUtils.floor(aabb.minX - aabbExpansion); + int minY = MiscUtils.floor(aabb.minY - aabbExpansion); + int minZ = MiscUtils.floor(aabb.minZ - aabbExpansion); + int maxX = MiscUtils.ceil(aabb.maxX + aabbExpansion); + int maxY = MiscUtils.ceil(aabb.maxY + aabbExpansion); + int maxZ = MiscUtils.ceil(aabb.maxZ + aabbExpansion); double cameraX = cameraPos.x; double cameraY = cameraPos.y; @@ -60,7 +64,8 @@ public final class EntityCulling { } // 如果设置了最大距离 - if (this.maxDistance > 0) { + int maxDistance = cullable.maxDistance; + if (maxDistance > 0) { // 计算AABB到相机的最小距离 double distanceSq = 0.0; // 计算XYZ轴方向的距离 @@ -68,7 +73,7 @@ public final class EntityCulling { distanceSq += distanceSq(minY, maxY, cameraY, relY); distanceSq += distanceSq(minZ, maxZ, cameraZ, relZ); // 检查距离是否超过最大值 - double maxDistanceSq = this.maxDistance * this.maxDistance; + double maxDistanceSq = maxDistance * maxDistance; // 超过最大距离,剔除 if (distanceSq > maxDistanceSq) { return false; @@ -277,21 +282,76 @@ public final class EntityCulling { return false; } - private boolean stepRay(int currentBlockX, int currentBlockY, int currentBlockZ, + // 0 = 没缓存 + // 1 = 没遮挡 + // 2 = 有遮挡 + private int getCacheState(int index) { + int arrayIndex = index / 32; // 获取数组下标 + int offset = (index % 32) * 2; // 每个状态占2bit,计算位偏移 + if (arrayIndex >= 0 && arrayIndex < this.occlusionCache.length) { + long cacheValue = this.occlusionCache[arrayIndex]; + // 提取2bit的状态值 (0-3) + return (int) ((cacheValue >> offset) & 0b11); + } + return 0; // 索引越界时返回默认状态 + } + + private void setCacheState(int index, int state) { + long[] cache = this.occlusionCache; + int arrayIndex = index / 32; + int offset = (index % 32) * 2; + + if (arrayIndex >= 0 && arrayIndex < cache.length) { + long mask = ~(0b11L << offset); // 创建清除掩码 + long newValue = (cache[arrayIndex] & mask) | ((state & 0b11L) << offset); + cache[arrayIndex] = newValue; + } + } + + private boolean stepRay(int startingX, int startingY, int startingZ, double stepSizeX, double stepSizeY, double stepSizeZ, - int remainingSteps, int stepDirectionX, int stepDirectionY, - int stepDirectionZ, double nextStepTimeY, double nextStepTimeX, - double nextStepTimeZ) { + int remainingSteps, + int stepDirectionX, int stepDirectionY, int stepDirectionZ, + double nextStepTimeY, double nextStepTimeX, double nextStepTimeZ) { + + int currentBlockX = startingX; + int currentBlockY = startingY; + int currentBlockZ = startingZ; // 遍历射线路径上的所有方块(跳过最后一个目标方块) for (; remainingSteps > 1; remainingSteps--) { + int cacheIndex = getCacheIndex(currentBlockX, currentBlockY, currentBlockZ, startingX, startingY, startingZ); + if (cacheIndex != -1) { + int cacheState = getCacheState(cacheIndex); + // 没遮挡 + if (cacheState == 1) { + continue; + } + // 有遮挡 + else if (cacheState == 2) { + this.lastHitBlock[this.hitBlockCount * 3] = currentBlockX; + this.lastHitBlock[this.hitBlockCount * 3 + 1] = currentBlockY; + this.lastHitBlock[this.hitBlockCount * 3 + 2] = currentBlockZ; + return false; + } + } + // 检查当前方块是否遮挡视线 if (isOccluding(currentBlockX, currentBlockY, currentBlockZ)) { this.lastHitBlock[this.hitBlockCount * 3] = currentBlockX; this.lastHitBlock[this.hitBlockCount * 3 + 1] = currentBlockY; this.lastHitBlock[this.hitBlockCount * 3 + 2] = currentBlockZ; + // 设置缓存 + if (cacheIndex != -1) { + setCacheState(cacheIndex, 2); + } return false; // 视线被遮挡,立即返回 + } else { + // 设置缓存 + if (cacheIndex != -1) { + setCacheState(cacheIndex, 1); + } } // 基于时间参数选择下一个要遍历的方块方向 @@ -315,6 +375,22 @@ public final class EntityCulling { return true; } + private int getCacheIndex(int x, int y, int z, int startX, int startY, int startZ) { + int deltaX = startX + 16 - x; + if (deltaX < 0 || deltaX >= 32) { + return -1; + } + int deltaY = startY + 16 - y; + if (deltaY < 0 || deltaY >= 32) { + return -1; + } + int deltaZ = startZ + 16 - z; + if (deltaZ < 0 || deltaZ >= 32) { + return -1; + } + return deltaX + 32 * deltaY + 32 * 32 * deltaZ; + } + private double distanceSq(int min, int max, double camera, Relative rel) { if (rel == Relative.NEGATIVE) { double dx = camera - max; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/AbstractJavaScheduler.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/AbstractJavaScheduler.java index c728459ac..8e2ab4818 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/AbstractJavaScheduler.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/AbstractJavaScheduler.java @@ -6,6 +6,7 @@ import java.lang.Thread.UncaughtExceptionHandler; import java.util.Arrays; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -46,6 +47,13 @@ public abstract class AbstractJavaScheduler implements SchedulerAdapter { return new AsyncTask(future); } + @Override + public SchedulerTask asyncRepeating(Consumer task, long delay, long interval, TimeUnit unit) { + LazyAsyncTask asyncTask = new LazyAsyncTask(); + asyncTask.future = this.scheduler.scheduleAtFixedRate(() -> this.worker.execute(() -> task.accept(asyncTask)), delay, interval, unit); + return asyncTask; + } + @Override public void shutdownScheduler() { this.scheduler.shutdown(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/LazyAsyncTask.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/LazyAsyncTask.java new file mode 100644 index 000000000..84ad86199 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/LazyAsyncTask.java @@ -0,0 +1,21 @@ +package net.momirealms.craftengine.core.plugin.scheduler; + +import java.util.concurrent.ScheduledFuture; + +public class LazyAsyncTask implements SchedulerTask { + + public ScheduledFuture future; + + @Override + public void cancel() { + if (future != null) { + future.cancel(false); + } + } + + @Override + public boolean cancelled() { + if (future == null) return false; + return future.isCancelled(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/SchedulerAdapter.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/SchedulerAdapter.java index fd7af51e3..786ea491c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/SchedulerAdapter.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/scheduler/SchedulerAdapter.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.plugin.scheduler; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; public interface SchedulerAdapter { @@ -25,6 +26,8 @@ public interface SchedulerAdapter { SchedulerTask asyncRepeating(Runnable task, long delay, long interval, TimeUnit unit); + SchedulerTask asyncRepeating(Consumer task, long delay, long interval, TimeUnit unit); + void shutdownScheduler(); void shutdownExecutor(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/Cullable.java b/core/src/main/java/net/momirealms/craftengine/core/world/Cullable.java index 700442fb3..404594503 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/Cullable.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/Cullable.java @@ -1,13 +1,15 @@ package net.momirealms.craftengine.core.world; import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.world.collision.AABB; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; +import org.jetbrains.annotations.Nullable; public interface Cullable { - AABB aabb(); - void show(Player player); void hide(Player player); + + @Nullable + CullingData cullingData(); } 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 4ca2fe5df..572904823 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 @@ -12,6 +12,7 @@ import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityEl import net.momirealms.craftengine.core.block.entity.tick.*; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.world.*; import net.momirealms.craftengine.core.world.chunk.client.VirtualCullableObject; @@ -139,7 +140,12 @@ public class CEChunk { BlockEntityElementConfig[] renderers = state.constantRenderers(); if (renderers != null && renderers.length > 0) { BlockEntityElement[] elements = new BlockEntityElement[renderers.length]; - ConstantBlockEntityRenderer renderer = new ConstantBlockEntityRenderer(elements, state.estimatedBoundingBox().move(pos)); + ConstantBlockEntityRenderer renderer = new ConstantBlockEntityRenderer( + elements, + Optional.ofNullable(state.cullingData()) + .map(data -> new CullingData(data.aabb.move(pos), data.maxDistance, data.aabbExpansion)) + .orElse(null) + ); World wrappedWorld = this.world.world(); List trackedBy = getTrackedBy(); boolean hasTrackedBy = trackedBy != null && !trackedBy.isEmpty(); @@ -159,7 +165,7 @@ public class CEChunk { // 如果启用实体剔除,那么只对已经渲染的进行变换 if (Config.enableEntityCulling()) { for (Player player : trackedBy) { - VirtualCullableObject trackedBlockEntity = player.getTrackedBlockEntity(pos); + VirtualCullableObject trackedBlockEntity = player.addTrackedBlockEntity(pos, renderer); if (trackedBlockEntity == null || trackedBlockEntity.isShown()) { element.transform(player); } @@ -207,7 +213,7 @@ public class CEChunk { // 如果启用实体剔除,那么只对已经渲染的进行变换 if (Config.enableEntityCulling()) { for (Player player : trackedBy) { - VirtualCullableObject trackedBlockEntity = player.getTrackedBlockEntity(pos); + VirtualCullableObject trackedBlockEntity = player.addTrackedBlockEntity(pos, renderer); if (trackedBlockEntity == null || trackedBlockEntity.isShown()) { newElement.transform(player); } From 19d1cb2a21ffdb159af6da3b606658b5271f3964 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 29 Nov 2025 02:13:06 +0800 Subject: [PATCH 036/135] =?UTF-8?q?=E7=BC=93=E5=AD=98=E6=98=AF=E8=B4=9F?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/user/BukkitServerPlayer.java | 3 +- .../plugin/entityculling/EntityCulling.java | 59 +------------------ 2 files changed, 2 insertions(+), 60 deletions(-) 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 e1b19a350..dd4c4e247 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 @@ -576,12 +576,11 @@ public class BukkitServerPlayer extends Player { CullingData cullingData = cullableObject.cullable.cullingData(); if (cullingData != null) { Vec3d vec3d = LocationUtils.toVec3d(platformPlayer().getEyeLocation()); - boolean visible = this.culling.isVisible(cullingData, vec3d, vec3d); + boolean visible = this.culling.isVisible(cullingData, vec3d); cullableObject.setShown(this, visible); } else { cullableObject.setShown(this, true); } - this.culling.resetCache(); } long nano2 = System.nanoTime(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java index b9e7d87b9..ecb9d9fdd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java @@ -17,8 +17,6 @@ public final class EntityCulling { private final MutableVec3d[] targetPoints = new MutableVec3d[MAX_SAMPLES]; private final int[] lastHitBlock = new int[MAX_SAMPLES * 3]; private final boolean[] canCheckLastHitBlock = new boolean[MAX_SAMPLES]; - // 相机附近的点位大概率会多次重复获取 - private final long[] occlusionCache = new long[32 * 32 * 32 / 32]; private int hitBlockCount = 0; private int lastVisitChunkX = Integer.MAX_VALUE; private int lastVisitChunkZ = Integer.MAX_VALUE; @@ -31,11 +29,7 @@ public final class EntityCulling { } } - public void resetCache() { - Arrays.fill(this.occlusionCache, 0); - } - - public boolean isVisible(CullingData cullable, Vec3d cameraPos, Vec3d playerHeadPos) { + public boolean isVisible(CullingData cullable, Vec3d cameraPos) { // 情空标志位 Arrays.fill(this.canCheckLastHitBlock, false); this.hitBlockCount = 0; @@ -282,32 +276,6 @@ public final class EntityCulling { return false; } - // 0 = 没缓存 - // 1 = 没遮挡 - // 2 = 有遮挡 - private int getCacheState(int index) { - int arrayIndex = index / 32; // 获取数组下标 - int offset = (index % 32) * 2; // 每个状态占2bit,计算位偏移 - if (arrayIndex >= 0 && arrayIndex < this.occlusionCache.length) { - long cacheValue = this.occlusionCache[arrayIndex]; - // 提取2bit的状态值 (0-3) - return (int) ((cacheValue >> offset) & 0b11); - } - return 0; // 索引越界时返回默认状态 - } - - private void setCacheState(int index, int state) { - long[] cache = this.occlusionCache; - int arrayIndex = index / 32; - int offset = (index % 32) * 2; - - if (arrayIndex >= 0 && arrayIndex < cache.length) { - long mask = ~(0b11L << offset); // 创建清除掩码 - long newValue = (cache[arrayIndex] & mask) | ((state & 0b11L) << offset); - cache[arrayIndex] = newValue; - } - } - private boolean stepRay(int startingX, int startingY, int startingZ, double stepSizeX, double stepSizeY, double stepSizeZ, int remainingSteps, @@ -321,37 +289,12 @@ public final class EntityCulling { // 遍历射线路径上的所有方块(跳过最后一个目标方块) for (; remainingSteps > 1; remainingSteps--) { - int cacheIndex = getCacheIndex(currentBlockX, currentBlockY, currentBlockZ, startingX, startingY, startingZ); - if (cacheIndex != -1) { - int cacheState = getCacheState(cacheIndex); - // 没遮挡 - if (cacheState == 1) { - continue; - } - // 有遮挡 - else if (cacheState == 2) { - this.lastHitBlock[this.hitBlockCount * 3] = currentBlockX; - this.lastHitBlock[this.hitBlockCount * 3 + 1] = currentBlockY; - this.lastHitBlock[this.hitBlockCount * 3 + 2] = currentBlockZ; - return false; - } - } - // 检查当前方块是否遮挡视线 if (isOccluding(currentBlockX, currentBlockY, currentBlockZ)) { this.lastHitBlock[this.hitBlockCount * 3] = currentBlockX; this.lastHitBlock[this.hitBlockCount * 3 + 1] = currentBlockY; this.lastHitBlock[this.hitBlockCount * 3 + 2] = currentBlockZ; - // 设置缓存 - if (cacheIndex != -1) { - setCacheState(cacheIndex, 2); - } return false; // 视线被遮挡,立即返回 - } else { - // 设置缓存 - if (cacheIndex != -1) { - setCacheState(cacheIndex, 1); - } } // 基于时间参数选择下一个要遍历的方块方向 From e54e6225803226e6632d8fc58a9c7711e70e6197 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Sat, 29 Nov 2025 04:08:48 +0800 Subject: [PATCH 037/135] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E8=87=AA=E6=89=98?= =?UTF-8?q?=E7=AE=A1=E7=9A=84=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/config.yml | 2 ++ .../core/pack/host/impl/SelfHost.java | 6 ++-- .../pack/host/impl/SelfHostHttpServer.java | 32 +++++++++++++------ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index d2a376c04..50f324641 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -197,6 +197,8 @@ resource-pack: deny-non-minecraft-request: true # Generates a single-use, time-limited download link for each player. one-time-token: true + # Enhances validation for deny-non-minecraft-request and one-time-token. + strict-validation: true rate-limiting: # Maximum bandwidth per second to prevent server instability for other players during resource pack downloads max-bandwidth-per-second: 5_000_000 # 5MB/s diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java index 7d8937de1..93cede575 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java @@ -28,7 +28,7 @@ public class SelfHost implements ResourcePackHost { @Override public CompletableFuture> requestResourcePackDownloadLink(UUID player) { - ResourcePackDownloadData data = SelfHostHttpServer.instance().generateOneTimeUrl(); + ResourcePackDownloadData data = SelfHostHttpServer.instance().generateOneTimeUrl(player); if (data == null) return CompletableFuture.completedFuture(List.of()); return CompletableFuture.completedFuture(List.of(data)); } @@ -77,7 +77,7 @@ public class SelfHost implements ResourcePackHost { boolean oneTimeToken = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("one-time-token", true), "one-time-token"); String protocol = arguments.getOrDefault("protocol", "http").toString(); boolean denyNonMinecraftRequest = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("deny-non-minecraft-request", true), "deny-non-minecraft-request"); - + boolean strictValidation = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("strict-validation", true), "strict-validation"); Bandwidth limit = null; Map rateLimitingSection = ResourceConfigUtils.getAsMapOrNull(arguments.get("rate-limiting"), "rate-limiting"); @@ -98,7 +98,7 @@ public class SelfHost implements ResourcePackHost { maxBandwidthUsage = ResourceConfigUtils.getAsLong(rateLimitingSection.getOrDefault("max-bandwidth-per-second", 0), "max-bandwidth"); minDownloadSpeed = ResourceConfigUtils.getAsLong(rateLimitingSection.getOrDefault("min-download-speed-per-player", 50_000), "min-download-speed-per-player"); } - selfHostHttpServer.updateProperties(ip, port, url, denyNonMinecraftRequest, protocol, limit, oneTimeToken, maxBandwidthUsage, minDownloadSpeed); + selfHostHttpServer.updateProperties(ip, port, url, denyNonMinecraftRequest, protocol, limit, oneTimeToken, maxBandwidthUsage, minDownloadSpeed, strictValidation); return INSTANCE; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java index 9a88c719c..da20a14e3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java @@ -33,6 +33,8 @@ import java.nio.file.Path; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.Duration; +import java.util.Collections; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -41,7 +43,7 @@ import java.util.concurrent.atomic.AtomicLong; public class SelfHostHttpServer { private static SelfHostHttpServer instance; - private final Cache oneTimePackUrls = Caffeine.newBuilder() + private final Cache oneTimePackUrls = Caffeine.newBuilder() .maximumSize(1024) .scheduler(Scheduler.systemScheduler()) .expireAfterWrite(1, TimeUnit.MINUTES) @@ -67,6 +69,7 @@ public class SelfHostHttpServer { private String url; private boolean denyNonMinecraft = true; private boolean useToken; + private boolean strictValidation = true; private long globalUploadRateLimit = 0; private long minDownloadSpeed = 50_000; @@ -97,13 +100,15 @@ public class SelfHostHttpServer { Bandwidth limitPerIp, boolean token, long globalUploadRateLimit, - long minDownloadSpeed) { + long minDownloadSpeed, + boolean strictValidation) { this.ip = ip; this.url = url; this.denyNonMinecraft = denyNonMinecraft; this.protocol = protocol; this.limitPerIp = limitPerIp; this.useToken = token; + this.strictValidation = strictValidation; if (this.globalUploadRateLimit != globalUploadRateLimit || this.minDownloadSpeed != minDownloadSpeed) { this.globalUploadRateLimit = globalUploadRateLimit; this.minDownloadSpeed = minDownloadSpeed; @@ -214,8 +219,9 @@ public class SelfHostHttpServer { private void handleDownload(ChannelHandlerContext ctx, FullHttpRequest request, QueryStringDecoder queryDecoder) { // 使用一次性token if (useToken) { - String token = queryDecoder.parameters().getOrDefault("token", java.util.Collections.emptyList()).stream().findFirst().orElse(null); - if (!validateToken(token)) { + String token = queryDecoder.parameters().getOrDefault("token", Collections.emptyList()).stream().findFirst().orElse(null); + String clientUUID = strictValidation ? request.headers().get("X-Minecraft-UUID") : null; + if (!validateToken(token, clientUUID)) { sendError(ctx, HttpResponseStatus.FORBIDDEN, "Invalid token"); blockedRequests.incrementAndGet(); return; @@ -225,7 +231,12 @@ public class SelfHostHttpServer { // 不是Minecraft客户端 if (denyNonMinecraft) { String userAgent = request.headers().get(HttpHeaderNames.USER_AGENT); - if (userAgent == null || !userAgent.startsWith("Minecraft Java/")) { + boolean nonMinecraftClient = userAgent == null || !userAgent.startsWith("Minecraft Java/"); + if (strictValidation && !nonMinecraftClient) { + String clientVersion = request.headers().get("X-Minecraft-Version"); + nonMinecraftClient = !Objects.equals(clientVersion, userAgent.substring(15)); + } + if (nonMinecraftClient) { sendError(ctx, HttpResponseStatus.FORBIDDEN, "Invalid client"); blockedRequests.incrementAndGet(); return; @@ -300,10 +311,11 @@ public class SelfHostHttpServer { return rateLimiter.tryConsume(1); } - private boolean validateToken(String token) { + private boolean validateToken(String token, String clientUUID) { if (token == null || token.length() != 36) return false; - Boolean valid = oneTimePackUrls.getIfPresent(token); - if (valid != null) { + String valid = oneTimePackUrls.getIfPresent(token); + boolean isValid = strictValidation ? Objects.equals(valid, clientUUID) : valid != null; + if (isValid) { oneTimePackUrls.invalidate(token); return true; } @@ -348,7 +360,7 @@ public class SelfHostHttpServer { } @Nullable - public ResourcePackDownloadData generateOneTimeUrl() { + public ResourcePackDownloadData generateOneTimeUrl(UUID user) { if (this.resourcePackBytes == null) return null; if (!this.useToken) { @@ -356,7 +368,7 @@ public class SelfHostHttpServer { } String token = UUID.randomUUID().toString(); - oneTimePackUrls.put(token, true); + oneTimePackUrls.put(token, strictValidation ? user.toString().replace("-", "") : ""); return new ResourcePackDownloadData( url() + "download?token=" + URLEncoder.encode(token, StandardCharsets.UTF_8), packUUID, From b77dbdfca95c632c0c57aa7a0d2c1b0911116e88 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 29 Nov 2025 04:20:49 +0800 Subject: [PATCH 038/135] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=89=94=E9=99=A4?= =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E8=B0=83=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/network/BukkitNetworkManager.java | 10 +-- .../plugin/user/BukkitServerPlayer.java | 45 +++---------- common-files/src/main/resources/config.yml | 6 +- .../entity/tick/TickingBlockEntityImpl.java | 2 +- .../core/entity/player/Player.java | 2 + .../craftengine/core/plugin/CraftEngine.java | 7 ++ .../core/plugin/config/Config.java | 17 +++-- .../plugin/entityculling/EntityCulling.java | 38 +++++------ .../entityculling/EntityCullingManager.java | 6 ++ .../EntityCullingManagerImpl.java | 32 +++++++++ .../entityculling/EntityCullingThread.java | 65 +++++++++++++++++++ .../core/plugin/logger/Debugger.java | 2 +- .../craftengine/core/world/chunk/CEChunk.java | 2 +- .../DefaultBlockEntitySerializer.java | 2 +- 14 files changed, 164 insertions(+), 72 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingManager.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingManagerImpl.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java 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 ba75b5f5e..df5990e82 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 @@ -84,6 +84,7 @@ 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.entityculling.EntityCullingThread; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.plugin.network.*; @@ -128,7 +129,6 @@ import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Predicate; @@ -462,14 +462,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes player.getScheduler().runAtFixedRate(plugin.javaPlugin(), (t) -> user.tick(), () -> {}, 1, 1); } - // 安排异步tick任务 - this.plugin.scheduler().asyncRepeating(t -> { - if (!user.isOnline()) { - t.cancel(); - return; - } - user.asyncTick(); - }, 50, 50, TimeUnit.MILLISECONDS); user.sendPacket(TotemAnimationCommand.FIX_TOTEM_SOUND_PACKET, false); } } 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 dd4c4e247..2bd1ed888 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 @@ -507,9 +507,6 @@ public class BukkitServerPlayer extends Player { this.encoderState = encoderState; } - private final Queue recentDurations = new LinkedList<>(); - private static final int SAMPLE_SIZE = 100; - @Override public void tick() { // not fully online @@ -569,38 +566,16 @@ public class BukkitServerPlayer extends Player { } } - public void asyncTick() { - if (Config.enableEntityCulling()) { - long nano1 = System.nanoTime(); - for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { - CullingData cullingData = cullableObject.cullable.cullingData(); - if (cullingData != null) { - Vec3d vec3d = LocationUtils.toVec3d(platformPlayer().getEyeLocation()); - boolean visible = this.culling.isVisible(cullingData, vec3d); - cullableObject.setShown(this, visible); - } else { - cullableObject.setShown(this, true); - } - } - long nano2 = System.nanoTime(); - - long duration = nano2 - nano1; - - // 添加到队列 - recentDurations.offer(duration); - - // 保持队列大小为100 - if (recentDurations.size() > SAMPLE_SIZE) { - recentDurations.poll(); - } - - // 每100次输出一次平均耗时 - if (recentDurations.size() == SAMPLE_SIZE) { - double averageMs = recentDurations.stream() - .mapToLong(Long::longValue) - .average() - .orElse(0) / 1_000_000d; - CraftEngine.instance().logger().info("EntityCulling 最近100次平均耗时: " + averageMs + "ms"); + @Override + public void entityCullingTick() { + for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { + CullingData cullingData = cullableObject.cullable.cullingData(); + if (cullingData != null) { + Vec3d vec3d = LocationUtils.toVec3d(platformPlayer().getEyeLocation()); + boolean visible = this.culling.isVisible(cullingData, vec3d); + cullableObject.setShown(this, visible); + } else { + cullableObject.setShown(this, true); } } } diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index d2a376c04..06785ab16 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -557,6 +557,8 @@ client-optimization: entity-culling: enable: true view-distance: 64 + # Determining the number of threads to execute these raytrace operations + threads: 1 # Enables or disables debug mode debug: @@ -564,4 +566,6 @@ debug: packet: false furniture: false item: false - resource-pack: false \ No newline at end of file + resource-pack: false + block: false + entity-culling: false \ No newline at end of file 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 index 12d09ee8c..85d4a1aba 100644 --- 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 @@ -32,7 +32,7 @@ public class TickingBlockEntityImpl implements TickingBlo // 不是合法方块 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); + Debugger.BLOCK.warn(() -> "Invalid block entity(" + this.blockEntity.getClass().getSimpleName() + ") with state " + state + " found at world " + this.chunk.world().name() + " " + pos, null); return; } try { 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 25b098bb1..7c0be0248 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 @@ -38,6 +38,8 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void setClientSideWorld(World world); + public abstract void entityCullingTick(); + public abstract float getDestroyProgress(Object blockState, BlockPos pos); public abstract void setClientSideCanBreakBlock(boolean canBreak); 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 2c32adc8b..3075ea81e 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 @@ -26,6 +26,8 @@ import net.momirealms.craftengine.core.plugin.dependency.Dependencies; import net.momirealms.craftengine.core.plugin.dependency.Dependency; import net.momirealms.craftengine.core.plugin.dependency.DependencyManager; import net.momirealms.craftengine.core.plugin.dependency.DependencyManagerImpl; +import net.momirealms.craftengine.core.plugin.entityculling.EntityCullingManager; +import net.momirealms.craftengine.core.plugin.entityculling.EntityCullingManagerImpl; import net.momirealms.craftengine.core.plugin.gui.GuiManager; import net.momirealms.craftengine.core.plugin.gui.category.ItemBrowserManager; import net.momirealms.craftengine.core.plugin.gui.category.ItemBrowserManagerImpl; @@ -79,6 +81,7 @@ public abstract class CraftEngine implements Plugin { protected GlobalVariableManager globalVariableManager; protected ProjectileManager projectileManager; protected SeatManager seatManager; + protected EntityCullingManager entityCullingManager; private final PluginTaskRegistry beforeEnableTaskRegistry = new PluginTaskRegistry(); private final PluginTaskRegistry afterEnableTaskRegistry = new PluginTaskRegistry(); @@ -118,6 +121,8 @@ public abstract class CraftEngine implements Plugin { this.globalVariableManager = new GlobalVariableManager(); // 初始化物品浏览器 this.itemBrowserManager = new ItemBrowserManagerImpl(this); + // 初始化实体剔除器 + this.entityCullingManager = new EntityCullingManagerImpl(); } public void setUpConfigAndLocale() { @@ -158,6 +163,7 @@ public abstract class CraftEngine implements Plugin { this.advancementManager.reload(); this.projectileManager.reload(); this.seatManager.reload(); + this.entityCullingManager.reload(); } private void runDelayTasks(boolean reloadRecipe) { @@ -349,6 +355,7 @@ public abstract class CraftEngine implements Plugin { if (this.translationManager != null) this.translationManager.disable(); if (this.globalVariableManager != null) this.globalVariableManager.disable(); if (this.projectileManager != null) this.projectileManager.disable(); + if (this.entityCullingManager != null) this.entityCullingManager.disable(); if (this.scheduler != null) this.scheduler.shutdownScheduler(); if (this.scheduler != null) this.scheduler.shutdownExecutor(); if (this.commandManager != null) this.commandManager.unregisterFeatures(); 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 d0b501b11..9b7473db3 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 @@ -54,6 +54,8 @@ public class Config { protected boolean debug$item; protected boolean debug$furniture; protected boolean debug$resource_pack; + protected boolean debug$block; + protected boolean debug$entity_culling; protected boolean resource_pack$remove_tinted_leaves_particle; protected boolean resource_pack$generate_mod_assets; @@ -205,6 +207,7 @@ public class Config { protected boolean client_optimization$entity_culling$enable; protected int client_optimization$entity_culling$view_distance; + protected int client_optimization$entity_culling$threads; public Config(CraftEngine plugin) { this.plugin = plugin; @@ -308,6 +311,8 @@ public class Config { debug$item = config.getBoolean("debug.item", false); debug$furniture = config.getBoolean("debug.furniture", false); debug$resource_pack = config.getBoolean("debug.resource-pack", false); + debug$block = config.getBoolean("debug.block", false); + debug$entity_culling = config.getBoolean("debug.entity-culling", false); // resource pack resource_pack$path = resolvePath(config.getString("resource-pack.path", "./generated/resource_pack.zip")); @@ -606,12 +611,12 @@ public class Config { return instance.debug$item; } - public static boolean debugBlockEntity() { - return false; + public static boolean debugBlock() { + return instance.debug$block; } - public static boolean debugBlock() { - return false; + public static boolean debugEntityCulling() { + return instance.debug$entity_culling; } public static boolean debugFurniture() { @@ -1167,6 +1172,10 @@ public class Config { return instance.client_optimization$entity_culling$view_distance; } + public static int entityCullingThreads() { + return instance.client_optimization$entity_culling$threads; + } + public YamlDocument loadOrCreateYamlData(String fileName) { Path path = this.plugin.dataFolderPath().resolve(fileName); if (!Files.exists(path)) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java index ecb9d9fdd..4304dcc72 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java @@ -93,24 +93,24 @@ public final class EntityCulling { } int size = 0; - if (this.dotSelectors[0]) targetPoints[size++].set(minX, minY, minZ); - if (this.dotSelectors[1]) targetPoints[size++].set(maxX, minY, minZ); - if (this.dotSelectors[2]) targetPoints[size++].set(minX, minY, maxZ); - if (this.dotSelectors[3]) targetPoints[size++].set(maxX, minY, maxZ); - if (this.dotSelectors[4]) targetPoints[size++].set(minX, maxY, minZ); - if (this.dotSelectors[5]) targetPoints[size++].set(maxX, maxY, minZ); - if (this.dotSelectors[6]) targetPoints[size++].set(minX, maxY, maxZ); - if (this.dotSelectors[7]) targetPoints[size++].set(maxX, maxY, maxZ); + if (this.dotSelectors[0]) targetPoints[size++].set(minX + 0.05, minY + 0.05, minZ + 0.05); + if (this.dotSelectors[1]) targetPoints[size++].set(maxX - 0.05, minY + 0.05, minZ + 0.05); + if (this.dotSelectors[2]) targetPoints[size++].set(minX + 0.05, minY + 0.05, maxZ - 0.05); + if (this.dotSelectors[3]) targetPoints[size++].set(maxX - 0.05, minY + 0.05, maxZ - 0.05); + if (this.dotSelectors[4]) targetPoints[size++].set(minX + 0.05, maxY - 0.05, minZ + 0.05); + if (this.dotSelectors[5]) targetPoints[size++].set(maxX - 0.05, maxY - 0.05, minZ + 0.05); + if (this.dotSelectors[6]) targetPoints[size++].set(minX + 0.05, maxY - 0.05, maxZ - 0.05); + if (this.dotSelectors[7]) targetPoints[size++].set(maxX - 0.05, maxY - 0.05, maxZ - 0.05); // 面中心点 double averageX = (minX + maxX) / 2.0; double averageY = (minY + maxY) / 2.0; double averageZ = (minZ + maxZ) / 2.0; - if (this.dotSelectors[8]) targetPoints[size++].set(averageX, averageY, minZ); - if (this.dotSelectors[9]) targetPoints[size++].set(averageX, averageY, maxZ); - if (this.dotSelectors[10]) targetPoints[size++].set(minX, averageY, averageZ); - if (this.dotSelectors[11]) targetPoints[size++].set(maxX, averageY, averageZ); - if (this.dotSelectors[12]) targetPoints[size++].set(averageX, minY, averageZ); - if (this.dotSelectors[13]) targetPoints[size].set(averageX, maxY, averageZ); + if (this.dotSelectors[8]) targetPoints[size++].set(averageX, averageY, minZ + 0.05); + if (this.dotSelectors[9]) targetPoints[size++].set(averageX, averageY, maxZ - 0.05); + if (this.dotSelectors[10]) targetPoints[size++].set(minX + 0.05, averageY, averageZ); + if (this.dotSelectors[11]) targetPoints[size++].set(maxX - 0.05, averageY, averageZ); + if (this.dotSelectors[12]) targetPoints[size++].set(averageX, minY + 0.05, averageZ); + if (this.dotSelectors[13]) targetPoints[size].set(averageX, maxY - 0.05, averageZ); return isVisible(cameraPos, this.targetPoints, size); } @@ -194,9 +194,9 @@ public final class EntityCulling { // 预计算每单位距离在各方块边界上的步进增量 // 这些值表示射线穿过一个方块所需的时间分数 - double stepIncrementX = 1.0 / (absDeltaX + 1e-10); // 避免除0 - double stepIncrementY = 1.0 / (absDeltaY + 1e-10); - double stepIncrementZ = 1.0 / (absDeltaZ + 1e-10); + double stepIncrementX = 1.0 / absDeltaX; + double stepIncrementY = 1.0 / absDeltaY; + double stepIncrementZ = 1.0 / absDeltaZ; // 射线将穿过的总方块数量(包括起点和终点) int totalBlocksToCheck = 1; @@ -286,8 +286,8 @@ public final class EntityCulling { int currentBlockY = startingY; int currentBlockZ = startingZ; - // 遍历射线路径上的所有方块(跳过最后一个目标方块) - for (; remainingSteps > 1; remainingSteps--) { + // 遍历射线路径上的所有方块 + for (; remainingSteps > 0; remainingSteps--) { // 检查当前方块是否遮挡视线 if (isOccluding(currentBlockX, currentBlockY, currentBlockZ)) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingManager.java new file mode 100644 index 000000000..123cfbdee --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingManager.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.core.plugin.entityculling; + +import net.momirealms.craftengine.core.plugin.Manageable; + +public interface EntityCullingManager extends Manageable { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingManagerImpl.java new file mode 100644 index 000000000..e01602e81 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingManagerImpl.java @@ -0,0 +1,32 @@ +package net.momirealms.craftengine.core.plugin.entityculling; + +import net.momirealms.craftengine.core.plugin.config.Config; + +import java.util.ArrayList; +import java.util.List; + +public class EntityCullingManagerImpl implements EntityCullingManager { + private final List threads = new ArrayList<>(); + + @Override + public void load() { + if (Config.enableEntityCulling()) { + int threads = Math.min(64, Math.max(Config.entityCullingThreads(), 1)); + for (int i = 0; i < threads; i++) { + EntityCullingThread thread = new EntityCullingThread(i, threads); + this.threads.add(thread); + thread.start(); + } + } + } + + @Override + public void unload() { + if (!this.threads.isEmpty()) { + for (EntityCullingThread thread : this.threads) { + thread.stop(); + } + this.threads.clear(); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java new file mode 100644 index 000000000..fefaa7896 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java @@ -0,0 +1,65 @@ +package net.momirealms.craftengine.core.plugin.entityculling; + +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.logger.Debugger; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +public class EntityCullingThread { + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + private final AtomicBoolean isRunning = new AtomicBoolean(false); + private final int id; + private final int threads; + + public EntityCullingThread(int id, int threads) { + this.id = id; + this.threads = threads; + } + + public void start() { + // 错开线程启动时间,避免所有线程同时执行 + long initialDelay = this.id * (50L / this.threads); + this.scheduler.scheduleAtFixedRate(this::scheduleTask, initialDelay, 50, TimeUnit.MILLISECONDS); + } + + private void scheduleTask() { + // 使用CAS操作,更安全 + if (!this.isRunning.compareAndSet(false, true)) { + return; + } + + this.scheduler.execute(() -> { + try { + int processed = 0; + long startTime = System.currentTimeMillis(); + + for (Player player : CraftEngine.instance().networkManager().onlineUsers()) { + // 使用绝对值确保非负,使用 threads 而不是 threads-1 确保均匀分布 + if (Math.abs(player.uuid().hashCode()) % this.threads == this.id) { + player.entityCullingTick(); + processed++; + } + } + + long duration = System.currentTimeMillis() - startTime; + if (duration > 45) { + String value = String.format("EntityCullingThread-%d processed %d players in %dms (over 45ms)", + this.id, processed, duration); + Debugger.ENTITY_CULLING.debug(() -> value); + } + } catch (Exception e) { + CraftEngine.instance().logger().severe("Failed to run entity culling tick: " + e.getMessage()); + } finally { + this.isRunning.set(false); + } + }); + } + + public void stop() { + this.scheduler.shutdown(); + } +} \ No newline at end of file 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 49dcb3ef7..3dd1cf3f2 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 @@ -12,7 +12,7 @@ public enum Debugger { RESOURCE_PACK(Config::debugResourcePack), ITEM(Config::debugItem), BLOCK(Config::debugBlock), - BLOCK_ENTITY(Config::debugBlockEntity); + ENTITY_CULLING(Config::debugEntityCulling); private final Supplier condition; 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 572904823..e8061a7d8 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 @@ -462,7 +462,7 @@ public class CEChunk { 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); + Debugger.BLOCK.debug(() -> "Failed to add invalid block entity " + blockEntity.saveAsTag() + " at " + pos); return; } // 设置方块实体所在世界 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 5c2a49712..3c331d193 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 @@ -38,7 +38,7 @@ public final class DefaultBlockEntitySerializer { 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); + Debugger.BLOCK.debug(() -> "Unknown block entity type: " + id); } else { BlockPos pos = BlockEntity.readPosAndVerify(data, chunk.chunkPos()); ImmutableBlockState blockState = chunk.getBlockState(pos); From 2a552fb6fa6d36833cef53a1ad7606fb525dbb5e Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 29 Nov 2025 04:23:22 +0800 Subject: [PATCH 039/135] Update gradle.properties --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 102014893..bf5da7ca3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings -project_version=0.0.65.13.1 +project_version=0.0.65.14 config_version=58 lang_version=40 project_group=net.momirealms From 3410cd6e8829429c782d315ef5993052a587a1b5 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 29 Nov 2025 17:01:01 +0800 Subject: [PATCH 040/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BB=A4=E7=89=8C?= =?UTF-8?q?=E6=A1=B6=E9=99=90=E9=80=9F+=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E8=A7=86=E8=B7=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/command/BukkitCommandManager.java | 1 + .../SetEntityViewDistanceScaleCommand.java | 52 +++++ .../plugin/user/BukkitServerPlayer.java | 22 +- common-files/src/main/resources/commands.yml | 6 + common-files/src/main/resources/config.yml | 8 + .../src/main/resources/translations/en.yml | 1 + .../core/entity/player/Player.java | 2 + .../core/plugin/config/Config.java | 22 +- .../plugin/entityculling/EntityCulling.java | 25 +- .../plugin/entityculling/VoxelIterator.java | 216 ------------------ .../core/plugin/locale/MessageConstants.java | 1 + .../chunk/client/VirtualCullableObject.java | 2 +- gradle.properties | 4 +- 13 files changed, 139 insertions(+), 223 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/VoxelIterator.java 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 6ce68c38d..00dbb694b 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 @@ -42,6 +42,7 @@ public class BukkitCommandManager extends AbstractCommandManager new SearchUsageAdminCommand(this, plugin), new TestCommand(this, plugin), new SetLocaleCommand(this, plugin), + new SetEntityViewDistanceScaleCommand(this, plugin), new UnsetLocaleCommand(this, plugin), new DebugGetBlockStateRegistryIdCommand(this, plugin), new DebugGetBlockInternalIdCommand(this, plugin), diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java new file mode 100644 index 000000000..bd24341e3 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java @@ -0,0 +1,52 @@ +package net.momirealms.craftengine.bukkit.plugin.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; +import net.momirealms.craftengine.core.plugin.command.FlagKeys; +import net.momirealms.craftengine.core.plugin.locale.MessageConstants; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.bukkit.parser.PlayerParser; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.DoubleParser; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.Locale; +import java.util.concurrent.CompletableFuture; + +public class SetEntityViewDistanceScaleCommand extends BukkitCommandFeature { + + public SetEntityViewDistanceScaleCommand(CraftEngineCommandManager commandManager, CraftEngine plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { + return builder + .flag(FlagKeys.SILENT_FLAG) + .required("player", PlayerParser.playerParser()) + .required("scale", DoubleParser.doubleParser(0.125, 8)) + .handler(context -> { + Player player = context.get("player"); + double scale = context.get("scale"); + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); + serverPlayer.setEntityCullingViewDistanceScale(scale); + handleFeedback(context, MessageConstants.COMMAND_ENTITY_VIEW_DISTANCE_SCALE_SET_SUCCESS, Component.text(scale), Component.text(player.getName())); + }); + } + + @Override + public String getFeatureID() { + return "set_entity_view_distance_scale"; + } +} 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 2bd1ed888..9ecf68413 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 @@ -72,6 +72,7 @@ import java.util.concurrent.ConcurrentHashMap; public class BukkitServerPlayer extends Player { public static final Key SELECTED_LOCALE_KEY = Key.of("craftengine:locale"); + public static final Key ENTITY_CULLING_VIEW_DISTANCE_SCALE = Key.of("craftengine:entity_culling_view_distance_scale"); private final BukkitCraftEngine plugin; // connection state @@ -143,7 +144,6 @@ public class BukkitServerPlayer extends Player { private int lastStopMiningTick; // 跟踪到的方块实体渲染器 private final Map trackedBlockEntityRenderers = new ConcurrentHashMap<>(); - private final EntityCulling culling; public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) { @@ -170,6 +170,8 @@ public class BukkitServerPlayer extends Player { this.isNameVerified = true; byte[] bytes = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(CooldownData.COOLDOWN_KEY), PersistentDataType.BYTE_ARRAY); String locale = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(SELECTED_LOCALE_KEY), PersistentDataType.STRING); + Double scale = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENTITY_CULLING_VIEW_DISTANCE_SCALE), PersistentDataType.DOUBLE); + this.culling.setDistanceScale(Optional.ofNullable(scale).orElse(1.0)); this.selectedLocale = TranslationManager.parseLocale(locale); this.trackedChunks = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(512, 0.5f); this.entityTypeView = new ConcurrentHashMap<>(256); @@ -568,12 +570,21 @@ public class BukkitServerPlayer extends Player { @Override public void entityCullingTick() { + this.culling.restoreTokenOnTick(); for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { CullingData cullingData = cullableObject.cullable.cullingData(); if (cullingData != null) { Vec3d vec3d = LocationUtils.toVec3d(platformPlayer().getEyeLocation()); boolean visible = this.culling.isVisible(cullingData, vec3d); - cullableObject.setShown(this, visible); + if (visible != cullableObject.isShown) { + if (Config.enableEntityCullingRateLimiting() && visible) { + if (this.culling.takeToken()) { + cullableObject.setShown(this, true); + } + } else { + cullableObject.setShown(this, visible); + } + } } else { cullableObject.setShown(this, true); } @@ -1285,6 +1296,13 @@ public class BukkitServerPlayer extends Player { } } + @Override + public void setEntityCullingViewDistanceScale(double value) { + value = Math.min(Math.max(0.125, value), 8); + this.culling.setDistanceScale(value); + platformPlayer().getPersistentDataContainer().set(KeyUtils.toNamespacedKey(ENTITY_CULLING_VIEW_DISTANCE_SCALE), PersistentDataType.DOUBLE, value); + } + @Override public void giveExperiencePoints(int xpPoints) { platformPlayer().giveExp(xpPoints); diff --git a/common-files/src/main/resources/commands.yml b/common-files/src/main/resources/commands.yml index a2bfdbeef..ffd2eadee 100644 --- a/common-files/src/main/resources/commands.yml +++ b/common-files/src/main/resources/commands.yml @@ -129,6 +129,12 @@ unset_locale: usage: - /ce feature locale unset +set_entity_view_distance_scale: + enable: true + permission: ce.command.admin.set_entity_view_distance_scale + usage: + - /ce feature entity-view-distance-scale set + # Debug commands debug_set_block: enable: true diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 06785ab16..c458a8f05 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -552,13 +552,21 @@ chunk-system: remove: [] convert: {} +# [Premium Exclusive] client-optimization: # Using server-side ray tracing algorithms to hide block entities/furniture and reduce client-side rendering pressure. + # Requires a restart to fully apply. entity-culling: enable: true view-distance: 64 # Determining the number of threads to execute these raytrace operations threads: 1 + # Limit the maximum number of entities with visibility changes per tick for one player + # This helps mitigate client-side performance impacts and server-side bandwidth spikes caused by a large number of entities appearing. + rate-limiting: + enable: true + bucket-size: 1000 + restore-per-tick: 25 # Enables or disables debug mode debug: diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index cd0751c7b..fce2f7ea7 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -74,6 +74,7 @@ command.send_resource_pack.success.multiple: "Send resource packs to Invalid locale format: " command.locale.set.success: "Updated selected locale to for " command.locale.unset.success: "Cleared selected locale for " +command.entity_view_distance_scale.set.success: "Updated entity view distance scale to for " 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." 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 7c0be0248..8c63646b2 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 @@ -191,6 +191,8 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void setSelectedLocale(@Nullable Locale locale); + public abstract void setEntityCullingViewDistanceScale(double value); + public abstract void giveExperiencePoints(int xpPoints); public abstract void giveExperienceLevels(int levels); 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 9b7473db3..db7db9bab 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 @@ -208,6 +208,9 @@ public class Config { protected boolean client_optimization$entity_culling$enable; protected int client_optimization$entity_culling$view_distance; protected int client_optimization$entity_culling$threads; + protected boolean client_optimization$entity_culling$rate_limiting$enable; + protected int client_optimization$entity_culling$rate_limiting$bucket_size; + protected int client_optimization$entity_culling$rate_limiting$restore_per_tick; public Config(CraftEngine plugin) { this.plugin = plugin; @@ -571,8 +574,13 @@ public class Config { emoji$max_emojis_per_parse = config.getInt("emoji.max-emojis-per-parse", 32); // client optimization - client_optimization$entity_culling$enable = config.getBoolean("client-optimization.entity-culling.enable", false); + if (firstTime) { + client_optimization$entity_culling$enable = VersionHelper.PREMIUM && config.getBoolean("client-optimization.entity-culling.enable", false); + } client_optimization$entity_culling$view_distance = config.getInt("client-optimization.entity-culling.view-distance", 64); + client_optimization$entity_culling$threads = config.getInt("client-optimization.entity-culling.threads", 1); + client_optimization$entity_culling$rate_limiting$bucket_size = config.getInt("client-optimization.entity-culling.rate-limiting.bucket-size", 300); + client_optimization$entity_culling$rate_limiting$restore_per_tick = config.getInt("client-optimization.entity-culling.rate-limiting.restore-per-tick", 5); firstTime = false; } @@ -1176,6 +1184,18 @@ public class Config { return instance.client_optimization$entity_culling$threads; } + public static boolean enableEntityCullingRateLimiting() { + return instance.client_optimization$entity_culling$rate_limiting$enable; + } + + public static int entityCullingRateLimitingBucketSize() { + return instance.client_optimization$entity_culling$rate_limiting$bucket_size; + } + + public static int entityCullingRateLimitingRestorePerTick() { + return instance.client_optimization$entity_culling$rate_limiting$restore_per_tick; + } + public YamlDocument loadOrCreateYamlData(String fileName) { Path path = this.plugin.dataFolderPath().resolve(fileName); if (!Files.exists(path)) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java index 4304dcc72..21efad1c6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.plugin.entityculling; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.MutableVec3d; @@ -21,6 +22,8 @@ public final class EntityCulling { private int lastVisitChunkX = Integer.MAX_VALUE; private int lastVisitChunkZ = Integer.MAX_VALUE; private ClientChunk lastVisitChunk = null; + private int currentTokens = Config.entityCullingRateLimitingBucketSize(); + private double distanceScale = 1d; public EntityCulling(Player player) { this.player = player; @@ -29,6 +32,26 @@ public final class EntityCulling { } } + public void setDistanceScale(double distanceScale) { + this.distanceScale = distanceScale; + } + + public double distanceScale() { + return distanceScale; + } + + public void restoreTokenOnTick() { + this.currentTokens = Math.min(Config.entityCullingRateLimitingBucketSize(), this.currentTokens + Config.entityCullingRateLimitingRestorePerTick()); + } + + public boolean takeToken() { + if (this.currentTokens > 0) { + this.currentTokens--; + return true; + } + return false; + } + public boolean isVisible(CullingData cullable, Vec3d cameraPos) { // 情空标志位 Arrays.fill(this.canCheckLastHitBlock, false); @@ -58,7 +81,7 @@ public final class EntityCulling { } // 如果设置了最大距离 - int maxDistance = cullable.maxDistance; + double maxDistance = cullable.maxDistance * this.distanceScale; if (maxDistance > 0) { // 计算AABB到相机的最小距离 double distanceSq = 0.0; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/VoxelIterator.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/VoxelIterator.java deleted file mode 100644 index a77160fa1..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/VoxelIterator.java +++ /dev/null @@ -1,216 +0,0 @@ -package net.momirealms.craftengine.core.plugin.entityculling; - -import java.util.Iterator; -import java.util.NoSuchElementException; - -// Amanatides, J., & Woo, A. A Fast Voxel Traversal Algorithm for Ray Tracing. http://www.cse.yorku.ca/~amana/research/grid.pdf. -public final class VoxelIterator implements Iterator { - private int x; - private int y; - private int z; - private int stepX; - private int stepY; - private int stepZ; - private double tMax; - private double tMaxX; - private double tMaxY; - private double tMaxZ; - private double tDeltaX; - private double tDeltaY; - private double tDeltaZ; - private int[] ref = new int[3]; // This implementation always returns ref or refSwap to avoid garbage. Can easily be changed if needed. - private int[] refSwap = new int[3]; - private int[] next; - - public VoxelIterator(double startX, double startY, double startZ, double endX, double endY, double endZ) { - initialize(startX, startY, startZ, endX, endY, endZ); - } - - public VoxelIterator(int x, int y, int z, double startX, double startY, double startZ, double endX, double endY, double endZ) { - initialize(x, y, z, startX, startY, startZ, endX, endY, endZ); - } - - public VoxelIterator(double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) { - initialize(startX, startY, startZ, directionX, directionY, directionZ, distance); - } - - public VoxelIterator(int x, int y, int z, double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) { - initialize(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, distance); - } - - public VoxelIterator(double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance, boolean normalized) { - if (normalized) { - initializeNormalized(startX, startY, startZ, directionX, directionY, directionZ, distance); - } else { - initialize(startX, startY, startZ, directionX, directionY, directionZ, distance); - } - } - - public VoxelIterator(int x, int y, int z, double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance, boolean normalized) { - if (normalized) { - initializeNormalized(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, distance); - } else { - initialize(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, distance); - } - } - - public VoxelIterator initialize(double startX, double startY, double startZ, double endX, double endY, double endZ) { - return initialize(floor(startX), floor(startY), floor(startZ), startX, startY, startZ, endX, endY, endZ); - } - - public VoxelIterator initialize(int x, int y, int z, double startX, double startY, double startZ, double endX, double endY, double endZ) { - double directionX = endX - startX; - double directionY = endY - startY; - double directionZ = endZ - startZ; - double distance = Math.sqrt(directionX * directionX + directionY * directionY + directionZ * directionZ); - double fixedDistance = distance == 0. ? Double.NaN : distance; - directionX /= fixedDistance; - directionY /= fixedDistance; - directionZ /= fixedDistance; - return initializeNormalized(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, distance); - } - - public VoxelIterator initialize(double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) { - return initialize(floor(startX), floor(startY), floor(startZ), startX, startY, startZ, directionX, directionY, directionZ, distance); - } - - public VoxelIterator initialize(int x, int y, int z, double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) { - double signum = Math.signum(distance); - directionX *= signum; - directionY *= signum; - directionZ *= signum; - double length = Math.sqrt(directionX * directionX + directionY * directionY + directionZ * directionZ); - - if (length == 0.) { - length = Double.NaN; - } - - directionX /= length; - directionY /= length; - directionZ /= length; - return initializeNormalized(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, Math.abs(distance)); - } - - public VoxelIterator initializeNormalized(double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) { - return initializeNormalized(floor(startX), floor(startY), floor(startZ), startX, startY, startZ, directionX, directionY, directionZ, Math.abs(distance)); - } - - public VoxelIterator initializeNormalized(int x, int y, int z, double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) { - this.x = x; - this.y = y; - this.z = z; - tMax = distance; - stepX = directionX < 0. ? -1 : 1; - stepY = directionY < 0. ? -1 : 1; - stepZ = directionZ < 0. ? -1 : 1; - tMaxX = directionX == 0. ? Double.POSITIVE_INFINITY : (x + (stepX + 1) / 2 - startX) / directionX; - tMaxY = directionY == 0. ? Double.POSITIVE_INFINITY : (y + (stepY + 1) / 2 - startY) / directionY; - tMaxZ = directionZ == 0. ? Double.POSITIVE_INFINITY : (z + (stepZ + 1) / 2 - startZ) / directionZ; - tDeltaX = 1. / Math.abs(directionX); - tDeltaY = 1. / Math.abs(directionY); - tDeltaZ = 1. / Math.abs(directionZ); - next = ref; - ref[0] = x; - ref[1] = y; - ref[2] = z; - return this; - } - - public int[] calculateNext() { - if (tMaxX < tMaxY) { - if (tMaxZ < tMaxX) { - if (tMaxZ <= tMax) { - z += stepZ; - // next = new int[] { x, y, z }; - ref[0] = x; - ref[1] = y; - ref[2] = z; - tMaxZ += tDeltaZ; - } else { - next = null; - } - } else { - if (tMaxX <= tMax) { - if (tMaxZ == tMaxX) { - z += stepZ; - tMaxZ += tDeltaZ; - } - - x += stepX; - // next = new int[] { x, y, z }; - ref[0] = x; - ref[1] = y; - ref[2] = z; - tMaxX += tDeltaX; - } else { - next = null; - } - } - } else if (tMaxY < tMaxZ) { - if (tMaxY <= tMax) { - if (tMaxX == tMaxY) { - x += stepX; - tMaxX += tDeltaX; - } - - y += stepY; - // next = new int[] { x, y, z }; - ref[0] = x; - ref[1] = y; - ref[2] = z; - tMaxY += tDeltaY; - } else { - next = null; - } - } else { - if (tMaxZ <= tMax) { - if (tMaxX == tMaxZ) { - x += stepX; - tMaxX += tDeltaX; - } - - if (tMaxY == tMaxZ) { - y += stepY; - tMaxY += tDeltaY; - } - - z += stepZ; - // next = new int[] { x, y, z }; - ref[0] = x; - ref[1] = y; - ref[2] = z; - tMaxZ += tDeltaZ; - } else { - next = null; - } - } - - return next; - } - - @Override - public boolean hasNext() { - return next != null; - } - - @Override - public int[] next() { - int[] next = this.next; - - if (next == null) { - throw new NoSuchElementException(); - } - - int[] temp = ref; - ref = refSwap; - refSwap = temp; - this.next = ref; - calculateNext(); - return next; - } - - private static int floor(double value) { - int i = (int) value; - return value < (double) i ? i - 1 : i; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java index b488bde08..81174c5d0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java @@ -41,4 +41,5 @@ public interface MessageConstants { TranslatableComponent.Builder COMMAND_ITEM_CLEAR_FAILED_MULTIPLE = Component.translatable().key("command.item.clear.failed.multiple"); TranslatableComponent.Builder COMMAND_ITEM_CLEAR_TEST_SINGLE = Component.translatable().key("command.item.clear.test.single"); TranslatableComponent.Builder COMMAND_ITEM_CLEAR_TEST_MULTIPLE = Component.translatable().key("command.item.clear.test.multiple"); + TranslatableComponent.Builder COMMAND_ENTITY_VIEW_DISTANCE_SCALE_SET_SUCCESS = Component.translatable().key("command.entity_view_distance_scale.set.success"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java index 80b6f9ef2..44af9f41b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java @@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.world.Cullable; public class VirtualCullableObject { public final Cullable cullable; - private boolean isShown; + public boolean isShown; public VirtualCullableObject(Cullable cullable) { this.cullable = cullable; diff --git a/gradle.properties b/gradle.properties index bf5da7ca3..1f7888077 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,8 +2,8 @@ org.gradle.jvmargs=-Xmx1G # Project settings project_version=0.0.65.14 -config_version=58 -lang_version=40 +config_version=60 +lang_version=41 project_group=net.momirealms latest_supported_version=1.21.10 From 0b3d52b39b48e1f355729547f8b58a848b4fb1e9 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 29 Nov 2025 17:03:09 +0800 Subject: [PATCH 041/135] Update Config.java --- .../net/momirealms/craftengine/core/plugin/config/Config.java | 1 + 1 file changed, 1 insertion(+) 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 db7db9bab..4addc3d9e 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 @@ -579,6 +579,7 @@ public class Config { } client_optimization$entity_culling$view_distance = config.getInt("client-optimization.entity-culling.view-distance", 64); client_optimization$entity_culling$threads = config.getInt("client-optimization.entity-culling.threads", 1); + client_optimization$entity_culling$rate_limiting$enable = config.getBoolean("client-optimization.entity-culling.rate-limiting.enable", true); client_optimization$entity_culling$rate_limiting$bucket_size = config.getInt("client-optimization.entity-culling.rate-limiting.bucket-size", 300); client_optimization$entity_culling$rate_limiting$restore_per_tick = config.getInt("client-optimization.entity-culling.rate-limiting.restore-per-tick", 5); From 8d9be05f112c952ade846d700cc2b56ede35bae5 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 29 Nov 2025 21:28:02 +0800 Subject: [PATCH 042/135] =?UTF-8?q?=E5=AE=8C=E6=88=90=E7=AC=AC=E4=B8=89?= =?UTF-8?q?=E4=BA=BA=E7=A7=B0=E5=85=89=E7=BA=BF=E8=BF=BD=E8=B8=AA=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=9C=BC=E7=9D=9B=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SetEntityViewDistanceScaleCommand.java | 10 -- .../plugin/network/BukkitNetworkManager.java | 9 +- .../plugin/user/BukkitServerPlayer.java | 132 ++++++++++++++---- common-files/src/main/resources/config.yml | 6 +- .../core/block/AbstractBlockManager.java | 5 +- .../core/entity/player/Player.java | 2 + .../core/plugin/config/Config.java | 6 + .../plugin/entityculling/CullingData.java | 14 +- .../plugin/entityculling/EntityCulling.java | 27 ++-- .../entityculling/EntityCullingThread.java | 12 +- .../craftengine/core/world/chunk/CEChunk.java | 2 +- gradle.properties | 2 +- 12 files changed, 152 insertions(+), 75 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java index bd24341e3..17d5ca2e3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java @@ -8,21 +8,11 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.FlagKeys; import net.momirealms.craftengine.core.plugin.locale.MessageConstants; -import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.checkerframework.checker.nullness.qual.NonNull; import org.incendo.cloud.Command; import org.incendo.cloud.bukkit.parser.PlayerParser; -import org.incendo.cloud.context.CommandContext; -import org.incendo.cloud.context.CommandInput; import org.incendo.cloud.parser.standard.DoubleParser; -import org.incendo.cloud.parser.standard.StringParser; -import org.incendo.cloud.suggestion.Suggestion; -import org.incendo.cloud.suggestion.SuggestionProvider; - -import java.util.Locale; -import java.util.concurrent.CompletableFuture; public class SetEntityViewDistanceScaleCommand extends BukkitCommandFeature { 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 df5990e82..8ea7189f7 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 @@ -84,7 +84,6 @@ 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.entityculling.EntityCullingThread; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.plugin.network.*; @@ -2036,7 +2035,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes boolean hasGlobalPalette = false; // 创建客户端侧世界(只在开启实体情况下创建) - ClientSection[] clientSections = Config.enableEntityCulling() ? new ClientSection[count] : null; + ClientSection[] clientSections = Config.entityCullingRayTracing() ? new ClientSection[count] : null; for (int i = 0; i < count; i++) { MCSection mcSection = new MCSection(user.clientBlockList(), this.blockList, this.biomeList); @@ -2215,7 +2214,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes // 获取客户端侧区域 ClientSection clientSection = null; - if (Config.enableEntityCulling()) { + if (Config.entityCullingRayTracing()) { SectionPos sectionPos = SectionPos.of(sPos); ClientChunk trackedChunk = user.getTrackedChunk(sectionPos.asChunkPos().longKey); clientSection = trackedChunk.sectionById(sectionPos.y); @@ -2261,7 +2260,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes FriendlyByteBuf buf = event.getBuffer(); BlockPos pos = buf.readBlockPos(); int before = buf.readVarInt(); - if (Config.enableEntityCulling()) { + if (Config.entityCullingRayTracing()) { ClientChunk trackedChunk = user.getTrackedChunk(ChunkPos.asLong(pos.x >> 4, pos.z >> 4)); if (trackedChunk != null) { trackedChunk.setOccluding(pos.x, pos.y, pos.z, this.occlusionPredicate.test(before)); @@ -2441,7 +2440,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes BlockPos blockPos = buf.readBlockPos(); int state = buf.readInt(); // 移除不透明设置 - if (Config.enableEntityCulling()) { + if (Config.entityCullingRayTracing()) { ClientChunk trackedChunk = user.getTrackedChunk(ChunkPos.asLong(blockPos.x >> 4, blockPos.z >> 4)); if (trackedChunk != null) { trackedChunk.setOccluding(blockPos.x, blockPos.y, blockPos.z, false); 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 9ecf68413..d6ac569ac 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 @@ -52,6 +52,7 @@ import org.bukkit.block.Block; import org.bukkit.damage.DamageSource; import org.bukkit.damage.DamageType; import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.inventory.EquipmentSlot; @@ -116,8 +117,6 @@ public class BukkitServerPlayer extends Player { private IntIdentityList blockList = new IntIdentityList(BlockStateUtils.vanillaBlockStateCount()); // cache if player can break blocks private boolean clientSideCanBreak = true; - // prevent AFK players from consuming too much CPU resource on predicting - private Location previousEyeLocation; // a cooldown for better breaking experience private int lastSuccessfulBreak; // player's game tick @@ -145,6 +144,10 @@ public class BukkitServerPlayer extends Player { // 跟踪到的方块实体渲染器 private final Map trackedBlockEntityRenderers = new ConcurrentHashMap<>(); private final EntityCulling culling; + private Vec3d firstPersonCameraVec3; + private Vec3d thirdPersonCameraVec3; + // 玩家眼睛所在位置 + private Location eyeLocation; public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) { this.channel = channel; @@ -512,10 +515,11 @@ public class BukkitServerPlayer extends Player { @Override public void tick() { // not fully online - if (serverPlayer() == null) return; + Object serverPlayer = serverPlayer(); + if (serverPlayer == null) return; + org.bukkit.entity.Player bukkitPlayer = platformPlayer(); if (VersionHelper.isFolia()) { try { - Object serverPlayer = serverPlayer(); Object gameMode = FastNMS.INSTANCE.field$ServerPlayer$gameMode(serverPlayer); this.gameTicks = (int) CoreReflections.field$ServerPlayerGameMode$gameTicks.get(gameMode); } catch (ReflectiveOperationException e) { @@ -527,12 +531,30 @@ public class BukkitServerPlayer extends Player { if (this.gameTicks % 20 == 0) { this.updateGUI(); } + + // 更新眼睛位置 + { + Location unsureEyeLocation = bukkitPlayer.getEyeLocation(); + Entity vehicle = bukkitPlayer.getVehicle(); + if (vehicle != null) { + Object mountPos = FastNMS.INSTANCE.method$Entity$getPassengerRidingPosition(FastNMS.INSTANCE.method$CraftEntity$getHandle(vehicle), serverPlayer); + unsureEyeLocation.set(FastNMS.INSTANCE.field$Vec3$x(mountPos), FastNMS.INSTANCE.field$Vec3$y(mountPos) + bukkitPlayer.getEyeHeight(), FastNMS.INSTANCE.field$Vec3$z(mountPos)); + } + if (Config.predictBreaking() && !this.isDestroyingCustomBlock && !unsureEyeLocation.equals(this.eyeLocation)) { + // if it's not destroying blocks, we do predict + if ((gameTicks() + entityID()) % Config.predictBreakingInterval() == 0) { + this.predictNextBlockToMine(); + } + } + this.eyeLocation = unsureEyeLocation; + } + if (hasSwingHand()) { if (this.isDestroyingBlock) { this.tickBlockDestroy(); } else if (this.lastStopMiningPos != null && this.gameTicks - this.lastStopMiningTick <= 5) { double range = getCachedInteractionRange(); - RayTraceResult result = platformPlayer().rayTraceBlocks(range, FluidCollisionMode.NEVER); + RayTraceResult result = rayTrace(this.eyeLocation, range, FluidCollisionMode.NEVER); if (result != null) { Block hitBlock = result.getHitBlock(); if (hitBlock != null) { @@ -555,36 +577,57 @@ public class BukkitServerPlayer extends Player { this.isHackedBreak = false; } } - if (Config.predictBreaking() && !this.isDestroyingCustomBlock) { - // if it's not destroying blocks, we do predict - if ((gameTicks() + entityID()) % Config.predictBreakingInterval() == 0) { - Location eyeLocation = platformPlayer().getEyeLocation(); - if (eyeLocation.equals(this.previousEyeLocation)) { - return; + + if (Config.entityCullingRayTracing()) { + org.bukkit.entity.Player player = platformPlayer(); + Location eyeLocation = this.eyeLocation.clone(); + this.firstPersonCameraVec3 = LocationUtils.toVec3d(eyeLocation); + int distance = 4; + if (VersionHelper.isOrAbove1_21_6()) { + Entity vehicle = player.getVehicle(); + if (vehicle != null && vehicle.getType() == EntityType.HAPPY_GHAST) { + distance = 8; } - this.previousEyeLocation = eyeLocation; - this.predictNextBlockToMine(); } + this.thirdPersonCameraVec3 = LocationUtils.toVec3d(eyeLocation.subtract(eyeLocation.getDirection().multiply(distance))); } } @Override public void entityCullingTick() { this.culling.restoreTokenOnTick(); + boolean useRayTracing = Config.entityCullingRayTracing(); for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { CullingData cullingData = cullableObject.cullable.cullingData(); if (cullingData != null) { - Vec3d vec3d = LocationUtils.toVec3d(platformPlayer().getEyeLocation()); - boolean visible = this.culling.isVisible(cullingData, vec3d); - if (visible != cullableObject.isShown) { - if (Config.enableEntityCullingRateLimiting() && visible) { - if (this.culling.takeToken()) { - cullableObject.setShown(this, true); - } - } else { - cullableObject.setShown(this, visible); + boolean firstPersonVisible = this.culling.isVisible(cullingData, this.firstPersonCameraVec3, useRayTracing); + // 之前可见 + if (cullableObject.isShown) { + boolean thirdPersonVisible = this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing); + if (!firstPersonVisible && !thirdPersonVisible) { + cullableObject.setShown(this, false); } } + // 之前不可见 + else { + // 但是第一人称可见了 + if (firstPersonVisible) { + // 下次再说 + if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { + continue; + } + cullableObject.setShown(this, true); + continue; + } + if (this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing)) { + // 下次再说 + if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { + continue; + } + cullableObject.setShown(this, true); + } + // 仍然不可见 + } } else { cullableObject.setShown(this, true); } @@ -607,17 +650,12 @@ public class BukkitServerPlayer extends Player { public boolean canInteractWithBlock(BlockPos pos, double distance) { double d = this.getCachedInteractionRange() + distance; - return (new AABB(pos)).distanceToSqr(this.getEyePosition()) < d * d; + return (new AABB(pos)).distanceToSqr(this.getEyePos()) < 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()); + return Vec3d.distanceToSqr(this.getEyePos(), pos) < d * d; } @Override @@ -777,7 +815,7 @@ public class BukkitServerPlayer extends Player { try { org.bukkit.entity.Player player = platformPlayer(); double range = getCachedInteractionRange(); - RayTraceResult result = player.rayTraceBlocks(range, FluidCollisionMode.NEVER); + RayTraceResult result = rayTrace(this.eyeLocation, range, FluidCollisionMode.NEVER); if (result == null) return; Block hitBlock = result.getHitBlock(); if (hitBlock == null) return; @@ -1217,7 +1255,7 @@ public class BukkitServerPlayer extends Player { @Override public void removeTrackedChunk(long chunkPos) { this.trackedChunks.remove(chunkPos); - if (Config.enableEntityCulling()) { + if (Config.entityCullingRayTracing()) { this.culling.removeLastVisitChunkIfMatches((int) chunkPos, (int) (chunkPos >> 32)); } } @@ -1367,4 +1405,36 @@ public class BukkitServerPlayer extends Player { public void clearTrackedBlockEntities() { this.trackedBlockEntityRenderers.clear(); } + + @Override + public WorldPosition eyePosition() { + return LocationUtils.toWorldPosition(this.getEyeLocation()); + } + + public Location getEyeLocation() { + org.bukkit.entity.Player player = platformPlayer(); + Location eyeLocation = player.getEyeLocation(); + Entity vehicle = player.getVehicle(); + if (vehicle != null) { + Object mountPos = FastNMS.INSTANCE.method$Entity$getPassengerRidingPosition(FastNMS.INSTANCE.method$CraftEntity$getHandle(vehicle), serverPlayer()); + eyeLocation.set(FastNMS.INSTANCE.field$Vec3$x(mountPos), FastNMS.INSTANCE.field$Vec3$y(mountPos) + player.getEyeHeight(), FastNMS.INSTANCE.field$Vec3$z(mountPos)); + } + return eyeLocation; + } + + public Vec3d getEyePos() { + org.bukkit.entity.Player player = platformPlayer(); + Entity vehicle = player.getVehicle(); + if (vehicle != null) { + Object mountPos = FastNMS.INSTANCE.method$Entity$getPassengerRidingPosition(FastNMS.INSTANCE.method$CraftEntity$getHandle(vehicle), serverPlayer()); + return new Vec3d(FastNMS.INSTANCE.field$Vec3$x(mountPos), FastNMS.INSTANCE.field$Vec3$y(mountPos) + player.getEyeHeight(), FastNMS.INSTANCE.field$Vec3$z(mountPos)); + } else { + Location location = player.getLocation(); + return new Vec3d(location.getX(), location.getY() + player.getEyeHeight(), location.getZ()); + } + } + + private RayTraceResult rayTrace(Location start, double range, FluidCollisionMode mode) { + return start.getWorld().rayTraceBlocks(start, start.getDirection(), range, mode); + } } diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index c458a8f05..6c3d4bf47 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -554,11 +554,13 @@ chunk-system: # [Premium Exclusive] client-optimization: - # Using server-side ray tracing algorithms to hide block entities/furniture and reduce client-side rendering pressure. # Requires a restart to fully apply. entity-culling: enable: true - view-distance: 64 + # Using server-side ray tracing algorithms to hide block entities/furniture and reduce client-side rendering pressure. + ray-tracing: true + # Cull entities based on distance + view-distance: 64 # -1 = no limit # Determining the number of threads to execute these raytrace operations threads: 1 # Limit the maximum number of entities with visibility changes per tick for one player 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 0570149e7..5b0dbf02b 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 @@ -715,13 +715,14 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem return null; } if (!(arguments instanceof Map)) { - return new CullingData(DEFAULT_BLOCK_ENTITY_AABB, Config.entityCullingViewDistance(), 0.5); + return new CullingData(DEFAULT_BLOCK_ENTITY_AABB, Config.entityCullingViewDistance(), 0.5, true); } Map argumentsMap = ResourceConfigUtils.getAsMap(arguments, "entity-culling"); return new CullingData( ResourceConfigUtils.getAsAABB(argumentsMap.getOrDefault("aabb", 1), "aabb"), ResourceConfigUtils.getAsInt(argumentsMap.getOrDefault("view-distance", Config.entityCullingViewDistance()), "view-distance"), - ResourceConfigUtils.getAsDouble(argumentsMap.getOrDefault("aabb-expansion", 0.5), "aabb-expansion") + ResourceConfigUtils.getAsDouble(argumentsMap.getOrDefault("aabb-expansion", 0.5), "aabb-expansion"), + ResourceConfigUtils.getAsBoolean(argumentsMap.getOrDefault("ray-tracing", true), "ray-tracing") ); } 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 8c63646b2..6826bbc12 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 @@ -218,4 +218,6 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { @Override public void remove() { } + + public abstract WorldPosition eyePosition(); } 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 4addc3d9e..df09a8b08 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 @@ -208,6 +208,7 @@ public class Config { protected boolean client_optimization$entity_culling$enable; protected int client_optimization$entity_culling$view_distance; protected int client_optimization$entity_culling$threads; + protected boolean client_optimization$entity_culling$ray_tracing; protected boolean client_optimization$entity_culling$rate_limiting$enable; protected int client_optimization$entity_culling$rate_limiting$bucket_size; protected int client_optimization$entity_culling$rate_limiting$restore_per_tick; @@ -579,6 +580,7 @@ public class Config { } client_optimization$entity_culling$view_distance = config.getInt("client-optimization.entity-culling.view-distance", 64); client_optimization$entity_culling$threads = config.getInt("client-optimization.entity-culling.threads", 1); + client_optimization$entity_culling$ray_tracing = client_optimization$entity_culling$enable && config.getBoolean("client-optimization.entity-culling.ray-tracing", true); client_optimization$entity_culling$rate_limiting$enable = config.getBoolean("client-optimization.entity-culling.rate-limiting.enable", true); client_optimization$entity_culling$rate_limiting$bucket_size = config.getInt("client-optimization.entity-culling.rate-limiting.bucket-size", 300); client_optimization$entity_culling$rate_limiting$restore_per_tick = config.getInt("client-optimization.entity-culling.rate-limiting.restore-per-tick", 5); @@ -1197,6 +1199,10 @@ public class Config { return instance.client_optimization$entity_culling$rate_limiting$restore_per_tick; } + public static boolean entityCullingRayTracing() { + return instance.client_optimization$entity_culling$ray_tracing; + } + public YamlDocument loadOrCreateYamlData(String fileName) { Path path = this.plugin.dataFolderPath().resolve(fileName); if (!Files.exists(path)) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/CullingData.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/CullingData.java index 8d67a1349..67c7131f0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/CullingData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/CullingData.java @@ -6,22 +6,28 @@ public final class CullingData { public final AABB aabb; public final int maxDistance; public final double aabbExpansion; + public final boolean rayTracing; - public CullingData(AABB aabb, int maxDistance, double aabbExpansion) { + public CullingData(AABB aabb, int maxDistance, double aabbExpansion, boolean rayTracing) { this.aabb = aabb; this.maxDistance = maxDistance; this.aabbExpansion = aabbExpansion; + this.rayTracing = rayTracing; } public AABB aabb() { - return aabb; + return this.aabb; } public int maxDistance() { - return maxDistance; + return this.maxDistance; } public double aabbExpansion() { - return aabbExpansion; + return this.aabbExpansion; + } + + public boolean rayTracing() { + return this.rayTracing; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java index 21efad1c6..5ced80c59 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java @@ -16,8 +16,8 @@ public final class EntityCulling { private final Player player; private final boolean[] dotSelectors = new boolean[MAX_SAMPLES]; private final MutableVec3d[] targetPoints = new MutableVec3d[MAX_SAMPLES]; - private final int[] lastHitBlock = new int[MAX_SAMPLES * 3]; - private final boolean[] canCheckLastHitBlock = new boolean[MAX_SAMPLES]; + private final int[] lastHitBlock = new int[3]; + private boolean canCheckLastHitBlock = false; private int hitBlockCount = 0; private int lastVisitChunkX = Integer.MAX_VALUE; private int lastVisitChunkZ = Integer.MAX_VALUE; @@ -52,9 +52,9 @@ public final class EntityCulling { return false; } - public boolean isVisible(CullingData cullable, Vec3d cameraPos) { + public boolean isVisible(CullingData cullable, Vec3d cameraPos, boolean rayTracing) { // 情空标志位 - Arrays.fill(this.canCheckLastHitBlock, false); + this.canCheckLastHitBlock = false; this.hitBlockCount = 0; AABB aabb = cullable.aabb; double aabbExpansion = cullable.aabbExpansion; @@ -97,6 +97,10 @@ public final class EntityCulling { } } + if (!rayTracing || !cullable.rayTracing) { + return true; + } + // 清空之前的缓存 Arrays.fill(this.dotSelectors, false); if (relX == Relative.POSITIVE) { @@ -190,7 +194,7 @@ public final class EntityCulling { int startBlockZ = MiscUtils.floor(start.z); // 遍历所有目标点进行视线检测 - outer: for (int targetIndex = 0; targetIndex < targetCount; targetIndex++) { + for (int targetIndex = 0; targetIndex < targetCount; targetIndex++) { MutableVec3d currentTarget = targets[targetIndex]; // 计算起点到目标的相对向量(世界坐标差) @@ -199,14 +203,9 @@ public final class EntityCulling { double deltaZ = start.z - currentTarget.z; // 检查之前命中的方块,大概率还是命中 - for (int i = 0; i < MAX_SAMPLES; i++) { - if (this.canCheckLastHitBlock[i]) { - int offset = i * 3; - if (rayIntersection(this.lastHitBlock[offset], this.lastHitBlock[offset + 1], this.lastHitBlock[offset + 2], start, new MutableVec3d(deltaX, deltaY, deltaZ).normalize())) { - continue outer; - } - } else { - break; + if (this.canCheckLastHitBlock) { + if (rayIntersection(this.lastHitBlock[0], this.lastHitBlock[1], this.lastHitBlock[2], start, new MutableVec3d(deltaX, deltaY, deltaZ).normalize())) { + continue; } } @@ -292,7 +291,7 @@ public final class EntityCulling { if (isLineOfSightClear) { return true; } else { - this.canCheckLastHitBlock[this.hitBlockCount++] = true; + this.canCheckLastHitBlock = true; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java index fefaa7896..7eb6f1e69 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.plugin.entityculling; 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.logger.Debugger; import java.util.concurrent.Executors; @@ -14,6 +15,7 @@ public class EntityCullingThread { private final AtomicBoolean isRunning = new AtomicBoolean(false); private final int id; private final int threads; + private int timer; public EntityCullingThread(int id, int threads) { this.id = id; @@ -35,7 +37,7 @@ public class EntityCullingThread { this.scheduler.execute(() -> { try { int processed = 0; - long startTime = System.currentTimeMillis(); + long startTime = System.nanoTime(); for (Player player : CraftEngine.instance().networkManager().onlineUsers()) { // 使用绝对值确保非负,使用 threads 而不是 threads-1 确保均匀分布 @@ -45,10 +47,10 @@ public class EntityCullingThread { } } - long duration = System.currentTimeMillis() - startTime; - if (duration > 45) { - String value = String.format("EntityCullingThread-%d processed %d players in %dms (over 45ms)", - this.id, processed, duration); + long duration = System.nanoTime() - startTime; + if (Config.debugEntityCulling() && this.timer++ % 20 == 0) { + String value = String.format("EntityCullingThread-%d processed %d players in %sms", + this.id, processed, String.format("%.2f", duration / 1_000_000.0)); Debugger.ENTITY_CULLING.debug(() -> value); } } catch (Exception e) { 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 e8061a7d8..5e59155d6 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 @@ -143,7 +143,7 @@ public class CEChunk { ConstantBlockEntityRenderer renderer = new ConstantBlockEntityRenderer( elements, Optional.ofNullable(state.cullingData()) - .map(data -> new CullingData(data.aabb.move(pos), data.maxDistance, data.aabbExpansion)) + .map(data -> new CullingData(data.aabb.move(pos), data.maxDistance, data.aabbExpansion, data.rayTracing)) .orElse(null) ); World wrappedWorld = this.world.world(); diff --git a/gradle.properties b/gradle.properties index 1f7888077..3691ff1e9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -48,7 +48,7 @@ byte_buddy_version=1.18.1 ahocorasick_version=0.6.3 snake_yaml_version=2.5 anti_grief_version=1.0.5 -nms_helper_version=1.0.137 +nms_helper_version=1.0.138 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.38.7 From d00f4789c49e9b36cad56f43da6926011079e2f9 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 29 Nov 2025 21:48:48 +0800 Subject: [PATCH 043/135] Update BukkitServerPlayer.java --- .../craftengine/bukkit/plugin/user/BukkitServerPlayer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d6ac569ac..2348ca5fc 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 @@ -675,7 +675,7 @@ public class BukkitServerPlayer extends Player { private void predictNextBlockToMine() { double range = getCachedInteractionRange() + Config.extendedInteractionRange(); - RayTraceResult result = platformPlayer().rayTraceBlocks(range, FluidCollisionMode.NEVER); + RayTraceResult result = rayTrace(this.eyeLocation, range, FluidCollisionMode.NEVER); if (result == null) { if (!this.clientSideCanBreak) { setClientSideCanBreakBlock(true); From e8ef466fce5be13f8ede4eff131e9500a0b788f9 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sun, 30 Nov 2025 00:49:37 +0800 Subject: [PATCH 044/135] Update CEChunk.java --- .../net/momirealms/craftengine/core/world/chunk/CEChunk.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 5e59155d6..67695fac8 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 @@ -187,7 +187,10 @@ public class CEChunk { // 如果启用实体剔除,那么只添加记录 if (Config.enableEntityCulling()) { for (Player player : trackedBy) { - player.addTrackedBlockEntity(pos, renderer); + VirtualCullableObject trackedBlockEntity = player.addTrackedBlockEntity(pos, renderer); + if (trackedBlockEntity != null && trackedBlockEntity.isShown()) { + trackedBlockEntity.setShown(player, false); + } } } // 否则直接显示 From ff920b74df9602682a635accc54069d4382f75cd Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sun, 30 Nov 2025 01:20:11 +0800 Subject: [PATCH 045/135] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=8B=E8=BD=AC?= =?UTF-8?q?=E8=BF=87=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ItemDisplayBlockEntityElementConfig.java | 24 +++++++------------ .../TextDisplayBlockEntityElementConfig.java | 18 ++++---------- .../default/configuration/blocks/sofa.yml | 18 +++++++------- 3 files changed, 22 insertions(+), 38 deletions(-) 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 index a8842a81f..e28d98421 100644 --- 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 @@ -61,14 +61,14 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo this.shadowStrength = shadowStrength; 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); - ItemDisplayEntityData.ShadowRadius.addEntityDataIfNotDefaultValue(this.shadowRadius, dataValues); - ItemDisplayEntityData.ShadowStrength.addEntityDataIfNotDefaultValue(this.shadowStrength, dataValues); + ItemDisplayEntityData.DisplayedItem.addEntityData(item.apply(player).getLiteralObject(), dataValues); + ItemDisplayEntityData.Scale.addEntityData(this.scale, dataValues); + ItemDisplayEntityData.RotationLeft.addEntityData(this.rotation, dataValues); + ItemDisplayEntityData.BillboardConstraints.addEntityData(this.billboard.id(), dataValues); + ItemDisplayEntityData.Translation.addEntityData(this.translation, dataValues); + ItemDisplayEntityData.DisplayType.addEntityData(this.displayContext.id(), dataValues); + ItemDisplayEntityData.ShadowRadius.addEntityData(this.shadowRadius, dataValues); + ItemDisplayEntityData.ShadowStrength.addEntityData(this.shadowStrength, dataValues); return dataValues; }; } @@ -80,14 +80,6 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo @Override public ItemDisplayBlockEntityElement create(World world, BlockPos pos, ItemDisplayBlockEntityElement previous) { - Quaternionf previousRotation = previous.config.rotation; - if (previousRotation.x != 0 || previousRotation.y != 0 || previousRotation.z != 0 || previousRotation.w != 1) { - return null; - } - Vector3f translation = previous.config.translation; - if (translation.x != 0 || translation.y != 0 || translation.z != 0) { - return null; - } return new ItemDisplayBlockEntityElement(this, pos, previous.entityId, previous.config.yRot != this.yRot || previous.config.xRot != this.xRot || 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 index 43b6af680..ebf46a890 100644 --- 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 @@ -52,11 +52,11 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo 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); + TextDisplayEntityData.Text.addEntityData(ComponentUtils.adventureToMinecraft(text(player)), dataValues); + TextDisplayEntityData.Scale.addEntityData(this.scale, dataValues); + TextDisplayEntityData.RotationLeft.addEntityData(this.rotation, dataValues); + TextDisplayEntityData.BillboardConstraints.addEntityData(this.billboard.id(), dataValues); + TextDisplayEntityData.Translation.addEntityData(this.translation, dataValues); return dataValues; }; } @@ -68,14 +68,6 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo @Override public TextDisplayBlockEntityElement create(World world, BlockPos pos, TextDisplayBlockEntityElement previous) { - Quaternionf previousRotation = previous.config.rotation; - if (previousRotation.x != 0 || previousRotation.y != 0 || previousRotation.z != 0 || previousRotation.w != 1) { - return null; - } - Vector3f translation = previous.config.translation; - if (translation.x != 0 || translation.y != 0 || translation.z != 0) { - return null; - } return new TextDisplayBlockEntityElement(this, pos, previous.entityId, previous.config.yRot != this.yRot || previous.config.xRot != this.xRot || 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 index 5aba431e7..120c02811 100644 --- a/common-files/src/main/resources/resources/default/configuration/blocks/sofa.yml +++ b/common-files/src/main/resources/resources/default/configuration/blocks/sofa.yml @@ -93,7 +93,7 @@ items: state: barrier entity-renderer: item: default:sofa - yaw: 90 + rotation: 90 facing=north,shape=straight: state: barrier entity-renderer: @@ -102,12 +102,12 @@ items: state: barrier entity-renderer: item: default:sofa - yaw: 180 + rotation: 180 facing=west,shape=straight: state: barrier entity-renderer: item: default:sofa - yaw: 270 + rotation: 270 facing=east,shape=inner_left: state: barrier entity-renderer: @@ -116,22 +116,22 @@ items: state: barrier entity-renderer: item: default:sofa_inner - yaw: 270 + rotation: 270 facing=south,shape=inner_left: state: barrier entity-renderer: item: default:sofa_inner - yaw: 90 + rotation: 90 facing=west,shape=inner_left: state: barrier entity-renderer: item: default:sofa_inner - yaw: 180 + rotation: 180 facing=east,shape=inner_right: state: barrier entity-renderer: item: default:sofa_inner - yaw: 90 + rotation: 90 facing=north,shape=inner_right: state: barrier entity-renderer: @@ -140,12 +140,12 @@ items: state: barrier entity-renderer: item: default:sofa_inner - yaw: 180 + rotation: 180 facing=west,shape=inner_right: state: barrier entity-renderer: item: default:sofa_inner - yaw: 270 + rotation: 270 variants: facing=east,shape=inner_left: appearance: facing=east,shape=inner_left From 12106c80c016bce07837272dd1b5eda95319a83c Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sun, 30 Nov 2025 02:56:01 +0800 Subject: [PATCH 046/135] =?UTF-8?q?=E6=94=B9=E8=BF=9Bentity=20renderer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../element/ItemBlockEntityElementConfig.java | 14 ++ .../ItemDisplayBlockEntityElementConfig.java | 19 ++ .../TextDisplayBlockEntityElementConfig.java | 19 ++ .../plugin/user/BukkitServerPlayer.java | 4 +- .../configuration/templates/block_states.yml | 192 +++++++++--------- .../element/BlockEntityElementConfig.java | 4 + .../core/entity/player/Player.java | 2 +- .../craftengine/core/world/chunk/CEChunk.java | 147 +++++++++----- .../chunk/client/VirtualCullableObject.java | 6 +- gradle.properties | 2 +- 10 files changed, 262 insertions(+), 147 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java index 96873d757..b68c2af5d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java @@ -45,6 +45,14 @@ public class ItemBlockEntityElementConfig implements BlockEntityElementConfig elementClass() { return ItemBlockEntityElement.class; @@ -62,6 +70,12 @@ public class ItemBlockEntityElementConfig implements BlockEntityElementConfig elementClass() { return ItemDisplayBlockEntityElement.class; @@ -140,6 +149,16 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo return this.lazyMetadataPacket.apply(player); } + @Override + public boolean equals(Object o) { + if (!(o instanceof ItemDisplayBlockEntityElementConfig that)) return false; + return Float.compare(xRot, that.xRot) == 0 && + Float.compare(yRot, that.yRot) == 0 && + Objects.equal(position, that.position) && + Objects.equal(translation, that.translation) && + Objects.equal(rotation, that.rotation); + } + public static class Factory implements BlockEntityElementConfigFactory { @SuppressWarnings("unchecked") 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 index ebf46a890..12012724d 100644 --- 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 @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.block.entity.renderer.element; +import com.google.common.base.Objects; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData; import net.momirealms.craftengine.bukkit.util.ComponentUtils; @@ -75,6 +76,14 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo ); } + @Override + public TextDisplayBlockEntityElement createExact(World world, BlockPos pos, TextDisplayBlockEntityElement previous) { + if (!previous.config.equals(this)) { + return null; + } + return new TextDisplayBlockEntityElement(this, pos, previous.entityId, false); + } + @Override public Class elementClass() { return TextDisplayBlockEntityElement.class; @@ -116,6 +125,16 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo return this.lazyMetadataPacket.apply(player); } + @Override + public boolean equals(Object o) { + if (!(o instanceof TextDisplayBlockEntityElementConfig that)) return false; + return Float.compare(xRot, that.xRot) == 0 && + Float.compare(yRot, that.yRot) == 0 && + Objects.equal(position, that.position) && + Objects.equal(translation, that.translation) && + Objects.equal(rotation, that.rotation); + } + public static class Factory implements BlockEntityElementConfigFactory { @SuppressWarnings("unchecked") 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 2348ca5fc..fc12b179f 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 @@ -1382,8 +1382,8 @@ public class BukkitServerPlayer extends Player { } @Override - public VirtualCullableObject addTrackedBlockEntity(BlockPos blockPos, ConstantBlockEntityRenderer renderer) { - return this.trackedBlockEntityRenderers.put(blockPos, new VirtualCullableObject(renderer)); + public void addTrackedBlockEntity(BlockPos blockPos, ConstantBlockEntityRenderer renderer) { + this.trackedBlockEntityRenderers.put(blockPos, new VirtualCullableObject(renderer)); } @Override diff --git a/common-files/src/main/resources/resources/default/configuration/templates/block_states.yml b/common-files/src/main/resources/resources/default/configuration/templates/block_states.yml index 3550304a2..c254f682e 100644 --- a/common-files/src/main/resources/resources/default/configuration/templates/block_states.yml +++ b/common-files/src/main/resources/resources/default/configuration/templates/block_states.yml @@ -1834,354 +1834,354 @@ templates: state: ${base_block}[east=false,north=false,south=false,waterlogged=false,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 east=true,north=false,south=false,waterlogged=false,west=false: state: ${base_block}[east=true,north=false,south=false,waterlogged=false,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=false,north=true,south=false,waterlogged=false,west=false: state: ${base_block}[east=false,north=true,south=false,waterlogged=false,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 180 + rotation: 180 east=false,north=false,south=true,waterlogged=false,west=false: state: ${base_block}[east=false,north=false,south=true,waterlogged=false,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 east=false,north=false,south=false,waterlogged=false,west=true: state: ${base_block}[east=false,north=false,south=false,waterlogged=false,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 east=true,north=true,south=false,waterlogged=false,west=false: state: ${base_block}[east=true,north=true,south=false,waterlogged=false,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 180 + rotation: 180 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=true,north=false,south=true,waterlogged=false,west=false: state: ${base_block}[east=true,north=false,south=true,waterlogged=false,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=true,north=false,south=false,waterlogged=false,west=true: state: ${base_block}[east=true,north=false,south=false,waterlogged=false,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=false,north=true,south=true,waterlogged=false,west=false: state: ${base_block}[east=false,north=true,south=true,waterlogged=false,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 - item: ${fence_side_item} - yaw: 180 + rotation: 180 east=false,north=true,south=false,waterlogged=false,west=true: state: ${base_block}[east=false,north=true,south=false,waterlogged=false,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 - item: ${fence_side_item} - yaw: 180 + rotation: 180 east=false,north=false,south=true,waterlogged=false,west=true: state: ${base_block}[east=false,north=false,south=true,waterlogged=false,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 east=true,north=true,south=true,waterlogged=false,west=false: state: ${base_block}[east=true,north=true,south=true,waterlogged=false,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 - item: ${fence_side_item} - yaw: 180 + rotation: 180 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=true,north=true,south=false,waterlogged=false,west=true: state: ${base_block}[east=true,north=true,south=false,waterlogged=false,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 - item: ${fence_side_item} - yaw: 180 + rotation: 180 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=true,north=false,south=true,waterlogged=false,west=true: state: ${base_block}[east=true,north=false,south=true,waterlogged=false,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=false,north=true,south=true,waterlogged=false,west=true: state: ${base_block}[east=false,north=true,south=true,waterlogged=false,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 - item: ${fence_side_item} - yaw: 180 + rotation: 180 east=true,north=true,south=true,waterlogged=false,west=true: state: ${base_block}[east=true,north=true,south=true,waterlogged=false,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 - item: ${fence_side_item} - yaw: 180 + rotation: 180 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=false,north=false,south=false,waterlogged=true,west=false: state: ${base_block}[east=false,north=false,south=false,waterlogged=true,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 east=true,north=false,south=false,waterlogged=true,west=false: state: ${base_block}[east=true,north=false,south=false,waterlogged=true,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=false,north=true,south=false,waterlogged=true,west=false: state: ${base_block}[east=false,north=true,south=false,waterlogged=true,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 180 + rotation: 180 east=false,north=false,south=true,waterlogged=true,west=false: state: ${base_block}[east=false,north=false,south=true,waterlogged=true,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 east=false,north=false,south=false,waterlogged=true,west=true: state: ${base_block}[east=false,north=false,south=false,waterlogged=true,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 east=true,north=true,south=false,waterlogged=true,west=false: state: ${base_block}[east=true,north=true,south=false,waterlogged=true,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 180 + rotation: 180 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=true,north=false,south=true,waterlogged=true,west=false: state: ${base_block}[east=true,north=false,south=true,waterlogged=true,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=true,north=false,south=false,waterlogged=true,west=true: state: ${base_block}[east=true,north=false,south=false,waterlogged=true,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=false,north=true,south=true,waterlogged=true,west=false: state: ${base_block}[east=false,north=true,south=true,waterlogged=true,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 - item: ${fence_side_item} - yaw: 180 + rotation: 180 east=false,north=true,south=false,waterlogged=true,west=true: state: ${base_block}[east=false,north=true,south=false,waterlogged=true,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 - item: ${fence_side_item} - yaw: 180 + rotation: 180 east=false,north=false,south=true,waterlogged=true,west=true: state: ${base_block}[east=false,north=false,south=true,waterlogged=true,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 east=true,north=true,south=true,waterlogged=true,west=false: state: ${base_block}[east=true,north=true,south=true,waterlogged=true,west=false] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 - item: ${fence_side_item} - yaw: 180 + rotation: 180 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=true,north=true,south=false,waterlogged=true,west=true: state: ${base_block}[east=true,north=true,south=false,waterlogged=true,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 - item: ${fence_side_item} - yaw: 180 + rotation: 180 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=true,north=false,south=true,waterlogged=true,west=true: state: ${base_block}[east=true,north=false,south=true,waterlogged=true,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 - item: ${fence_side_item} - yaw: 270 + rotation: 270 east=false,north=true,south=true,waterlogged=true,west=true: state: ${base_block}[east=false,north=true,south=true,waterlogged=true,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 - item: ${fence_side_item} - yaw: 180 + rotation: 180 east=true,north=true,south=true,waterlogged=true,west=true: state: ${base_block}[east=true,north=true,south=true,waterlogged=true,west=true] entity-renderer: - item: ${fence_post_item} - yaw: 180 + rotation: 180 scale: 1.0003 translation: 0,0.0001,0 - item: ${fence_side_item} - yaw: 0 + rotation: 0 - item: ${fence_side_item} - yaw: 90 + rotation: 90 - item: ${fence_side_item} - yaw: 180 + rotation: 180 - item: ${fence_side_item} - yaw: 270 + rotation: 270 variants: waterlogged=true: settings: 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 index 0d3c320ad..99346e5bc 100644 --- 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 @@ -11,5 +11,9 @@ public interface BlockEntityElementConfig { return null; } + default E createExact(World world, BlockPos pos, E previous) { + return null; + } + Class elementClass(); } 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 6826bbc12..847fe6d1c 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 @@ -207,7 +207,7 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void addTrackedBlockEntities(Map renders); - public abstract VirtualCullableObject addTrackedBlockEntity(BlockPos blockPos, ConstantBlockEntityRenderer renderer); + public abstract void addTrackedBlockEntity(BlockPos blockPos, ConstantBlockEntityRenderer renderer); public abstract VirtualCullableObject getTrackedBlockEntity(BlockPos blockPos); 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 67695fac8..50eda3140 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 @@ -153,6 +153,12 @@ public class CEChunk { if (previous != null) { // 由于entity-render的体量基本都很小,所以考虑一个特殊情况,即前后都是1个renderer,对此情况进行简化和优化 BlockEntityElement[] previousElements = previous.elements().clone(); + + /* + * + * 1 对 1,命中率最高 + * + */ if (previousElements.length == 1 && renderers.length == 1) { BlockEntityElement previousElement = previousElements[0]; BlockEntityElementConfig config = renderers[0]; @@ -162,18 +168,19 @@ public class CEChunk { if (element != null) { elements[0] = element; if (hasTrackedBy) { - // 如果启用实体剔除,那么只对已经渲染的进行变换 - if (Config.enableEntityCulling()) { - for (Player player : trackedBy) { - VirtualCullableObject trackedBlockEntity = player.addTrackedBlockEntity(pos, renderer); - if (trackedBlockEntity == null || trackedBlockEntity.isShown()) { + for (Player player : trackedBy) { + // 如果启用剔除,则暂时保留原先可见度,因为大概率可见度不发生变化 + if (Config.enableEntityCulling()) { + VirtualCullableObject trackedBlockEntity = player.getTrackedBlockEntity(pos); + if (trackedBlockEntity == null || trackedBlockEntity.isShown) { element.transform(player); } - } - } - // 否则直接变换 - else { - for (Player player : trackedBy) { + if (trackedBlockEntity != null) { + trackedBlockEntity.setCullable(renderer); + } else { + player.addTrackedBlockEntity(pos, renderer); + } + } else { element.transform(player); } } @@ -184,47 +191,56 @@ public class CEChunk { BlockEntityElement element = config.create(wrappedWorld, pos); elements[0] = element; if (hasTrackedBy) { - // 如果启用实体剔除,那么只添加记录 - if (Config.enableEntityCulling()) { - for (Player player : trackedBy) { - VirtualCullableObject trackedBlockEntity = player.addTrackedBlockEntity(pos, renderer); - if (trackedBlockEntity != null && trackedBlockEntity.isShown()) { - trackedBlockEntity.setShown(player, false); + for (Player player : trackedBy) { + if (Config.enableEntityCulling()) { + VirtualCullableObject trackedBlockEntity = player.getTrackedBlockEntity(pos); + if (trackedBlockEntity != null) { + if (trackedBlockEntity.isShown) { + trackedBlockEntity.setShown(player, false); + } + trackedBlockEntity.setCullable(renderer); + } else { + player.addTrackedBlockEntity(pos, renderer); } - } - } - // 否则直接显示 - else { - for (Player player : trackedBy) { + } else { previousElement.hide(player); element.show(player); } } } } - } else { + } + /* + * + * 1 对 多, 多 对 多 + * + */ + else { + + VirtualCullableObject[] previousObjects = hasTrackedBy ? new VirtualCullableObject[trackedBy.size()] : null; + if (hasTrackedBy) { + for (int j = 0; j < previousObjects.length; j++) { + previousObjects[j] = trackedBy.get(j).getTrackedBlockEntity(pos); + } + } + outer: for (int i = 0; i < elements.length; i++) { BlockEntityElementConfig config = renderers[i]; + /* + * 严格可变换部分 + */ for (int j = 0; j < previousElements.length; j++) { BlockEntityElement previousElement = previousElements[j]; if (previousElement != null && config.elementClass().isInstance(previousElement)) { - BlockEntityElement newElement = ((BlockEntityElementConfig) config).create(wrappedWorld, pos, previousElement); + BlockEntityElement newElement = ((BlockEntityElementConfig) config).createExact(wrappedWorld, pos, previousElement); if (newElement != null) { previousElements[j] = null; elements[i] = newElement; if (hasTrackedBy) { - // 如果启用实体剔除,那么只对已经渲染的进行变换 - if (Config.enableEntityCulling()) { - for (Player player : trackedBy) { - VirtualCullableObject trackedBlockEntity = player.addTrackedBlockEntity(pos, renderer); - if (trackedBlockEntity == null || trackedBlockEntity.isShown()) { - newElement.transform(player); - } - } - } - // 否则直接变换 - else { - for (Player player : trackedBy) { + for (int k = 0; k < trackedBy.size(); k++) { + Player player = trackedBy.get(k); + VirtualCullableObject cullableObject = previousObjects[k]; + if (cullableObject == null || cullableObject.isShown) { newElement.transform(player); } } @@ -233,21 +249,48 @@ public class CEChunk { } } } + /* + * 可变换部分 + */ + for (int j = 0; j < previousElements.length; j++) { + BlockEntityElement previousElement = previousElements[j]; + if (previousElement != null && config.elementClass().isInstance(previousElement)) { + BlockEntityElement newElement = ((BlockEntityElementConfig) config).create(wrappedWorld, pos, previousElement); + if (newElement != null) { + previousElements[j] = null; + elements[i] = newElement; + if (hasTrackedBy) { + for (int k = 0; k < trackedBy.size(); k++) { + Player player = trackedBy.get(k); + VirtualCullableObject cullableObject = previousObjects[k]; + if (cullableObject == null || cullableObject.isShown) { + newElement.transform(player); + } + } + } + continue outer; + } + } + } + /* + * 不可变换的直接生成 + */ BlockEntityElement newElement = config.create(wrappedWorld, pos); elements[i] = newElement; if (hasTrackedBy) { - if (Config.enableEntityCulling()) { - for (Player player : trackedBy) { - player.addTrackedBlockEntity(pos, renderer); - } - } else { - for (Player player : trackedBy) { + for (int k = 0; k < trackedBy.size(); k++) { + Player player = trackedBy.get(k); + VirtualCullableObject cullableObject = previousObjects[k]; + if (cullableObject == null || cullableObject.isShown) { newElement.show(player); } } } } if (hasTrackedBy) { + /* + * 未能完成变化的,需要直接删除 + */ for (int i = 0; i < previousElements.length; i++) { BlockEntityElement previousElement = previousElements[i]; if (previousElement != null) { @@ -256,19 +299,31 @@ public class CEChunk { } } } + // 添加 track + for (int i = 0; i < previousObjects.length; i++) { + VirtualCullableObject previousObject = previousObjects[i]; + if (previousObject != null) { + previousObject.setCullable(renderer); + } else { + trackedBy.get(i).addTrackedBlockEntity(pos, renderer); + } + } } } } else { + /* + * + * 全新方块实体 + * + */ for (int i = 0; i < elements.length; i++) { elements[i] = renderers[i].create(wrappedWorld, pos); } if (hasTrackedBy) { - if (Config.enableEntityCulling()) { - for (Player player : trackedBy) { + for (Player player : trackedBy) { + if (Config.enableEntityCulling()) { player.addTrackedBlockEntity(pos, renderer); - } - } else { - for (Player player : trackedBy) { + } else { renderer.show(player); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java index 44af9f41b..fef5e547d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/VirtualCullableObject.java @@ -4,7 +4,7 @@ import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.world.Cullable; public class VirtualCullableObject { - public final Cullable cullable; + public Cullable cullable; public boolean isShown; public VirtualCullableObject(Cullable cullable) { @@ -12,6 +12,10 @@ public class VirtualCullableObject { this.isShown = false; } + public void setCullable(Cullable cullable) { + this.cullable = cullable; + } + public Cullable cullable() { return cullable; } diff --git a/gradle.properties b/gradle.properties index 3691ff1e9..46db05f12 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings -project_version=0.0.65.14 +project_version=0.0.65.15 config_version=60 lang_version=41 project_group=net.momirealms From ac6d9f03907dbeea9ccf1ac0a79df91e56e00e07 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sun, 30 Nov 2025 16:16:25 +0800 Subject: [PATCH 047/135] Update ItemBlockEntityElementConfig.java --- .../entity/renderer/element/ItemBlockEntityElementConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java index b68c2af5d..01151fab5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java @@ -29,8 +29,8 @@ public class ItemBlockEntityElementConfig implements BlockEntityElementConfig { List dataValues = new ArrayList<>(); - ItemEntityData.Item.addEntityDataIfNotDefaultValue(item.apply(player).getLiteralObject(), dataValues); - ItemEntityData.NoGravity.addEntityDataIfNotDefaultValue(true, dataValues); + ItemEntityData.Item.addEntityData(item.apply(player).getLiteralObject(), dataValues); + ItemEntityData.NoGravity.addEntityData(true, dataValues); return dataValues; }; } From 1fcf903036dfab3768b93f538ff2fb25befff0f3 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Mon, 1 Dec 2025 00:29:30 +0800 Subject: [PATCH 048/135] Update BukkitNetworkManager.java --- .../bukkit/plugin/network/BukkitNetworkManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 8ea7189f7..54482047e 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 @@ -2217,7 +2217,9 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes if (Config.entityCullingRayTracing()) { SectionPos sectionPos = SectionPos.of(sPos); ClientChunk trackedChunk = user.getTrackedChunk(sectionPos.asChunkPos().longKey); - clientSection = trackedChunk.sectionById(sectionPos.y); + if (trackedChunk != null) { + clientSection = trackedChunk.sectionById(sectionPos.y); + } } for (int i = 0; i < blocks; i++) { From cf18f40c795333e2cca343ce3564c765c31c2b61 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 05:03:32 +0800 Subject: [PATCH 049/135] =?UTF-8?q?=E6=9B=B4=E8=B4=B4=E5=88=87=E5=8E=9F?= =?UTF-8?q?=E7=89=88=E7=9A=84snbt=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/plugin/BukkitPlatform.java | 23 - .../src/main/resources/translations/en.yml | 26 + .../src/main/resources/translations/zh_cn.yml | 26 + .../item/modifier/ComponentsModifier.java | 10 +- .../craftengine/core/plugin/Platform.java | 4 - .../config/template/ArgumentString.java | 11 +- .../craftengine/core/util/MiscUtils.java | 4 + .../craftengine/core/util/SNBTReader.java | 289 ------ .../core/util/snbt/SnbtGrammar.java | 865 ++++++++++++++++++ .../core/util/snbt/SnbtOperations.java | 93 ++ .../craftengine/core/util/snbt/TagParser.java | 87 ++ .../core/util/snbt/parse/Atom.java | 15 + .../util/snbt/parse/CachedParseState.java | 253 +++++ .../core/util/snbt/parse/Control.java | 18 + .../util/snbt/parse/DelayedException.java | 19 + .../core/util/snbt/parse/Dictionary.java | 92 ++ .../core/util/snbt/parse/ErrorCollector.java | 98 ++ .../core/util/snbt/parse/ErrorEntry.java | 4 + .../LocalizedCommandSyntaxException.java | 84 ++ .../LocalizedDynamicCommandExceptionType.java | 28 + .../util/snbt/parse/LocalizedMessage.java | 53 ++ .../LocalizedSimpleCommandExceptionType.java | 25 + .../core/util/snbt/parse/NamedRule.java | 7 + .../core/util/snbt/parse/ParseState.java | 42 + .../core/util/snbt/parse/Rule.java | 54 ++ .../core/util/snbt/parse/Scope.java | 316 +++++++ .../util/snbt/parse/SuggestionSupplier.java | 11 + .../core/util/snbt/parse/Term.java | 237 +++++ .../core/util/snbt/parse/grammar/Grammar.java | 53 ++ .../parse/grammar/GreedyPatternParseRule.java | 34 + .../grammar/GreedyPredicateParseRule.java | 49 + .../parse/grammar/NumberRunParseRule.java | 47 + .../grammar/StringReaderParserState.java | 29 + .../snbt/parse/grammar/StringReaderTerms.java | 65 ++ .../grammar/UnquotedStringParseRule.java | 33 + gradle.properties | 2 +- 36 files changed, 2786 insertions(+), 320 deletions(-) delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Atom.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Control.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorEntry.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedMessage.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedSimpleCommandExceptionType.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/NamedRule.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java 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 9a6729b0a..21bd8791a 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 @@ -31,34 +31,11 @@ public class BukkitPlatform implements Platform { Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), command); } - @SuppressWarnings("unchecked") - @Override - public Object snbtToJava(String nbt) { - try { - Object tag = FastNMS.INSTANCE.method$TagParser$parseCompoundFully("{\"root\":" + nbt + "}"); - Map map = (Map) MRegistryOps.NBT.convertTo(MRegistryOps.JAVA, tag); - return map.get("root"); - } catch (CommandSyntaxException e) { - throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e, nbt); - } - } - @Override public Tag jsonToSparrowNBT(JsonElement json) { return MRegistryOps.JSON.convertTo(MRegistryOps.SPARROW_NBT, json); } - @Override - public Tag snbtToSparrowNBT(String nbt) { - try { - Object tag = FastNMS.INSTANCE.method$TagParser$parseCompoundFully("{\"root\":" + nbt + "}"); - CompoundTag map = (CompoundTag) MRegistryOps.NBT.convertTo(MRegistryOps.SPARROW_NBT, tag); - return map.get("root"); - } catch (CommandSyntaxException e) { - throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e, nbt); - } - } - @Override public Tag javaToSparrowNBT(Object object) { return MRegistryOps.JAVA.convertTo(MRegistryOps.SPARROW_NBT, object); diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index fce2f7ea7..63419843e 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -90,6 +90,32 @@ warning.config.type.vec3d: "Issue found in file - Failed to load warning.config.type.map: "Issue found in file - Failed to load '': Cannot cast '' to Map type for option ''." warning.config.type.aabb: "Issue found in file - Failed to load '': Cannot cast '' to AABB type for option ''." warning.config.type.snbt.invalid_syntax: "Issue found in file - Failed to load '': Invalid snbt syntax ''." +warning.config.type.snbt.invalid_syntax.parse_error: " at position : " +warning.config.type.snbt.invalid_syntax.here: "<--[HERE]" +warning.config.type.snbt.parser.expected_string_uuid: "Expected a string representing a valid UUID" +warning.config.type.snbt.parser.expected_number_or_boolean: "Expected a number or a boolean" +warning.config.type.snbt.parser.trailing: "Unexpected trailing data" +warning.config.type.snbt.parser.expected.compound: "Expected compound tag" +warning.config.type.snbt.parser.number_parse_failure: "Failed to parse number: " +warning.config.type.snbt.parser.expected_hex_escape: "Expected a character literal of length " +warning.config.type.snbt.parser.invalid_codepoint: "Invalid Unicode character value: " +warning.config.type.snbt.parser.no_such_operation: "No such operation: " +warning.config.type.snbt.parser.expected_integer_type: "Expected an integer number" +warning.config.type.snbt.parser.expected_float_type: "Expected a floating point number" +warning.config.type.snbt.parser.expected_non_negative_number: "Expected a non-negative number" +warning.config.type.snbt.parser.invalid_character_name: "Invalid Unicode character name" +warning.config.type.snbt.parser.invalid_array_element_type: "Invalid array element type" +warning.config.type.snbt.parser.invalid_unquoted_start: "Unquoted strings can't start with digits 0-9, + or -" +warning.config.type.snbt.parser.expected_unquoted_string: "Expected a valid unquoted string" +warning.config.type.snbt.parser.invalid_string_contents: "Invalid string contents" +warning.config.type.snbt.parser.expected_binary_numeral: "Expected a binary number" +warning.config.type.snbt.parser.underscore_not_allowed: "Underscore is not allowed in binary numerals" +warning.config.type.snbt.parser.expected_decimal_numeral: "Expected a decimal number" +warning.config.type.snbt.parser.expected_hex_numeral: "Expected a hexadecimal number" +warning.config.type.snbt.parser.empty_key: "Key cannot be empty" +warning.config.type.snbt.parser.leading_zero_not_allowed: "Decimal numbers can't start with 0" +warning.config.type.snbt.parser.infinity_not_allowed: "Non-finite numbers are not allowed" +warning.config.type.snbt.parser.incorrect: "Expected literal " warning.config.number.missing_type: "Issue found in file - The config '' is missing the required 'type' argument for number argument." warning.config.number.invalid_type: "Issue found in file - The config '' is using an invalid number argument type ''." warning.config.number.missing_argument: "Issue found in file - The config '' is missing the argument for 'number'." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index da06eeff2..160e44580 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -88,6 +88,32 @@ warning.config.type.vector3f: "在文件 发现问题 - 无法 warning.config.type.vec3d: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为双精度浮点数三维向量类型 (选项 '')" warning.config.type.map: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为映射类型 (选项 '')" warning.config.type.snbt.invalid_syntax: "在文件 发现问题 - 无法加载 '': 无效的 SNBT 语法 ''" +warning.config.type.snbt.invalid_syntax.parse_error: ", 位于第 个字符: " +warning.config.type.snbt.invalid_syntax.here: "<--[此处]" +warning.config.type.snbt.parser.expected_string_uuid: "应为表示有效 UUID 的字符串" +warning.config.type.snbt.parser.expected_number_or_boolean: "应为数字或布尔型" +warning.config.type.snbt.parser.trailing: "多余的尾随数据" +warning.config.type.snbt.parser.expected.compound: "应为复合标签" +warning.config.type.snbt.parser.number_parse_failure: "解析数字失败: " +warning.config.type.snbt.parser.expected_hex_escape: "字符字面量长度应为 " +warning.config.type.snbt.parser.invalid_codepoint: "无效的 Unicode 字符码位: " +warning.config.type.snbt.parser.no_such_operation: "不存在的操作: " +warning.config.type.snbt.parser.expected_integer_type: "应为整数" +warning.config.type.snbt.parser.expected_float_type: "应为浮点数" +warning.config.type.snbt.parser.expected_non_negative_number: "应为非负数" +warning.config.type.snbt.parser.invalid_character_name: "无效的 Unicode 字符名称" +warning.config.type.snbt.parser.invalid_array_element_type: "无效的数组元素类型" +warning.config.type.snbt.parser.invalid_unquoted_start: "无引号字符串不能以数字 0-9、+ 或 - 开头" +warning.config.type.snbt.parser.expected_unquoted_string: "应为有效的无引号字符串" +warning.config.type.snbt.parser.invalid_string_contents: "无效的字符串内容" +warning.config.type.snbt.parser.expected_binary_numeral: "应为二进制数" +warning.config.type.snbt.parser.underscore_not_allowed: "数字的开头和结尾不允许使用下划线字符" +warning.config.type.snbt.parser.expected_decimal_numeral: "应为十进制数" +warning.config.type.snbt.parser.expected_hex_numeral: "应为十六进制数" +warning.config.type.snbt.parser.empty_key: "键不能为空" +warning.config.type.snbt.parser.leading_zero_not_allowed: "十进制数不能以 0 开头" +warning.config.type.snbt.parser.infinity_not_allowed: "不允许使用非有限数的数值" +warning.config.type.snbt.parser.incorrect: "应为字面量 " warning.config.number.missing_type: "在文件 发现问题 - 配置项 '' 缺少数字类型所需的 'type' 参数" warning.config.number.invalid_type: "在文件 发现问题 - 配置项 '' 使用了无效的数字类型 ''" warning.config.number.missing_argument: "在文件 发现问题 - 配置项 '' 缺少数字参数" diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java index 143e4572f..113f33324 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java @@ -1,12 +1,15 @@ package net.momirealms.craftengine.core.item.modifier; import com.google.gson.JsonElement; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.momirealms.craftengine.core.item.*; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Pair; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.snbt.TagParser; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.Tag; @@ -41,7 +44,12 @@ public class ComponentsModifier implements ItemDataModifier { if (string.startsWith("(json) ")) { return CraftEngine.instance().platform().jsonToSparrowNBT(GsonHelper.get().fromJson(string.substring("(json) ".length()), JsonElement.class)); } else if (string.startsWith("(snbt) ")) { - return CraftEngine.instance().platform().snbtToSparrowNBT(string.substring("(snbt) ".length())); + String snbt = string.substring("(snbt) ".length()); + try { + return TagParser.parseCompoundFully(snbt); + } catch (CommandSyntaxException e) { + throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e.getMessage()); + } } } return CraftEngine.instance().platform().javaToSparrowNBT(value); 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 6a8ec531b..1195d6904 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 @@ -10,12 +10,8 @@ public interface Platform { void dispatchCommand(String command); - Object snbtToJava(String nbt); - Tag jsonToSparrowNBT(JsonElement json); - Tag snbtToSparrowNBT(String nbt); - Tag javaToSparrowNBT(Object object); World getWorld(String name); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java index f3846da86..15f821b76 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java @@ -1,8 +1,9 @@ package net.momirealms.craftengine.core.plugin.config.template; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.momirealms.craftengine.core.plugin.config.template.argument.TemplateArgument; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.SNBTReader; +import net.momirealms.craftengine.core.util.snbt.TagParser; import java.util.ArrayList; import java.util.List; @@ -67,8 +68,14 @@ public interface ArgumentString { } else { this.placeholder = placeholderContent.substring(0, separatorIndex); String defaultValueString = placeholderContent.substring(separatorIndex + 2); + Object parsed; try { - this.defaultValue = ((TemplateManagerImpl) TemplateManager.INSTANCE).preprocessUnknownValue(new SNBTReader(defaultValueString).deserializeAsJava()); + parsed = TagParser.parseObjectFully(defaultValueString); + } catch (CommandSyntaxException e) { + throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e.getMessage()); + } + try { + this.defaultValue = ((TemplateManagerImpl) TemplateManager.INSTANCE).preprocessUnknownValue(parsed); } catch (LocalizedResourceConfigException e) { e.appendTailArgument(this.placeholder); throw e; 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 523bbc364..6f327817a 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 @@ -415,4 +415,8 @@ public class MiscUtils { } return false; } + + public static int growByHalf(int value, int minValue) { + return (int) Math.max(Math.min((long) value + (value >> 1), 2147483639L), minValue); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java deleted file mode 100644 index 0be81388d..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java +++ /dev/null @@ -1,289 +0,0 @@ -package net.momirealms.craftengine.core.util; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; - -public final class SNBTReader extends DefaultStringReader { - private static final char COMPOUND_START = '{'; - private static final char COMPOUND_END = '}'; - private static final char LIST_START = '['; - private static final char LIST_END = ']'; - private static final char STRING_DELIMITER = '"'; - private static final char SINGLE_QUOTES = '\''; - private static final char DOUBLE_QUOTES = '"'; - private static final char KEY_VALUE_SEPARATOR = ':'; - private static final char ELEMENT_SEPARATOR = ','; - - private static final char ARRAY_DELIMITER = ';'; - private static final char BYTE_ARRAY = 'b'; - private static final char INT_ARRAY = 'i'; - private static final char LONG_ARRAY = 'l'; - - public SNBTReader(String content) { - super(content); - } - - public Object deserializeAsJava() { - Object result = this.parseValue(); - this.skipWhitespace(); - if (getCursor() != getTotalLength()) - throw new IllegalArgumentException("Extra content at end: " + substring(getCursor(), getTotalLength())); - return result; - } - - // 开始解析, 步进字符. - private Object parseValue() { - skipWhitespace(); - return switch (peek()) { - case COMPOUND_START -> parseCompound(); - case LIST_START -> parseList(); - case DOUBLE_QUOTES -> { - skip(); - yield readStringUntil(DOUBLE_QUOTES); - } - case SINGLE_QUOTES -> { - skip(); - yield readStringUntil(SINGLE_QUOTES); - } - default -> parsePrimitive(); - }; - } - - // 解析包小肠 {} - private Map parseCompound() { - skip(); // 跳过 '{' - skipWhitespace(); - - Map compoundMap = new LinkedHashMap<>(); - - if (canRead() && peek() != COMPOUND_END) { - do { - String key = parseKey(); - if (!canRead() || peek() != KEY_VALUE_SEPARATOR) { - throw new IllegalArgumentException("Expected ':' at position " + getCursor()); - } - skip(); // 跳过 ':' - Object value = parseValue(); - compoundMap.put(key, value); - skipWhitespace(); - } while (canRead() && peek() == ELEMENT_SEPARATOR && ++super.cursor > 0 /* 跳过 ',' */); - } - - if (!canRead() || peek() != COMPOUND_END) { - throw new IllegalArgumentException("Expected '}' at position " + getCursor()); - } - skip(); // 跳过 '}' - return compoundMap; - } - - // 解析列表值 [1, 2, 3] - private Object parseList() { - skip(); // 跳过 '[' - skipWhitespace(); - - // 检查接下来的2个非空格字符, 确认是否要走数组解析. - if (canRead()) { - setMarker(cursor); // 记录指针, 尝试解析数组. - char typeChar = Character.toLowerCase(peek()); - if (typeChar == BYTE_ARRAY || typeChar == INT_ARRAY || typeChar == LONG_ARRAY) { - skip(); - skipWhitespace(); - if (canRead() && peek() == ARRAY_DELIMITER) { // 下一个必须是 ';' - skip(); - switch (typeChar) { // 解析并返回数组喵 - case BYTE_ARRAY -> { - return parseArray(list -> { - byte[] bytes = new byte[list.size()]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = list.get(i).byteValue(); - } - return bytes; - }); - } - case INT_ARRAY -> { - return parseArray(list -> { - int[] ints = new int[list.size()]; - for (int i = 0; i < ints.length; i++) { - ints[i] = list.get(i).intValue(); - } - return ints; - }); - } - case LONG_ARRAY -> { - return parseArray(list -> { - long[] longs = new long[list.size()]; - for (int i = 0; i < longs.length; i++) { - longs[i] = list.get(i).longValue(); - } - return longs; - }); - } - } - } - } - restore(); // 复原指针. - } - - List elementList = new ArrayList<>(); - - if (canRead() && peek() != LIST_END) { - do { - elementList.add(parseValue()); - skipWhitespace(); - } while (canRead() && peek() == ELEMENT_SEPARATOR && ++super.cursor > 0 /* 跳过 ',' */); - } - - if (!canRead() || peek() != LIST_END) { - throw new IllegalArgumentException("Expected ']' at position " + getCursor()); - } - skip(); // 跳过 ']' - return elementList; - } - - // 解析数组 [I; 11, 41, 54] - // ArrayType -> B, I, L. - private Object parseArray(Function, Object> convertor) { - skipWhitespace(); - // 用来暂存解析出的数字 - List elements = new ArrayList<>(); - if (canRead() && peek() != LIST_END) { - do { - Object element = parseValue(); - - // 1.21.6的SNBT原版是支持 {key:[B;1,2b,0xFF]} 这种奇葩写法的, 越界部分会被自动舍弃, 如0xff的byte值为-1. - // 如果需要和原版对齐, 那么只需要判断是否是数字就行了. - // if (!(element instanceof Number number)) - // throw new IllegalArgumentException("Error element type at pos " + getCursor()); - if (!(element instanceof Number number)) - throw new IllegalArgumentException("Error parsing number at pos " + getCursor()); - - elements.add(number); // 校验通过后加入 - skipWhitespace(); - } while (canRead() && peek() == ELEMENT_SEPARATOR && ++cursor > 0 /* 跳过 ',' */); - } - - if (!canRead() || peek() != LIST_END) - throw new IllegalArgumentException("Expected ']' at position " + getCursor()); - skip(); // 跳过 ']' - return convertor.apply(elements); - } - - // 解析Key值 - private String parseKey() { - skipWhitespace(); - if (!canRead()) { - throw new IllegalArgumentException("Unterminated key at " + getCursor()); - } - - // 如果有双引号就委托给string解析处理. - char peek = peek(); - if (peek == STRING_DELIMITER) { - skip(); - return readStringUntil(STRING_DELIMITER); - } else if (peek == SINGLE_QUOTES) { - skip(); - return readStringUntil(SINGLE_QUOTES); - } - - int start = getCursor(); - while (canRead()) { - char c = peek(); - if (c == ' ') break; // 忽略 key 后面的空格, { a :1} 应当解析成 {a:1} - if (Character.isJavaIdentifierPart(c)) skip(); else break; - } - - String key = substring(start, getCursor()); - skipWhitespace(); // 跳过 key 后面的空格. - return key; - } - - // 解析原生值 - private Object parsePrimitive() { - // 先解析获取值的长度 - int tokenStart = getCursor(); - int lastWhitespace = 0; // 记录值末尾的空格数量,{a:炒鸡 大保健} 和 {a: 炒鸡 大保健 } 都应解析成 "炒鸡 大保健". - boolean contentHasWhitespace = false; // 记录值中有没有空格. - while (canRead()) { - char c = peek(); - if (c == ',' || c == ']' || c == '}') break; - skip(); - if (c == ' ') { - lastWhitespace++; // 遇到空格先增加值, 代表值尾部空格数量. - continue; - } - if (lastWhitespace > 0) { - lastWhitespace = 0; // 遇到正常字符时清空记录的尾部空格数. - contentHasWhitespace = true; - } - } - int tokenLength = getCursor() - tokenStart - lastWhitespace; // 计算值长度需要再减去尾部空格. - if (tokenLength == 0) return null; // 如果值长度为0则返回null. - if (contentHasWhitespace) return substring(tokenStart, tokenStart + tokenLength); // 如果值的中间有空格, 一定是字符串, 可直接返回. - - // 布尔值检查 - if (tokenLength == 4) { - if (matchesAt(tokenStart, "true")) return Boolean.TRUE; - if (matchesAt(tokenStart, "null")) return null; // 支持 {key:null}. - } else if (tokenLength == 5) { - if (matchesAt(tokenStart, "false")) return Boolean.FALSE; - } - if (tokenLength > 1) { - // 至少有1个字符,给了后缀的可能性 - char lastChar = charAt(tokenStart + tokenLength - 1); - try { - switch (lastChar) { - case 'b', 'B' -> { - return Byte.parseByte(substring(tokenStart, tokenStart + tokenLength - 1)); - } - case 's', 'S' -> { - return Short.parseShort(substring(tokenStart, tokenStart + tokenLength - 1)); - } - case 'l', 'L' -> { - return Long.parseLong(substring(tokenStart, tokenStart + tokenLength - 1)); - } - case 'f', 'F' -> { - return Float.parseFloat(substring(tokenStart, tokenStart + tokenLength)); - } - case 'd', 'D' -> { - return Double.parseDouble(substring(tokenStart, tokenStart + tokenLength)); - } - default -> { - String fullString = substring(tokenStart, tokenStart + tokenLength); - try { - double d = Double.parseDouble(fullString); - if (d % 1 != 0 || fullString.contains(".") || fullString.contains("e")) { - return d; - } else { - return (int) d; - } - } catch (NumberFormatException e) { - return fullString; - } - } - } - } catch (NumberFormatException e) { - return substring(tokenStart, tokenStart + tokenLength); - } - } else { - char onlyChar = charAt(tokenStart); - if (isNumber(onlyChar)) { - return onlyChar - '0'; - } else { - return String.valueOf(onlyChar); - } - } - } - - // 工具函数: 快速检查布尔值字符串匹配, 忽略大小写. - private boolean matchesAt(int start, String target) { - for (int i = 0; i < target.length(); i++) { - char c1 = charAt(start + i); - char c2 = target.charAt(i); - if (c1 != c2 && c1 != (c2 ^ 32)) return false; // 忽略大小写比较 - } - return true; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java new file mode 100644 index 000000000..ad96f56e9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -0,0 +1,865 @@ +package net.momirealms.craftengine.core.util.snbt; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.JavaOps; +import it.unimi.dsi.fastutil.bytes.ByteArrayList; +import it.unimi.dsi.fastutil.bytes.ByteList; +import it.unimi.dsi.fastutil.chars.CharList; +import net.momirealms.craftengine.core.util.snbt.parse.*; +import net.momirealms.craftengine.core.util.snbt.parse.Dictionary; +import net.momirealms.craftengine.core.util.snbt.parse.grammar.*; + +import javax.annotation.Nullable; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.Map.Entry; +import java.util.regex.Pattern; +import java.util.stream.IntStream; +import java.util.stream.LongStream; + +public class SnbtGrammar { + private static final DynamicCommandExceptionType ERROR_NUMBER_PARSE_FAILURE = new LocalizedDynamicCommandExceptionType( + number -> new LocalizedMessage("warning.config.type.snbt.parser.number_parse_failure", String.valueOf(number)) + ); + static final DynamicCommandExceptionType ERROR_EXPECTED_HEX_ESCAPE = new LocalizedDynamicCommandExceptionType( + length -> new LocalizedMessage("warning.config.type.snbt.parser.expected_hex_escape", String.valueOf(length)) + ); + private static final DynamicCommandExceptionType ERROR_INVALID_CODEPOINT = new LocalizedDynamicCommandExceptionType( + codepoint -> new LocalizedMessage("warning.config.type.snbt.parser.invalid_codepoint", String.valueOf(codepoint)) + ); + private static final DynamicCommandExceptionType ERROR_NO_SUCH_OPERATION = new LocalizedDynamicCommandExceptionType( + operation -> new LocalizedMessage("warning.config.type.snbt.parser.no_such_operation", String.valueOf(operation)) + ); + static final DelayedException ERROR_EXPECTED_INTEGER_TYPE = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_integer_type")) + ); + private static final DelayedException ERROR_EXPECTED_FLOAT_TYPE = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_float_type")) + ); + static final DelayedException ERROR_EXPECTED_NON_NEGATIVE_NUMBER = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_non_negative_number")) + ); + private static final DelayedException ERROR_INVALID_CHARACTER_NAME = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.invalid_character_name")) + ); + static final DelayedException ERROR_INVALID_ARRAY_ELEMENT_TYPE = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.invalid_array_element_type")) + ); + private static final DelayedException ERROR_INVALID_UNQUOTED_START = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.invalid_unquoted_start")) + ); + private static final DelayedException ERROR_EXPECTED_UNQUOTED_STRING = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_unquoted_string")) + ); + private static final DelayedException ERROR_INVALID_STRING_CONTENTS = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.invalid_string_contents")) + ); + private static final DelayedException ERROR_EXPECTED_BINARY_NUMERAL = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_binary_numeral")) + ); + private static final DelayedException ERROR_UNDERSCORE_NOT_ALLOWED = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.underscore_not_allowed")) + ); + private static final DelayedException ERROR_EXPECTED_DECIMAL_NUMERAL = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_decimal_numeral")) + ); + private static final DelayedException ERROR_EXPECTED_HEX_NUMERAL = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("Expected a hexadecimal number")) + ); + private static final DelayedException ERROR_EMPTY_KEY = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.empty_key")) + ); + private static final DelayedException ERROR_LEADING_ZERO_NOT_ALLOWED = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.leading_zero_not_allowed")) + ); + private static final DelayedException ERROR_INFINITY_NOT_ALLOWED = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.infinity_not_allowed")) + ); + private static final NumberRunParseRule BINARY_NUMERAL = new NumberRunParseRule(ERROR_EXPECTED_BINARY_NUMERAL, ERROR_UNDERSCORE_NOT_ALLOWED) { + @Override + protected boolean isAccepted(char c) { + return switch (c) { + case '0', '1', '_' -> true; + default -> false; + }; + } + }; + private static final NumberRunParseRule DECIMAL_NUMERAL = new NumberRunParseRule(ERROR_EXPECTED_DECIMAL_NUMERAL, ERROR_UNDERSCORE_NOT_ALLOWED) { + @Override + protected boolean isAccepted(char c) { + return switch (c) { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_' -> true; + default -> false; + }; + } + }; + private static final NumberRunParseRule HEX_NUMERAL = new NumberRunParseRule(ERROR_EXPECTED_HEX_NUMERAL, ERROR_UNDERSCORE_NOT_ALLOWED) { + @Override + protected boolean isAccepted(char c) { + return switch (c) { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', '_', 'a', 'b', 'c', 'd', 'e', 'f' -> true; + default -> false; + }; + } + }; + private static final GreedyPredicateParseRule PLAIN_STRING_CHUNK = new GreedyPredicateParseRule(1, ERROR_INVALID_STRING_CONTENTS) { + @Override + protected boolean isAccepted(char c) { + return switch (c) { + case '"', '\'', '\\' -> false; + default -> true; + }; + } + }; + private static final StringReaderTerms.TerminalCharacters NUMBER_LOOKEAHEAD = new StringReaderTerms.TerminalCharacters(CharList.of()) { + @Override + protected boolean isAccepted(char c) { + return canStartNumber(c); + } + }; + private static final Pattern UNICODE_NAME = Pattern.compile("[-a-zA-Z0-9 ]+"); + + static DelayedException createNumberParseError(NumberFormatException numberFormatException) { + return DelayedException.create(ERROR_NUMBER_PARSE_FAILURE, numberFormatException.getMessage()); + } + + private static boolean isAllowedToStartUnquotedString(char c) { + return !canStartNumber(c); + } + + static boolean canStartNumber(char c) { + return switch (c) { + case '+', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> true; + default -> false; + }; + } + + static boolean needsUnderscoreRemoval(String text) { + return text.indexOf(95) != -1; + } + + private static void cleanAndAppend(StringBuilder stringBuilder, String text) { + cleanAndAppend(stringBuilder, text, needsUnderscoreRemoval(text)); + } + + static void cleanAndAppend(StringBuilder stringBuilder, String text, boolean removeUnderscores) { + if (removeUnderscores) { + for (char c : text.toCharArray()) { + if (c != '_') { + stringBuilder.append(c); + } + } + } + stringBuilder.append(text); + } + + static short parseUnsignedShort(String text, int radix) { + int i = Integer.parseInt(text, radix); + if (i >> 16 == 0) { + return (short)i; + } + throw new NumberFormatException("out of range: " + i); + } + + @Nullable + private static T createFloat( + DynamicOps ops, + Sign sign, + @Nullable String wholePart, + @Nullable String fractionPart, + @Nullable Signed exponentPart, + @Nullable TypeSuffix suffix, + ParseState parseState + ) { + StringBuilder stringBuilder = new StringBuilder(); + sign.append(stringBuilder); + if (wholePart != null) { + cleanAndAppend(stringBuilder, wholePart); + } + + if (fractionPart != null) { + stringBuilder.append('.'); + cleanAndAppend(stringBuilder, fractionPart); + } + + if (exponentPart != null) { + stringBuilder.append('e'); + exponentPart.sign().append(stringBuilder); + cleanAndAppend(stringBuilder, exponentPart.value); + } + + try { + String string = stringBuilder.toString(); + + return switch (suffix) { + case null -> convertDouble(ops, parseState, string); + case FLOAT -> convertFloat(ops, parseState, string); + case DOUBLE -> convertDouble(ops, parseState, string); + default -> { + parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_FLOAT_TYPE); + yield null; + } + }; + } catch (NumberFormatException var11) { + parseState.errorCollector().store(parseState.mark(), createNumberParseError(var11)); + return null; + } + } + + @Nullable + private static T convertFloat(DynamicOps ops, ParseState parseState, String value) { + float f = Float.parseFloat(value); + if (!Float.isFinite(f)) { + parseState.errorCollector().store(parseState.mark(), ERROR_INFINITY_NOT_ALLOWED); + return null; + } + return ops.createFloat(f); + } + + @Nullable + private static T convertDouble(DynamicOps ops, ParseState parseState, String value) { + double d = Double.parseDouble(value); + if (!Double.isFinite(d)) { + parseState.errorCollector().store(parseState.mark(), ERROR_INFINITY_NOT_ALLOWED); + return null; + } + return ops.createDouble(d); + } + + private static String joinList(List list) { + return switch (list.size()) { + case 0 -> ""; + case 1 -> list.getFirst(); + default -> String.join("", list); + }; + } + + @SuppressWarnings("unchecked") + public static Grammar createParser(DynamicOps ops) { + T object = ops.createBoolean(true); + T object1 = ops.createBoolean(false); + T object2 = ops.emptyMap(); + T object3 = ops.emptyList(); + Dictionary dictionary = new Dictionary<>(); + Atom atom = Atom.of("sign"); + dictionary.put( + atom, + Term.alternative( + Term.sequence(StringReaderTerms.character('+'), Term.marker(atom, Sign.PLUS)), + Term.sequence(StringReaderTerms.character('-'), Term.marker(atom, Sign.MINUS)) + ), + scope -> scope.getOrThrow(atom) + ); + Atom atom1 = Atom.of("integer_suffix"); + dictionary.put( + atom1, + Term.alternative( + Term.sequence( + StringReaderTerms.characters('u', 'U'), + Term.alternative( + Term.sequence( + StringReaderTerms.characters('b', 'B'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.BYTE)) + ), + Term.sequence( + StringReaderTerms.characters('s', 'S'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.SHORT)) + ), + Term.sequence( + StringReaderTerms.characters('i', 'I'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.INT)) + ), + Term.sequence( + StringReaderTerms.characters('l', 'L'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.LONG)) + ) + ) + ), + Term.sequence( + StringReaderTerms.characters('s', 'S'), + Term.alternative( + Term.sequence( + StringReaderTerms.characters('b', 'B'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.BYTE)) + ), + Term.sequence( + StringReaderTerms.characters('s', 'S'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.SHORT)) + ), + Term.sequence( + StringReaderTerms.characters('i', 'I'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.INT)) + ), + Term.sequence( + StringReaderTerms.characters('l', 'L'), + Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.LONG)) + ) + ) + ), + Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.BYTE))), + Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.SHORT))), + Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.INT))), + Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.LONG))) + ), + scope -> scope.getOrThrow(atom1) + ); + Atom atom2 = Atom.of("binary_numeral"); + dictionary.put(atom2, BINARY_NUMERAL); + Atom atom3 = Atom.of("decimal_numeral"); + dictionary.put(atom3, DECIMAL_NUMERAL); + Atom atom4 = Atom.of("hex_numeral"); + dictionary.put(atom4, HEX_NUMERAL); + Atom atom5 = Atom.of("integer_literal"); + NamedRule namedRule = dictionary.put( + atom5, + Term.sequence( + Term.optional(dictionary.named(atom)), + Term.alternative( + Term.sequence( + StringReaderTerms.character('0'), + Term.cut(), + Term.alternative( + Term.sequence(StringReaderTerms.characters('x', 'X'), Term.cut(), dictionary.named(atom4)), + Term.sequence(StringReaderTerms.characters('b', 'B'), dictionary.named(atom2)), + Term.sequence(dictionary.named(atom3), Term.cut(), Term.fail(ERROR_LEADING_ZERO_NOT_ALLOWED)), + Term.marker(atom3, "0") + ) + ), + dictionary.named(atom3) + ), + Term.optional(dictionary.named(atom1)) + ), + scope -> { + IntegerSuffix integerSuffix = scope.getOrDefault(atom1, IntegerSuffix.EMPTY); + Sign sign = scope.getOrDefault(atom, Sign.PLUS); + String string = scope.get(atom3); + if (string != null) { + return new IntegerLiteral(sign, Base.DECIMAL, string, integerSuffix); + } + String string1 = scope.get(atom4); + if (string1 != null) { + return new IntegerLiteral(sign, Base.HEX, string1, integerSuffix); + } + String string2 = scope.getOrThrow(atom2); + return new IntegerLiteral(sign, Base.BINARY, string2, integerSuffix); + } + ); + Atom atom6 = Atom.of("float_type_suffix"); + dictionary.put( + atom6, + Term.alternative( + Term.sequence(StringReaderTerms.characters('f', 'F'), Term.marker(atom6, TypeSuffix.FLOAT)), + Term.sequence(StringReaderTerms.characters('d', 'D'), Term.marker(atom6, TypeSuffix.DOUBLE)) + ), + scope -> scope.getOrThrow(atom6) + ); + Atom> atom7 = Atom.of("float_exponent_part"); + dictionary.put( + atom7, + Term.sequence(StringReaderTerms.characters('e', 'E'), Term.optional(dictionary.named(atom)), dictionary.named(atom3)), + scope -> new Signed<>(scope.getOrDefault(atom, Sign.PLUS), scope.getOrThrow(atom3)) + ); + Atom atom8 = Atom.of("float_whole_part"); + Atom atom9 = Atom.of("float_fraction_part"); + Atom atom10 = Atom.of("float_literal"); + dictionary.putComplex( + atom10, + Term.sequence( + Term.optional(dictionary.named(atom)), + Term.alternative( + Term.sequence( + dictionary.namedWithAlias(atom3, atom8), + StringReaderTerms.character('.'), + Term.cut(), + Term.optional(dictionary.namedWithAlias(atom3, atom9)), + Term.optional(dictionary.named(atom7)), + Term.optional(dictionary.named(atom6)) + ), + Term.sequence( + StringReaderTerms.character('.'), + Term.cut(), + dictionary.namedWithAlias(atom3, atom9), + Term.optional(dictionary.named(atom7)), + Term.optional(dictionary.named(atom6)) + ), + Term.sequence(dictionary.namedWithAlias(atom3, atom8), dictionary.named(atom7), Term.cut(), Term.optional(dictionary.named(atom6))), + Term.sequence(dictionary.namedWithAlias(atom3, atom8), Term.optional(dictionary.named(atom7)), dictionary.named(atom6)) + ) + ), + parseState -> { + Scope scope = parseState.scope(); + Sign sign = scope.getOrDefault(atom, Sign.PLUS); + String string = scope.get(atom8); + String string1 = scope.get(atom9); + Signed signed = scope.get(atom7); + TypeSuffix typeSuffix = scope.get(atom6); + return createFloat(ops, sign, string, string1, signed, typeSuffix, parseState); + } + ); + Atom atom11 = Atom.of("string_hex_2"); + dictionary.put(atom11, new SimpleHexLiteralParseRule(2)); + Atom atom12 = Atom.of("string_hex_4"); + dictionary.put(atom12, new SimpleHexLiteralParseRule(4)); + Atom atom13 = Atom.of("string_hex_8"); + dictionary.put(atom13, new SimpleHexLiteralParseRule(8)); + Atom atom14 = Atom.of("string_unicode_name"); + dictionary.put(atom14, new GreedyPatternParseRule(UNICODE_NAME, ERROR_INVALID_CHARACTER_NAME)); + Atom atom15 = Atom.of("string_escape_sequence"); + dictionary.putComplex( + atom15, + Term.alternative( + Term.sequence(StringReaderTerms.character('b'), Term.marker(atom15, "\b")), + Term.sequence(StringReaderTerms.character('s'), Term.marker(atom15, " ")), + Term.sequence(StringReaderTerms.character('t'), Term.marker(atom15, "\t")), + Term.sequence(StringReaderTerms.character('n'), Term.marker(atom15, "\n")), + Term.sequence(StringReaderTerms.character('f'), Term.marker(atom15, "\f")), + Term.sequence(StringReaderTerms.character('r'), Term.marker(atom15, "\r")), + Term.sequence(StringReaderTerms.character('\\'), Term.marker(atom15, "\\")), + Term.sequence(StringReaderTerms.character('\''), Term.marker(atom15, "'")), + Term.sequence(StringReaderTerms.character('"'), Term.marker(atom15, "\"")), + Term.sequence(StringReaderTerms.character('x'), dictionary.named(atom11)), + Term.sequence(StringReaderTerms.character('u'), dictionary.named(atom12)), + Term.sequence(StringReaderTerms.character('U'), dictionary.named(atom13)), + Term.sequence(StringReaderTerms.character('N'), StringReaderTerms.character('{'), dictionary.named(atom14), StringReaderTerms.character('}')) + ), + parseState -> { + Scope scope = parseState.scope(); + String string = scope.getAny(atom15); + if (string != null) { + return string; + } + String string1 = scope.getAny(atom11, atom12, atom13); + if (string1 != null) { + int i = HexFormat.fromHexDigits(string1); + if (!Character.isValidCodePoint(i)) { + parseState.errorCollector() + .store(parseState.mark(), DelayedException.create(ERROR_INVALID_CODEPOINT, String.format(Locale.ROOT, "U+%08X", i))); + return null; + } + return Character.toString(i); + } + String string2 = scope.getOrThrow(atom14); + + int i1; + try { + i1 = Character.codePointOf(string2); + } catch (IllegalArgumentException var12x) { + parseState.errorCollector().store(parseState.mark(), ERROR_INVALID_CHARACTER_NAME); + return null; + } + + return Character.toString(i1); + } + ); + Atom atom16 = Atom.of("string_plain_contents"); + dictionary.put(atom16, PLAIN_STRING_CHUNK); + Atom> atom17 = Atom.of("string_chunks"); + Atom atom18 = Atom.of("string_contents"); + Atom atom19 = Atom.of("single_quoted_string_chunk"); + NamedRule namedRule1 = dictionary.put( + atom19, + Term.alternative( + dictionary.namedWithAlias(atom16, atom18), + Term.sequence(StringReaderTerms.character('\\'), dictionary.namedWithAlias(atom15, atom18)), + Term.sequence(StringReaderTerms.character('"'), Term.marker(atom18, "\"")) + ), + scope -> scope.getOrThrow(atom18) + ); + Atom atom20 = Atom.of("single_quoted_string_contents"); + dictionary.put(atom20, Term.repeated(namedRule1, atom17), scope -> joinList(scope.getOrThrow(atom17))); + Atom atom21 = Atom.of("double_quoted_string_chunk"); + NamedRule namedRule2 = dictionary.put( + atom21, + Term.alternative( + dictionary.namedWithAlias(atom16, atom18), + Term.sequence(StringReaderTerms.character('\\'), dictionary.namedWithAlias(atom15, atom18)), + Term.sequence(StringReaderTerms.character('\''), Term.marker(atom18, "'")) + ), + scope -> scope.getOrThrow(atom18) + ); + Atom atom22 = Atom.of("double_quoted_string_contents"); + dictionary.put(atom22, Term.repeated(namedRule2, atom17), scope -> joinList(scope.getOrThrow(atom17))); + Atom atom23 = Atom.of("quoted_string_literal"); + dictionary.put( + atom23, + Term.alternative( + Term.sequence( + StringReaderTerms.character('"'), Term.cut(), Term.optional(dictionary.namedWithAlias(atom22, atom18)), StringReaderTerms.character('"') + ), + Term.sequence(StringReaderTerms.character('\''), Term.optional(dictionary.namedWithAlias(atom20, atom18)), StringReaderTerms.character('\'')) + ), + scope -> scope.getOrThrow(atom18) + ); + Atom atom24 = Atom.of("unquoted_string"); + dictionary.put(atom24, new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING)); + Atom atom25 = Atom.of("literal"); + Atom> atom26 = Atom.of("arguments"); + dictionary.put( + atom26, Term.repeatedWithTrailingSeparator(dictionary.forward(atom25), atom26, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom26) + ); + Atom atom27 = Atom.of("unquoted_string_or_builtin"); + dictionary.putComplex( + atom27, + Term.sequence( + dictionary.named(atom24), + Term.optional(Term.sequence(StringReaderTerms.character('('), dictionary.named(atom26), StringReaderTerms.character(')'))) + ), + parseState -> { + Scope scope = parseState.scope(); + String string = scope.getOrThrow(atom24); + if (!string.isEmpty() && isAllowedToStartUnquotedString(string.charAt(0))) { + List list = scope.get(atom26); + if (list != null) { + SnbtOperations.BuiltinKey builtinKey = new SnbtOperations.BuiltinKey(string, list.size()); + SnbtOperations.BuiltinOperation builtinOperation = SnbtOperations.BUILTIN_OPERATIONS.get(builtinKey); + if (builtinOperation != null) { + return builtinOperation.run(ops, list, parseState); + } + parseState.errorCollector().store(parseState.mark(), DelayedException.create(ERROR_NO_SUCH_OPERATION, builtinKey.toString())); + return null; + } else if (string.equalsIgnoreCase("true")) { + return object; + } else if (string.equalsIgnoreCase("false")) { + return object1; + } else if (string.equalsIgnoreCase("null")) { + return Objects.requireNonNullElseGet(ops.empty(), () -> { + T nullString = ops.createString("null"); + if ("null".equals(nullString)) { // 确定是 Java 类型的 + return (T) CachedParseState.JAVA_NULL_VALUE_MARKER; + } + return nullString; + }); + } + return ops.createString(string); + } + parseState.errorCollector().store(parseState.mark(), SnbtOperations.BUILTIN_IDS, ERROR_INVALID_UNQUOTED_START); + return null; + } + ); + Atom atom28 = Atom.of("map_key"); + dictionary.put(atom28, Term.alternative(dictionary.named(atom23), dictionary.named(atom24)), scope -> scope.getAnyOrThrow(atom23, atom24)); + Atom> atom29 = Atom.of("map_entry"); + NamedRule> namedRule3 = dictionary.putComplex( + atom29, Term.sequence(dictionary.named(atom28), StringReaderTerms.character(TagParser.NAME_VALUE_SEPARATOR), dictionary.named(atom25)), parseState -> { + Scope scope = parseState.scope(); + String string = scope.getOrThrow(atom28); + if (string.isEmpty()) { + parseState.errorCollector().store(parseState.mark(), ERROR_EMPTY_KEY); + return null; + } + T orThrow = scope.getOrThrow(atom25); + return Map.entry(string, orThrow); + } + ); + Atom>> atom30 = Atom.of("map_entries"); + dictionary.put(atom30, Term.repeatedWithTrailingSeparator(namedRule3, atom30, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom30)); + Atom atom31 = Atom.of("map_literal"); + dictionary.put(atom31, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), dictionary.named(atom30), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { // Paper - track depth + List> list = scope.getOrThrow(atom30); + if (list.isEmpty()) { + return object2; + } else { + Builder builder = ImmutableMap.builderWithExpectedSize(list.size()); + + for (Entry entry : list) { + builder.put(ops.createString(entry.getKey()), entry.getValue()); + } + + return ops.createMap(builder.buildKeepingLast()); + } + }); + Atom> atom32 = Atom.of("list_entries"); + dictionary.put( + atom32, Term.repeatedWithTrailingSeparator(dictionary.forward(atom25), atom32, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom32) + ); + Atom atom33 = Atom.of("array_prefix"); + dictionary.put( + atom33, + Term.alternative( + Term.sequence(StringReaderTerms.character('B'), Term.marker(atom33, ArrayPrefix.BYTE)), + Term.sequence(StringReaderTerms.character('L'), Term.marker(atom33, ArrayPrefix.LONG)), + Term.sequence(StringReaderTerms.character('I'), Term.marker(atom33, ArrayPrefix.INT)) + ), + scope -> scope.getOrThrow(atom33) + ); + Atom> atom34 = Atom.of("int_array_entries"); + dictionary.put(atom34, Term.repeatedWithTrailingSeparator(namedRule, atom34, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom34)); + Atom atom35 = Atom.of("list_literal"); + dictionary.putComplex( + atom35, + Term.sequence( + StringReaderTerms.character('['), + Scope.increaseDepth(), + Term.alternative(Term.sequence(dictionary.named(atom33), StringReaderTerms.character(';'), dictionary.named(atom34)), dictionary.named(atom32)), + Scope.decreaseDepth(), + StringReaderTerms.character(']') + ), + parseState -> { + Scope scope = parseState.scope(); + ArrayPrefix arrayPrefix = scope.get(atom33); + if (arrayPrefix != null) { + List list = scope.getOrThrow(atom34); + return list.isEmpty() ? arrayPrefix.create(ops) : arrayPrefix.create(ops, list, parseState); + } + List list = scope.getOrThrow(atom32); + return list.isEmpty() ? object3 : ops.createList(list.stream()); + } + ); + NamedRule namedRule4 = dictionary.putComplex( + atom25, + Term.alternative( + Term.sequence(Term.positiveLookahead(NUMBER_LOOKEAHEAD), Term.alternative(dictionary.namedWithAlias(atom10, atom25), dictionary.named(atom5))), + Term.sequence(Term.positiveLookahead(StringReaderTerms.characters('"', '\'')), Term.cut(), dictionary.named(atom23)), + Term.sequence(Term.positiveLookahead(StringReaderTerms.character('{')), Term.cut(), dictionary.namedWithAlias(atom31, atom25)), + Term.sequence(Term.positiveLookahead(StringReaderTerms.character('[')), Term.cut(), dictionary.namedWithAlias(atom35, atom25)), + dictionary.namedWithAlias(atom27, atom25) + ), + parseState -> { + Scope scope = parseState.scope(); + String string = scope.get(atom23); + if (string != null) { + return ops.createString(string); + } + IntegerLiteral integerLiteral = scope.get(atom5); + return integerLiteral != null ? integerLiteral.create(ops, parseState) : scope.getOrThrow(atom25); + } + ); + return new Grammar<>(dictionary, namedRule4); + } + + enum ArrayPrefix { + BYTE(TypeSuffix.BYTE) { + private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap(new byte[0]); + + @Override + public T create(DynamicOps ops) { + return ops.createByteList(EMPTY_BUFFER); + } + + @Nullable + @Override + public T create(DynamicOps ops, List values, ParseState parseState) { + ByteList list = new ByteArrayList(); + + for (IntegerLiteral integerLiteral : values) { + Number number = this.buildNumber(integerLiteral, parseState); + if (number == null) { + return null; + } + + list.add(number.byteValue()); + } + + return ops.createByteList(ByteBuffer.wrap(list.toByteArray())); + } + }, + INT(TypeSuffix.INT, TypeSuffix.BYTE, TypeSuffix.SHORT) { + @Override + public T create(DynamicOps ops) { + return ops.createIntList(IntStream.empty()); + } + + @Nullable + @Override + public T create(DynamicOps ops, List values, ParseState parseState) { + IntStream.Builder builder = IntStream.builder(); + + for (IntegerLiteral integerLiteral : values) { + Number number = this.buildNumber(integerLiteral, parseState); + if (number == null) { + return null; + } + + builder.add(number.intValue()); + } + + return ops.createIntList(builder.build()); + } + }, + LONG(TypeSuffix.LONG, TypeSuffix.BYTE, TypeSuffix.SHORT, TypeSuffix.INT) { + @Override + public T create(DynamicOps ops) { + return ops.createLongList(LongStream.empty()); + } + + @Nullable + @Override + public T create(DynamicOps ops, List values, ParseState parseState) { + LongStream.Builder builder = LongStream.builder(); + + for (IntegerLiteral integerLiteral : values) { + Number number = this.buildNumber(integerLiteral, parseState); + if (number == null) { + return null; + } + + builder.add(number.longValue()); + } + + return ops.createLongList(builder.build()); + } + }; + + private final TypeSuffix defaultType; + private final Set additionalTypes; + + ArrayPrefix(final TypeSuffix defaultType, final TypeSuffix... additionalTypes) { + this.additionalTypes = Set.of(additionalTypes); + this.defaultType = defaultType; + } + + public boolean isAllowed(TypeSuffix suffix) { + return suffix == this.defaultType || this.additionalTypes.contains(suffix); + } + + public abstract T create(DynamicOps ops); + + @Nullable + public abstract T create(DynamicOps ops, List values, ParseState parseState); + + @Nullable + protected Number buildNumber(IntegerLiteral value, ParseState parseState) { + TypeSuffix typeSuffix = this.computeType(value.suffix); + if (typeSuffix == null) { + parseState.errorCollector().store(parseState.mark(), ERROR_INVALID_ARRAY_ELEMENT_TYPE); + return null; + } + return (Number)value.create(JavaOps.INSTANCE, typeSuffix, parseState); + } + + @Nullable + private TypeSuffix computeType(IntegerSuffix suffix) { + TypeSuffix typeSuffix = suffix.type(); + if (typeSuffix == null) { + return this.defaultType; + } + return !this.isAllowed(typeSuffix) ? null : typeSuffix; + } + } + + enum Base { + BINARY, + DECIMAL, + HEX + } + + record IntegerLiteral(Sign sign, Base base, String digits, IntegerSuffix suffix) { + private SignedPrefix signedOrDefault() { + return Objects.requireNonNullElseGet(this.suffix.signed, () -> switch (this.base) { + case BINARY, HEX -> SignedPrefix.UNSIGNED; + case DECIMAL -> SignedPrefix.SIGNED; + }); + } + + private String cleanupDigits(Sign sign) { + boolean flag = needsUnderscoreRemoval(this.digits); + if (sign != Sign.MINUS && !flag) { + return this.digits; + } + StringBuilder stringBuilder = new StringBuilder(); + sign.append(stringBuilder); + cleanAndAppend(stringBuilder, this.digits, flag); + return stringBuilder.toString(); + } + + @Nullable + public T create(DynamicOps ops, ParseState parseState) { + return this.create(ops, Objects.requireNonNullElse(this.suffix.type, TypeSuffix.INT), parseState); + } + + @Nullable + public T create(DynamicOps ops, TypeSuffix typeSuffix, ParseState parseState) { + boolean flag = this.signedOrDefault() == SignedPrefix.SIGNED; + if (!flag && this.sign == Sign.MINUS) { + parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_NON_NEGATIVE_NUMBER); + return null; + } + String string = this.cleanupDigits(this.sign); + + int i = switch (this.base) { + case BINARY -> 2; + case DECIMAL -> 10; + case HEX -> 16; + }; + + try { + if (flag) { + return switch (typeSuffix) { + case BYTE -> ops.createByte(Byte.parseByte(string, i)); + case SHORT -> ops.createShort(Short.parseShort(string, i)); + case INT -> ops.createInt(Integer.parseInt(string, i)); + case LONG -> ops.createLong(Long.parseLong(string, i)); + default -> { + parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_INTEGER_TYPE); + yield null; + } + }; + } + return switch (typeSuffix) { + case BYTE -> ops.createByte(com.google.common.primitives.UnsignedBytes.parseUnsignedByte(string, i)); + case SHORT -> ops.createShort(parseUnsignedShort(string, i)); + case INT -> ops.createInt(Integer.parseUnsignedInt(string, i)); + case LONG -> ops.createLong(Long.parseUnsignedLong(string, i)); + default -> { + parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_INTEGER_TYPE); + yield null; + } + }; + } catch (NumberFormatException var8) { + parseState.errorCollector().store(parseState.mark(), createNumberParseError(var8)); + return null; + } + } + } + + record IntegerSuffix(@Nullable SignedPrefix signed, @Nullable TypeSuffix type) { + public static final IntegerSuffix EMPTY = new IntegerSuffix(null, null); + } + + enum Sign { + PLUS, + MINUS; + + public void append(StringBuilder stringBuilder) { + if (this == MINUS) { + stringBuilder.append("-"); + } + } + } + + record Signed(Sign sign, T value) { + } + + enum SignedPrefix { + SIGNED, + UNSIGNED + } + + static class SimpleHexLiteralParseRule extends GreedyPredicateParseRule { + public SimpleHexLiteralParseRule(int minSize) { + super(minSize, minSize, DelayedException.create(ERROR_EXPECTED_HEX_ESCAPE, String.valueOf(minSize))); + } + + @Override + protected boolean isAccepted(char c) { + return switch (c) { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f' -> true; + default -> false; + }; + } + } + + enum TypeSuffix { + FLOAT, + DOUBLE, + BYTE, + SHORT, + INT, + LONG + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java new file mode 100644 index 000000000..9c684725d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java @@ -0,0 +1,93 @@ +package net.momirealms.craftengine.core.util.snbt; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.serialization.DynamicOps; +import net.momirealms.craftengine.core.util.snbt.parse.*; +import net.momirealms.sparrow.nbt.util.UUIDUtil; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public class SnbtOperations { + static final DelayedException ERROR_EXPECTED_STRING_UUID = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_string_uuid")) + ); + static final DelayedException ERROR_EXPECTED_NUMBER_OR_BOOLEAN = DelayedException.create( + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_number_or_boolean")) + ); + public static final String BUILTIN_TRUE = "true"; + public static final String BUILTIN_FALSE = "false"; + public static final Map BUILTIN_OPERATIONS = Map.of( + new BuiltinKey("bool", 1), new BuiltinOperation() { + @Override + public T run(DynamicOps ops, List args, ParseState parseState) { + Boolean bool = convert(ops, args.getFirst()); + if (bool == null) { + parseState.errorCollector().store(parseState.mark(), SnbtOperations.ERROR_EXPECTED_NUMBER_OR_BOOLEAN); + return null; + } else { + return ops.createBoolean(bool); + } + } + + @Nullable + private static Boolean convert(DynamicOps ops, T value) { + Optional optional = ops.getBooleanValue(value).result(); + if (optional.isPresent()) { + return optional.get(); + } else { + Optional optional1 = ops.getNumberValue(value).result(); + return optional1.isPresent() ? optional1.get().doubleValue() != 0.0 : null; + } + } + }, new BuiltinKey("uuid", 1), new BuiltinOperation() { + @Override + public T run(DynamicOps ops, List args, ParseState parseState) { + Optional optional = ops.getStringValue(args.getFirst()).result(); + if (optional.isEmpty()) { + parseState.errorCollector().store(parseState.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID); + return null; + } else { + UUID uuid; + try { + uuid = UUID.fromString(optional.get()); + } catch (IllegalArgumentException var7) { + parseState.errorCollector().store(parseState.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID); + return null; + } + + return ops.createIntList(IntStream.of(UUIDUtil.uuidToIntArray(uuid))); + } + } + } + ); + public static final SuggestionSupplier BUILTIN_IDS = new SuggestionSupplier<>() { + private final Set keys = Stream.concat( + Stream.of(BUILTIN_FALSE, BUILTIN_TRUE), SnbtOperations.BUILTIN_OPERATIONS.keySet().stream().map(BuiltinKey::id) + ) + .collect(Collectors.toSet()); + + @Override + public Stream possibleValues(ParseState parseState) { + return this.keys.stream(); + } + }; + + public record BuiltinKey(String id, int argCount) { + @Override + public @NotNull String toString() { + return this.id + "/" + this.argCount; + } + } + + public interface BuiltinOperation { + @Nullable + T run(DynamicOps ops, List args, ParseState parseState); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java new file mode 100644 index 000000000..6c7badbf9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java @@ -0,0 +1,87 @@ +package net.momirealms.craftengine.core.util.snbt; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.JavaOps; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.util.snbt.parse.LocalizedMessage; +import net.momirealms.craftengine.core.util.snbt.parse.LocalizedSimpleCommandExceptionType; +import net.momirealms.craftengine.core.util.snbt.parse.grammar.Grammar; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.Tag; +import net.momirealms.sparrow.nbt.codec.LegacyJavaOps; +import net.momirealms.sparrow.nbt.codec.LegacyNBTOps; +import net.momirealms.sparrow.nbt.codec.NBTOps; + +public class TagParser { + public static final SimpleCommandExceptionType ERROR_TRAILING_DATA = new LocalizedSimpleCommandExceptionType( + new LocalizedMessage("warning.config.type.snbt.parser.trailing") + ); + public static final SimpleCommandExceptionType ERROR_EXPECTED_COMPOUND = new LocalizedSimpleCommandExceptionType( + new LocalizedMessage("warning.config.type.snbt.parser.expected.compound") + ); + public static final char ELEMENT_SEPARATOR = ','; + public static final char NAME_VALUE_SEPARATOR = ':'; + private static final TagParser NBT_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? NBTOps.INSTANCE : LegacyNBTOps.INSTANCE); + private static final TagParser JAVA_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? JavaOps.INSTANCE : LegacyJavaOps.INSTANCE); + private final DynamicOps ops; + private final Grammar grammar; + + private TagParser(DynamicOps ops, Grammar grammar) { + this.ops = ops; + this.grammar = grammar; + } + + public DynamicOps ops() { + return this.ops; + } + + public static TagParser create(DynamicOps ops) { + return new TagParser<>(ops, SnbtGrammar.createParser(ops)); + } + + private static CompoundTag castToCompoundOrThrow(StringReader reader, Tag tag) throws CommandSyntaxException { + if (tag instanceof CompoundTag compoundTag) { + return compoundTag; + } + throw ERROR_EXPECTED_COMPOUND.createWithContext(reader); + } + + public static CompoundTag parseCompoundFully(String data) throws CommandSyntaxException { + StringReader stringReader = new StringReader(data); + return parseCompoundAsArgument(stringReader); + } + + public static Object parseObjectFully(String data) throws CommandSyntaxException { + StringReader stringReader = new StringReader(data); + return parseObjectAsArgument(stringReader); + } + + public T parseFully(String text) throws CommandSyntaxException { + return this.parseFully(new StringReader(text)); + } + + public T parseFully(StringReader reader) throws CommandSyntaxException { + T object = this.grammar.parse(reader); + reader.skipWhitespace(); + if (reader.canRead()) { + throw ERROR_TRAILING_DATA.createWithContext(reader); + } + return object; + } + + public T parseAsArgument(StringReader reader) throws CommandSyntaxException { + return this.grammar.parse(reader); + } + + public static CompoundTag parseCompoundAsArgument(StringReader reader) throws CommandSyntaxException { + Tag tag = NBT_OPS_PARSER.parseAsArgument(reader); + return castToCompoundOrThrow(reader, tag); + } + + public static Object parseObjectAsArgument(StringReader reader) throws CommandSyntaxException { + return JAVA_OPS_PARSER.parseAsArgument(reader); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Atom.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Atom.java new file mode 100644 index 000000000..1601e947b --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Atom.java @@ -0,0 +1,15 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import org.jetbrains.annotations.NotNull; + +@SuppressWarnings("unused") +public record Atom(String name) { + @Override + public @NotNull String toString() { + return "<" + this.name + ">"; + } + + public static Atom of(String name) { + return new Atom<>(name); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java new file mode 100644 index 000000000..03c439c82 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java @@ -0,0 +1,253 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import net.momirealms.craftengine.core.util.MiscUtils; + +import javax.annotation.Nullable; + +@SuppressWarnings("unchecked") +public abstract class CachedParseState implements ParseState { + private PositionCache[] positionCache = new PositionCache[256]; + private final ErrorCollector errorCollector; + private final Scope scope = new Scope(); + private SimpleControl[] controlCache = new SimpleControl[16]; + private int nextControlToReturn; + private final Silent silent = new Silent(); + private final IntList markedNull = new IntArrayList(); + public static final Object JAVA_NULL_VALUE_MARKER = new Object() { + @Override + public String toString() { + return "null"; + } + }; + + protected CachedParseState(ErrorCollector errorCollector) { + this.errorCollector = errorCollector; + } + + @Override + public Scope scope() { + return this.scope; + } + + @Override + public ErrorCollector errorCollector() { + return this.errorCollector; + } + + @Nullable + @Override + public T parse(NamedRule rule) { + int i = this.mark(); + PositionCache cacheForPosition = this.getCacheForPosition(i); + int i1 = cacheForPosition.findKeyIndex(rule.name()); + if (i1 != -1) { + CacheEntry value = cacheForPosition.getValue(i1); + if (value != null) { + if (value == CachedParseState.CacheEntry.NEGATIVE) { + return null; + } + this.restore(value.markAfterParse); + return value.value; + } + } else { + i1 = cacheForPosition.allocateNewEntry(rule.name()); + } + + T object = rule.value().parse(this); + CacheEntry cacheEntry; + if (object == null) { + cacheEntry = (CacheEntry) CacheEntry.NEGATIVE; + } else { + cacheEntry = new CacheEntry<>(object, this.mark()); + } + + cacheForPosition.setValue(i1, cacheEntry); + return object; + } + + private PositionCache getCacheForPosition(int position) { + int i = this.positionCache.length; + if (position >= i) { + int i1 = MiscUtils.growByHalf(i, position + 1); + PositionCache[] positionCaches = new PositionCache[i1]; + System.arraycopy(this.positionCache, 0, positionCaches, 0, i); + this.positionCache = positionCaches; + } + + PositionCache positionCache = this.positionCache[position]; + if (positionCache == null) { + positionCache = new PositionCache(); + this.positionCache[position] = positionCache; + } + + return positionCache; + } + + @Override + public Control acquireControl() { + int i = this.controlCache.length; + if (this.nextControlToReturn >= i) { + int i1 = MiscUtils.growByHalf(i, this.nextControlToReturn + 1); + SimpleControl[] simpleControls = new SimpleControl[i1]; + System.arraycopy(this.controlCache, 0, simpleControls, 0, i); + this.controlCache = simpleControls; + } + + int i1 = this.nextControlToReturn++; + SimpleControl simpleControl = this.controlCache[i1]; + if (simpleControl == null) { + simpleControl = new SimpleControl(); + this.controlCache[i1] = simpleControl; + } else { + simpleControl.reset(); + } + + return simpleControl; + } + + @Override + public void releaseControl() { + this.nextControlToReturn--; + } + + @Override + public ParseState silent() { + return this.silent; + } + + @Override + public void markNull(int mark) { + this.markedNull.add(mark); + } + + @Override + public boolean isNull(int mark) { + return this.markedNull.contains(mark); + } + + record CacheEntry(@Nullable T value, int markAfterParse) { + public static final CacheEntry NEGATIVE = new CacheEntry<>(null, -1); + } + + static class PositionCache { + public static final int ENTRY_STRIDE = 2; + private static final int NOT_FOUND = -1; + private Object[] atomCache = new Object[16]; + private int nextKey; + + public int findKeyIndex(Atom atom) { + for (int i = 0; i < this.nextKey; i += ENTRY_STRIDE) { + if (this.atomCache[i] == atom) { + return i; + } + } + + return NOT_FOUND; + } + + public int allocateNewEntry(Atom entry) { + int i = this.nextKey; + this.nextKey += 2; + int i1 = i + 1; + int i2 = this.atomCache.length; + if (i1 >= i2) { + int i3 = MiscUtils.growByHalf(i2, i1 + 1); + Object[] objects = new Object[i3]; + System.arraycopy(this.atomCache, 0, objects, 0, i2); + this.atomCache = objects; + } + + this.atomCache[i] = entry; + return i; + } + + @Nullable + public CacheEntry getValue(int index) { + return (CacheEntry)this.atomCache[index + 1]; + } + + public void setValue(int index, CacheEntry value) { + this.atomCache[index + 1] = value; + } + } + + class Silent implements ParseState { + private final ErrorCollector silentCollector = new ErrorCollector.Nop<>(); + + @Override + public ErrorCollector errorCollector() { + return this.silentCollector; + } + + @Override + public Scope scope() { + return CachedParseState.this.scope(); + } + + @Nullable + @Override + public T parse(NamedRule rule) { + return CachedParseState.this.parse(rule); + } + + @Override + public S input() { + return CachedParseState.this.input(); + } + + @Override + public int mark() { + return CachedParseState.this.mark(); + } + + @Override + public void restore(int cursor) { + CachedParseState.this.restore(cursor); + } + + @Override + public Control acquireControl() { + return CachedParseState.this.acquireControl(); + } + + @Override + public void releaseControl() { + CachedParseState.this.releaseControl(); + } + + @Override + public ParseState silent() { + return this; + } + + @Override + public void markNull(int mark) { + CachedParseState.this.markNull(mark); + } + + @Override + public boolean isNull(int mark) { + return CachedParseState.this.isNull(mark); + } + } + + static class SimpleControl implements Control { + private boolean hasCut; + + @Override + public void cut() { + this.hasCut = true; + } + + @Override + public boolean hasCut() { + return this.hasCut; + } + + public void reset() { + this.hasCut = false; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Control.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Control.java new file mode 100644 index 000000000..b17ce13ec --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Control.java @@ -0,0 +1,18 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +public interface Control { + Control UNBOUND = new Control() { + @Override + public void cut() { + } + + @Override + public boolean hasCut() { + return false; + } + }; + + void cut(); + + boolean hasCut(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java new file mode 100644 index 000000000..ee79b450c --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java @@ -0,0 +1,19 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import net.momirealms.craftengine.core.util.snbt.parse.grammar.StringReaderTerms; + +@FunctionalInterface +public interface DelayedException { + T create(String message, int cursor); + + static DelayedException create(SimpleCommandExceptionType exception) { + return (message, cursor) -> exception.createWithContext(StringReaderTerms.createReader(message, cursor)); + } + + static DelayedException create(DynamicCommandExceptionType exception, String argument) { + return (message, cursor) -> exception.createWithContext(StringReaderTerms.createReader(message, cursor), argument); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java new file mode 100644 index 000000000..20d6c4bb2 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java @@ -0,0 +1,92 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import javax.annotation.Nullable; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; + +@SuppressWarnings("unchecked") +public class Dictionary { + private final Map, Entry> terms = new IdentityHashMap<>(); + + public NamedRule put(Atom name, Rule rule) { + Entry entry = (Entry)this.terms.computeIfAbsent(name, Entry::new); + if (entry.value != null) { + throw new IllegalArgumentException("Trying to override rule: " + name); + } else { + entry.value = rule; + return entry; + } + } + + public NamedRule putComplex(Atom name, Term term, Rule.RuleAction ruleAction) { + return this.put(name, Rule.fromTerm(term, ruleAction)); + } + + public NamedRule put(Atom name, Term term, Rule.SimpleRuleAction ruleAction) { + return this.put(name, Rule.fromTerm(term, ruleAction)); + } + + public void checkAllBound() { + List> list = this.terms.entrySet().stream().filter(entry -> entry.getValue() == null).map(Map.Entry::getKey).toList(); + if (!list.isEmpty()) { + throw new IllegalStateException("Unbound names: " + list); + } + } + + public NamedRule forward(Atom name) { + return this.getOrCreateEntry(name); + } + + private Entry getOrCreateEntry(Atom name) { + return (Entry)this.terms.computeIfAbsent(name, Entry::new); + } + + public Term named(Atom name) { + return new Reference<>(this.getOrCreateEntry(name), name); + } + + public Term namedWithAlias(Atom name, Atom alias) { + return new Reference<>(this.getOrCreateEntry(name), alias); + } + + static class Entry implements NamedRule, Supplier { + private final Atom name; + @Nullable + Rule value; + + private Entry(Atom name) { + this.name = name; + } + + @Override + public Atom name() { + return this.name; + } + + @Override + public Rule value() { + return Objects.requireNonNull(this.value, this); + } + + @Override + public String get() { + return "Unbound rule " + this.name; + } + } + + record Reference(Entry ruleToParse, Atom nameToStore) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + T object = parseState.parse(this.ruleToParse); + if (object == null) { + return false; + } else { + scope.put(this.nameToStore, object); + return true; + } + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java new file mode 100644 index 000000000..ef0aac7e3 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java @@ -0,0 +1,98 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import net.momirealms.craftengine.core.util.MiscUtils; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("unchecked") +public interface ErrorCollector { + void store(int cursor, SuggestionSupplier suggestions, Object reason); + + default void store(int cursor, Object reason) { + this.store(cursor, SuggestionSupplier.empty(), reason); + } + + void finish(int cursor); + + class LongestOnly implements ErrorCollector { + private MutableErrorEntry[] entries = new MutableErrorEntry[16]; + private int nextErrorEntry; + private int lastCursor = -1; + + private void discardErrorsFromShorterParse(int cursor) { + if (cursor > this.lastCursor) { + this.lastCursor = cursor; + this.nextErrorEntry = 0; + } + } + + @Override + public void finish(int cursor) { + this.discardErrorsFromShorterParse(cursor); + } + + @Override + public void store(int cursor, SuggestionSupplier suggestions, Object reason) { + this.discardErrorsFromShorterParse(cursor); + if (cursor == this.lastCursor) { + this.addErrorEntry(suggestions, reason); + } + } + + private void addErrorEntry(SuggestionSupplier suggestions, Object reason) { + int i = this.entries.length; + if (this.nextErrorEntry >= i) { + int i1 = MiscUtils.growByHalf(i, this.nextErrorEntry + 1); + MutableErrorEntry[] mutableErrorEntrys = new MutableErrorEntry[i1]; + System.arraycopy(this.entries, 0, mutableErrorEntrys, 0, i); + this.entries = mutableErrorEntrys; + } + + int i1 = this.nextErrorEntry++; + MutableErrorEntry mutableErrorEntry = this.entries[i1]; + if (mutableErrorEntry == null) { + mutableErrorEntry = new MutableErrorEntry<>(); + this.entries[i1] = mutableErrorEntry; + } + + mutableErrorEntry.suggestions = suggestions; + mutableErrorEntry.reason = reason; + } + + public List> entries() { + int i = this.nextErrorEntry; + if (i == 0) { + return List.of(); + } else { + List> list = new ArrayList<>(i); + + for (int i1 = 0; i1 < i; i1++) { + MutableErrorEntry mutableErrorEntry = this.entries[i1]; + list.add(new ErrorEntry<>(this.lastCursor, mutableErrorEntry.suggestions, mutableErrorEntry.reason)); + } + + return list; + } + } + + public int cursor() { + return this.lastCursor; + } + + static class MutableErrorEntry { + SuggestionSupplier suggestions = SuggestionSupplier.empty(); + Object reason = "empty"; + } + } + + class Nop implements ErrorCollector { + @Override + public void store(int cursor, SuggestionSupplier suggestions, Object reason) { + } + + @Override + public void finish(int cursor) { + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorEntry.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorEntry.java new file mode 100644 index 000000000..dff487beb --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorEntry.java @@ -0,0 +1,4 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +public record ErrorEntry(int cursor, SuggestionSupplier suggestions, Object reason) { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java new file mode 100644 index 000000000..cb4912898 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java @@ -0,0 +1,84 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import com.mojang.brigadier.Message; +import com.mojang.brigadier.exceptions.CommandExceptionType; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.util.AdventureHelper; + +import java.util.Optional; +import java.util.function.Supplier; + +public class LocalizedCommandSyntaxException extends CommandSyntaxException { + public static final int CONTEXT_AMOUNT = 10; + public static final String PARSE_ERROR_NODE = "warning.config.type.snbt.invalid_syntax.parse_error"; + public static final String HERE_NODE = "warning.config.type.snbt.invalid_syntax.here"; + private final Message message; + private final String input; + private final int cursor; + + public LocalizedCommandSyntaxException(CommandExceptionType type, Message message) { + super(type, message); + this.message = message; + this.input = null; + this.cursor = -1; + } + + public LocalizedCommandSyntaxException(CommandExceptionType type, Message message, String input, int cursor) { + super(type, message, input, cursor); + this.message = message; + this.input = input; + this.cursor = cursor; + } + + @Override + public String getMessage() { + String message = this.message.getString(); + final String context = getContext(); + if (context == null) { + return message; + } + return generateLocalizedMessage( + PARSE_ERROR_NODE, + () -> message + " at position " + this.cursor + ": " + context, + message, String.valueOf(this.cursor), context + ); + } + + @Override + public String getContext() { + if (this.input == null || this.cursor < 0) { + return null; + } + final StringBuilder builder = new StringBuilder(); + final int cursor = Math.min(this.input.length(), this.cursor); + + if (cursor > CONTEXT_AMOUNT) { + builder.append("..."); + } + + builder.append(this.input, Math.max(0, cursor - CONTEXT_AMOUNT), cursor); + builder.append(generateLocalizedMessage(HERE_NODE, () -> "<--[HERE]")); + + return builder.toString(); + } + + + private String generateLocalizedMessage(String node, Supplier fallback, String... arguments) { + try { + String rawMessage = Optional.ofNullable(TranslationManager.instance() + .miniMessageTranslation(node)).orElse(fallback.get()); + String cleanMessage = AdventureHelper.miniMessage() + .stripTags(rawMessage); + for (int i = 0; i < arguments.length; i++) { + cleanMessage = cleanMessage.replace( + "", + arguments[i] != null ? arguments[i] : "null" + ); + } + return cleanMessage; + } catch (Exception e) { + return fallback.get(); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java new file mode 100644 index 000000000..ee80a3bee --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java @@ -0,0 +1,28 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import com.mojang.brigadier.ImmutableStringReader; +import com.mojang.brigadier.Message; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; + +import java.util.function.Function; + +public class LocalizedDynamicCommandExceptionType extends DynamicCommandExceptionType { + private final Function function; + + public LocalizedDynamicCommandExceptionType(Function function) { + super(function); + this.function = function; + } + + @Override + public CommandSyntaxException create(final Object arg) { + return new LocalizedCommandSyntaxException(this, function.apply(arg)); + } + + @Override + public CommandSyntaxException createWithContext(final ImmutableStringReader reader, final Object arg) { + return new LocalizedCommandSyntaxException(this, function.apply(arg), reader.getString(), reader.getCursor()); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedMessage.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedMessage.java new file mode 100644 index 000000000..c8da1cab7 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedMessage.java @@ -0,0 +1,53 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import com.mojang.brigadier.Message; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.util.AdventureHelper; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Optional; + +public class LocalizedMessage implements Message { + private final String node; + private final String[] arguments; + + public LocalizedMessage( + @NotNull String node, + @Nullable String... arguments + ) { + this.node = node; + this.arguments = arguments != null + ? Arrays.copyOf(arguments, arguments.length) + : new String[0]; + } + + @Override + public String getString() { + return generateLocalizedMessage(); + } + + private String generateLocalizedMessage() { + try { + String rawMessage = Optional.ofNullable(TranslationManager.instance() + .miniMessageTranslation(this.node)).orElse(this.node); + String cleanMessage = AdventureHelper.miniMessage() + .stripTags(rawMessage); + for (int i = 0; i < arguments.length; i++) { + cleanMessage = cleanMessage.replace( + "", + arguments[i] != null ? arguments[i] : "null" + ); + } + return cleanMessage; + } catch (Exception e) { + return String.format( + "Failed to translate. Node: %s, Arguments: %s. Cause: %s", + node, + Arrays.toString(arguments), + e.getMessage() + ); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedSimpleCommandExceptionType.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedSimpleCommandExceptionType.java new file mode 100644 index 000000000..b046fab0c --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedSimpleCommandExceptionType.java @@ -0,0 +1,25 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import com.mojang.brigadier.ImmutableStringReader; +import com.mojang.brigadier.Message; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; + +public class LocalizedSimpleCommandExceptionType extends SimpleCommandExceptionType { + private final Message message; + + public LocalizedSimpleCommandExceptionType(Message message) { + super(message); + this.message = message; + } + + @Override + public CommandSyntaxException create() { + return new LocalizedCommandSyntaxException(this, message); + } + + @Override + public CommandSyntaxException createWithContext(final ImmutableStringReader reader) { + return new LocalizedCommandSyntaxException(this, message, reader.getString(), reader.getCursor()); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/NamedRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/NamedRule.java new file mode 100644 index 000000000..d4f634e1d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/NamedRule.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +public interface NamedRule { + Atom name(); + + Rule value(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java new file mode 100644 index 000000000..5a631476b --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java @@ -0,0 +1,42 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import javax.annotation.Nullable; +import java.util.Optional; + +public interface ParseState { + Scope scope(); + + ErrorCollector errorCollector(); + + default Optional parseTopRule(NamedRule rule) { + T object = this.parse(rule); + if (object != null) { + this.errorCollector().finish(this.mark()); + } + + if (!this.scope().hasOnlySingleFrame()) { + throw new IllegalStateException("Malformed scope: " + this.scope()); + } else { + return Optional.ofNullable(object); + } + } + + @Nullable + T parse(NamedRule rule); + + S input(); + + int mark(); + + void restore(int cursor); + + Control acquireControl(); + + void releaseControl(); + + ParseState silent(); + + void markNull(int mark); + + boolean isNull(int mark); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java new file mode 100644 index 000000000..6a5728ec4 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java @@ -0,0 +1,54 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import javax.annotation.Nullable; + +public interface Rule { + @Nullable + T parse(ParseState parseState); + + static Rule fromTerm(Term child, RuleAction action) { + return new WrappedTerm<>(action, child); + } + + static Rule fromTerm(Term child, SimpleRuleAction action) { + return new WrappedTerm<>(action, child); + } + + @FunctionalInterface + interface RuleAction { + @Nullable + T run(ParseState parseState); + } + + @FunctionalInterface + interface SimpleRuleAction extends RuleAction { + T run(Scope scope); + + @Override + default T run(ParseState parseState) { + return this.run(parseState.scope()); + } + } + + record WrappedTerm(RuleAction action, Term child) implements Rule { + @Nullable + @Override + public T parse(ParseState parseState) { + Scope scope = parseState.scope(); + scope.pushFrame(); + + T var3; + try { + if (!this.child.parse(parseState, scope, Control.UNBOUND)) { + return null; + } + + var3 = this.action.run(parseState); + } finally { + scope.popFrame(); + } + + return var3; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java new file mode 100644 index 000000000..81ad7ecfd --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java @@ -0,0 +1,316 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import com.google.common.annotations.VisibleForTesting; +import net.momirealms.craftengine.core.util.MiscUtils; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +@SuppressWarnings("unchecked") +public final class Scope { + private static final int NOT_FOUND = -1; + private static final Object FRAME_START_MARKER = new Object() { + @Override + public String toString() { + return "frame"; + } + }; + private static final int ENTRY_STRIDE = 2; + private Object[] stack = new Object[128]; + private int topEntryKeyIndex = 0; + private int topMarkerKeyIndex = 0; + private int depth; + + public Scope() { + this.stack[0] = FRAME_START_MARKER; + this.stack[1] = null; + } + + private int valueIndex(Atom name) { + for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { + Object object = this.stack[i]; + + assert object instanceof Atom; + + if (object == name) { + return i + 1; + } + } + + return NOT_FOUND; + } + + public int valueIndexForAny(Atom... names) { + for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { + Object object = this.stack[i]; + + assert object instanceof Atom; + + for (Atom atom : names) { + if (atom == object) { + return i + 1; + } + } + } + + return NOT_FOUND; + } + + private void ensureCapacity(int requiredCapacitty) { + int i = this.stack.length; + int i1 = this.topEntryKeyIndex + 1; + int i2 = i1 + requiredCapacitty * 2; + if (i2 >= i) { + int i3 = MiscUtils.growByHalf(i, i2 + 1); + Object[] objects = new Object[i3]; + System.arraycopy(this.stack, 0, objects, 0, i); + this.stack = objects; + } + + assert this.validateStructure(); + } + + private void setupNewFrame() { + this.topEntryKeyIndex += ENTRY_STRIDE; + this.stack[this.topEntryKeyIndex] = FRAME_START_MARKER; + this.stack[this.topEntryKeyIndex + 1] = this.topMarkerKeyIndex; + this.topMarkerKeyIndex = this.topEntryKeyIndex; + } + + public void pushFrame() { + this.ensureCapacity(1); + this.setupNewFrame(); + + assert this.validateStructure(); + } + + private int getPreviousMarkerIndex(int markerIndex) { + return (Integer)this.stack[markerIndex + 1]; + } + + public void popFrame() { + assert this.topMarkerKeyIndex != 0; + + this.topEntryKeyIndex = this.topMarkerKeyIndex - ENTRY_STRIDE; + this.topMarkerKeyIndex = this.getPreviousMarkerIndex(this.topMarkerKeyIndex); + + assert this.validateStructure(); + } + + public void splitFrame() { + int i = this.topMarkerKeyIndex; + int i1 = (this.topEntryKeyIndex - this.topMarkerKeyIndex) / ENTRY_STRIDE; + this.ensureCapacity(i1 + 1); + this.setupNewFrame(); + int i2 = i + ENTRY_STRIDE; + int i3 = this.topEntryKeyIndex; + + for (int i4 = 0; i4 < i1; i4++) { + i3 += ENTRY_STRIDE; + Object object = this.stack[i2]; + + assert object != null; + + this.stack[i3] = object; + this.stack[i3 + 1] = null; + i2 += ENTRY_STRIDE; + } + + this.topEntryKeyIndex = i3; + + assert this.validateStructure(); + } + + public void clearFrameValues() { + for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { + assert this.stack[i] instanceof Atom; + + this.stack[i + 1] = null; + } + + assert this.validateStructure(); + } + + public void mergeFrame() { + int previousMarkerIndex = this.getPreviousMarkerIndex(this.topMarkerKeyIndex); + int i = previousMarkerIndex; + int i1 = this.topMarkerKeyIndex; + + while (i1 < this.topEntryKeyIndex) { + i += ENTRY_STRIDE; + i1 += ENTRY_STRIDE; + Object object = this.stack[i1]; + + assert object instanceof Atom; + + Object object1 = this.stack[i1 + 1]; + Object object2 = this.stack[i]; + if (object2 != object) { + this.stack[i] = object; + this.stack[i + 1] = object1; + } else if (object1 != null) { + this.stack[i + 1] = object1; + } + } + + this.topEntryKeyIndex = i; + this.topMarkerKeyIndex = previousMarkerIndex; + + assert this.validateStructure(); + } + + public void put(Atom atom, @Nullable T value) { + int i = this.valueIndex(atom); + if (i != NOT_FOUND) { + this.stack[i] = value; + } else { + this.ensureCapacity(1); + this.topEntryKeyIndex += ENTRY_STRIDE; + this.stack[this.topEntryKeyIndex] = atom; + this.stack[this.topEntryKeyIndex + 1] = value; + } + + assert this.validateStructure(); + } + + @Nullable + public T get(Atom atom) { + int i = this.valueIndex(atom); + return (T)(i != NOT_FOUND ? this.stack[i] : null); + } + + public T getOrThrow(Atom atom) { + int i = this.valueIndex(atom); + if (i == NOT_FOUND) { + throw new IllegalArgumentException("No value for atom " + atom); + } else { + return (T)this.stack[i]; + } + } + + public T getOrDefault(Atom atom, T defaultValue) { + int i = this.valueIndex(atom); + return (T)(i != NOT_FOUND ? this.stack[i] : defaultValue); + } + + @Nullable + @SafeVarargs + public final T getAny(Atom... atoms) { + int i = this.valueIndexForAny(atoms); + return (T)(i != NOT_FOUND ? this.stack[i] : null); + } + + @SafeVarargs + public final T getAnyOrThrow(Atom... atoms) { + int i = this.valueIndexForAny(atoms); + if (i == NOT_FOUND) { + throw new IllegalArgumentException("No value for atoms " + Arrays.toString(atoms)); + } else { + return (T)this.stack[i]; + } + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + boolean flag = true; + + for (int i = 0; i <= this.topEntryKeyIndex; i += ENTRY_STRIDE) { + Object object = this.stack[i]; + Object object1 = this.stack[i + 1]; + if (object == FRAME_START_MARKER) { + stringBuilder.append('|'); + flag = true; + } else { + if (!flag) { + stringBuilder.append(','); + } + + flag = false; + stringBuilder.append(object).append(':').append(object1); + } + } + + return stringBuilder.toString(); + } + + @VisibleForTesting + public Map, ?> lastFrame() { + HashMap, Object> map = new HashMap<>(); + + for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { + Object object = this.stack[i]; + Object object1 = this.stack[i + 1]; + map.put((Atom)object, object1); + } + + return map; + } + + public boolean hasOnlySingleFrame() { + for (int i = this.topEntryKeyIndex; i > 0; i--) { + if (this.stack[i] == FRAME_START_MARKER) { + return false; + } + } + + if (this.stack[0] != FRAME_START_MARKER) { + throw new IllegalStateException("Corrupted stack"); + } else { + return true; + } + } + + private boolean validateStructure() { + assert this.topMarkerKeyIndex >= 0; + + assert this.topEntryKeyIndex >= this.topMarkerKeyIndex; + + for (int i = 0; i <= this.topEntryKeyIndex; i += ENTRY_STRIDE) { + Object object = this.stack[i]; + if (object != FRAME_START_MARKER && !(object instanceof Atom)) { + return false; + } + } + + for (int ix = this.topMarkerKeyIndex; ix != 0; ix = this.getPreviousMarkerIndex(ix)) { + Object object = this.stack[ix]; + if (object != FRAME_START_MARKER) { + return false; + } + } + + return true; + } + + @SuppressWarnings({"unchecked","rawtypes"}) + public static Term increaseDepth() { + class IncreasingDepthTerm implements Term { + public static final IncreasingDepthTerm INSTANCE = new IncreasingDepthTerm(); + @Override + public boolean parse(final ParseState parseState, final Scope scope, final Control control) { + if (++scope.depth > 512) { + parseState.errorCollector().store(parseState.mark(), new IllegalStateException("Too deep")); + return false; + } + return true; + } + } + return (Term) IncreasingDepthTerm.INSTANCE; + } + + @SuppressWarnings({"unchecked","rawtypes"}) + public static Term decreaseDepth() { + class DecreasingDepthTerm implements Term { + public static final DecreasingDepthTerm INSTANCE = new DecreasingDepthTerm(); + @Override + public boolean parse(final ParseState parseState, final Scope scope, final Control control) { + scope.depth--; + return true; + } + } + return (Term) DecreasingDepthTerm.INSTANCE; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java new file mode 100644 index 000000000..ed88ccd42 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java @@ -0,0 +1,11 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import java.util.stream.Stream; + +public interface SuggestionSupplier { + Stream possibleValues(ParseState parseState); + + static SuggestionSupplier empty() { + return parseState -> Stream.empty(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java new file mode 100644 index 000000000..ceab78ef0 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java @@ -0,0 +1,237 @@ +package net.momirealms.craftengine.core.util.snbt.parse; + +import java.util.ArrayList; +import java.util.List; + +public interface Term { + boolean parse(ParseState parseState, Scope scope, Control control); + + static Term marker(Atom name, T value) { + return new Marker<>(name, value); + } + + @SafeVarargs + static Term sequence(Term... elements) { + return new Sequence<>(elements); + } + + @SafeVarargs + static Term alternative(Term... elements) { + return new Alternative<>(elements); + } + + static Term optional(Term term) { + return new Maybe<>(term); + } + + static Term repeated(NamedRule element, Atom> listName) { + return repeated(element, listName, 0); + } + + static Term repeated(NamedRule element, Atom> listName, int minRepetitions) { + return new Repeated<>(element, listName, minRepetitions); + } + + static Term repeatedWithTrailingSeparator(NamedRule element, Atom> listName, Term seperator) { + return repeatedWithTrailingSeparator(element, listName, seperator, 0); + } + + static Term repeatedWithTrailingSeparator(NamedRule element, Atom> listName, Term seperator, int minRepetitions) { + return new RepeatedWithSeparator<>(element, listName, seperator, minRepetitions, true); + } + + static Term positiveLookahead(Term term) { + return new LookAhead<>(term, true); + } + + static Term cut() { + return new Term<>() { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + control.cut(); + return true; + } + + @Override + public String toString() { + return "↑"; + } + }; + } + + static Term empty() { + return new Term<>() { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + return true; + } + + @Override + public String toString() { + return "ε"; + } + }; + } + + static Term fail(final Object reason) { + return new Term<>() { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + parseState.errorCollector().store(parseState.mark(), reason); + return false; + } + + @Override + public String toString() { + return "fail"; + } + }; + } + + record Alternative(Term[] elements) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + Control control1 = parseState.acquireControl(); + + try { + int i = parseState.mark(); + scope.splitFrame(); + + for (Term term : this.elements) { + if (term.parse(parseState, scope, control1)) { + scope.mergeFrame(); + return true; + } + + scope.clearFrameValues(); + parseState.restore(i); + if (control1.hasCut()) { + break; + } + } + + scope.popFrame(); + return false; + } finally { + parseState.releaseControl(); + } + } + } + + record LookAhead(Term term, boolean positive) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + int i = parseState.mark(); + boolean flag = this.term.parse(parseState.silent(), scope, control); + parseState.restore(i); + return this.positive == flag; + } + } + + record Marker(Atom name, T value) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + scope.put(this.name, this.value); + return true; + } + } + + record Maybe(Term term) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + int i = parseState.mark(); + if (!this.term.parse(parseState, scope, control)) { + parseState.restore(i); + } + + return true; + } + } + + record Repeated(NamedRule element, Atom> listName, int minRepetitions) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + int i = parseState.mark(); + List list = new ArrayList<>(this.minRepetitions); + + while (true) { + int i1 = parseState.mark(); + T object = parseState.parse(this.element); + if (object == null) { + parseState.restore(i1); + if (list.size() < this.minRepetitions) { + parseState.restore(i); + return false; + } else { + scope.put(this.listName, list); + return true; + } + } + + list.add(object); + } + } + } + + record RepeatedWithSeparator( + NamedRule element, Atom> listName, Term separator, int minRepetitions, boolean allowTrailingSeparator + ) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + int i = parseState.mark(); + List list = new ArrayList<>(this.minRepetitions); + boolean flag = true; + + while (true) { + int i1 = parseState.mark(); + if (!flag && !this.separator.parse(parseState, scope, control)) { + parseState.restore(i1); + break; + } + + int i2 = parseState.mark(); + T object = parseState.parse(this.element); + if (object == null) { + if (flag) { + parseState.restore(i2); + } else { + if (!this.allowTrailingSeparator) { + parseState.restore(i); + return false; + } + + parseState.restore(i2); + } + break; + } + + list.add(object); + flag = false; + } + + if (list.size() < this.minRepetitions) { + parseState.restore(i); + return false; + } else { + scope.put(this.listName, list); + return true; + } + } + } + + record Sequence(Term[] elements) implements Term { + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + int i = parseState.mark(); + + for (Term term : this.elements) { + if (!term.parse(parseState, scope, control)) { + parseState.restore(i); + return false; + } + } + + return true; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java new file mode 100644 index 000000000..2f13a7448 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java @@ -0,0 +1,53 @@ +package net.momirealms.craftengine.core.util.snbt.parse.grammar; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.momirealms.craftengine.core.util.snbt.parse.*; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public record Grammar(Dictionary rules, NamedRule top) { + public Grammar { + rules.checkAllBound(); + } + + public Optional parse(ParseState parseState) { + return parseState.parseTopRule(this.top); + } + + public T parse(StringReader reader) throws CommandSyntaxException { + ErrorCollector.LongestOnly longestOnly = new ErrorCollector.LongestOnly<>(); + StringReaderParserState stringReaderParserState = new StringReaderParserState(longestOnly, reader); + Optional optional = this.parse(stringReaderParserState); + if (optional.isPresent()) { + T result = optional.get(); + if (CachedParseState.JAVA_NULL_VALUE_MARKER.equals(result)) { + result = null; + } + return result; + } else { + List> list = longestOnly.entries(); + List list1 = list.stream().mapMulti((errorEntry, consumer) -> { + if (errorEntry.reason() instanceof DelayedException delayedException) { + consumer.accept(delayedException.create(reader.getString(), errorEntry.cursor())); + } else if (errorEntry.reason() instanceof Exception exception1) { + consumer.accept(exception1); + } + }).toList(); + + for (Exception exception : list1) { + if (exception instanceof CommandSyntaxException commandSyntaxException) { + throw commandSyntaxException; + } + } + + if (list1.size() == 1 && list1.getFirst() instanceof RuntimeException runtimeException) { + throw runtimeException; + } else { + throw new IllegalStateException("Failed to parse: " + list.stream().map(ErrorEntry::toString).collect(Collectors.joining(", "))); + } + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java new file mode 100644 index 000000000..9d5e07b91 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java @@ -0,0 +1,34 @@ +package net.momirealms.craftengine.core.util.snbt.parse.grammar; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.momirealms.craftengine.core.util.snbt.parse.DelayedException; +import net.momirealms.craftengine.core.util.snbt.parse.ParseState; +import net.momirealms.craftengine.core.util.snbt.parse.Rule; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class GreedyPatternParseRule implements Rule { + private final Pattern pattern; + private final DelayedException error; + + public GreedyPatternParseRule(Pattern pattern, DelayedException error) { + this.pattern = pattern; + this.error = error; + } + + @Override + public String parse(ParseState parseState) { + StringReader stringReader = parseState.input(); + String string = stringReader.getString(); + Matcher matcher = this.pattern.matcher(string).region(stringReader.getCursor(), string.length()); + if (!matcher.lookingAt()) { + parseState.errorCollector().store(parseState.mark(), this.error); + return null; + } else { + stringReader.setCursor(matcher.end()); + return matcher.group(0); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java new file mode 100644 index 000000000..d3c8d8870 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java @@ -0,0 +1,49 @@ +package net.momirealms.craftengine.core.util.snbt.parse.grammar; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.momirealms.craftengine.core.util.snbt.parse.DelayedException; +import net.momirealms.craftengine.core.util.snbt.parse.ParseState; +import net.momirealms.craftengine.core.util.snbt.parse.Rule; + +import javax.annotation.Nullable; + +public abstract class GreedyPredicateParseRule implements Rule { + private final int minSize; + private final int maxSize; + private final DelayedException error; + + public GreedyPredicateParseRule(int minSize, DelayedException error) { + this(minSize, Integer.MAX_VALUE, error); + } + + public GreedyPredicateParseRule(int minSize, int maxSize, DelayedException error) { + this.minSize = minSize; + this.maxSize = maxSize; + this.error = error; + } + + @Nullable + @Override + public String parse(ParseState parseState) { + StringReader stringReader = parseState.input(); + String string = stringReader.getString(); + int cursor = stringReader.getCursor(); + int i = cursor; + + while (i < string.length() && this.isAccepted(string.charAt(i)) && i - cursor < this.maxSize) { + i++; + } + + int i1 = i - cursor; + if (i1 < this.minSize) { + parseState.errorCollector().store(parseState.mark(), this.error); + return null; + } else { + stringReader.setCursor(i); + return string.substring(cursor, i); + } + } + + protected abstract boolean isAccepted(char c); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java new file mode 100644 index 000000000..71a919544 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java @@ -0,0 +1,47 @@ +package net.momirealms.craftengine.core.util.snbt.parse.grammar; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.momirealms.craftengine.core.util.snbt.parse.DelayedException; +import net.momirealms.craftengine.core.util.snbt.parse.ParseState; +import net.momirealms.craftengine.core.util.snbt.parse.Rule; + +import javax.annotation.Nullable; + +public abstract class NumberRunParseRule implements Rule { + private final DelayedException noValueError; + private final DelayedException underscoreNotAllowedError; + + public NumberRunParseRule(DelayedException noValueError, DelayedException underscoreNotAllowedError) { + this.noValueError = noValueError; + this.underscoreNotAllowedError = underscoreNotAllowedError; + } + + @Nullable + @Override + public String parse(ParseState parseState) { + StringReader stringReader = parseState.input(); + stringReader.skipWhitespace(); + String string = stringReader.getString(); + int cursor = stringReader.getCursor(); + int i = cursor; + + while (i < string.length() && this.isAccepted(string.charAt(i))) { + i++; + } + + int i1 = i - cursor; + if (i1 == 0) { + parseState.errorCollector().store(parseState.mark(), this.noValueError); + return null; + } else if (string.charAt(cursor) != '_' && string.charAt(i - 1) != '_') { + stringReader.setCursor(i); + return string.substring(cursor, i); + } else { + parseState.errorCollector().store(parseState.mark(), this.underscoreNotAllowedError); + return null; + } + } + + protected abstract boolean isAccepted(char c); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java new file mode 100644 index 000000000..9bf0a87b9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java @@ -0,0 +1,29 @@ +package net.momirealms.craftengine.core.util.snbt.parse.grammar; + +import com.mojang.brigadier.StringReader; +import net.momirealms.craftengine.core.util.snbt.parse.CachedParseState; +import net.momirealms.craftengine.core.util.snbt.parse.ErrorCollector; + +public class StringReaderParserState extends CachedParseState { + private final StringReader input; + + public StringReaderParserState(ErrorCollector errorCollector, StringReader input) { + super(errorCollector); + this.input = input; + } + + @Override + public StringReader input() { + return this.input; + } + + @Override + public int mark() { + return this.input.getCursor(); + } + + @Override + public void restore(int cursor) { + this.input.setCursor(cursor); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java new file mode 100644 index 000000000..12cb7def3 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java @@ -0,0 +1,65 @@ +package net.momirealms.craftengine.core.util.snbt.parse.grammar; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import it.unimi.dsi.fastutil.chars.CharList; +import net.momirealms.craftengine.core.util.snbt.parse.*; + +import java.util.stream.Collectors; + +public interface StringReaderTerms { + DynamicCommandExceptionType LITERAL_INCORRECT = new LocalizedDynamicCommandExceptionType( + expected -> new LocalizedMessage("warning.config.type.snbt.parser.incorrect", String.valueOf(expected)) + ); + + static Term character(final char value) { + return new TerminalCharacters(CharList.of(value)) { + @Override + protected boolean isAccepted(char c) { + return value == c; + } + }; + } + + static Term characters(final char value1, final char value2) { + return new TerminalCharacters(CharList.of(value1, value2)) { + @Override + protected boolean isAccepted(char c) { + return c == value1 || c == value2; + } + }; + } + + static StringReader createReader(String input, int cursor) { + StringReader stringReader = new StringReader(input); + stringReader.setCursor(cursor); + return stringReader; + } + + abstract class TerminalCharacters implements Term { + private final DelayedException error; + private final SuggestionSupplier suggestions; + + public TerminalCharacters(CharList characters) { + String string = characters.intStream().mapToObj(Character::toString).collect(Collectors.joining("|")); + this.error = DelayedException.create(LITERAL_INCORRECT, string); + this.suggestions = parseState -> characters.intStream().mapToObj(Character::toString); + } + + @Override + public boolean parse(ParseState parseState, Scope scope, Control control) { + parseState.input().skipWhitespace(); + int i = parseState.mark(); + if (parseState.input().canRead() && this.isAccepted(parseState.input().read())) { + return true; + } else { + parseState.errorCollector().store(i, this.suggestions, this.error); + return false; + } + } + + protected abstract boolean isAccepted(char c); + } + +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java new file mode 100644 index 000000000..1909633c8 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java @@ -0,0 +1,33 @@ +package net.momirealms.craftengine.core.util.snbt.parse.grammar; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.momirealms.craftengine.core.util.snbt.parse.DelayedException; +import net.momirealms.craftengine.core.util.snbt.parse.ParseState; +import net.momirealms.craftengine.core.util.snbt.parse.Rule; + +import javax.annotation.Nullable; + +public class UnquotedStringParseRule implements Rule { + private final int minSize; + private final DelayedException error; + + public UnquotedStringParseRule(int minSize, DelayedException error) { + this.minSize = minSize; + this.error = error; + } + + @Nullable + @Override + public String parse(ParseState parseState) { + parseState.input().skipWhitespace(); + int i = parseState.mark(); + String unquotedString = parseState.input().readUnquotedString(); + if (unquotedString.length() < this.minSize) { + parseState.errorCollector().store(i, this.error); + return null; + } else { + return unquotedString; + } + } +} diff --git a/gradle.properties b/gradle.properties index 46db05f12..a82325cc5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings project_version=0.0.65.15 config_version=60 -lang_version=41 +lang_version=42 project_group=net.momirealms latest_supported_version=1.21.10 From 563dc931e97c6703b9607d2197cea8f2e26bf500 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 05:07:02 +0800 Subject: [PATCH 050/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=BB=E9=80=A0?= =?UTF-8?q?=E9=85=8D=E6=96=B9=E5=90=8E=E5=A4=84=E7=90=86=E5=99=A8=E5=A6=82?= =?UTF-8?q?=E6=9E=9C=E4=BF=9D=E7=95=99custom=5Fdata=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E4=BC=9A=E5=86=99=E5=85=A5=E9=94=99=E8=AF=AF=E7=9A=84=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E7=89=A9=E5=93=81ID=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/item/recipe/CustomSmithingTransformRecipe.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 190ee8004..cad232493 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 @@ -19,10 +19,7 @@ import net.momirealms.craftengine.core.util.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -158,6 +155,7 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip private T createSmithingResult(Item base, T result) { Item wrappedResult = (Item) CraftEngine.instance().itemManager().wrap(result); Item finalResult = wrappedResult; + Optional customId = finalResult.customId(); // 修复在保留custom_data组件的时候意外保留前物品的id if (this.mergeComponents) { finalResult = base.mergeCopy(wrappedResult); } @@ -166,6 +164,9 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip processor.accept(base, wrappedResult, finalResult); } } + if (customId.isPresent()) { + finalResult.customId(customId.get()); + } return finalResult.getItem(); } From 52a83a18d611e3868f6ec62c5d723dba0f832321 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 05:33:32 +0800 Subject: [PATCH 051/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java index ad96f56e9..cf69f1474 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -69,7 +69,7 @@ public class SnbtGrammar { new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_decimal_numeral")) ); private static final DelayedException ERROR_EXPECTED_HEX_NUMERAL = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("Expected a hexadecimal number")) + new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_hex_numeral")) ); private static final DelayedException ERROR_EMPTY_KEY = DelayedException.create( new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.empty_key")) From bdc247dc469698338938d19248c6a45e984e59c0 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 05:37:21 +0800 Subject: [PATCH 052/135] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=B8=8D=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E7=9A=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java index cf69f1474..4415e2655 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -559,7 +559,7 @@ public class SnbtGrammar { Atom>> atom30 = Atom.of("map_entries"); dictionary.put(atom30, Term.repeatedWithTrailingSeparator(namedRule3, atom30, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom30)); Atom atom31 = Atom.of("map_literal"); - dictionary.put(atom31, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), dictionary.named(atom30), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { // Paper - track depth + dictionary.put(atom31, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), dictionary.named(atom30), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { List> list = scope.getOrThrow(atom30); if (list.isEmpty()) { return object2; From 16aedafbe4c40db87d6d173695bd5a3de3d64ee0 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 06:55:42 +0800 Subject: [PATCH 053/135] =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8D=E6=88=90?= =?UTF-8?q?=E4=BA=BA=E7=B1=BB=E5=8F=AF=E8=AF=BB=E7=9A=84=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/util/snbt/SnbtGrammar.java | 731 +++++++++--------- .../core/util/snbt/SnbtOperations.java | 54 +- .../craftengine/core/util/snbt/TagParser.java | 24 +- .../util/snbt/parse/CachedParseState.java | 144 ++-- .../util/snbt/parse/DelayedException.java | 10 +- .../core/util/snbt/parse/Dictionary.java | 43 +- .../core/util/snbt/parse/ErrorCollector.java | 55 +- .../core/util/snbt/parse/ParseState.java | 13 +- .../core/util/snbt/parse/Rule.java | 21 +- .../core/util/snbt/parse/Scope.java | 195 +++-- .../util/snbt/parse/SuggestionSupplier.java | 4 +- .../core/util/snbt/parse/Term.java | 136 ++-- .../core/util/snbt/parse/grammar/Grammar.java | 48 +- .../parse/grammar/GreedyPatternParseRule.java | 15 +- .../grammar/GreedyPredicateParseRule.java | 25 +- .../parse/grammar/NumberRunParseRule.java | 33 +- .../grammar/StringReaderParserState.java | 4 +- .../snbt/parse/grammar/StringReaderTerms.java | 43 +- .../grammar/UnquotedStringParseRule.java | 15 +- 19 files changed, 788 insertions(+), 825 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java index 4415e2655..74512176c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -124,8 +124,8 @@ public class SnbtGrammar { }; private static final Pattern UNICODE_NAME = Pattern.compile("[-a-zA-Z0-9 ]+"); - static DelayedException createNumberParseError(NumberFormatException numberFormatException) { - return DelayedException.create(ERROR_NUMBER_PARSE_FAILURE, numberFormatException.getMessage()); + static DelayedException createNumberParseError(NumberFormatException ex) { + return DelayedException.create(ERROR_NUMBER_PARSE_FAILURE, ex.getMessage()); } private static boolean isAllowedToStartUnquotedString(char c) { @@ -139,96 +139,97 @@ public class SnbtGrammar { }; } - static boolean needsUnderscoreRemoval(String text) { - return text.indexOf(95) != -1; + static boolean needsUnderscoreRemoval(String contents) { + return contents.indexOf(95) != -1; } - private static void cleanAndAppend(StringBuilder stringBuilder, String text) { - cleanAndAppend(stringBuilder, text, needsUnderscoreRemoval(text)); + private static void cleanAndAppend(StringBuilder output, String contents) { + cleanAndAppend(output, contents, needsUnderscoreRemoval(contents)); } - static void cleanAndAppend(StringBuilder stringBuilder, String text, boolean removeUnderscores) { - if (removeUnderscores) { - for (char c : text.toCharArray()) { + static void cleanAndAppend(StringBuilder output, String contents, boolean needsUnderscoreRemoval) { + if (needsUnderscoreRemoval) { + for (char c : contents.toCharArray()) { if (c != '_') { - stringBuilder.append(c); + output.append(c); } } + return; } - stringBuilder.append(text); + output.append(contents); } - static short parseUnsignedShort(String text, int radix) { - int i = Integer.parseInt(text, radix); - if (i >> 16 == 0) { - return (short)i; + static short parseUnsignedShort(String string, int radix) { + int parse = Integer.parseInt(string, radix); + if (parse >> 16 == 0) { + return (short) parse; } - throw new NumberFormatException("out of range: " + i); + throw new NumberFormatException("out of range: " + parse); } @Nullable private static T createFloat( DynamicOps ops, Sign sign, - @Nullable String wholePart, - @Nullable String fractionPart, - @Nullable Signed exponentPart, - @Nullable TypeSuffix suffix, - ParseState parseState + @Nullable String whole, + @Nullable String fraction, + @Nullable Signed exponent, + @Nullable TypeSuffix typeSuffix, + ParseState state ) { - StringBuilder stringBuilder = new StringBuilder(); - sign.append(stringBuilder); - if (wholePart != null) { - cleanAndAppend(stringBuilder, wholePart); + StringBuilder result = new StringBuilder(); + sign.append(result); + if (whole != null) { + cleanAndAppend(result, whole); } - if (fractionPart != null) { - stringBuilder.append('.'); - cleanAndAppend(stringBuilder, fractionPart); + if (fraction != null) { + result.append('.'); + cleanAndAppend(result, fraction); } - if (exponentPart != null) { - stringBuilder.append('e'); - exponentPart.sign().append(stringBuilder); - cleanAndAppend(stringBuilder, exponentPart.value); + if (exponent != null) { + result.append('e'); + exponent.sign().append(result); + cleanAndAppend(result, exponent.value); } try { - String string = stringBuilder.toString(); + String string = result.toString(); - return switch (suffix) { - case null -> convertDouble(ops, parseState, string); - case FLOAT -> convertFloat(ops, parseState, string); - case DOUBLE -> convertDouble(ops, parseState, string); + return switch (typeSuffix) { + case null -> convertDouble(ops, state, string); + case FLOAT -> convertFloat(ops, state, string); + case DOUBLE -> convertDouble(ops, state, string); default -> { - parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_FLOAT_TYPE); + state.errorCollector().store(state.mark(), ERROR_EXPECTED_FLOAT_TYPE); yield null; } }; - } catch (NumberFormatException var11) { - parseState.errorCollector().store(parseState.mark(), createNumberParseError(var11)); + } catch (NumberFormatException e) { + state.errorCollector().store(state.mark(), createNumberParseError(e)); return null; } } @Nullable - private static T convertFloat(DynamicOps ops, ParseState parseState, String value) { - float f = Float.parseFloat(value); - if (!Float.isFinite(f)) { - parseState.errorCollector().store(parseState.mark(), ERROR_INFINITY_NOT_ALLOWED); + private static T convertFloat(DynamicOps ops, ParseState state, String contents) { + float value = Float.parseFloat(contents); + if (!Float.isFinite(value)) { + state.errorCollector().store(state.mark(), ERROR_INFINITY_NOT_ALLOWED); return null; } - return ops.createFloat(f); + return ops.createFloat(value); } @Nullable - private static T convertDouble(DynamicOps ops, ParseState parseState, String value) { - double d = Double.parseDouble(value); - if (!Double.isFinite(d)) { - parseState.errorCollector().store(parseState.mark(), ERROR_INFINITY_NOT_ALLOWED); + private static T convertDouble(DynamicOps ops, ParseState state, String contents) { + double value = Double.parseDouble(contents); + if (!Double.isFinite(value)) { + state.errorCollector().store(state.mark(), ERROR_INFINITY_NOT_ALLOWED); return null; } - return ops.createDouble(d); + return ops.createDouble(value); } private static String joinList(List list) { @@ -241,42 +242,44 @@ public class SnbtGrammar { @SuppressWarnings("unchecked") public static Grammar createParser(DynamicOps ops) { - T object = ops.createBoolean(true); - T object1 = ops.createBoolean(false); - T object2 = ops.emptyMap(); - T object3 = ops.emptyList(); - Dictionary dictionary = new Dictionary<>(); - Atom atom = Atom.of("sign"); - dictionary.put( - atom, + T trueValue = ops.createBoolean(true); + T falseValue = ops.createBoolean(false); + T emptyMapValue = ops.emptyMap(); + T emptyList = ops.emptyList(); + T nullString = ops.createString("null"); + boolean isJavaType = "null".equals(nullString); // 确定是 Java 类型的 + Dictionary rules = new Dictionary<>(); + Atom sign = Atom.of("sign"); + rules.put( + sign, Term.alternative( - Term.sequence(StringReaderTerms.character('+'), Term.marker(atom, Sign.PLUS)), - Term.sequence(StringReaderTerms.character('-'), Term.marker(atom, Sign.MINUS)) + Term.sequence(StringReaderTerms.character('+'), Term.marker(sign, Sign.PLUS)), + Term.sequence(StringReaderTerms.character('-'), Term.marker(sign, Sign.MINUS)) ), - scope -> scope.getOrThrow(atom) + scope -> scope.getOrThrow(sign) ); - Atom atom1 = Atom.of("integer_suffix"); - dictionary.put( - atom1, + Atom integerSuffix = Atom.of("integer_suffix"); + rules.put( + integerSuffix, Term.alternative( Term.sequence( StringReaderTerms.characters('u', 'U'), Term.alternative( Term.sequence( StringReaderTerms.characters('b', 'B'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.BYTE)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.BYTE)) ), Term.sequence( StringReaderTerms.characters('s', 'S'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.SHORT)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.SHORT)) ), Term.sequence( StringReaderTerms.characters('i', 'I'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.INT)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.INT)) ), Term.sequence( StringReaderTerms.characters('l', 'L'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.LONG)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.LONG)) ) ) ), @@ -285,351 +288,349 @@ public class SnbtGrammar { Term.alternative( Term.sequence( StringReaderTerms.characters('b', 'B'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.BYTE)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.BYTE)) ), Term.sequence( StringReaderTerms.characters('s', 'S'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.SHORT)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.SHORT)) ), Term.sequence( StringReaderTerms.characters('i', 'I'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.INT)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.INT)) ), Term.sequence( StringReaderTerms.characters('l', 'L'), - Term.marker(atom1, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.LONG)) + Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.LONG)) ) ) ), - Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.BYTE))), - Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.SHORT))), - Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.INT))), - Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(atom1, new IntegerSuffix(null, TypeSuffix.LONG))) + Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.BYTE))), + Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.SHORT))), + Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.INT))), + Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.LONG))) ), - scope -> scope.getOrThrow(atom1) + scope -> scope.getOrThrow(integerSuffix) ); - Atom atom2 = Atom.of("binary_numeral"); - dictionary.put(atom2, BINARY_NUMERAL); - Atom atom3 = Atom.of("decimal_numeral"); - dictionary.put(atom3, DECIMAL_NUMERAL); - Atom atom4 = Atom.of("hex_numeral"); - dictionary.put(atom4, HEX_NUMERAL); - Atom atom5 = Atom.of("integer_literal"); - NamedRule namedRule = dictionary.put( - atom5, + Atom binaryNumeral = Atom.of("binary_numeral"); + rules.put(binaryNumeral, BINARY_NUMERAL); + Atom decimalNumeral = Atom.of("decimal_numeral"); + rules.put(decimalNumeral, DECIMAL_NUMERAL); + Atom hexNumeral = Atom.of("hex_numeral"); + rules.put(hexNumeral, HEX_NUMERAL); + Atom integerLiteral = Atom.of("integer_literal"); + NamedRule integerLiteralRule = rules.put( + integerLiteral, Term.sequence( - Term.optional(dictionary.named(atom)), + Term.optional(rules.named(sign)), Term.alternative( Term.sequence( StringReaderTerms.character('0'), Term.cut(), Term.alternative( - Term.sequence(StringReaderTerms.characters('x', 'X'), Term.cut(), dictionary.named(atom4)), - Term.sequence(StringReaderTerms.characters('b', 'B'), dictionary.named(atom2)), - Term.sequence(dictionary.named(atom3), Term.cut(), Term.fail(ERROR_LEADING_ZERO_NOT_ALLOWED)), - Term.marker(atom3, "0") + Term.sequence(StringReaderTerms.characters('x', 'X'), Term.cut(), rules.named(hexNumeral)), + Term.sequence(StringReaderTerms.characters('b', 'B'), rules.named(binaryNumeral)), + Term.sequence(rules.named(decimalNumeral), Term.cut(), Term.fail(ERROR_LEADING_ZERO_NOT_ALLOWED)), + Term.marker(decimalNumeral, "0") ) ), - dictionary.named(atom3) + rules.named(decimalNumeral) ), - Term.optional(dictionary.named(atom1)) + Term.optional(rules.named(integerSuffix)) ), scope -> { - IntegerSuffix integerSuffix = scope.getOrDefault(atom1, IntegerSuffix.EMPTY); - Sign sign = scope.getOrDefault(atom, Sign.PLUS); - String string = scope.get(atom3); - if (string != null) { - return new IntegerLiteral(sign, Base.DECIMAL, string, integerSuffix); + IntegerSuffix suffix = scope.getOrDefault(integerSuffix, IntegerSuffix.EMPTY); + Sign signValue = scope.getOrDefault(sign, Sign.PLUS); + String decimalContents = scope.get(decimalNumeral); + if (decimalContents != null) { + return new IntegerLiteral(signValue, Base.DECIMAL, decimalContents, suffix); } - String string1 = scope.get(atom4); + String string1 = scope.get(hexNumeral); if (string1 != null) { - return new IntegerLiteral(sign, Base.HEX, string1, integerSuffix); + return new IntegerLiteral(signValue, Base.HEX, string1, suffix); } - String string2 = scope.getOrThrow(atom2); - return new IntegerLiteral(sign, Base.BINARY, string2, integerSuffix); + String string2 = scope.getOrThrow(binaryNumeral); + return new IntegerLiteral(signValue, Base.BINARY, string2, suffix); } ); - Atom atom6 = Atom.of("float_type_suffix"); - dictionary.put( - atom6, + Atom floatTypeSuffix = Atom.of("float_type_suffix"); + rules.put( + floatTypeSuffix, Term.alternative( - Term.sequence(StringReaderTerms.characters('f', 'F'), Term.marker(atom6, TypeSuffix.FLOAT)), - Term.sequence(StringReaderTerms.characters('d', 'D'), Term.marker(atom6, TypeSuffix.DOUBLE)) + Term.sequence(StringReaderTerms.characters('f', 'F'), Term.marker(floatTypeSuffix, TypeSuffix.FLOAT)), + Term.sequence(StringReaderTerms.characters('d', 'D'), Term.marker(floatTypeSuffix, TypeSuffix.DOUBLE)) ), - scope -> scope.getOrThrow(atom6) + scope -> scope.getOrThrow(floatTypeSuffix) ); - Atom> atom7 = Atom.of("float_exponent_part"); - dictionary.put( - atom7, - Term.sequence(StringReaderTerms.characters('e', 'E'), Term.optional(dictionary.named(atom)), dictionary.named(atom3)), - scope -> new Signed<>(scope.getOrDefault(atom, Sign.PLUS), scope.getOrThrow(atom3)) + Atom> floatExponentPart = Atom.of("float_exponent_part"); + rules.put( + floatExponentPart, + Term.sequence(StringReaderTerms.characters('e', 'E'), Term.optional(rules.named(sign)), rules.named(decimalNumeral)), + scope -> new Signed<>(scope.getOrDefault(sign, Sign.PLUS), scope.getOrThrow(decimalNumeral)) ); - Atom atom8 = Atom.of("float_whole_part"); - Atom atom9 = Atom.of("float_fraction_part"); - Atom atom10 = Atom.of("float_literal"); - dictionary.putComplex( - atom10, + Atom floatWholePart = Atom.of("float_whole_part"); + Atom floatFractionPart = Atom.of("float_fraction_part"); + Atom floatLiteral = Atom.of("float_literal"); + rules.putComplex( + floatLiteral, Term.sequence( - Term.optional(dictionary.named(atom)), + Term.optional(rules.named(sign)), Term.alternative( Term.sequence( - dictionary.namedWithAlias(atom3, atom8), + rules.namedWithAlias(decimalNumeral, floatWholePart), StringReaderTerms.character('.'), Term.cut(), - Term.optional(dictionary.namedWithAlias(atom3, atom9)), - Term.optional(dictionary.named(atom7)), - Term.optional(dictionary.named(atom6)) + Term.optional(rules.namedWithAlias(decimalNumeral, floatFractionPart)), + Term.optional(rules.named(floatExponentPart)), + Term.optional(rules.named(floatTypeSuffix)) ), Term.sequence( StringReaderTerms.character('.'), Term.cut(), - dictionary.namedWithAlias(atom3, atom9), - Term.optional(dictionary.named(atom7)), - Term.optional(dictionary.named(atom6)) + rules.namedWithAlias(decimalNumeral, floatFractionPart), + Term.optional(rules.named(floatExponentPart)), + Term.optional(rules.named(floatTypeSuffix)) ), - Term.sequence(dictionary.namedWithAlias(atom3, atom8), dictionary.named(atom7), Term.cut(), Term.optional(dictionary.named(atom6))), - Term.sequence(dictionary.namedWithAlias(atom3, atom8), Term.optional(dictionary.named(atom7)), dictionary.named(atom6)) + Term.sequence(rules.namedWithAlias(decimalNumeral, floatWholePart), rules.named(floatExponentPart), Term.cut(), Term.optional(rules.named(floatTypeSuffix))), + Term.sequence(rules.namedWithAlias(decimalNumeral, floatWholePart), Term.optional(rules.named(floatExponentPart)), rules.named(floatTypeSuffix)) ) ), - parseState -> { - Scope scope = parseState.scope(); - Sign sign = scope.getOrDefault(atom, Sign.PLUS); - String string = scope.get(atom8); - String string1 = scope.get(atom9); - Signed signed = scope.get(atom7); - TypeSuffix typeSuffix = scope.get(atom6); - return createFloat(ops, sign, string, string1, signed, typeSuffix, parseState); + state -> { + Scope scope = state.scope(); + Sign wholeSign = scope.getOrDefault(sign, Sign.PLUS); + String whole = scope.get(floatWholePart); + String fraction = scope.get(floatFractionPart); + Signed exponent = scope.get(floatExponentPart); + TypeSuffix typeSuffix = scope.get(floatTypeSuffix); + return createFloat(ops, wholeSign, whole, fraction, exponent, typeSuffix, state); } ); - Atom atom11 = Atom.of("string_hex_2"); - dictionary.put(atom11, new SimpleHexLiteralParseRule(2)); - Atom atom12 = Atom.of("string_hex_4"); - dictionary.put(atom12, new SimpleHexLiteralParseRule(4)); - Atom atom13 = Atom.of("string_hex_8"); - dictionary.put(atom13, new SimpleHexLiteralParseRule(8)); - Atom atom14 = Atom.of("string_unicode_name"); - dictionary.put(atom14, new GreedyPatternParseRule(UNICODE_NAME, ERROR_INVALID_CHARACTER_NAME)); - Atom atom15 = Atom.of("string_escape_sequence"); - dictionary.putComplex( - atom15, + Atom stringHex2 = Atom.of("string_hex_2"); + rules.put(stringHex2, new SimpleHexLiteralParseRule(2)); + Atom stringHex4 = Atom.of("string_hex_4"); + rules.put(stringHex4, new SimpleHexLiteralParseRule(4)); + Atom stringHex8 = Atom.of("string_hex_8"); + rules.put(stringHex8, new SimpleHexLiteralParseRule(8)); + Atom stringUnicodeName = Atom.of("string_unicode_name"); + rules.put(stringUnicodeName, new GreedyPatternParseRule(UNICODE_NAME, ERROR_INVALID_CHARACTER_NAME)); + Atom stringEscapeSequence = Atom.of("string_escape_sequence"); + rules.putComplex( + stringEscapeSequence, Term.alternative( - Term.sequence(StringReaderTerms.character('b'), Term.marker(atom15, "\b")), - Term.sequence(StringReaderTerms.character('s'), Term.marker(atom15, " ")), - Term.sequence(StringReaderTerms.character('t'), Term.marker(atom15, "\t")), - Term.sequence(StringReaderTerms.character('n'), Term.marker(atom15, "\n")), - Term.sequence(StringReaderTerms.character('f'), Term.marker(atom15, "\f")), - Term.sequence(StringReaderTerms.character('r'), Term.marker(atom15, "\r")), - Term.sequence(StringReaderTerms.character('\\'), Term.marker(atom15, "\\")), - Term.sequence(StringReaderTerms.character('\''), Term.marker(atom15, "'")), - Term.sequence(StringReaderTerms.character('"'), Term.marker(atom15, "\"")), - Term.sequence(StringReaderTerms.character('x'), dictionary.named(atom11)), - Term.sequence(StringReaderTerms.character('u'), dictionary.named(atom12)), - Term.sequence(StringReaderTerms.character('U'), dictionary.named(atom13)), - Term.sequence(StringReaderTerms.character('N'), StringReaderTerms.character('{'), dictionary.named(atom14), StringReaderTerms.character('}')) + Term.sequence(StringReaderTerms.character('b'), Term.marker(stringEscapeSequence, "\b")), + Term.sequence(StringReaderTerms.character('s'), Term.marker(stringEscapeSequence, " ")), + Term.sequence(StringReaderTerms.character('t'), Term.marker(stringEscapeSequence, "\t")), + Term.sequence(StringReaderTerms.character('n'), Term.marker(stringEscapeSequence, "\n")), + Term.sequence(StringReaderTerms.character('f'), Term.marker(stringEscapeSequence, "\f")), + Term.sequence(StringReaderTerms.character('r'), Term.marker(stringEscapeSequence, "\r")), + Term.sequence(StringReaderTerms.character('\\'), Term.marker(stringEscapeSequence, "\\")), + Term.sequence(StringReaderTerms.character('\''), Term.marker(stringEscapeSequence, "'")), + Term.sequence(StringReaderTerms.character('"'), Term.marker(stringEscapeSequence, "\"")), + Term.sequence(StringReaderTerms.character('x'), rules.named(stringHex2)), + Term.sequence(StringReaderTerms.character('u'), rules.named(stringHex4)), + Term.sequence(StringReaderTerms.character('U'), rules.named(stringHex8)), + Term.sequence(StringReaderTerms.character('N'), StringReaderTerms.character('{'), rules.named(stringUnicodeName), StringReaderTerms.character('}')) ), - parseState -> { - Scope scope = parseState.scope(); - String string = scope.getAny(atom15); - if (string != null) { - return string; + state -> { + Scope scope = state.scope(); + String plainEscape = scope.getAny(stringEscapeSequence); + if (plainEscape != null) { + return plainEscape; } - String string1 = scope.getAny(atom11, atom12, atom13); - if (string1 != null) { - int i = HexFormat.fromHexDigits(string1); - if (!Character.isValidCodePoint(i)) { - parseState.errorCollector() - .store(parseState.mark(), DelayedException.create(ERROR_INVALID_CODEPOINT, String.format(Locale.ROOT, "U+%08X", i))); + String hexEscape = scope.getAny(stringHex2, stringHex4, stringHex8); + if (hexEscape != null) { + int codePoint = HexFormat.fromHexDigits(hexEscape); + if (!Character.isValidCodePoint(codePoint)) { + state.errorCollector() + .store(state.mark(), DelayedException.create(ERROR_INVALID_CODEPOINT, String.format(Locale.ROOT, "U+%08X", codePoint))); return null; } - return Character.toString(i); + return Character.toString(codePoint); } - String string2 = scope.getOrThrow(atom14); + String character = scope.getOrThrow(stringUnicodeName); - int i1; + int codePoint; try { - i1 = Character.codePointOf(string2); + codePoint = Character.codePointOf(character); } catch (IllegalArgumentException var12x) { - parseState.errorCollector().store(parseState.mark(), ERROR_INVALID_CHARACTER_NAME); + state.errorCollector().store(state.mark(), ERROR_INVALID_CHARACTER_NAME); return null; } - return Character.toString(i1); + return Character.toString(codePoint); } ); - Atom atom16 = Atom.of("string_plain_contents"); - dictionary.put(atom16, PLAIN_STRING_CHUNK); - Atom> atom17 = Atom.of("string_chunks"); - Atom atom18 = Atom.of("string_contents"); - Atom atom19 = Atom.of("single_quoted_string_chunk"); - NamedRule namedRule1 = dictionary.put( - atom19, + Atom stringPlainContents = Atom.of("string_plain_contents"); + rules.put(stringPlainContents, PLAIN_STRING_CHUNK); + Atom> stringChunks = Atom.of("string_chunks"); + Atom stringContents = Atom.of("string_contents"); + Atom singleQuotedStringChunk = Atom.of("single_quoted_string_chunk"); + NamedRule namedRule1 = rules.put( + singleQuotedStringChunk, Term.alternative( - dictionary.namedWithAlias(atom16, atom18), - Term.sequence(StringReaderTerms.character('\\'), dictionary.namedWithAlias(atom15, atom18)), - Term.sequence(StringReaderTerms.character('"'), Term.marker(atom18, "\"")) + rules.namedWithAlias(stringPlainContents, stringContents), + Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), + Term.sequence(StringReaderTerms.character('"'), Term.marker(stringContents, "\"")) ), - scope -> scope.getOrThrow(atom18) + scope -> scope.getOrThrow(stringContents) ); - Atom atom20 = Atom.of("single_quoted_string_contents"); - dictionary.put(atom20, Term.repeated(namedRule1, atom17), scope -> joinList(scope.getOrThrow(atom17))); - Atom atom21 = Atom.of("double_quoted_string_chunk"); - NamedRule namedRule2 = dictionary.put( - atom21, + Atom singleQuotedStringContents = Atom.of("single_quoted_string_contents"); + rules.put(singleQuotedStringContents, Term.repeated(namedRule1, stringChunks), scope -> joinList(scope.getOrThrow(stringChunks))); + Atom doubleQuotedStringChunk = Atom.of("double_quoted_string_chunk"); + NamedRule namedRule2 = rules.put( + doubleQuotedStringChunk, Term.alternative( - dictionary.namedWithAlias(atom16, atom18), - Term.sequence(StringReaderTerms.character('\\'), dictionary.namedWithAlias(atom15, atom18)), - Term.sequence(StringReaderTerms.character('\''), Term.marker(atom18, "'")) + rules.namedWithAlias(stringPlainContents, stringContents), + Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), + Term.sequence(StringReaderTerms.character('\''), Term.marker(stringContents, "'")) ), - scope -> scope.getOrThrow(atom18) + scope -> scope.getOrThrow(stringContents) ); - Atom atom22 = Atom.of("double_quoted_string_contents"); - dictionary.put(atom22, Term.repeated(namedRule2, atom17), scope -> joinList(scope.getOrThrow(atom17))); - Atom atom23 = Atom.of("quoted_string_literal"); - dictionary.put( - atom23, + Atom doubleQuotedStringContents = Atom.of("double_quoted_string_contents"); + rules.put(doubleQuotedStringContents, Term.repeated(namedRule2, stringChunks), scope -> joinList(scope.getOrThrow(stringChunks))); + Atom quotedStringLiteral = Atom.of("quoted_string_literal"); + rules.put( + quotedStringLiteral, Term.alternative( Term.sequence( - StringReaderTerms.character('"'), Term.cut(), Term.optional(dictionary.namedWithAlias(atom22, atom18)), StringReaderTerms.character('"') + StringReaderTerms.character('"'), Term.cut(), Term.optional(rules.namedWithAlias(doubleQuotedStringContents, stringContents)), StringReaderTerms.character('"') ), - Term.sequence(StringReaderTerms.character('\''), Term.optional(dictionary.namedWithAlias(atom20, atom18)), StringReaderTerms.character('\'')) + Term.sequence(StringReaderTerms.character('\''), Term.optional(rules.namedWithAlias(singleQuotedStringContents, stringContents)), StringReaderTerms.character('\'')) ), - scope -> scope.getOrThrow(atom18) + scope -> scope.getOrThrow(stringContents) ); - Atom atom24 = Atom.of("unquoted_string"); - dictionary.put(atom24, new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING)); - Atom atom25 = Atom.of("literal"); - Atom> atom26 = Atom.of("arguments"); - dictionary.put( - atom26, Term.repeatedWithTrailingSeparator(dictionary.forward(atom25), atom26, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom26) + Atom unquotedString = Atom.of("unquoted_string"); + rules.put(unquotedString, new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING)); + Atom literal = Atom.of("literal"); + Atom> argumentList = Atom.of("arguments"); + rules.put( + argumentList, Term.repeatedWithTrailingSeparator(rules.forward(literal), argumentList, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(argumentList) ); - Atom atom27 = Atom.of("unquoted_string_or_builtin"); - dictionary.putComplex( - atom27, + Atom unquotedStringOrBuiltIn = Atom.of("unquoted_string_or_builtin"); + rules.putComplex( + unquotedStringOrBuiltIn, Term.sequence( - dictionary.named(atom24), - Term.optional(Term.sequence(StringReaderTerms.character('('), dictionary.named(atom26), StringReaderTerms.character(')'))) + rules.named(unquotedString), + Term.optional(Term.sequence(StringReaderTerms.character('('), rules.named(argumentList), StringReaderTerms.character(')'))) ), - parseState -> { - Scope scope = parseState.scope(); - String string = scope.getOrThrow(atom24); - if (!string.isEmpty() && isAllowedToStartUnquotedString(string.charAt(0))) { - List list = scope.get(atom26); - if (list != null) { - SnbtOperations.BuiltinKey builtinKey = new SnbtOperations.BuiltinKey(string, list.size()); - SnbtOperations.BuiltinOperation builtinOperation = SnbtOperations.BUILTIN_OPERATIONS.get(builtinKey); - if (builtinOperation != null) { - return builtinOperation.run(ops, list, parseState); + state -> { + Scope scope = state.scope(); + String contents = scope.getOrThrow(unquotedString); + if (!contents.isEmpty() && isAllowedToStartUnquotedString(contents.charAt(0))) { + List arguments = scope.get(argumentList); + if (arguments != null) { + SnbtOperations.BuiltinKey key = new SnbtOperations.BuiltinKey(contents, arguments.size()); + SnbtOperations.BuiltinOperation operation = SnbtOperations.BUILTIN_OPERATIONS.get(key); + if (operation != null) { + return operation.run(ops, arguments, state); } - parseState.errorCollector().store(parseState.mark(), DelayedException.create(ERROR_NO_SUCH_OPERATION, builtinKey.toString())); + state.errorCollector().store(state.mark(), DelayedException.create(ERROR_NO_SUCH_OPERATION, key.toString())); return null; - } else if (string.equalsIgnoreCase("true")) { - return object; - } else if (string.equalsIgnoreCase("false")) { - return object1; - } else if (string.equalsIgnoreCase("null")) { + } else if (contents.equalsIgnoreCase("true")) { + return trueValue; + } else if (contents.equalsIgnoreCase("false")) { + return falseValue; + } else if (contents.equalsIgnoreCase("null")) { return Objects.requireNonNullElseGet(ops.empty(), () -> { - T nullString = ops.createString("null"); - if ("null".equals(nullString)) { // 确定是 Java 类型的 + if (isJavaType) { return (T) CachedParseState.JAVA_NULL_VALUE_MARKER; } return nullString; }); } - return ops.createString(string); + return ops.createString(contents); } - parseState.errorCollector().store(parseState.mark(), SnbtOperations.BUILTIN_IDS, ERROR_INVALID_UNQUOTED_START); + state.errorCollector().store(state.mark(), SnbtOperations.BUILTIN_IDS, ERROR_INVALID_UNQUOTED_START); return null; } ); - Atom atom28 = Atom.of("map_key"); - dictionary.put(atom28, Term.alternative(dictionary.named(atom23), dictionary.named(atom24)), scope -> scope.getAnyOrThrow(atom23, atom24)); - Atom> atom29 = Atom.of("map_entry"); - NamedRule> namedRule3 = dictionary.putComplex( - atom29, Term.sequence(dictionary.named(atom28), StringReaderTerms.character(TagParser.NAME_VALUE_SEPARATOR), dictionary.named(atom25)), parseState -> { - Scope scope = parseState.scope(); - String string = scope.getOrThrow(atom28); - if (string.isEmpty()) { - parseState.errorCollector().store(parseState.mark(), ERROR_EMPTY_KEY); + Atom mapKey = Atom.of("map_key"); + rules.put(mapKey, Term.alternative(rules.named(quotedStringLiteral), rules.named(unquotedString)), scope -> scope.getAnyOrThrow(quotedStringLiteral, unquotedString)); + Atom> mapEntry = Atom.of("map_entry"); + NamedRule> mapEntryRule = rules.putComplex( + mapEntry, Term.sequence(rules.named(mapKey), StringReaderTerms.character(TagParser.NAME_VALUE_SEPARATOR), rules.named(literal)), state -> { + Scope scope = state.scope(); + String key = scope.getOrThrow(mapKey); + if (key.isEmpty()) { + state.errorCollector().store(state.mark(), ERROR_EMPTY_KEY); return null; } - T orThrow = scope.getOrThrow(atom25); - return Map.entry(string, orThrow); + T value = scope.getOrThrow(literal); + return Map.entry(key, value); } ); - Atom>> atom30 = Atom.of("map_entries"); - dictionary.put(atom30, Term.repeatedWithTrailingSeparator(namedRule3, atom30, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom30)); - Atom atom31 = Atom.of("map_literal"); - dictionary.put(atom31, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), dictionary.named(atom30), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { - List> list = scope.getOrThrow(atom30); - if (list.isEmpty()) { - return object2; - } else { - Builder builder = ImmutableMap.builderWithExpectedSize(list.size()); - - for (Entry entry : list) { - builder.put(ops.createString(entry.getKey()), entry.getValue()); - } - - return ops.createMap(builder.buildKeepingLast()); + Atom>> mapEntries = Atom.of("map_entries"); + rules.put(mapEntries, Term.repeatedWithTrailingSeparator(mapEntryRule, mapEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(mapEntries)); + Atom mapLiteral = Atom.of("map_literal"); + rules.put(mapLiteral, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), rules.named(mapEntries), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { + List> entries = scope.getOrThrow(mapEntries); + if (entries.isEmpty()) { + return emptyMapValue; } + Builder builder = ImmutableMap.builderWithExpectedSize(entries.size()); + + for (Entry e : entries) { + builder.put(ops.createString(e.getKey()), e.getValue()); + } + + return ops.createMap(builder.buildKeepingLast()); }); - Atom> atom32 = Atom.of("list_entries"); - dictionary.put( - atom32, Term.repeatedWithTrailingSeparator(dictionary.forward(atom25), atom32, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom32) + Atom> listEntries = Atom.of("list_entries"); + rules.put( + listEntries, Term.repeatedWithTrailingSeparator(rules.forward(literal), listEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(listEntries) ); - Atom atom33 = Atom.of("array_prefix"); - dictionary.put( - atom33, + Atom arrayPrefix = Atom.of("array_prefix"); + rules.put( + arrayPrefix, Term.alternative( - Term.sequence(StringReaderTerms.character('B'), Term.marker(atom33, ArrayPrefix.BYTE)), - Term.sequence(StringReaderTerms.character('L'), Term.marker(atom33, ArrayPrefix.LONG)), - Term.sequence(StringReaderTerms.character('I'), Term.marker(atom33, ArrayPrefix.INT)) + Term.sequence(StringReaderTerms.character('B'), Term.marker(arrayPrefix, ArrayPrefix.BYTE)), + Term.sequence(StringReaderTerms.character('L'), Term.marker(arrayPrefix, ArrayPrefix.LONG)), + Term.sequence(StringReaderTerms.character('I'), Term.marker(arrayPrefix, ArrayPrefix.INT)) ), - scope -> scope.getOrThrow(atom33) + scope -> scope.getOrThrow(arrayPrefix) ); - Atom> atom34 = Atom.of("int_array_entries"); - dictionary.put(atom34, Term.repeatedWithTrailingSeparator(namedRule, atom34, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(atom34)); - Atom atom35 = Atom.of("list_literal"); - dictionary.putComplex( - atom35, + Atom> intArrayEntries = Atom.of("int_array_entries"); + rules.put(intArrayEntries, Term.repeatedWithTrailingSeparator(integerLiteralRule, intArrayEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(intArrayEntries)); + Atom listLiteral = Atom.of("list_literal"); + rules.putComplex( + listLiteral, Term.sequence( StringReaderTerms.character('['), Scope.increaseDepth(), - Term.alternative(Term.sequence(dictionary.named(atom33), StringReaderTerms.character(';'), dictionary.named(atom34)), dictionary.named(atom32)), + Term.alternative(Term.sequence(rules.named(arrayPrefix), StringReaderTerms.character(';'), rules.named(intArrayEntries)), rules.named(listEntries)), Scope.decreaseDepth(), StringReaderTerms.character(']') ), - parseState -> { - Scope scope = parseState.scope(); - ArrayPrefix arrayPrefix = scope.get(atom33); - if (arrayPrefix != null) { - List list = scope.getOrThrow(atom34); - return list.isEmpty() ? arrayPrefix.create(ops) : arrayPrefix.create(ops, list, parseState); + state -> { + Scope scope = state.scope(); + ArrayPrefix arrayType = scope.get(arrayPrefix); + if (arrayType != null) { + List entries = scope.getOrThrow(intArrayEntries); + return entries.isEmpty() ? arrayType.create(ops) : arrayType.create(ops, entries, state); } - List list = scope.getOrThrow(atom32); - return list.isEmpty() ? object3 : ops.createList(list.stream()); + List entries = scope.getOrThrow(listEntries); + return entries.isEmpty() ? emptyList : ops.createList(entries.stream()); } ); - NamedRule namedRule4 = dictionary.putComplex( - atom25, + NamedRule literalRule = rules.putComplex( + literal, Term.alternative( - Term.sequence(Term.positiveLookahead(NUMBER_LOOKEAHEAD), Term.alternative(dictionary.namedWithAlias(atom10, atom25), dictionary.named(atom5))), - Term.sequence(Term.positiveLookahead(StringReaderTerms.characters('"', '\'')), Term.cut(), dictionary.named(atom23)), - Term.sequence(Term.positiveLookahead(StringReaderTerms.character('{')), Term.cut(), dictionary.namedWithAlias(atom31, atom25)), - Term.sequence(Term.positiveLookahead(StringReaderTerms.character('[')), Term.cut(), dictionary.namedWithAlias(atom35, atom25)), - dictionary.namedWithAlias(atom27, atom25) + Term.sequence(Term.positiveLookahead(NUMBER_LOOKEAHEAD), Term.alternative(rules.namedWithAlias(floatLiteral, literal), rules.named(integerLiteral))), + Term.sequence(Term.positiveLookahead(StringReaderTerms.characters('"', '\'')), Term.cut(), rules.named(quotedStringLiteral)), + Term.sequence(Term.positiveLookahead(StringReaderTerms.character('{')), Term.cut(), rules.namedWithAlias(mapLiteral, literal)), + Term.sequence(Term.positiveLookahead(StringReaderTerms.character('[')), Term.cut(), rules.namedWithAlias(listLiteral, literal)), + rules.namedWithAlias(unquotedStringOrBuiltIn, literal) ), - parseState -> { - Scope scope = parseState.scope(); - String string = scope.get(atom23); - if (string != null) { - return ops.createString(string); + state -> { + Scope scope = state.scope(); + String quotedString = scope.get(quotedStringLiteral); + if (quotedString != null) { + return ops.createString(quotedString); } - IntegerLiteral integerLiteral = scope.get(atom5); - return integerLiteral != null ? integerLiteral.create(ops, parseState) : scope.getOrThrow(atom25); + IntegerLiteral integer = scope.get(integerLiteral); + return integer != null ? integer.create(ops, state) : scope.getOrThrow(literal); } ); - return new Grammar<>(dictionary, namedRule4); + return new Grammar<>(rules, literalRule); } enum ArrayPrefix { @@ -643,19 +644,19 @@ public class SnbtGrammar { @Nullable @Override - public T create(DynamicOps ops, List values, ParseState parseState) { - ByteList list = new ByteArrayList(); + public T create(DynamicOps ops, List entries, ParseState state) { + ByteList result = new ByteArrayList(); - for (IntegerLiteral integerLiteral : values) { - Number number = this.buildNumber(integerLiteral, parseState); + for (IntegerLiteral entry : entries) { + Number number = this.buildNumber(entry, state); if (number == null) { return null; } - list.add(number.byteValue()); + result.add(number.byteValue()); } - return ops.createByteList(ByteBuffer.wrap(list.toByteArray())); + return ops.createByteList(ByteBuffer.wrap(result.toByteArray())); } }, INT(TypeSuffix.INT, TypeSuffix.BYTE, TypeSuffix.SHORT) { @@ -666,19 +667,19 @@ public class SnbtGrammar { @Nullable @Override - public T create(DynamicOps ops, List values, ParseState parseState) { - IntStream.Builder builder = IntStream.builder(); + public T create(DynamicOps ops, List entries, ParseState state) { + IntStream.Builder result = IntStream.builder(); - for (IntegerLiteral integerLiteral : values) { - Number number = this.buildNumber(integerLiteral, parseState); - if (number == null) { + for (IntegerLiteral entry : entries) { + Number parsedNumber = this.buildNumber(entry, state); + if (parsedNumber == null) { return null; } - builder.add(number.intValue()); + result.add(parsedNumber.intValue()); } - return ops.createIntList(builder.build()); + return ops.createIntList(result.build()); } }, LONG(TypeSuffix.LONG, TypeSuffix.BYTE, TypeSuffix.SHORT, TypeSuffix.INT) { @@ -689,19 +690,19 @@ public class SnbtGrammar { @Nullable @Override - public T create(DynamicOps ops, List values, ParseState parseState) { - LongStream.Builder builder = LongStream.builder(); + public T create(DynamicOps ops, List entries, ParseState state) { + LongStream.Builder result = LongStream.builder(); - for (IntegerLiteral integerLiteral : values) { - Number number = this.buildNumber(integerLiteral, parseState); - if (number == null) { + for (IntegerLiteral entry : entries) { + Number parsedNumber = this.buildNumber(entry, state); + if (parsedNumber == null) { return null; } - builder.add(number.longValue()); + result.add(parsedNumber.longValue()); } - return ops.createLongList(builder.build()); + return ops.createLongList(result.build()); } }; @@ -713,32 +714,32 @@ public class SnbtGrammar { this.defaultType = defaultType; } - public boolean isAllowed(TypeSuffix suffix) { - return suffix == this.defaultType || this.additionalTypes.contains(suffix); + public boolean isAllowed(TypeSuffix type) { + return type == this.defaultType || this.additionalTypes.contains(type); } public abstract T create(DynamicOps ops); @Nullable - public abstract T create(DynamicOps ops, List values, ParseState parseState); + public abstract T create(DynamicOps ops, List entries, ParseState state); @Nullable - protected Number buildNumber(IntegerLiteral value, ParseState parseState) { - TypeSuffix typeSuffix = this.computeType(value.suffix); - if (typeSuffix == null) { - parseState.errorCollector().store(parseState.mark(), ERROR_INVALID_ARRAY_ELEMENT_TYPE); + protected Number buildNumber(IntegerLiteral entry, ParseState state) { + TypeSuffix actualType = this.computeType(entry.suffix); + if (actualType == null) { + state.errorCollector().store(state.mark(), ERROR_INVALID_ARRAY_ELEMENT_TYPE); return null; } - return (Number)value.create(JavaOps.INSTANCE, typeSuffix, parseState); + return (Number) entry.create(JavaOps.INSTANCE, actualType, state); } @Nullable - private TypeSuffix computeType(IntegerSuffix suffix) { - TypeSuffix typeSuffix = suffix.type(); - if (typeSuffix == null) { + private TypeSuffix computeType(IntegerSuffix value) { + TypeSuffix type = value.type(); + if (type == null) { return this.defaultType; } - return !this.isAllowed(typeSuffix) ? null : typeSuffix; + return !this.isAllowed(type) ? null : type; } } @@ -757,61 +758,61 @@ public class SnbtGrammar { } private String cleanupDigits(Sign sign) { - boolean flag = needsUnderscoreRemoval(this.digits); - if (sign != Sign.MINUS && !flag) { + boolean needsUnderscoreRemoval = needsUnderscoreRemoval(this.digits); + if (sign != Sign.MINUS && !needsUnderscoreRemoval) { return this.digits; } - StringBuilder stringBuilder = new StringBuilder(); - sign.append(stringBuilder); - cleanAndAppend(stringBuilder, this.digits, flag); - return stringBuilder.toString(); + StringBuilder result = new StringBuilder(); + sign.append(result); + cleanAndAppend(result, this.digits, needsUnderscoreRemoval); + return result.toString(); } @Nullable - public T create(DynamicOps ops, ParseState parseState) { - return this.create(ops, Objects.requireNonNullElse(this.suffix.type, TypeSuffix.INT), parseState); + public T create(DynamicOps ops, ParseState state) { + return this.create(ops, Objects.requireNonNullElse(this.suffix.type, TypeSuffix.INT), state); } @Nullable - public T create(DynamicOps ops, TypeSuffix typeSuffix, ParseState parseState) { - boolean flag = this.signedOrDefault() == SignedPrefix.SIGNED; - if (!flag && this.sign == Sign.MINUS) { - parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_NON_NEGATIVE_NUMBER); + public T create(DynamicOps ops, TypeSuffix type, ParseState state) { + boolean isSigned = this.signedOrDefault() == SignedPrefix.SIGNED; + if (!isSigned && this.sign == Sign.MINUS) { + state.errorCollector().store(state.mark(), ERROR_EXPECTED_NON_NEGATIVE_NUMBER); return null; } - String string = this.cleanupDigits(this.sign); + String fixedDigits = this.cleanupDigits(this.sign); - int i = switch (this.base) { + int radix = switch (this.base) { case BINARY -> 2; case DECIMAL -> 10; case HEX -> 16; }; try { - if (flag) { - return switch (typeSuffix) { - case BYTE -> ops.createByte(Byte.parseByte(string, i)); - case SHORT -> ops.createShort(Short.parseShort(string, i)); - case INT -> ops.createInt(Integer.parseInt(string, i)); - case LONG -> ops.createLong(Long.parseLong(string, i)); + if (isSigned) { + return switch (type) { + case BYTE -> ops.createByte(Byte.parseByte(fixedDigits, radix)); + case SHORT -> ops.createShort(Short.parseShort(fixedDigits, radix)); + case INT -> ops.createInt(Integer.parseInt(fixedDigits, radix)); + case LONG -> ops.createLong(Long.parseLong(fixedDigits, radix)); default -> { - parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_INTEGER_TYPE); + state.errorCollector().store(state.mark(), ERROR_EXPECTED_INTEGER_TYPE); yield null; } }; } - return switch (typeSuffix) { - case BYTE -> ops.createByte(com.google.common.primitives.UnsignedBytes.parseUnsignedByte(string, i)); - case SHORT -> ops.createShort(parseUnsignedShort(string, i)); - case INT -> ops.createInt(Integer.parseUnsignedInt(string, i)); - case LONG -> ops.createLong(Long.parseUnsignedLong(string, i)); + return switch (type) { + case BYTE -> ops.createByte(com.google.common.primitives.UnsignedBytes.parseUnsignedByte(fixedDigits, radix)); + case SHORT -> ops.createShort(parseUnsignedShort(fixedDigits, radix)); + case INT -> ops.createInt(Integer.parseUnsignedInt(fixedDigits, radix)); + case LONG -> ops.createLong(Long.parseUnsignedLong(fixedDigits, radix)); default -> { - parseState.errorCollector().store(parseState.mark(), ERROR_EXPECTED_INTEGER_TYPE); + state.errorCollector().store(state.mark(), ERROR_EXPECTED_INTEGER_TYPE); yield null; } }; } catch (NumberFormatException var8) { - parseState.errorCollector().store(parseState.mark(), createNumberParseError(var8)); + state.errorCollector().store(state.mark(), createNumberParseError(var8)); return null; } } @@ -825,9 +826,9 @@ public class SnbtGrammar { PLUS, MINUS; - public void append(StringBuilder stringBuilder) { + public void append(StringBuilder output) { if (this == MINUS) { - stringBuilder.append("-"); + output.append("-"); } } } @@ -841,8 +842,8 @@ public class SnbtGrammar { } static class SimpleHexLiteralParseRule extends GreedyPredicateParseRule { - public SimpleHexLiteralParseRule(int minSize) { - super(minSize, minSize, DelayedException.create(ERROR_EXPECTED_HEX_ESCAPE, String.valueOf(minSize))); + public SimpleHexLiteralParseRule(int size) { + super(size, size, DelayedException.create(ERROR_EXPECTED_HEX_ESCAPE, String.valueOf(size))); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java index 9c684725d..589426cad 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java @@ -26,44 +26,42 @@ public class SnbtOperations { public static final Map BUILTIN_OPERATIONS = Map.of( new BuiltinKey("bool", 1), new BuiltinOperation() { @Override - public T run(DynamicOps ops, List args, ParseState parseState) { - Boolean bool = convert(ops, args.getFirst()); - if (bool == null) { - parseState.errorCollector().store(parseState.mark(), SnbtOperations.ERROR_EXPECTED_NUMBER_OR_BOOLEAN); + public T run(DynamicOps ops, List arguments, ParseState state) { + Boolean result = convert(ops, arguments.getFirst()); + if (result == null) { + state.errorCollector().store(state.mark(), SnbtOperations.ERROR_EXPECTED_NUMBER_OR_BOOLEAN); return null; - } else { - return ops.createBoolean(bool); } + return ops.createBoolean(result); } @Nullable - private static Boolean convert(DynamicOps ops, T value) { - Optional optional = ops.getBooleanValue(value).result(); - if (optional.isPresent()) { - return optional.get(); + private static Boolean convert(DynamicOps ops, T arg) { + Optional asBoolean = ops.getBooleanValue(arg).result(); + if (asBoolean.isPresent()) { + return asBoolean.get(); } else { - Optional optional1 = ops.getNumberValue(value).result(); - return optional1.isPresent() ? optional1.get().doubleValue() != 0.0 : null; + Optional asNumber = ops.getNumberValue(arg).result(); + return asNumber.isPresent() ? asNumber.get().doubleValue() != 0.0 : null; } } }, new BuiltinKey("uuid", 1), new BuiltinOperation() { @Override - public T run(DynamicOps ops, List args, ParseState parseState) { - Optional optional = ops.getStringValue(args.getFirst()).result(); - if (optional.isEmpty()) { - parseState.errorCollector().store(parseState.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID); + public T run(DynamicOps ops, List arguments, ParseState state) { + Optional arg = ops.getStringValue(arguments.getFirst()).result(); + if (arg.isEmpty()) { + state.errorCollector().store(state.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID); return null; - } else { - UUID uuid; - try { - uuid = UUID.fromString(optional.get()); - } catch (IllegalArgumentException var7) { - parseState.errorCollector().store(parseState.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID); - return null; - } - - return ops.createIntList(IntStream.of(UUIDUtil.uuidToIntArray(uuid))); } + UUID uuid; + try { + uuid = UUID.fromString(arg.get()); + } catch (IllegalArgumentException var7) { + state.errorCollector().store(state.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID); + return null; + } + + return ops.createIntList(IntStream.of(UUIDUtil.uuidToIntArray(uuid))); } } ); @@ -74,7 +72,7 @@ public class SnbtOperations { .collect(Collectors.toSet()); @Override - public Stream possibleValues(ParseState parseState) { + public Stream possibleValues(ParseState state) { return this.keys.stream(); } }; @@ -88,6 +86,6 @@ public class SnbtOperations { public interface BuiltinOperation { @Nullable - T run(DynamicOps ops, List args, ParseState parseState); + T run(DynamicOps ops, List arguments, ParseState state); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java index 6c7badbf9..d399e6151 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java @@ -42,34 +42,34 @@ public class TagParser { return new TagParser<>(ops, SnbtGrammar.createParser(ops)); } - private static CompoundTag castToCompoundOrThrow(StringReader reader, Tag tag) throws CommandSyntaxException { - if (tag instanceof CompoundTag compoundTag) { + private static CompoundTag castToCompoundOrThrow(StringReader reader, Tag result) throws CommandSyntaxException { + if (result instanceof CompoundTag compoundTag) { return compoundTag; } throw ERROR_EXPECTED_COMPOUND.createWithContext(reader); } - public static CompoundTag parseCompoundFully(String data) throws CommandSyntaxException { - StringReader stringReader = new StringReader(data); - return parseCompoundAsArgument(stringReader); + public static CompoundTag parseCompoundFully(String input) throws CommandSyntaxException { + StringReader reader = new StringReader(input); + return parseCompoundAsArgument(reader); } - public static Object parseObjectFully(String data) throws CommandSyntaxException { - StringReader stringReader = new StringReader(data); - return parseObjectAsArgument(stringReader); + public static Object parseObjectFully(String input) throws CommandSyntaxException { + StringReader reader = new StringReader(input); + return parseObjectAsArgument(reader); } - public T parseFully(String text) throws CommandSyntaxException { - return this.parseFully(new StringReader(text)); + public T parseFully(String input) throws CommandSyntaxException { + return this.parseFully(new StringReader(input)); } public T parseFully(StringReader reader) throws CommandSyntaxException { - T object = this.grammar.parse(reader); + T result = this.grammar.parse(reader); reader.skipWhitespace(); if (reader.canRead()) { throw ERROR_TRAILING_DATA.createWithContext(reader); } - return object; + return result; } public T parseAsArgument(StringReader reader) throws CommandSyntaxException { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java index 03c439c82..b71d66c24 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java @@ -1,7 +1,5 @@ package net.momirealms.craftengine.core.util.snbt.parse; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; import net.momirealms.craftengine.core.util.MiscUtils; import javax.annotation.Nullable; @@ -14,7 +12,6 @@ public abstract class CachedParseState implements ParseState { private SimpleControl[] controlCache = new SimpleControl[16]; private int nextControlToReturn; private final Silent silent = new Silent(); - private final IntList markedNull = new IntArrayList(); public static final Object JAVA_NULL_VALUE_MARKER = new Object() { @Override public String toString() { @@ -39,11 +36,11 @@ public abstract class CachedParseState implements ParseState { @Nullable @Override public T parse(NamedRule rule) { - int i = this.mark(); - PositionCache cacheForPosition = this.getCacheForPosition(i); - int i1 = cacheForPosition.findKeyIndex(rule.name()); - if (i1 != -1) { - CacheEntry value = cacheForPosition.getValue(i1); + int markBeforeParse = this.mark(); + PositionCache positionCache = this.getCacheForPosition(markBeforeParse); + int entryIndex = positionCache.findKeyIndex(rule.name()); + if (entryIndex != -1) { + CacheEntry value = positionCache.getValue(entryIndex); if (value != null) { if (value == CachedParseState.CacheEntry.NEGATIVE) { return null; @@ -52,59 +49,60 @@ public abstract class CachedParseState implements ParseState { return value.value; } } else { - i1 = cacheForPosition.allocateNewEntry(rule.name()); + entryIndex = positionCache.allocateNewEntry(rule.name()); } - T object = rule.value().parse(this); - CacheEntry cacheEntry; - if (object == null) { - cacheEntry = (CacheEntry) CacheEntry.NEGATIVE; + T result = rule.value().parse(this); + CacheEntry entry; + if (result == null) { + entry = CacheEntry.negativeEntry(); } else { - cacheEntry = new CacheEntry<>(object, this.mark()); + int markAfterParse = this.mark(); + entry = new CacheEntry<>(result, markAfterParse); } - cacheForPosition.setValue(i1, cacheEntry); - return object; + positionCache.setValue(entryIndex, entry); + return result; } - private PositionCache getCacheForPosition(int position) { - int i = this.positionCache.length; - if (position >= i) { - int i1 = MiscUtils.growByHalf(i, position + 1); - PositionCache[] positionCaches = new PositionCache[i1]; - System.arraycopy(this.positionCache, 0, positionCaches, 0, i); - this.positionCache = positionCaches; + private PositionCache getCacheForPosition(int index) { + int currentSize = this.positionCache.length; + if (index >= currentSize) { + int newSize = MiscUtils.growByHalf(currentSize, index + 1); + PositionCache[] newCache = new PositionCache[newSize]; + System.arraycopy(this.positionCache, 0, newCache, 0, currentSize); + this.positionCache = newCache; } - PositionCache positionCache = this.positionCache[position]; - if (positionCache == null) { - positionCache = new PositionCache(); - this.positionCache[position] = positionCache; + PositionCache result = this.positionCache[index]; + if (result == null) { + result = new PositionCache(); + this.positionCache[index] = result; } - return positionCache; + return result; } @Override public Control acquireControl() { - int i = this.controlCache.length; - if (this.nextControlToReturn >= i) { - int i1 = MiscUtils.growByHalf(i, this.nextControlToReturn + 1); - SimpleControl[] simpleControls = new SimpleControl[i1]; - System.arraycopy(this.controlCache, 0, simpleControls, 0, i); - this.controlCache = simpleControls; + int currentSize = this.controlCache.length; + if (this.nextControlToReturn >= currentSize) { + int newSize = MiscUtils.growByHalf(currentSize, this.nextControlToReturn + 1); + SimpleControl[] newControlCache = new SimpleControl[newSize]; + System.arraycopy(this.controlCache, 0, newControlCache, 0, currentSize); + this.controlCache = newControlCache; } - int i1 = this.nextControlToReturn++; - SimpleControl simpleControl = this.controlCache[i1]; - if (simpleControl == null) { - simpleControl = new SimpleControl(); - this.controlCache[i1] = simpleControl; + int controlIndex = this.nextControlToReturn++; + SimpleControl entry = this.controlCache[controlIndex]; + if (entry == null) { + entry = new SimpleControl(); + this.controlCache[controlIndex] = entry; } else { - simpleControl.reset(); + entry.reset(); } - return simpleControl; + return entry; } @Override @@ -117,18 +115,12 @@ public abstract class CachedParseState implements ParseState { return this.silent; } - @Override - public void markNull(int mark) { - this.markedNull.add(mark); - } - - @Override - public boolean isNull(int mark) { - return this.markedNull.contains(mark); - } - record CacheEntry(@Nullable T value, int markAfterParse) { public static final CacheEntry NEGATIVE = new CacheEntry<>(null, -1); + + public static CacheEntry negativeEntry() { + return (CacheEntry) NEGATIVE; + } } static class PositionCache { @@ -137,9 +129,9 @@ public abstract class CachedParseState implements ParseState { private Object[] atomCache = new Object[16]; private int nextKey; - public int findKeyIndex(Atom atom) { + public int findKeyIndex(Atom key) { for (int i = 0; i < this.nextKey; i += ENTRY_STRIDE) { - if (this.atomCache[i] == atom) { + if (this.atomCache[i] == key) { return i; } } @@ -147,29 +139,29 @@ public abstract class CachedParseState implements ParseState { return NOT_FOUND; } - public int allocateNewEntry(Atom entry) { - int i = this.nextKey; - this.nextKey += 2; - int i1 = i + 1; - int i2 = this.atomCache.length; - if (i1 >= i2) { - int i3 = MiscUtils.growByHalf(i2, i1 + 1); - Object[] objects = new Object[i3]; - System.arraycopy(this.atomCache, 0, objects, 0, i2); - this.atomCache = objects; + public int allocateNewEntry(Atom key) { + int newKeyIndex = this.nextKey; + this.nextKey += ENTRY_STRIDE; + int newValueIndex = newKeyIndex + 1; + int currentSize = this.atomCache.length; + if (newValueIndex >= currentSize) { + int newSize = MiscUtils.growByHalf(currentSize, newValueIndex + 1); + Object[] newCache = new Object[newSize]; + System.arraycopy(this.atomCache, 0, newCache, 0, currentSize); + this.atomCache = newCache; } - this.atomCache[i] = entry; - return i; + this.atomCache[newKeyIndex] = key; + return newKeyIndex; } @Nullable - public CacheEntry getValue(int index) { - return (CacheEntry)this.atomCache[index + 1]; + public CacheEntry getValue(int keyIndex) { + return (CacheEntry) this.atomCache[keyIndex + 1]; } - public void setValue(int index, CacheEntry value) { - this.atomCache[index + 1] = value; + public void setValue(int keyIndex, CacheEntry entry) { + this.atomCache[keyIndex + 1] = entry; } } @@ -203,8 +195,8 @@ public abstract class CachedParseState implements ParseState { } @Override - public void restore(int cursor) { - CachedParseState.this.restore(cursor); + public void restore(int mark) { + CachedParseState.this.restore(mark); } @Override @@ -221,16 +213,6 @@ public abstract class CachedParseState implements ParseState { public ParseState silent() { return this; } - - @Override - public void markNull(int mark) { - CachedParseState.this.markNull(mark); - } - - @Override - public boolean isNull(int mark) { - return CachedParseState.this.isNull(mark); - } } static class SimpleControl implements Control { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java index ee79b450c..173b85840 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java @@ -7,13 +7,13 @@ import net.momirealms.craftengine.core.util.snbt.parse.grammar.StringReaderTerms @FunctionalInterface public interface DelayedException { - T create(String message, int cursor); + T create(String contents, int position); - static DelayedException create(SimpleCommandExceptionType exception) { - return (message, cursor) -> exception.createWithContext(StringReaderTerms.createReader(message, cursor)); + static DelayedException create(SimpleCommandExceptionType type) { + return (contents, position) -> type.createWithContext(StringReaderTerms.createReader(contents, position)); } - static DelayedException create(DynamicCommandExceptionType exception, String argument) { - return (message, cursor) -> exception.createWithContext(StringReaderTerms.createReader(message, cursor), argument); + static DelayedException create(DynamicCommandExceptionType type, String argument) { + return (contents, position) -> type.createWithContext(StringReaderTerms.createReader(contents, position), argument); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java index 20d6c4bb2..66ea4e088 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java @@ -11,28 +11,30 @@ import java.util.function.Supplier; public class Dictionary { private final Map, Entry> terms = new IdentityHashMap<>(); - public NamedRule put(Atom name, Rule rule) { - Entry entry = (Entry)this.terms.computeIfAbsent(name, Entry::new); - if (entry.value != null) { + public NamedRule put(Atom name, Rule entry) { + Entry holder = (Entry)this.terms.computeIfAbsent(name, Entry::new); + if (holder.value != null) { throw new IllegalArgumentException("Trying to override rule: " + name); - } else { - entry.value = rule; - return entry; } + holder.value = entry; + return holder; } - public NamedRule putComplex(Atom name, Term term, Rule.RuleAction ruleAction) { - return this.put(name, Rule.fromTerm(term, ruleAction)); + public NamedRule putComplex(Atom name, Term term, Rule.RuleAction action) { + return this.put(name, Rule.fromTerm(term, action)); } - public NamedRule put(Atom name, Term term, Rule.SimpleRuleAction ruleAction) { - return this.put(name, Rule.fromTerm(term, ruleAction)); + public NamedRule put(Atom name, Term term, Rule.SimpleRuleAction action) { + return this.put(name, Rule.fromTerm(term, action)); } public void checkAllBound() { - List> list = this.terms.entrySet().stream().filter(entry -> entry.getValue() == null).map(Map.Entry::getKey).toList(); - if (!list.isEmpty()) { - throw new IllegalStateException("Unbound names: " + list); + List> unboundNames = this.terms.entrySet().stream() + .filter(entry -> entry.getValue() == null) + .map(Map.Entry::getKey) + .toList(); + if (!unboundNames.isEmpty()) { + throw new IllegalStateException("Unbound names: " + unboundNames); } } @@ -48,8 +50,8 @@ public class Dictionary { return new Reference<>(this.getOrCreateEntry(name), name); } - public Term namedWithAlias(Atom name, Atom alias) { - return new Reference<>(this.getOrCreateEntry(name), alias); + public Term namedWithAlias(Atom nameToParse, Atom nameToStore) { + return new Reference<>(this.getOrCreateEntry(nameToParse), nameToStore); } static class Entry implements NamedRule, Supplier { @@ -79,14 +81,13 @@ public class Dictionary { record Reference(Entry ruleToParse, Atom nameToStore) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - T object = parseState.parse(this.ruleToParse); - if (object == null) { + public boolean parse(ParseState state, Scope scope, Control control) { + T result = state.parse(this.ruleToParse); + if (result == null) { return false; - } else { - scope.put(this.nameToStore, object); - return true; } + scope.put(this.nameToStore, result); + return true; } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java index ef0aac7e3..1bb43a0ae 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java @@ -13,7 +13,7 @@ public interface ErrorCollector { this.store(cursor, SuggestionSupplier.empty(), reason); } - void finish(int cursor); + void finish(int finalCursor); class LongestOnly implements ErrorCollector { private MutableErrorEntry[] entries = new MutableErrorEntry[16]; @@ -28,8 +28,8 @@ public interface ErrorCollector { } @Override - public void finish(int cursor) { - this.discardErrorsFromShorterParse(cursor); + public void finish(int finalCursor) { + this.discardErrorsFromShorterParse(finalCursor); } @Override @@ -41,39 +41,38 @@ public interface ErrorCollector { } private void addErrorEntry(SuggestionSupplier suggestions, Object reason) { - int i = this.entries.length; - if (this.nextErrorEntry >= i) { - int i1 = MiscUtils.growByHalf(i, this.nextErrorEntry + 1); - MutableErrorEntry[] mutableErrorEntrys = new MutableErrorEntry[i1]; - System.arraycopy(this.entries, 0, mutableErrorEntrys, 0, i); - this.entries = mutableErrorEntrys; + int currentSize = this.entries.length; + if (this.nextErrorEntry >= currentSize) { + int newSize = MiscUtils.growByHalf(currentSize, this.nextErrorEntry + 1); + MutableErrorEntry[] newEntries = new MutableErrorEntry[newSize]; + System.arraycopy(this.entries, 0, newEntries, 0, currentSize); + this.entries = newEntries; } - int i1 = this.nextErrorEntry++; - MutableErrorEntry mutableErrorEntry = this.entries[i1]; - if (mutableErrorEntry == null) { - mutableErrorEntry = new MutableErrorEntry<>(); - this.entries[i1] = mutableErrorEntry; + int entryIndex = this.nextErrorEntry++; + MutableErrorEntry entry = this.entries[entryIndex]; + if (entry == null) { + entry = new MutableErrorEntry<>(); + this.entries[entryIndex] = entry; } - mutableErrorEntry.suggestions = suggestions; - mutableErrorEntry.reason = reason; + entry.suggestions = suggestions; + entry.reason = reason; } public List> entries() { - int i = this.nextErrorEntry; - if (i == 0) { + int errorCount = this.nextErrorEntry; + if (errorCount == 0) { return List.of(); - } else { - List> list = new ArrayList<>(i); - - for (int i1 = 0; i1 < i; i1++) { - MutableErrorEntry mutableErrorEntry = this.entries[i1]; - list.add(new ErrorEntry<>(this.lastCursor, mutableErrorEntry.suggestions, mutableErrorEntry.reason)); - } - - return list; } + List> result = new ArrayList<>(errorCount); + + for (int i = 0; i < errorCount; i++) { + MutableErrorEntry mutableErrorEntry = this.entries[i]; + result.add(new ErrorEntry<>(this.lastCursor, mutableErrorEntry.suggestions, mutableErrorEntry.reason)); + } + + return result; } public int cursor() { @@ -92,7 +91,7 @@ public interface ErrorCollector { } @Override - public void finish(int cursor) { + public void finish(int finalCursor) { } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java index 5a631476b..2ab971364 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java @@ -9,16 +9,15 @@ public interface ParseState { ErrorCollector errorCollector(); default Optional parseTopRule(NamedRule rule) { - T object = this.parse(rule); - if (object != null) { + T result = this.parse(rule); + if (result != null) { this.errorCollector().finish(this.mark()); } if (!this.scope().hasOnlySingleFrame()) { throw new IllegalStateException("Malformed scope: " + this.scope()); - } else { - return Optional.ofNullable(object); } + return Optional.ofNullable(result); } @Nullable @@ -28,15 +27,11 @@ public interface ParseState { int mark(); - void restore(int cursor); + void restore(int mark); Control acquireControl(); void releaseControl(); ParseState silent(); - - void markNull(int mark); - - boolean isNull(int mark); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java index 6a5728ec4..cfb7403c4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java @@ -4,7 +4,7 @@ import javax.annotation.Nullable; public interface Rule { @Nullable - T parse(ParseState parseState); + T parse(ParseState state); static Rule fromTerm(Term child, RuleAction action) { return new WrappedTerm<>(action, child); @@ -17,38 +17,35 @@ public interface Rule { @FunctionalInterface interface RuleAction { @Nullable - T run(ParseState parseState); + T run(ParseState state); } @FunctionalInterface interface SimpleRuleAction extends RuleAction { - T run(Scope scope); + T run(Scope ruleScope); @Override - default T run(ParseState parseState) { - return this.run(parseState.scope()); + default T run(ParseState state) { + return this.run(state.scope()); } } record WrappedTerm(RuleAction action, Term child) implements Rule { @Nullable @Override - public T parse(ParseState parseState) { - Scope scope = parseState.scope(); + public T parse(ParseState state) { + Scope scope = state.scope(); scope.pushFrame(); - T var3; try { - if (!this.child.parse(parseState, scope, Control.UNBOUND)) { + if (!this.child.parse(state, scope, Control.UNBOUND)) { return null; } - var3 = this.action.run(parseState); + return this.action.run(state); } finally { scope.popFrame(); } - - return var3; } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java index 81ad7ecfd..d152ecc89 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java @@ -28,13 +28,13 @@ public final class Scope { this.stack[1] = null; } - private int valueIndex(Atom name) { + private int valueIndex(Atom atom) { for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { - Object object = this.stack[i]; + Object key = this.stack[i]; - assert object instanceof Atom; + assert key instanceof Atom; - if (object == name) { + if (key == atom) { return i + 1; } } @@ -42,14 +42,14 @@ public final class Scope { return NOT_FOUND; } - public int valueIndexForAny(Atom... names) { + public int valueIndexForAny(Atom... atoms) { for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { - Object object = this.stack[i]; + Object key = this.stack[i]; - assert object instanceof Atom; + assert key instanceof Atom; - for (Atom atom : names) { - if (atom == object) { + for (Atom atom : atoms) { + if (atom == key) { return i + 1; } } @@ -58,15 +58,15 @@ public final class Scope { return NOT_FOUND; } - private void ensureCapacity(int requiredCapacitty) { - int i = this.stack.length; - int i1 = this.topEntryKeyIndex + 1; - int i2 = i1 + requiredCapacitty * 2; - if (i2 >= i) { - int i3 = MiscUtils.growByHalf(i, i2 + 1); - Object[] objects = new Object[i3]; - System.arraycopy(this.stack, 0, objects, 0, i); - this.stack = objects; + private void ensureCapacity(int additionalEntryCount) { + int currentSize = this.stack.length; + int currentLastValueIndex = this.topEntryKeyIndex + 1; + int newLastValueIndex = currentLastValueIndex + additionalEntryCount * 2; + if (newLastValueIndex >= currentSize) { + int newSize = MiscUtils.growByHalf(currentSize, newLastValueIndex + 1); + Object[] newStack = new Object[newSize]; + System.arraycopy(this.stack, 0, newStack, 0, currentSize); + this.stack = newStack; } assert this.validateStructure(); @@ -86,8 +86,8 @@ public final class Scope { assert this.validateStructure(); } - private int getPreviousMarkerIndex(int markerIndex) { - return (Integer)this.stack[markerIndex + 1]; + private int getPreviousMarkerIndex(int markerKeyIndex) { + return (Integer) this.stack[markerKeyIndex + 1]; } public void popFrame() { @@ -100,25 +100,25 @@ public final class Scope { } public void splitFrame() { - int i = this.topMarkerKeyIndex; - int i1 = (this.topEntryKeyIndex - this.topMarkerKeyIndex) / ENTRY_STRIDE; - this.ensureCapacity(i1 + 1); + int currentFrameMarkerIndex = this.topMarkerKeyIndex; + int nonMarkerEntriesInFrame = (this.topEntryKeyIndex - this.topMarkerKeyIndex) / ENTRY_STRIDE; + this.ensureCapacity(nonMarkerEntriesInFrame + 1); this.setupNewFrame(); - int i2 = i + ENTRY_STRIDE; - int i3 = this.topEntryKeyIndex; + int sourceCursor = currentFrameMarkerIndex + ENTRY_STRIDE; + int targetCursor = this.topEntryKeyIndex; - for (int i4 = 0; i4 < i1; i4++) { - i3 += ENTRY_STRIDE; - Object object = this.stack[i2]; + for (int i = 0; i < nonMarkerEntriesInFrame; i++) { + targetCursor += ENTRY_STRIDE; + Object key = this.stack[sourceCursor]; - assert object != null; + assert key != null; - this.stack[i3] = object; - this.stack[i3 + 1] = null; - i2 += ENTRY_STRIDE; + this.stack[targetCursor] = key; + this.stack[targetCursor + 1] = null; + sourceCursor += ENTRY_STRIDE; } - this.topEntryKeyIndex = i3; + this.topEntryKeyIndex = targetCursor; assert this.validateStructure(); } @@ -135,40 +135,40 @@ public final class Scope { public void mergeFrame() { int previousMarkerIndex = this.getPreviousMarkerIndex(this.topMarkerKeyIndex); - int i = previousMarkerIndex; - int i1 = this.topMarkerKeyIndex; + int previousFrameCursor = previousMarkerIndex; + int currentFrameCursor = this.topMarkerKeyIndex; - while (i1 < this.topEntryKeyIndex) { - i += ENTRY_STRIDE; - i1 += ENTRY_STRIDE; - Object object = this.stack[i1]; + while (currentFrameCursor < this.topEntryKeyIndex) { + previousFrameCursor += ENTRY_STRIDE; + currentFrameCursor += ENTRY_STRIDE; + Object newKey = this.stack[currentFrameCursor]; - assert object instanceof Atom; + assert newKey instanceof Atom; - Object object1 = this.stack[i1 + 1]; - Object object2 = this.stack[i]; - if (object2 != object) { - this.stack[i] = object; - this.stack[i + 1] = object1; - } else if (object1 != null) { - this.stack[i + 1] = object1; + Object newValue = this.stack[currentFrameCursor + 1]; + Object oldKey = this.stack[previousFrameCursor]; + if (oldKey != newKey) { + this.stack[previousFrameCursor] = newKey; + this.stack[previousFrameCursor + 1] = newValue; + } else if (newValue != null) { + this.stack[previousFrameCursor + 1] = newValue; } } - this.topEntryKeyIndex = i; + this.topEntryKeyIndex = previousFrameCursor; this.topMarkerKeyIndex = previousMarkerIndex; assert this.validateStructure(); } - public void put(Atom atom, @Nullable T value) { - int i = this.valueIndex(atom); - if (i != NOT_FOUND) { - this.stack[i] = value; + public void put(Atom name, @Nullable T value) { + int valueIndex = this.valueIndex(name); + if (valueIndex != NOT_FOUND) { + this.stack[valueIndex] = value; } else { this.ensureCapacity(1); this.topEntryKeyIndex += ENTRY_STRIDE; - this.stack[this.topEntryKeyIndex] = atom; + this.stack[this.topEntryKeyIndex] = name; this.stack[this.topEntryKeyIndex + 1] = value; } @@ -176,77 +176,75 @@ public final class Scope { } @Nullable - public T get(Atom atom) { - int i = this.valueIndex(atom); - return (T)(i != NOT_FOUND ? this.stack[i] : null); + public T get(Atom name) { + int valueIndex = this.valueIndex(name); + return (T) (valueIndex != NOT_FOUND ? this.stack[valueIndex] : null); } - public T getOrThrow(Atom atom) { - int i = this.valueIndex(atom); - if (i == NOT_FOUND) { - throw new IllegalArgumentException("No value for atom " + atom); - } else { - return (T)this.stack[i]; + public T getOrThrow(Atom name) { + int valueIndex = this.valueIndex(name); + if (valueIndex == NOT_FOUND) { + throw new IllegalArgumentException("No value for atom " + name); } + return (T) this.stack[valueIndex]; } - public T getOrDefault(Atom atom, T defaultValue) { - int i = this.valueIndex(atom); - return (T)(i != NOT_FOUND ? this.stack[i] : defaultValue); + public T getOrDefault(Atom name, T fallback) { + int valueIndex = this.valueIndex(name); + return (T) (valueIndex != NOT_FOUND ? this.stack[valueIndex] : fallback); } @Nullable @SafeVarargs - public final T getAny(Atom... atoms) { - int i = this.valueIndexForAny(atoms); - return (T)(i != NOT_FOUND ? this.stack[i] : null); + public final T getAny(Atom... names) { + int valueIndex = this.valueIndexForAny(names); + return (T) (valueIndex != NOT_FOUND ? this.stack[valueIndex] : null); } @SafeVarargs - public final T getAnyOrThrow(Atom... atoms) { - int i = this.valueIndexForAny(atoms); - if (i == NOT_FOUND) { - throw new IllegalArgumentException("No value for atoms " + Arrays.toString(atoms)); - } else { - return (T)this.stack[i]; + public final T getAnyOrThrow(Atom... names) { + int valueIndex = this.valueIndexForAny(names); + if (valueIndex == NOT_FOUND) { + throw new IllegalArgumentException("No value for atoms " + Arrays.toString(names)); } + return (T) this.stack[valueIndex]; } @Override public String toString() { - StringBuilder stringBuilder = new StringBuilder(); - boolean flag = true; + StringBuilder result = new StringBuilder(); + boolean afterFrame = true; for (int i = 0; i <= this.topEntryKeyIndex; i += ENTRY_STRIDE) { - Object object = this.stack[i]; - Object object1 = this.stack[i + 1]; - if (object == FRAME_START_MARKER) { - stringBuilder.append('|'); - flag = true; + Object key = this.stack[i]; + Object value = this.stack[i + 1]; + if (key == FRAME_START_MARKER) { + result.append('|'); + afterFrame = true; } else { - if (!flag) { - stringBuilder.append(','); + if (!afterFrame) { + result.append(','); } - flag = false; - stringBuilder.append(object).append(':').append(object1); + afterFrame = false; + result.append(key).append(':').append(value); } } - return stringBuilder.toString(); + return result.toString(); } @VisibleForTesting public Map, ?> lastFrame() { - HashMap, Object> map = new HashMap<>(); + HashMap, Object> result = new HashMap<>(); for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { - Object object = this.stack[i]; - Object object1 = this.stack[i + 1]; - map.put((Atom)object, object1); + Object key = this.stack[i]; + Object value = this.stack[i + 1]; + result.put((Atom) key, value); } - return map; + return result; } public boolean hasOnlySingleFrame() { @@ -258,9 +256,8 @@ public final class Scope { if (this.stack[0] != FRAME_START_MARKER) { throw new IllegalStateException("Corrupted stack"); - } else { - return true; } + return true; } private boolean validateStructure() { @@ -285,14 +282,15 @@ public final class Scope { return true; } - @SuppressWarnings({"unchecked","rawtypes"}) + @SuppressWarnings({"unchecked", "rawtypes"}) public static Term increaseDepth() { class IncreasingDepthTerm implements Term { public static final IncreasingDepthTerm INSTANCE = new IncreasingDepthTerm(); + @Override - public boolean parse(final ParseState parseState, final Scope scope, final Control control) { + public boolean parse(final ParseState state, final Scope scope, final Control control) { if (++scope.depth > 512) { - parseState.errorCollector().store(parseState.mark(), new IllegalStateException("Too deep")); + state.errorCollector().store(state.mark(), new IllegalStateException("Too deep")); return false; } return true; @@ -301,12 +299,13 @@ public final class Scope { return (Term) IncreasingDepthTerm.INSTANCE; } - @SuppressWarnings({"unchecked","rawtypes"}) + @SuppressWarnings({"unchecked", "rawtypes"}) public static Term decreaseDepth() { class DecreasingDepthTerm implements Term { public static final DecreasingDepthTerm INSTANCE = new DecreasingDepthTerm(); + @Override - public boolean parse(final ParseState parseState, final Scope scope, final Control control) { + public boolean parse(final ParseState state, final Scope scope, final Control control) { scope.depth--; return true; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java index ed88ccd42..30c32ac7c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java @@ -3,9 +3,9 @@ package net.momirealms.craftengine.core.util.snbt.parse; import java.util.stream.Stream; public interface SuggestionSupplier { - Stream possibleValues(ParseState parseState); + Stream possibleValues(ParseState state); static SuggestionSupplier empty() { - return parseState -> Stream.empty(); + return state -> Stream.empty(); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java index ceab78ef0..383602b0e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java @@ -4,20 +4,20 @@ import java.util.ArrayList; import java.util.List; public interface Term { - boolean parse(ParseState parseState, Scope scope, Control control); + boolean parse(ParseState state, Scope scope, Control control); static Term marker(Atom name, T value) { return new Marker<>(name, value); } @SafeVarargs - static Term sequence(Term... elements) { - return new Sequence<>(elements); + static Term sequence(Term... terms) { + return new Sequence<>(terms); } @SafeVarargs - static Term alternative(Term... elements) { - return new Alternative<>(elements); + static Term alternative(Term... terms) { + return new Alternative<>(terms); } static Term optional(Term term) { @@ -32,8 +32,8 @@ public interface Term { return new Repeated<>(element, listName, minRepetitions); } - static Term repeatedWithTrailingSeparator(NamedRule element, Atom> listName, Term seperator) { - return repeatedWithTrailingSeparator(element, listName, seperator, 0); + static Term repeatedWithTrailingSeparator(NamedRule element, Atom> listName, Term separator) { + return repeatedWithTrailingSeparator(element, listName, separator, 0); } static Term repeatedWithTrailingSeparator(NamedRule element, Atom> listName, Term seperator, int minRepetitions) { @@ -47,7 +47,7 @@ public interface Term { static Term cut() { return new Term<>() { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { + public boolean parse(ParseState state, Scope scope, Control control) { control.cut(); return true; } @@ -62,7 +62,7 @@ public interface Term { static Term empty() { return new Term<>() { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { + public boolean parse(ParseState state, Scope scope, Control control) { return true; } @@ -73,11 +73,11 @@ public interface Term { }; } - static Term fail(final Object reason) { + static Term fail(final Object message) { return new Term<>() { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - parseState.errorCollector().store(parseState.mark(), reason); + public boolean parse(ParseState state, Scope scope, Control control) { + state.errorCollector().store(state.mark(), message); return false; } @@ -90,22 +90,22 @@ public interface Term { record Alternative(Term[] elements) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - Control control1 = parseState.acquireControl(); + public boolean parse(ParseState state, Scope scope, Control control) { + Control controlForThis = state.acquireControl(); try { - int i = parseState.mark(); + int mark = state.mark(); scope.splitFrame(); - for (Term term : this.elements) { - if (term.parse(parseState, scope, control1)) { + for (Term element : this.elements) { + if (element.parse(state, scope, controlForThis)) { scope.mergeFrame(); return true; } scope.clearFrameValues(); - parseState.restore(i); - if (control1.hasCut()) { + state.restore(mark); + if (controlForThis.hasCut()) { break; } } @@ -113,24 +113,24 @@ public interface Term { scope.popFrame(); return false; } finally { - parseState.releaseControl(); + state.releaseControl(); } } } record LookAhead(Term term, boolean positive) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - int i = parseState.mark(); - boolean flag = this.term.parse(parseState.silent(), scope, control); - parseState.restore(i); - return this.positive == flag; + public boolean parse(ParseState state, Scope scope, Control control) { + int mark = state.mark(); + boolean result = this.term.parse(state.silent(), scope, control); + state.restore(mark); + return this.positive == result; } } record Marker(Atom name, T value) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { + public boolean parse(ParseState state, Scope scope, Control control) { scope.put(this.name, this.value); return true; } @@ -138,10 +138,10 @@ public interface Term { record Maybe(Term term) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - int i = parseState.mark(); - if (!this.term.parse(parseState, scope, control)) { - parseState.restore(i); + public boolean parse(ParseState state, Scope scope, Control control) { + int mark = state.mark(); + if (!this.term.parse(state, scope, control)) { + state.restore(mark); } return true; @@ -150,25 +150,24 @@ public interface Term { record Repeated(NamedRule element, Atom> listName, int minRepetitions) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - int i = parseState.mark(); - List list = new ArrayList<>(this.minRepetitions); + public boolean parse(ParseState state, Scope scope, Control control) { + int mark = state.mark(); + List elements = new ArrayList<>(this.minRepetitions); while (true) { - int i1 = parseState.mark(); - T object = parseState.parse(this.element); - if (object == null) { - parseState.restore(i1); - if (list.size() < this.minRepetitions) { - parseState.restore(i); + int entryMark = state.mark(); + T parsedElement = state.parse(this.element); + if (parsedElement == null) { + state.restore(entryMark); + if (elements.size() < this.minRepetitions) { + state.restore(mark); return false; - } else { - scope.put(this.listName, list); - return true; } + scope.put(this.listName, elements); + return true; } - list.add(object); + elements.add(parsedElement); } } } @@ -177,56 +176,55 @@ public interface Term { NamedRule element, Atom> listName, Term separator, int minRepetitions, boolean allowTrailingSeparator ) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - int i = parseState.mark(); - List list = new ArrayList<>(this.minRepetitions); - boolean flag = true; + public boolean parse(ParseState state, Scope scope, Control control) { + int listMark = state.mark(); + List elements = new ArrayList<>(this.minRepetitions); + boolean first = true; while (true) { - int i1 = parseState.mark(); - if (!flag && !this.separator.parse(parseState, scope, control)) { - parseState.restore(i1); + int markBeforeSeparator = state.mark(); + if (!first && !this.separator.parse(state, scope, control)) { + state.restore(markBeforeSeparator); break; } - int i2 = parseState.mark(); - T object = parseState.parse(this.element); - if (object == null) { - if (flag) { - parseState.restore(i2); + int markAfterSeparator = state.mark(); + T parsedElement = state.parse(this.element); + if (parsedElement == null) { + if (first) { + state.restore(markAfterSeparator); } else { if (!this.allowTrailingSeparator) { - parseState.restore(i); + state.restore(listMark); return false; } - parseState.restore(i2); + state.restore(markAfterSeparator); } break; } - list.add(object); - flag = false; + elements.add(parsedElement); + first = false; } - if (list.size() < this.minRepetitions) { - parseState.restore(i); + if (elements.size() < this.minRepetitions) { + state.restore(listMark); return false; - } else { - scope.put(this.listName, list); - return true; } + scope.put(this.listName, elements); + return true; } } record Sequence(Term[] elements) implements Term { @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - int i = parseState.mark(); + public boolean parse(ParseState state, Scope scope, Control control) { + int mark = state.mark(); - for (Term term : this.elements) { - if (!term.parse(parseState, scope, control)) { - parseState.restore(i); + for (Term element : this.elements) { + if (!element.parse(state, scope, control)) { + state.restore(mark); return false; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java index 2f13a7448..e93117570 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java @@ -13,41 +13,39 @@ public record Grammar(Dictionary rules, NamedRule parse(ParseState parseState) { - return parseState.parseTopRule(this.top); + public Optional parse(ParseState state) { + return state.parseTopRule(this.top); } public T parse(StringReader reader) throws CommandSyntaxException { - ErrorCollector.LongestOnly longestOnly = new ErrorCollector.LongestOnly<>(); - StringReaderParserState stringReaderParserState = new StringReaderParserState(longestOnly, reader); - Optional optional = this.parse(stringReaderParserState); - if (optional.isPresent()) { - T result = optional.get(); + ErrorCollector.LongestOnly errorCollector = new ErrorCollector.LongestOnly<>(); + StringReaderParserState stringReaderParserState = new StringReaderParserState(errorCollector, reader); + Optional optionalResult = this.parse(stringReaderParserState); + if (optionalResult.isPresent()) { + T result = optionalResult.get(); if (CachedParseState.JAVA_NULL_VALUE_MARKER.equals(result)) { result = null; } return result; - } else { - List> list = longestOnly.entries(); - List list1 = list.stream().mapMulti((errorEntry, consumer) -> { - if (errorEntry.reason() instanceof DelayedException delayedException) { - consumer.accept(delayedException.create(reader.getString(), errorEntry.cursor())); - } else if (errorEntry.reason() instanceof Exception exception1) { - consumer.accept(exception1); - } - }).toList(); - - for (Exception exception : list1) { - if (exception instanceof CommandSyntaxException commandSyntaxException) { - throw commandSyntaxException; - } + } + List> errorEntries = errorCollector.entries(); + List exceptions = errorEntries.stream().mapMulti((entry, output) -> { + if (entry.reason() instanceof DelayedException delayedException) { + output.accept(delayedException.create(reader.getString(), entry.cursor())); + } else if (entry.reason() instanceof Exception exception1) { + output.accept(exception1); } + }).toList(); - if (list1.size() == 1 && list1.getFirst() instanceof RuntimeException runtimeException) { - throw runtimeException; - } else { - throw new IllegalStateException("Failed to parse: " + list.stream().map(ErrorEntry::toString).collect(Collectors.joining(", "))); + for (Exception exception : exceptions) { + if (exception instanceof CommandSyntaxException cse) { + throw cse; } } + + if (exceptions.size() == 1 && exceptions.getFirst() instanceof RuntimeException re) { + throw re; + } + throw new IllegalStateException("Failed to parse: " + errorEntries.stream().map(ErrorEntry::toString).collect(Collectors.joining(", "))); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java index 9d5e07b91..58bf9d797 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java @@ -19,16 +19,15 @@ public final class GreedyPatternParseRule implements Rule } @Override - public String parse(ParseState parseState) { - StringReader stringReader = parseState.input(); - String string = stringReader.getString(); - Matcher matcher = this.pattern.matcher(string).region(stringReader.getCursor(), string.length()); + public String parse(ParseState state) { + StringReader input = state.input(); + String fullString = input.getString(); + Matcher matcher = this.pattern.matcher(fullString).region(input.getCursor(), fullString.length()); if (!matcher.lookingAt()) { - parseState.errorCollector().store(parseState.mark(), this.error); + state.errorCollector().store(state.mark(), this.error); return null; - } else { - stringReader.setCursor(matcher.end()); - return matcher.group(0); } + input.setCursor(matcher.end()); + return matcher.group(0); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java index d3c8d8870..3144dcc27 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java @@ -25,24 +25,23 @@ public abstract class GreedyPredicateParseRule implements Rule parseState) { - StringReader stringReader = parseState.input(); - String string = stringReader.getString(); - int cursor = stringReader.getCursor(); - int i = cursor; + public String parse(ParseState state) { + StringReader input = state.input(); + String fullString = input.getString(); + int start = input.getCursor(); + int pos = start; - while (i < string.length() && this.isAccepted(string.charAt(i)) && i - cursor < this.maxSize) { - i++; + while (pos < fullString.length() && this.isAccepted(fullString.charAt(pos)) && pos - start < this.maxSize) { + pos++; } - int i1 = i - cursor; - if (i1 < this.minSize) { - parseState.errorCollector().store(parseState.mark(), this.error); + int length = pos - start; + if (length < this.minSize) { + state.errorCollector().store(state.mark(), this.error); return null; - } else { - stringReader.setCursor(i); - return string.substring(cursor, i); } + input.setCursor(pos); + return fullString.substring(start, pos); } protected abstract boolean isAccepted(char c); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java index 71a919544..dd4e6249b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java @@ -19,28 +19,27 @@ public abstract class NumberRunParseRule implements Rule { @Nullable @Override - public String parse(ParseState parseState) { - StringReader stringReader = parseState.input(); - stringReader.skipWhitespace(); - String string = stringReader.getString(); - int cursor = stringReader.getCursor(); - int i = cursor; + public String parse(ParseState state) { + StringReader input = state.input(); + input.skipWhitespace(); + String fullString = input.getString(); + int start = input.getCursor(); + int pos = start; - while (i < string.length() && this.isAccepted(string.charAt(i))) { - i++; + while (pos < fullString.length() && this.isAccepted(fullString.charAt(pos))) { + pos++; } - int i1 = i - cursor; - if (i1 == 0) { - parseState.errorCollector().store(parseState.mark(), this.noValueError); - return null; - } else if (string.charAt(cursor) != '_' && string.charAt(i - 1) != '_') { - stringReader.setCursor(i); - return string.substring(cursor, i); - } else { - parseState.errorCollector().store(parseState.mark(), this.underscoreNotAllowedError); + int length = pos - start; + if (length == 0) { + state.errorCollector().store(state.mark(), this.noValueError); return null; + } else if (fullString.charAt(start) != '_' && fullString.charAt(pos - 1) != '_') { + input.setCursor(pos); + return fullString.substring(start, pos); } + state.errorCollector().store(state.mark(), this.underscoreNotAllowedError); + return null; } protected abstract boolean isAccepted(char c); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java index 9bf0a87b9..d076fafd4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java @@ -23,7 +23,7 @@ public class StringReaderParserState extends CachedParseState { } @Override - public void restore(int cursor) { - this.input.setCursor(cursor); + public void restore(int mark) { + this.input.setCursor(mark); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java index 12cb7def3..14fe3f3aa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java @@ -16,50 +16,49 @@ public interface StringReaderTerms { static Term character(final char value) { return new TerminalCharacters(CharList.of(value)) { @Override - protected boolean isAccepted(char c) { - return value == c; + protected boolean isAccepted(char v) { + return value == v; } }; } - static Term characters(final char value1, final char value2) { - return new TerminalCharacters(CharList.of(value1, value2)) { + static Term characters(final char v1, final char v2) { + return new TerminalCharacters(CharList.of(v1, v2)) { @Override - protected boolean isAccepted(char c) { - return c == value1 || c == value2; + protected boolean isAccepted(char v) { + return v == v1 || v == v2; } }; } - static StringReader createReader(String input, int cursor) { - StringReader stringReader = new StringReader(input); - stringReader.setCursor(cursor); - return stringReader; + static StringReader createReader(String contents, int cursor) { + StringReader reader = new StringReader(contents); + reader.setCursor(cursor); + return reader; } abstract class TerminalCharacters implements Term { private final DelayedException error; private final SuggestionSupplier suggestions; - public TerminalCharacters(CharList characters) { - String string = characters.intStream().mapToObj(Character::toString).collect(Collectors.joining("|")); - this.error = DelayedException.create(LITERAL_INCORRECT, string); - this.suggestions = parseState -> characters.intStream().mapToObj(Character::toString); + public TerminalCharacters(CharList values) { + String joinedValues = values.intStream().mapToObj(Character::toString).collect(Collectors.joining("|")); + this.error = DelayedException.create(LITERAL_INCORRECT, joinedValues); + this.suggestions = s -> values.intStream().mapToObj(Character::toString); } @Override - public boolean parse(ParseState parseState, Scope scope, Control control) { - parseState.input().skipWhitespace(); - int i = parseState.mark(); - if (parseState.input().canRead() && this.isAccepted(parseState.input().read())) { + public boolean parse(ParseState state, Scope scope, Control control) { + state.input().skipWhitespace(); + int cursor = state.mark(); + if (state.input().canRead() && this.isAccepted(state.input().read())) { return true; - } else { - parseState.errorCollector().store(i, this.suggestions, this.error); - return false; } + state.errorCollector().store(cursor, this.suggestions, this.error); + return false; } - protected abstract boolean isAccepted(char c); + protected abstract boolean isAccepted(char value); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java index 1909633c8..2d46092b7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java @@ -19,15 +19,14 @@ public class UnquotedStringParseRule implements Rule { @Nullable @Override - public String parse(ParseState parseState) { - parseState.input().skipWhitespace(); - int i = parseState.mark(); - String unquotedString = parseState.input().readUnquotedString(); - if (unquotedString.length() < this.minSize) { - parseState.errorCollector().store(i, this.error); + public String parse(ParseState state) { + state.input().skipWhitespace(); + int cursor = state.mark(); + String value = state.input().readUnquotedString(); + if (value.length() < this.minSize) { + state.errorCollector().store(cursor, this.error); return null; - } else { - return unquotedString; } + return value; } } From edfdf083e062c2d9f62a44a64dfbb3056ffe6f06 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 07:57:46 +0800 Subject: [PATCH 054/135] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/util/snbt/SnbtGrammar.java | 556 +++++++++++------- .../LocalizedCommandSyntaxException.java | 2 +- 2 files changed, 334 insertions(+), 224 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java index 74512176c..3dfa6cbde 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -24,7 +24,7 @@ import java.util.stream.LongStream; public class SnbtGrammar { private static final DynamicCommandExceptionType ERROR_NUMBER_PARSE_FAILURE = new LocalizedDynamicCommandExceptionType( - number -> new LocalizedMessage("warning.config.type.snbt.parser.number_parse_failure", String.valueOf(number)) + message -> new LocalizedMessage("warning.config.type.snbt.parser.number_parse_failure", String.valueOf(message)) ); static final DynamicCommandExceptionType ERROR_EXPECTED_HEX_ESCAPE = new LocalizedDynamicCommandExceptionType( length -> new LocalizedMessage("warning.config.type.snbt.parser.expected_hex_escape", String.valueOf(length)) @@ -248,152 +248,151 @@ public class SnbtGrammar { T emptyList = ops.emptyList(); T nullString = ops.createString("null"); boolean isJavaType = "null".equals(nullString); // 确定是 Java 类型的 + Dictionary rules = new Dictionary<>(); + Atom sign = Atom.of("sign"); rules.put( sign, - Term.alternative( - Term.sequence(StringReaderTerms.character('+'), Term.marker(sign, Sign.PLUS)), - Term.sequence(StringReaderTerms.character('-'), Term.marker(sign, Sign.MINUS)) - ), + Term.alternative( + Term.sequence(StringReaderTerms.character('+'), Term.marker(sign, Sign.PLUS)), + Term.sequence(StringReaderTerms.character('-'), Term.marker(sign, Sign.MINUS)) + ), scope -> scope.getOrThrow(sign) ); + Atom integerSuffix = Atom.of("integer_suffix"); rules.put( integerSuffix, - Term.alternative( - Term.sequence( - StringReaderTerms.characters('u', 'U'), - Term.alternative( + Term.alternative( Term.sequence( - StringReaderTerms.characters('b', 'B'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.BYTE)) + StringReaderTerms.characters('u', 'U'), + Term.alternative( + Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.BYTE))), + Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.SHORT))), + Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.INT))), + Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.LONG))) + ) ), Term.sequence( - StringReaderTerms.characters('s', 'S'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.SHORT)) + StringReaderTerms.characters('s', 'S'), + Term.alternative( + Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.BYTE))), + Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.SHORT))), + Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.INT))), + Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.LONG))) + ) ), - Term.sequence( - StringReaderTerms.characters('i', 'I'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.INT)) - ), - Term.sequence( - StringReaderTerms.characters('l', 'L'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.LONG)) - ) - ) + Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.BYTE))), + Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.SHORT))), + Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.INT))), + Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.LONG))) ), - Term.sequence( - StringReaderTerms.characters('s', 'S'), - Term.alternative( - Term.sequence( - StringReaderTerms.characters('b', 'B'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.BYTE)) - ), - Term.sequence( - StringReaderTerms.characters('s', 'S'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.SHORT)) - ), - Term.sequence( - StringReaderTerms.characters('i', 'I'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.INT)) - ), - Term.sequence( - StringReaderTerms.characters('l', 'L'), - Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.LONG)) - ) - ) - ), - Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.BYTE))), - Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.SHORT))), - Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.INT))), - Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.LONG))) - ), scope -> scope.getOrThrow(integerSuffix) ); + Atom binaryNumeral = Atom.of("binary_numeral"); rules.put(binaryNumeral, BINARY_NUMERAL); + Atom decimalNumeral = Atom.of("decimal_numeral"); rules.put(decimalNumeral, DECIMAL_NUMERAL); + Atom hexNumeral = Atom.of("hex_numeral"); rules.put(hexNumeral, HEX_NUMERAL); + Atom integerLiteral = Atom.of("integer_literal"); NamedRule integerLiteralRule = rules.put( integerLiteral, - Term.sequence( - Term.optional(rules.named(sign)), - Term.alternative( - Term.sequence( - StringReaderTerms.character('0'), - Term.cut(), + Term.sequence( + Term.optional(rules.named(sign)), Term.alternative( - Term.sequence(StringReaderTerms.characters('x', 'X'), Term.cut(), rules.named(hexNumeral)), - Term.sequence(StringReaderTerms.characters('b', 'B'), rules.named(binaryNumeral)), - Term.sequence(rules.named(decimalNumeral), Term.cut(), Term.fail(ERROR_LEADING_ZERO_NOT_ALLOWED)), - Term.marker(decimalNumeral, "0") - ) - ), - rules.named(decimalNumeral) + Term.sequence( + StringReaderTerms.character('0'), + Term.cut(), + Term.alternative( + Term.sequence(StringReaderTerms.characters('x', 'X'), Term.cut(), rules.named(hexNumeral)), + Term.sequence(StringReaderTerms.characters('b', 'B'), rules.named(binaryNumeral)), + Term.sequence(rules.named(decimalNumeral), Term.cut(), Term.fail(ERROR_LEADING_ZERO_NOT_ALLOWED)), + Term.marker(decimalNumeral, "0") + ) + ), + rules.named(decimalNumeral) + ), + Term.optional(rules.named(integerSuffix)) ), - Term.optional(rules.named(integerSuffix)) - ), - scope -> { - IntegerSuffix suffix = scope.getOrDefault(integerSuffix, IntegerSuffix.EMPTY); - Sign signValue = scope.getOrDefault(sign, Sign.PLUS); - String decimalContents = scope.get(decimalNumeral); - if (decimalContents != null) { - return new IntegerLiteral(signValue, Base.DECIMAL, decimalContents, suffix); + scope -> { + IntegerSuffix suffix = scope.getOrDefault(integerSuffix, IntegerSuffix.EMPTY); + Sign signValue = scope.getOrDefault(sign, Sign.PLUS); + String decimalContents = scope.get(decimalNumeral); + if (decimalContents != null) { + return new IntegerLiteral(signValue, Base.DECIMAL, decimalContents, suffix); + } + String hexContents = scope.get(hexNumeral); + if (hexContents != null) { + return new IntegerLiteral(signValue, Base.HEX, hexContents, suffix); + } + String binaryContents = scope.getOrThrow(binaryNumeral); + return new IntegerLiteral(signValue, Base.BINARY, binaryContents, suffix); } - String string1 = scope.get(hexNumeral); - if (string1 != null) { - return new IntegerLiteral(signValue, Base.HEX, string1, suffix); - } - String string2 = scope.getOrThrow(binaryNumeral); - return new IntegerLiteral(signValue, Base.BINARY, string2, suffix); - } ); + Atom floatTypeSuffix = Atom.of("float_type_suffix"); rules.put( floatTypeSuffix, - Term.alternative( - Term.sequence(StringReaderTerms.characters('f', 'F'), Term.marker(floatTypeSuffix, TypeSuffix.FLOAT)), - Term.sequence(StringReaderTerms.characters('d', 'D'), Term.marker(floatTypeSuffix, TypeSuffix.DOUBLE)) - ), + Term.alternative( + Term.sequence(StringReaderTerms.characters('f', 'F'), Term.marker(floatTypeSuffix, TypeSuffix.FLOAT)), + Term.sequence(StringReaderTerms.characters('d', 'D'), Term.marker(floatTypeSuffix, TypeSuffix.DOUBLE)) + ), scope -> scope.getOrThrow(floatTypeSuffix) ); + Atom> floatExponentPart = Atom.of("float_exponent_part"); rules.put( floatExponentPart, - Term.sequence(StringReaderTerms.characters('e', 'E'), Term.optional(rules.named(sign)), rules.named(decimalNumeral)), + Term.sequence( + StringReaderTerms.characters('e', 'E'), + Term.optional(rules.named(sign)), + rules.named(decimalNumeral) + ), scope -> new Signed<>(scope.getOrDefault(sign, Sign.PLUS), scope.getOrThrow(decimalNumeral)) ); + Atom floatWholePart = Atom.of("float_whole_part"); Atom floatFractionPart = Atom.of("float_fraction_part"); Atom floatLiteral = Atom.of("float_literal"); rules.putComplex( floatLiteral, - Term.sequence( - Term.optional(rules.named(sign)), - Term.alternative( - Term.sequence( - rules.namedWithAlias(decimalNumeral, floatWholePart), - StringReaderTerms.character('.'), - Term.cut(), - Term.optional(rules.namedWithAlias(decimalNumeral, floatFractionPart)), - Term.optional(rules.named(floatExponentPart)), - Term.optional(rules.named(floatTypeSuffix)) - ), - Term.sequence( - StringReaderTerms.character('.'), - Term.cut(), - rules.namedWithAlias(decimalNumeral, floatFractionPart), - Term.optional(rules.named(floatExponentPart)), - Term.optional(rules.named(floatTypeSuffix)) - ), - Term.sequence(rules.namedWithAlias(decimalNumeral, floatWholePart), rules.named(floatExponentPart), Term.cut(), Term.optional(rules.named(floatTypeSuffix))), - Term.sequence(rules.namedWithAlias(decimalNumeral, floatWholePart), Term.optional(rules.named(floatExponentPart)), rules.named(floatTypeSuffix)) - ) - ), + Term.sequence( + Term.optional(rules.named(sign)), + Term.alternative( + Term.sequence( + rules.namedWithAlias(decimalNumeral, floatWholePart), + StringReaderTerms.character('.'), + Term.cut(), + Term.optional(rules.namedWithAlias(decimalNumeral, floatFractionPart)), + Term.optional(rules.named(floatExponentPart)), + Term.optional(rules.named(floatTypeSuffix)) + ), + Term.sequence( + StringReaderTerms.character('.'), + Term.cut(), + rules.namedWithAlias(decimalNumeral, floatFractionPart), + Term.optional(rules.named(floatExponentPart)), + Term.optional(rules.named(floatTypeSuffix)) + ), + Term.sequence( + rules.namedWithAlias(decimalNumeral, floatWholePart), + rules.named(floatExponentPart), + Term.cut(), + Term.optional(rules.named(floatTypeSuffix)) + ), + Term.sequence( + rules.namedWithAlias(decimalNumeral, floatWholePart), + Term.optional(rules.named(floatExponentPart)), + rules.named(floatTypeSuffix) + ) + ) + ), state -> { Scope scope = state.scope(); Sign wholeSign = scope.getOrDefault(sign, Sign.PLUS); @@ -402,116 +401,160 @@ public class SnbtGrammar { Signed exponent = scope.get(floatExponentPart); TypeSuffix typeSuffix = scope.get(floatTypeSuffix); return createFloat(ops, wholeSign, whole, fraction, exponent, typeSuffix, state); - } + } ); + Atom stringHex2 = Atom.of("string_hex_2"); rules.put(stringHex2, new SimpleHexLiteralParseRule(2)); + Atom stringHex4 = Atom.of("string_hex_4"); rules.put(stringHex4, new SimpleHexLiteralParseRule(4)); + Atom stringHex8 = Atom.of("string_hex_8"); rules.put(stringHex8, new SimpleHexLiteralParseRule(8)); + Atom stringUnicodeName = Atom.of("string_unicode_name"); rules.put(stringUnicodeName, new GreedyPatternParseRule(UNICODE_NAME, ERROR_INVALID_CHARACTER_NAME)); + Atom stringEscapeSequence = Atom.of("string_escape_sequence"); rules.putComplex( stringEscapeSequence, - Term.alternative( - Term.sequence(StringReaderTerms.character('b'), Term.marker(stringEscapeSequence, "\b")), - Term.sequence(StringReaderTerms.character('s'), Term.marker(stringEscapeSequence, " ")), - Term.sequence(StringReaderTerms.character('t'), Term.marker(stringEscapeSequence, "\t")), - Term.sequence(StringReaderTerms.character('n'), Term.marker(stringEscapeSequence, "\n")), - Term.sequence(StringReaderTerms.character('f'), Term.marker(stringEscapeSequence, "\f")), - Term.sequence(StringReaderTerms.character('r'), Term.marker(stringEscapeSequence, "\r")), - Term.sequence(StringReaderTerms.character('\\'), Term.marker(stringEscapeSequence, "\\")), - Term.sequence(StringReaderTerms.character('\''), Term.marker(stringEscapeSequence, "'")), - Term.sequence(StringReaderTerms.character('"'), Term.marker(stringEscapeSequence, "\"")), - Term.sequence(StringReaderTerms.character('x'), rules.named(stringHex2)), - Term.sequence(StringReaderTerms.character('u'), rules.named(stringHex4)), - Term.sequence(StringReaderTerms.character('U'), rules.named(stringHex8)), - Term.sequence(StringReaderTerms.character('N'), StringReaderTerms.character('{'), rules.named(stringUnicodeName), StringReaderTerms.character('}')) - ), + Term.alternative( + Term.sequence(StringReaderTerms.character('b'), Term.marker(stringEscapeSequence, "\b")), + Term.sequence(StringReaderTerms.character('s'), Term.marker(stringEscapeSequence, " ")), + Term.sequence(StringReaderTerms.character('t'), Term.marker(stringEscapeSequence, "\t")), + Term.sequence(StringReaderTerms.character('n'), Term.marker(stringEscapeSequence, "\n")), + Term.sequence(StringReaderTerms.character('f'), Term.marker(stringEscapeSequence, "\f")), + Term.sequence(StringReaderTerms.character('r'), Term.marker(stringEscapeSequence, "\r")), + Term.sequence(StringReaderTerms.character('\\'), Term.marker(stringEscapeSequence, "\\")), + Term.sequence(StringReaderTerms.character('\''), Term.marker(stringEscapeSequence, "'")), + Term.sequence(StringReaderTerms.character('"'), Term.marker(stringEscapeSequence, "\"")), + Term.sequence(StringReaderTerms.character('x'), rules.named(stringHex2)), + Term.sequence(StringReaderTerms.character('u'), rules.named(stringHex4)), + Term.sequence(StringReaderTerms.character('U'), rules.named(stringHex8)), + Term.sequence( + StringReaderTerms.character('N'), + StringReaderTerms.character('{'), + rules.named(stringUnicodeName), + StringReaderTerms.character('}') + ) + ), state -> { Scope scope = state.scope(); String plainEscape = scope.getAny(stringEscapeSequence); if (plainEscape != null) { return plainEscape; - } + } String hexEscape = scope.getAny(stringHex2, stringHex4, stringHex8); if (hexEscape != null) { int codePoint = HexFormat.fromHexDigits(hexEscape); if (!Character.isValidCodePoint(codePoint)) { - state.errorCollector() - .store(state.mark(), DelayedException.create(ERROR_INVALID_CODEPOINT, String.format(Locale.ROOT, "U+%08X", codePoint))); - return null; - } + state.errorCollector().store(state.mark(), DelayedException.create(ERROR_INVALID_CODEPOINT, String.format(Locale.ROOT, "U+%08X", codePoint))); + return null; + } return Character.toString(codePoint); - } + } String character = scope.getOrThrow(stringUnicodeName); int codePoint; - try { - codePoint = Character.codePointOf(character); - } catch (IllegalArgumentException var12x) { - state.errorCollector().store(state.mark(), ERROR_INVALID_CHARACTER_NAME); - return null; - } + try { + codePoint = Character.codePointOf(character); + } catch (IllegalArgumentException var12x) { + state.errorCollector().store(state.mark(), ERROR_INVALID_CHARACTER_NAME); + return null; + } return Character.toString(codePoint); - } + } ); + Atom stringPlainContents = Atom.of("string_plain_contents"); rules.put(stringPlainContents, PLAIN_STRING_CHUNK); + Atom> stringChunks = Atom.of("string_chunks"); Atom stringContents = Atom.of("string_contents"); Atom singleQuotedStringChunk = Atom.of("single_quoted_string_chunk"); - NamedRule namedRule1 = rules.put( + NamedRule singleQuotedStringChunkRule = rules.put( singleQuotedStringChunk, - Term.alternative( - rules.namedWithAlias(stringPlainContents, stringContents), - Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), - Term.sequence(StringReaderTerms.character('"'), Term.marker(stringContents, "\"")) - ), + Term.alternative( + rules.namedWithAlias(stringPlainContents, stringContents), + Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), + Term.sequence(StringReaderTerms.character('"'), Term.marker(stringContents, "\"")) + ), scope -> scope.getOrThrow(stringContents) ); Atom singleQuotedStringContents = Atom.of("single_quoted_string_contents"); - rules.put(singleQuotedStringContents, Term.repeated(namedRule1, stringChunks), scope -> joinList(scope.getOrThrow(stringChunks))); + rules.put( + singleQuotedStringContents, + Term.repeated(singleQuotedStringChunkRule, stringChunks), + scope -> joinList(scope.getOrThrow(stringChunks)) + ); + Atom doubleQuotedStringChunk = Atom.of("double_quoted_string_chunk"); - NamedRule namedRule2 = rules.put( + NamedRule doubleQuotedStringChunkRule = rules.put( doubleQuotedStringChunk, - Term.alternative( - rules.namedWithAlias(stringPlainContents, stringContents), - Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), - Term.sequence(StringReaderTerms.character('\''), Term.marker(stringContents, "'")) - ), + Term.alternative( + rules.namedWithAlias(stringPlainContents, stringContents), + Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), + Term.sequence(StringReaderTerms.character('\''), Term.marker(stringContents, "'")) + ), scope -> scope.getOrThrow(stringContents) ); Atom doubleQuotedStringContents = Atom.of("double_quoted_string_contents"); - rules.put(doubleQuotedStringContents, Term.repeated(namedRule2, stringChunks), scope -> joinList(scope.getOrThrow(stringChunks))); + rules.put( + doubleQuotedStringContents, + Term.repeated(doubleQuotedStringChunkRule, stringChunks), + scope -> joinList(scope.getOrThrow(stringChunks)) + ); + Atom quotedStringLiteral = Atom.of("quoted_string_literal"); rules.put( quotedStringLiteral, - Term.alternative( - Term.sequence( - StringReaderTerms.character('"'), Term.cut(), Term.optional(rules.namedWithAlias(doubleQuotedStringContents, stringContents)), StringReaderTerms.character('"') + Term.alternative( + Term.sequence( + StringReaderTerms.character('"'), + Term.cut(), + Term.optional(rules.namedWithAlias(doubleQuotedStringContents, stringContents)), + StringReaderTerms.character('"') + ), + Term.sequence( + StringReaderTerms.character('\''), + Term.optional(rules.namedWithAlias(singleQuotedStringContents, stringContents)), + StringReaderTerms.character('\'') + ) ), - Term.sequence(StringReaderTerms.character('\''), Term.optional(rules.namedWithAlias(singleQuotedStringContents, stringContents)), StringReaderTerms.character('\'')) - ), scope -> scope.getOrThrow(stringContents) ); + Atom unquotedString = Atom.of("unquoted_string"); - rules.put(unquotedString, new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING)); + rules.put( + unquotedString, + new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING) + ); + Atom literal = Atom.of("literal"); Atom> argumentList = Atom.of("arguments"); rules.put( - argumentList, Term.repeatedWithTrailingSeparator(rules.forward(literal), argumentList, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(argumentList) + argumentList, + Term.repeatedWithTrailingSeparator( + rules.forward(literal), + argumentList, + StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) + ), + scope -> scope.getOrThrow(argumentList) ); + Atom unquotedStringOrBuiltIn = Atom.of("unquoted_string_or_builtin"); rules.putComplex( unquotedStringOrBuiltIn, - Term.sequence( - rules.named(unquotedString), - Term.optional(Term.sequence(StringReaderTerms.character('('), rules.named(argumentList), StringReaderTerms.character(')'))) - ), + Term.sequence( + rules.named(unquotedString), + Term.optional(Term.sequence( + StringReaderTerms.character('('), + rules.named(argumentList), + StringReaderTerms.character(')') + )) + ), state -> { Scope scope = state.scope(); String contents = scope.getOrThrow(unquotedString); @@ -522,114 +565,192 @@ public class SnbtGrammar { SnbtOperations.BuiltinOperation operation = SnbtOperations.BUILTIN_OPERATIONS.get(key); if (operation != null) { return operation.run(ops, arguments, state); - } + } state.errorCollector().store(state.mark(), DelayedException.create(ERROR_NO_SUCH_OPERATION, key.toString())); - return null; + return null; } else if (contents.equalsIgnoreCase("true")) { return trueValue; } else if (contents.equalsIgnoreCase("false")) { return falseValue; } else if (contents.equalsIgnoreCase("null")) { - return Objects.requireNonNullElseGet(ops.empty(), () -> { - if (isJavaType) { - return (T) CachedParseState.JAVA_NULL_VALUE_MARKER; - } - return nullString; - }); - } + return Objects.requireNonNullElseGet(ops.empty(), () -> { + if (isJavaType) { + return (T) CachedParseState.JAVA_NULL_VALUE_MARKER; + } + return nullString; + }); + } return ops.createString(contents); - } + } state.errorCollector().store(state.mark(), SnbtOperations.BUILTIN_IDS, ERROR_INVALID_UNQUOTED_START); - return null; - } + return null; + } ); + Atom mapKey = Atom.of("map_key"); - rules.put(mapKey, Term.alternative(rules.named(quotedStringLiteral), rules.named(unquotedString)), scope -> scope.getAnyOrThrow(quotedStringLiteral, unquotedString)); + rules.put( + mapKey, + Term.alternative( + rules.named(quotedStringLiteral), + rules.named(unquotedString) + ), + scope -> scope.getAnyOrThrow(quotedStringLiteral, unquotedString) + ); + Atom> mapEntry = Atom.of("map_entry"); NamedRule> mapEntryRule = rules.putComplex( - mapEntry, Term.sequence(rules.named(mapKey), StringReaderTerms.character(TagParser.NAME_VALUE_SEPARATOR), rules.named(literal)), state -> { + mapEntry, + Term.sequence( + rules.named(mapKey), + StringReaderTerms.character(TagParser.NAME_VALUE_SEPARATOR), + rules.named(literal) + ), + state -> { Scope scope = state.scope(); String key = scope.getOrThrow(mapKey); if (key.isEmpty()) { state.errorCollector().store(state.mark(), ERROR_EMPTY_KEY); - return null; - } + return null; + } T value = scope.getOrThrow(literal); return Map.entry(key, value); - } + } ); + Atom>> mapEntries = Atom.of("map_entries"); - rules.put(mapEntries, Term.repeatedWithTrailingSeparator(mapEntryRule, mapEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(mapEntries)); + rules.put( + mapEntries, + Term.repeatedWithTrailingSeparator( + mapEntryRule, + mapEntries, + StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) + ), + scope -> scope.getOrThrow(mapEntries) + ); + Atom mapLiteral = Atom.of("map_literal"); - rules.put(mapLiteral, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), rules.named(mapEntries), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { - List> entries = scope.getOrThrow(mapEntries); - if (entries.isEmpty()) { - return emptyMapValue; - } - Builder builder = ImmutableMap.builderWithExpectedSize(entries.size()); + rules.put( + mapLiteral, + Term.sequence( + StringReaderTerms.character('{'), + Scope.increaseDepth(), + rules.named(mapEntries), + Scope.decreaseDepth(), + StringReaderTerms.character('}') + ), + scope -> { + List> entries = scope.getOrThrow(mapEntries); + if (entries.isEmpty()) { + return emptyMapValue; + } + Builder builder = ImmutableMap.builderWithExpectedSize(entries.size()); + for (Entry e : entries) { + builder.put(ops.createString(e.getKey()), e.getValue()); + } + return ops.createMap(builder.buildKeepingLast()); + } + ); - for (Entry e : entries) { - builder.put(ops.createString(e.getKey()), e.getValue()); - } - - return ops.createMap(builder.buildKeepingLast()); - }); Atom> listEntries = Atom.of("list_entries"); rules.put( - listEntries, Term.repeatedWithTrailingSeparator(rules.forward(literal), listEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(listEntries) + listEntries, + Term.repeatedWithTrailingSeparator( + rules.forward(literal), + listEntries, + StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) + ), + scope -> scope.getOrThrow(listEntries) ); + Atom arrayPrefix = Atom.of("array_prefix"); rules.put( arrayPrefix, - Term.alternative( - Term.sequence(StringReaderTerms.character('B'), Term.marker(arrayPrefix, ArrayPrefix.BYTE)), - Term.sequence(StringReaderTerms.character('L'), Term.marker(arrayPrefix, ArrayPrefix.LONG)), - Term.sequence(StringReaderTerms.character('I'), Term.marker(arrayPrefix, ArrayPrefix.INT)) - ), + Term.alternative( + Term.sequence(StringReaderTerms.character('B'), Term.marker(arrayPrefix, ArrayPrefix.BYTE)), + Term.sequence(StringReaderTerms.character('L'), Term.marker(arrayPrefix, ArrayPrefix.LONG)), + Term.sequence(StringReaderTerms.character('I'), Term.marker(arrayPrefix, ArrayPrefix.INT)) + ), scope -> scope.getOrThrow(arrayPrefix) ); + Atom> intArrayEntries = Atom.of("int_array_entries"); - rules.put(intArrayEntries, Term.repeatedWithTrailingSeparator(integerLiteralRule, intArrayEntries, StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR)), scope -> scope.getOrThrow(intArrayEntries)); + rules.put( + intArrayEntries, + Term.repeatedWithTrailingSeparator( + integerLiteralRule, + intArrayEntries, + StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) + ), + scope -> scope.getOrThrow(intArrayEntries) + ); + Atom listLiteral = Atom.of("list_literal"); rules.putComplex( listLiteral, - Term.sequence( - StringReaderTerms.character('['), - Scope.increaseDepth(), - Term.alternative(Term.sequence(rules.named(arrayPrefix), StringReaderTerms.character(';'), rules.named(intArrayEntries)), rules.named(listEntries)), - Scope.decreaseDepth(), - StringReaderTerms.character(']') - ), + Term.sequence( + StringReaderTerms.character('['), + Scope.increaseDepth(), + Term.alternative( + Term.sequence( + rules.named(arrayPrefix), + StringReaderTerms.character(';'), + rules.named(intArrayEntries) + ), + rules.named(listEntries) + ), + Scope.decreaseDepth(), + StringReaderTerms.character(']') + ), state -> { Scope scope = state.scope(); ArrayPrefix arrayType = scope.get(arrayPrefix); if (arrayType != null) { List entries = scope.getOrThrow(intArrayEntries); return entries.isEmpty() ? arrayType.create(ops) : arrayType.create(ops, entries, state); - } + } List entries = scope.getOrThrow(listEntries); return entries.isEmpty() ? emptyList : ops.createList(entries.stream()); - } + } ); + NamedRule literalRule = rules.putComplex( literal, - Term.alternative( - Term.sequence(Term.positiveLookahead(NUMBER_LOOKEAHEAD), Term.alternative(rules.namedWithAlias(floatLiteral, literal), rules.named(integerLiteral))), - Term.sequence(Term.positiveLookahead(StringReaderTerms.characters('"', '\'')), Term.cut(), rules.named(quotedStringLiteral)), - Term.sequence(Term.positiveLookahead(StringReaderTerms.character('{')), Term.cut(), rules.namedWithAlias(mapLiteral, literal)), - Term.sequence(Term.positiveLookahead(StringReaderTerms.character('[')), Term.cut(), rules.namedWithAlias(listLiteral, literal)), - rules.namedWithAlias(unquotedStringOrBuiltIn, literal) - ), + Term.alternative( + Term.sequence( + Term.positiveLookahead(NUMBER_LOOKEAHEAD), + Term.alternative( + rules.namedWithAlias(floatLiteral, literal), + rules.named(integerLiteral) + ) + ), + Term.sequence( + Term.positiveLookahead(StringReaderTerms.characters('"', '\'')), + Term.cut(), + rules.named(quotedStringLiteral) + ), + Term.sequence( + Term.positiveLookahead(StringReaderTerms.character('{')), + Term.cut(), + rules.namedWithAlias(mapLiteral, literal) + ), + Term.sequence( + Term.positiveLookahead(StringReaderTerms.character('[')), + Term.cut(), + rules.namedWithAlias(listLiteral, literal) + ), + rules.namedWithAlias(unquotedStringOrBuiltIn, literal) + ), state -> { Scope scope = state.scope(); String quotedString = scope.get(quotedStringLiteral); if (quotedString != null) { return ops.createString(quotedString); - } + } IntegerLiteral integer = scope.get(integerLiteral); return integer != null ? integer.create(ops, state) : scope.getOrThrow(literal); - } + } ); + return new Grammar<>(rules, literalRule); } @@ -646,16 +767,13 @@ public class SnbtGrammar { @Override public T create(DynamicOps ops, List entries, ParseState state) { ByteList result = new ByteArrayList(); - for (IntegerLiteral entry : entries) { Number number = this.buildNumber(entry, state); if (number == null) { return null; } - result.add(number.byteValue()); } - return ops.createByteList(ByteBuffer.wrap(result.toByteArray())); } }, @@ -669,16 +787,13 @@ public class SnbtGrammar { @Override public T create(DynamicOps ops, List entries, ParseState state) { IntStream.Builder result = IntStream.builder(); - for (IntegerLiteral entry : entries) { Number parsedNumber = this.buildNumber(entry, state); if (parsedNumber == null) { return null; } - result.add(parsedNumber.intValue()); } - return ops.createIntList(result.build()); } }, @@ -692,16 +807,13 @@ public class SnbtGrammar { @Override public T create(DynamicOps ops, List entries, ParseState state) { LongStream.Builder result = LongStream.builder(); - for (IntegerLiteral entry : entries) { Number parsedNumber = this.buildNumber(entry, state); if (parsedNumber == null) { return null; } - result.add(parsedNumber.longValue()); } - return ops.createLongList(result.build()); } }; @@ -717,7 +829,6 @@ public class SnbtGrammar { public boolean isAllowed(TypeSuffix type) { return type == this.defaultType || this.additionalTypes.contains(type); } - public abstract T create(DynamicOps ops); @Nullable @@ -742,7 +853,6 @@ public class SnbtGrammar { return !this.isAllowed(type) ? null : type; } } - enum Base { BINARY, DECIMAL, diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java index cb4912898..21204ec40 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java @@ -10,7 +10,7 @@ import java.util.Optional; import java.util.function.Supplier; public class LocalizedCommandSyntaxException extends CommandSyntaxException { - public static final int CONTEXT_AMOUNT = 10; + public static final int CONTEXT_AMOUNT = 50; public static final String PARSE_ERROR_NODE = "warning.config.type.snbt.invalid_syntax.parse_error"; public static final String HERE_NODE = "warning.config.type.snbt.invalid_syntax.here"; private final Message message; From 392bd814e3584e666c07d8a4b65d6a8a219c7e2f Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 08:48:32 +0800 Subject: [PATCH 055/135] =?UTF-8?q?=E5=85=88=E5=86=99=E4=B8=80=E7=82=B9?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/util/snbt/SnbtGrammar.java | 67 ++++++++++++++----- .../core/util/snbt/SnbtOperations.java | 3 +- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java index 3dfa6cbde..c61899b05 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -246,11 +246,12 @@ public class SnbtGrammar { T falseValue = ops.createBoolean(false); T emptyMapValue = ops.emptyMap(); T emptyList = ops.emptyList(); - T nullString = ops.createString("null"); - boolean isJavaType = "null".equals(nullString); // 确定是 Java 类型的 + T nullString = ops.createString(SnbtOperations.BUILTIN_NULL); + boolean isJavaType = SnbtOperations.BUILTIN_NULL.equals(nullString); // 确定是 Java 类型的 Dictionary rules = new Dictionary<>(); + // 符号解析规则 Atom sign = Atom.of("sign"); rules.put( sign, @@ -261,6 +262,7 @@ public class SnbtGrammar { scope -> scope.getOrThrow(sign) ); + // 整数后缀解析规则 Atom integerSuffix = Atom.of("integer_suffix"); rules.put( integerSuffix, @@ -291,15 +293,19 @@ public class SnbtGrammar { scope -> scope.getOrThrow(integerSuffix) ); + // 二进制解析规则 Atom binaryNumeral = Atom.of("binary_numeral"); rules.put(binaryNumeral, BINARY_NUMERAL); + // 十进制解析规则 Atom decimalNumeral = Atom.of("decimal_numeral"); rules.put(decimalNumeral, DECIMAL_NUMERAL); + // 十六进制解析规则 Atom hexNumeral = Atom.of("hex_numeral"); rules.put(hexNumeral, HEX_NUMERAL); + // 整数常量解析规则 Atom integerLiteral = Atom.of("integer_literal"); NamedRule integerLiteralRule = rules.put( integerLiteral, @@ -336,6 +342,7 @@ public class SnbtGrammar { } ); + // 浮点型后缀解析规则 Atom floatTypeSuffix = Atom.of("float_type_suffix"); rules.put( floatTypeSuffix, @@ -346,6 +353,7 @@ public class SnbtGrammar { scope -> scope.getOrThrow(floatTypeSuffix) ); + // 浮点数指数部分解析规则 Atom> floatExponentPart = Atom.of("float_exponent_part"); rules.put( floatExponentPart, @@ -357,8 +365,9 @@ public class SnbtGrammar { scope -> new Signed<>(scope.getOrDefault(sign, Sign.PLUS), scope.getOrThrow(decimalNumeral)) ); - Atom floatWholePart = Atom.of("float_whole_part"); - Atom floatFractionPart = Atom.of("float_fraction_part"); + // 浮点数常量解析规则 + Atom floatWholePart = Atom.of("float_whole_part"); // 整数部分 + Atom floatFractionPart = Atom.of("float_fraction_part"); // 小数部分 Atom floatLiteral = Atom.of("float_literal"); rules.putComplex( floatLiteral, @@ -404,18 +413,23 @@ public class SnbtGrammar { } ); + // 二位十六进制字符串解析规则 Atom stringHex2 = Atom.of("string_hex_2"); rules.put(stringHex2, new SimpleHexLiteralParseRule(2)); + // 四位十六进制字符串解析规则 Atom stringHex4 = Atom.of("string_hex_4"); rules.put(stringHex4, new SimpleHexLiteralParseRule(4)); + // 八位十六进制字符串解析规则 Atom stringHex8 = Atom.of("string_hex_8"); rules.put(stringHex8, new SimpleHexLiteralParseRule(8)); + // Unicode名称字符串解析规则 Atom stringUnicodeName = Atom.of("string_unicode_name"); rules.put(stringUnicodeName, new GreedyPatternParseRule(UNICODE_NAME, ERROR_INVALID_CHARACTER_NAME)); + // 字符串转义序列解析规则 Atom stringEscapeSequence = Atom.of("string_escape_sequence"); rules.putComplex( stringEscapeSequence, @@ -468,11 +482,15 @@ public class SnbtGrammar { } ); + // 纯文本字符串解析规则 Atom stringPlainContents = Atom.of("string_plain_contents"); rules.put(stringPlainContents, PLAIN_STRING_CHUNK); - Atom> stringChunks = Atom.of("string_chunks"); - Atom stringContents = Atom.of("string_contents"); + // 字符串解析规则 + Atom> stringChunks = Atom.of("string_chunks"); // 字符串块 + Atom stringContents = Atom.of("string_contents"); // 字符串内容 + + // 单引号字符串块解析规则 Atom singleQuotedStringChunk = Atom.of("single_quoted_string_chunk"); NamedRule singleQuotedStringChunkRule = rules.put( singleQuotedStringChunk, @@ -483,6 +501,8 @@ public class SnbtGrammar { ), scope -> scope.getOrThrow(stringContents) ); + + // 单引号字符串内容解析规则 Atom singleQuotedStringContents = Atom.of("single_quoted_string_contents"); rules.put( singleQuotedStringContents, @@ -490,6 +510,7 @@ public class SnbtGrammar { scope -> joinList(scope.getOrThrow(stringChunks)) ); + // 双引号字符串块解析规则 Atom doubleQuotedStringChunk = Atom.of("double_quoted_string_chunk"); NamedRule doubleQuotedStringChunkRule = rules.put( doubleQuotedStringChunk, @@ -500,6 +521,8 @@ public class SnbtGrammar { ), scope -> scope.getOrThrow(stringContents) ); + + // 双引号字符串内容解析规则 Atom doubleQuotedStringContents = Atom.of("double_quoted_string_contents"); rules.put( doubleQuotedStringContents, @@ -507,6 +530,7 @@ public class SnbtGrammar { scope -> joinList(scope.getOrThrow(stringChunks)) ); + // 带引号的字符串字面量解析规则 Atom quotedStringLiteral = Atom.of("quoted_string_literal"); rules.put( quotedStringLiteral, @@ -526,14 +550,16 @@ public class SnbtGrammar { scope -> scope.getOrThrow(stringContents) ); + // 不带引号的字符串解析规则 Atom unquotedString = Atom.of("unquoted_string"); rules.put( unquotedString, new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING) ); - Atom literal = Atom.of("literal"); - Atom> argumentList = Atom.of("arguments"); + // 列表解析规则 + Atom literal = Atom.of("literal"); // 字面量 + Atom> argumentList = Atom.of("arguments"); // 参数 rules.put( argumentList, Term.repeatedWithTrailingSeparator( @@ -544,6 +570,7 @@ public class SnbtGrammar { scope -> scope.getOrThrow(argumentList) ); + // 不带引号的字符串或内置表达式解析规则 Atom unquotedStringOrBuiltIn = Atom.of("unquoted_string_or_builtin"); rules.putComplex( unquotedStringOrBuiltIn, @@ -558,9 +585,9 @@ public class SnbtGrammar { state -> { Scope scope = state.scope(); String contents = scope.getOrThrow(unquotedString); - if (!contents.isEmpty() && isAllowedToStartUnquotedString(contents.charAt(0))) { + if (!contents.isEmpty() && isAllowedToStartUnquotedString(contents.charAt(0))) { // 非空且是合法开头字符 List arguments = scope.get(argumentList); - if (arguments != null) { + if (arguments != null) { // 带参数尝试解析内置表达式 SnbtOperations.BuiltinKey key = new SnbtOperations.BuiltinKey(contents, arguments.size()); SnbtOperations.BuiltinOperation operation = SnbtOperations.BUILTIN_OPERATIONS.get(key); if (operation != null) { @@ -568,16 +595,17 @@ public class SnbtGrammar { } state.errorCollector().store(state.mark(), DelayedException.create(ERROR_NO_SUCH_OPERATION, key.toString())); return null; - } else if (contents.equalsIgnoreCase("true")) { + } else if (contents.equalsIgnoreCase(SnbtOperations.BUILTIN_TRUE)) { // 解析不带引号的 true 为布尔值 return trueValue; - } else if (contents.equalsIgnoreCase("false")) { + } else if (contents.equalsIgnoreCase(SnbtOperations.BUILTIN_FALSE)) { // 解析不带引号的 false 为布尔值 return falseValue; - } else if (contents.equalsIgnoreCase("null")) { - return Objects.requireNonNullElseGet(ops.empty(), () -> { + } else if (contents.equalsIgnoreCase(SnbtOperations.BUILTIN_NULL)) { // 解析不带引号的 null 为空值,该功能并非标准 SNBT 语法 + return Objects.requireNonNullElseGet(ops.empty() /*一般来说这里都不会是null*/, () -> { if (isJavaType) { + // 如果是 null 一般就是使用 Object 对象的 Java 类型,可以安全的强行转换 return (T) CachedParseState.JAVA_NULL_VALUE_MARKER; } - return nullString; + return nullString; // 意外情况保持 SNBT 默认行为 }); } return ops.createString(contents); @@ -587,6 +615,7 @@ public class SnbtGrammar { } ); + // 映射键解析规则 Atom mapKey = Atom.of("map_key"); rules.put( mapKey, @@ -597,6 +626,7 @@ public class SnbtGrammar { scope -> scope.getAnyOrThrow(quotedStringLiteral, unquotedString) ); + // 映射条目解析规则 Atom> mapEntry = Atom.of("map_entry"); NamedRule> mapEntryRule = rules.putComplex( mapEntry, @@ -617,6 +647,7 @@ public class SnbtGrammar { } ); + // 映射条目集合解析规则 Atom>> mapEntries = Atom.of("map_entries"); rules.put( mapEntries, @@ -628,6 +659,7 @@ public class SnbtGrammar { scope -> scope.getOrThrow(mapEntries) ); + // 映射字面量解析规则 Atom mapLiteral = Atom.of("map_literal"); rules.put( mapLiteral, @@ -651,6 +683,7 @@ public class SnbtGrammar { } ); + // 列表条目集合解析规则 Atom> listEntries = Atom.of("list_entries"); rules.put( listEntries, @@ -662,6 +695,7 @@ public class SnbtGrammar { scope -> scope.getOrThrow(listEntries) ); + // 数组前缀解析规则 Atom arrayPrefix = Atom.of("array_prefix"); rules.put( arrayPrefix, @@ -673,6 +707,7 @@ public class SnbtGrammar { scope -> scope.getOrThrow(arrayPrefix) ); + // 整数数组条目集合解析规则 Atom> intArrayEntries = Atom.of("int_array_entries"); rules.put( intArrayEntries, @@ -684,6 +719,7 @@ public class SnbtGrammar { scope -> scope.getOrThrow(intArrayEntries) ); + // 列表字面量解析规则 Atom listLiteral = Atom.of("list_literal"); rules.putComplex( listLiteral, @@ -713,6 +749,7 @@ public class SnbtGrammar { } ); + // 基本规则解析规则 NamedRule literalRule = rules.putComplex( literal, Term.alternative( diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java index 589426cad..53a6fa7f0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java @@ -23,6 +23,7 @@ public class SnbtOperations { ); public static final String BUILTIN_TRUE = "true"; public static final String BUILTIN_FALSE = "false"; + public static final String BUILTIN_NULL = "null"; public static final Map BUILTIN_OPERATIONS = Map.of( new BuiltinKey("bool", 1), new BuiltinOperation() { @Override @@ -67,7 +68,7 @@ public class SnbtOperations { ); public static final SuggestionSupplier BUILTIN_IDS = new SuggestionSupplier<>() { private final Set keys = Stream.concat( - Stream.of(BUILTIN_FALSE, BUILTIN_TRUE), SnbtOperations.BUILTIN_OPERATIONS.keySet().stream().map(BuiltinKey::id) + Stream.of(BUILTIN_FALSE, BUILTIN_TRUE, BUILTIN_NULL), SnbtOperations.BUILTIN_OPERATIONS.keySet().stream().map(BuiltinKey::id) ) .collect(Collectors.toSet()); From 5d9ede392adcae03f6a3a5e8e2d627845c2772bc Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 08:50:15 +0800 Subject: [PATCH 056/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=BD=9C=E5=9C=A8?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../momirealms/craftengine/core/util/snbt/SnbtGrammar.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java index c61899b05..0ff4982f2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java @@ -10,9 +10,11 @@ import com.mojang.serialization.JavaOps; import it.unimi.dsi.fastutil.bytes.ByteArrayList; import it.unimi.dsi.fastutil.bytes.ByteList; import it.unimi.dsi.fastutil.chars.CharList; +import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.snbt.parse.*; import net.momirealms.craftengine.core.util.snbt.parse.Dictionary; import net.momirealms.craftengine.core.util.snbt.parse.grammar.*; +import net.momirealms.sparrow.nbt.codec.LegacyJavaOps; import javax.annotation.Nullable; import java.nio.ByteBuffer; @@ -878,7 +880,7 @@ public class SnbtGrammar { state.errorCollector().store(state.mark(), ERROR_INVALID_ARRAY_ELEMENT_TYPE); return null; } - return (Number) entry.create(JavaOps.INSTANCE, actualType, state); + return (Number) entry.create(VersionHelper.isOrAbove1_20_5() ? JavaOps.INSTANCE : LegacyJavaOps.INSTANCE, actualType, state); } @Nullable From ac48881ae029bbca6e319e99bbe8e9c5a7c74e8a Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 10:47:27 +0800 Subject: [PATCH 057/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E6=8A=9B=E5=87=BA=E6=9C=AA=E8=AF=BB=E5=AE=8C=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/translations/zh_cn.yml | 18 +++++++------- .../item/modifier/ComponentsModifier.java | 2 +- .../craftengine/core/util/snbt/TagParser.java | 24 +++++++++++++------ 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 160e44580..bd6ace4e4 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -88,22 +88,22 @@ warning.config.type.vector3f: "在文件 发现问题 - 无法 warning.config.type.vec3d: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为双精度浮点数三维向量类型 (选项 '')" warning.config.type.map: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为映射类型 (选项 '')" warning.config.type.snbt.invalid_syntax: "在文件 发现问题 - 无法加载 '': 无效的 SNBT 语法 ''" -warning.config.type.snbt.invalid_syntax.parse_error: ", 位于第 个字符: " +warning.config.type.snbt.invalid_syntax.parse_error: ",位于第个字符:" warning.config.type.snbt.invalid_syntax.here: "<--[此处]" -warning.config.type.snbt.parser.expected_string_uuid: "应为表示有效 UUID 的字符串" +warning.config.type.snbt.parser.expected_string_uuid: "应为表示有效UUID的字符串" warning.config.type.snbt.parser.expected_number_or_boolean: "应为数字或布尔型" warning.config.type.snbt.parser.trailing: "多余的尾随数据" warning.config.type.snbt.parser.expected.compound: "应为复合标签" -warning.config.type.snbt.parser.number_parse_failure: "解析数字失败: " -warning.config.type.snbt.parser.expected_hex_escape: "字符字面量长度应为 " -warning.config.type.snbt.parser.invalid_codepoint: "无效的 Unicode 字符码位: " +warning.config.type.snbt.parser.number_parse_failure: "解析数字失败:" +warning.config.type.snbt.parser.expected_hex_escape: "字符字面量长度应为" +warning.config.type.snbt.parser.invalid_codepoint: "无效的Unicode字符码位:" warning.config.type.snbt.parser.no_such_operation: "不存在的操作: " warning.config.type.snbt.parser.expected_integer_type: "应为整数" warning.config.type.snbt.parser.expected_float_type: "应为浮点数" warning.config.type.snbt.parser.expected_non_negative_number: "应为非负数" -warning.config.type.snbt.parser.invalid_character_name: "无效的 Unicode 字符名称" +warning.config.type.snbt.parser.invalid_character_name: "无效的Unicode字符名称" warning.config.type.snbt.parser.invalid_array_element_type: "无效的数组元素类型" -warning.config.type.snbt.parser.invalid_unquoted_start: "无引号字符串不能以数字 0-9、+ 或 - 开头" +warning.config.type.snbt.parser.invalid_unquoted_start: "无引号字符串不能以数字0-9、+或-开头" warning.config.type.snbt.parser.expected_unquoted_string: "应为有效的无引号字符串" warning.config.type.snbt.parser.invalid_string_contents: "无效的字符串内容" warning.config.type.snbt.parser.expected_binary_numeral: "应为二进制数" @@ -111,9 +111,9 @@ warning.config.type.snbt.parser.underscore_not_allowed: "数字的开头 warning.config.type.snbt.parser.expected_decimal_numeral: "应为十进制数" warning.config.type.snbt.parser.expected_hex_numeral: "应为十六进制数" warning.config.type.snbt.parser.empty_key: "键不能为空" -warning.config.type.snbt.parser.leading_zero_not_allowed: "十进制数不能以 0 开头" +warning.config.type.snbt.parser.leading_zero_not_allowed: "十进制数不能以0开头" warning.config.type.snbt.parser.infinity_not_allowed: "不允许使用非有限数的数值" -warning.config.type.snbt.parser.incorrect: "应为字面量 " +warning.config.type.snbt.parser.incorrect: "应为字面量" warning.config.number.missing_type: "在文件 发现问题 - 配置项 '' 缺少数字类型所需的 'type' 参数" warning.config.number.invalid_type: "在文件 发现问题 - 配置项 '' 使用了无效的数字类型 ''" warning.config.number.missing_argument: "在文件 发现问题 - 配置项 '' 缺少数字参数" diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java index 113f33324..e5f551690 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java @@ -46,7 +46,7 @@ public class ComponentsModifier implements ItemDataModifier { } else if (string.startsWith("(snbt) ")) { String snbt = string.substring("(snbt) ".length()); try { - return TagParser.parseCompoundFully(snbt); + return TagParser.parseTagFully(snbt); } catch (CommandSyntaxException e) { throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e.getMessage()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java index d399e6151..f963c635a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java @@ -15,6 +15,7 @@ import net.momirealms.sparrow.nbt.codec.LegacyJavaOps; import net.momirealms.sparrow.nbt.codec.LegacyNBTOps; import net.momirealms.sparrow.nbt.codec.NBTOps; +@SuppressWarnings("unused") public class TagParser { public static final SimpleCommandExceptionType ERROR_TRAILING_DATA = new LocalizedSimpleCommandExceptionType( new LocalizedMessage("warning.config.type.snbt.parser.trailing") @@ -24,8 +25,8 @@ public class TagParser { ); public static final char ELEMENT_SEPARATOR = ','; public static final char NAME_VALUE_SEPARATOR = ':'; - private static final TagParser NBT_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? NBTOps.INSTANCE : LegacyNBTOps.INSTANCE); - private static final TagParser JAVA_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? JavaOps.INSTANCE : LegacyJavaOps.INSTANCE); + public static final TagParser NBT_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? NBTOps.INSTANCE : LegacyNBTOps.INSTANCE); + public static final TagParser JAVA_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? JavaOps.INSTANCE : LegacyJavaOps.INSTANCE); private final DynamicOps ops; private final Grammar grammar; @@ -48,15 +49,20 @@ public class TagParser { } throw ERROR_EXPECTED_COMPOUND.createWithContext(reader); } - public static CompoundTag parseCompoundFully(String input) throws CommandSyntaxException { StringReader reader = new StringReader(input); - return parseCompoundAsArgument(reader); + Tag result = NBT_OPS_PARSER.parseFully(reader); + return castToCompoundOrThrow(reader, result); + } + + public static Tag parseTagFully(String input) throws CommandSyntaxException { + StringReader reader = new StringReader(input); + return NBT_OPS_PARSER.parseFully(reader); } public static Object parseObjectFully(String input) throws CommandSyntaxException { StringReader reader = new StringReader(input); - return parseObjectAsArgument(reader); + return JAVA_OPS_PARSER.parseFully(reader); } public T parseFully(String input) throws CommandSyntaxException { @@ -77,8 +83,12 @@ public class TagParser { } public static CompoundTag parseCompoundAsArgument(StringReader reader) throws CommandSyntaxException { - Tag tag = NBT_OPS_PARSER.parseAsArgument(reader); - return castToCompoundOrThrow(reader, tag); + Tag result = parseTagAsArgument(reader); + return castToCompoundOrThrow(reader, result); + } + + public static Tag parseTagAsArgument(StringReader reader) throws CommandSyntaxException { + return NBT_OPS_PARSER.parseAsArgument(reader); } public static Object parseObjectAsArgument(StringReader reader) throws CommandSyntaxException { From 8dbee19faebd1ed4d95b6b02ac4c87e1d8d727b8 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Mon, 1 Dec 2025 11:15:46 +0800 Subject: [PATCH 058/135] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=AF=AD=E8=A8=80?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/translations/zh_cn.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index ab537a422..25a7c52b0 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -74,6 +74,7 @@ command.send_resource_pack.success.multiple: "发送资源包给 command.locale.set.failure: "区域设置格式无效: " command.locale.set.success: "已为 更新选定区域设置为 " command.locale.unset.success: "已清除 的选定区域设置" +command.entity_view_distance_scale.set.success: "已为 的实体可见距离百分比更新为 " warning.network.resource_pack.unverified_uuid: "玩家 使用未经服务器验证的 UUID () 尝试请求获取资源包" warning.config.pack.duplicated_files: "发现重复文件 请通过 config.yml 的 'resource-pack.duplicated-files-handler' 部分解决" warning.config.yaml.duplicated_key: "在文件 发现问题 - 在第行发现重复的键 '', 这可能会导致一些意料之外的问题" From 02630c835057f5948948778f1bd480a4fe1f011e Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Mon, 1 Dec 2025 15:49:58 +0800 Subject: [PATCH 059/135] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E4=B8=8D=E4=B8=A5?= =?UTF-8?q?=E6=A0=BC=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/config.yml | 3 ++- .../momirealms/craftengine/core/pack/host/impl/SelfHost.java | 2 +- .../craftengine/core/pack/host/impl/SelfHostHttpServer.java | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 95815b4c5..4050974da 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -198,7 +198,8 @@ resource-pack: # Generates a single-use, time-limited download link for each player. one-time-token: true # Enhances validation for deny-non-minecraft-request and one-time-token. - strict-validation: true + # Do not enable this on an offline server + strict-validation: false rate-limiting: # Maximum bandwidth per second to prevent server instability for other players during resource pack downloads max-bandwidth-per-second: 5_000_000 # 5MB/s diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java index 93cede575..9d08e7c78 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java @@ -77,7 +77,7 @@ public class SelfHost implements ResourcePackHost { boolean oneTimeToken = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("one-time-token", true), "one-time-token"); String protocol = arguments.getOrDefault("protocol", "http").toString(); boolean denyNonMinecraftRequest = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("deny-non-minecraft-request", true), "deny-non-minecraft-request"); - boolean strictValidation = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("strict-validation", true), "strict-validation"); + boolean strictValidation = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("strict-validation", false), "strict-validation"); Bandwidth limit = null; Map rateLimitingSection = ResourceConfigUtils.getAsMapOrNull(arguments.get("rate-limiting"), "rate-limiting"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java index da20a14e3..39961f3aa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java @@ -69,7 +69,7 @@ public class SelfHostHttpServer { private String url; private boolean denyNonMinecraft = true; private boolean useToken; - private boolean strictValidation = true; + private boolean strictValidation = false; private long globalUploadRateLimit = 0; private long minDownloadSpeed = 50_000; @@ -234,7 +234,7 @@ public class SelfHostHttpServer { boolean nonMinecraftClient = userAgent == null || !userAgent.startsWith("Minecraft Java/"); if (strictValidation && !nonMinecraftClient) { String clientVersion = request.headers().get("X-Minecraft-Version"); - nonMinecraftClient = !Objects.equals(clientVersion, userAgent.substring(15)); + nonMinecraftClient = !Objects.equals(clientVersion, userAgent.substring("Minecraft Java/".length())); } if (nonMinecraftClient) { sendError(ctx, HttpResponseStatus.FORBIDDEN, "Invalid client"); From 6f1e730d032ea5654a35b5a229071f8cc9c51ea6 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Mon, 1 Dec 2025 16:09:10 +0800 Subject: [PATCH 060/135] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/config.yml | 3 +-- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 4050974da..294a644e2 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -197,8 +197,7 @@ resource-pack: deny-non-minecraft-request: true # Generates a single-use, time-limited download link for each player. one-time-token: true - # Enhances validation for deny-non-minecraft-request and one-time-token. - # Do not enable this on an offline server + # Improved validation for deny-non-minecraft-request and one-time-token; do not enable on offline servers or validation will fail. strict-validation: false rate-limiting: # Maximum bandwidth per second to prevent server instability for other players during resource pack downloads diff --git a/gradle.properties b/gradle.properties index 46db05f12..a8e0f9e03 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings -project_version=0.0.65.15 +project_version=0.0.65.15.1 config_version=60 lang_version=41 project_group=net.momirealms From f0f2181a186552a7a532bc1e812d5739d128e73a Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Mon, 1 Dec 2025 16:45:26 +0800 Subject: [PATCH 061/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E5=AE=9E=E4=BD=93=E5=89=94=E9=99=A4=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/command/BukkitCommandManager.java | 1 + .../SetEntityViewDistanceScaleCommand.java | 10 +++ .../feature/ToggleEntityCullingCommand.java | 57 +++++++++++++++ .../plugin/user/BukkitServerPlayer.java | 73 ++++++++++++------- common-files/src/main/resources/commands.yml | 6 ++ .../src/main/resources/translations/en.yml | 5 +- .../core/entity/player/Player.java | 4 + .../core/plugin/locale/MessageConstants.java | 1 + 8 files changed, 129 insertions(+), 28 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java 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 00dbb694b..fb43eac52 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 @@ -43,6 +43,7 @@ public class BukkitCommandManager extends AbstractCommandManager new TestCommand(this, plugin), new SetLocaleCommand(this, plugin), new SetEntityViewDistanceScaleCommand(this, plugin), + new ToggleEntityCullingCommand(this, plugin), new UnsetLocaleCommand(this, plugin), new DebugGetBlockStateRegistryIdCommand(this, plugin), new DebugGetBlockInternalIdCommand(this, plugin), diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java index 17d5ca2e3..df4afd16e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java @@ -1,12 +1,14 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.FlagKeys; +import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.locale.MessageConstants; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -27,6 +29,14 @@ public class SetEntityViewDistanceScaleCommand extends BukkitCommandFeature { + if (!Config.enableEntityCulling()) { + context.sender().sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); + return; + } + if (Config.entityCullingViewDistance() <= 0) { + context.sender().sendMessage(Component.text("View distance is not enabled on this server").color(NamedTextColor.RED)); + return; + } Player player = context.get("player"); double scale = context.get("scale"); BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java new file mode 100644 index 000000000..b46f47699 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java @@ -0,0 +1,57 @@ +package net.momirealms.craftengine.bukkit.plugin.command.feature; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; +import net.momirealms.craftengine.core.plugin.command.FlagKeys; +import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.bukkit.parser.PlayerParser; +import org.incendo.cloud.parser.standard.BooleanParser; +import org.incendo.cloud.parser.standard.DoubleParser; + +import java.util.Optional; + +public class ToggleEntityCullingCommand extends BukkitCommandFeature { + + public ToggleEntityCullingCommand(CraftEngineCommandManager commandManager, CraftEngine plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { + return builder + .flag(FlagKeys.SILENT_FLAG) + .required("player", PlayerParser.playerParser()) + .optional("state", BooleanParser.booleanParser()) + .handler(context -> { + if (!Config.enableEntityCulling()) { + context.sender().sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); + return; + } + Player player = context.get("player"); + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); + Optional state = context.optional("state"); + boolean isEnabled = serverPlayer.enableEntityCulling(); + if (state.isPresent()) { + serverPlayer.setEnableEntityCulling(state.get()); + handleFeedback(context, MessageConstants.COMMAND_TOGGLE_ENTITY_CULLING_SUCCESS, Component.text(state.get()), Component.text(player.getName())); + } else { + serverPlayer.setEnableEntityCulling(!isEnabled); + handleFeedback(context, MessageConstants.COMMAND_TOGGLE_ENTITY_CULLING_SUCCESS, Component.text(!isEnabled), Component.text(player.getName())); + } + }); + } + + @Override + public String getFeatureID() { + return "toggle_entity_culling"; + } +} 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 fc12b179f..e7bdc3bea 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 @@ -74,6 +74,7 @@ import java.util.concurrent.ConcurrentHashMap; public class BukkitServerPlayer extends Player { public static final Key SELECTED_LOCALE_KEY = Key.of("craftengine:locale"); public static final Key ENTITY_CULLING_VIEW_DISTANCE_SCALE = Key.of("craftengine:entity_culling_view_distance_scale"); + public static final Key ENABLE_ENTITY_CULLING = Key.of("craftengine:enable_entity_culling"); private final BukkitCraftEngine plugin; // connection state @@ -146,6 +147,8 @@ public class BukkitServerPlayer extends Player { private final EntityCulling culling; private Vec3d firstPersonCameraVec3; private Vec3d thirdPersonCameraVec3; + // 是否启用实体剔除 + private boolean enableEntityCulling; // 玩家眼睛所在位置 private Location eyeLocation; @@ -174,6 +177,7 @@ public class BukkitServerPlayer extends Player { byte[] bytes = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(CooldownData.COOLDOWN_KEY), PersistentDataType.BYTE_ARRAY); String locale = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(SELECTED_LOCALE_KEY), PersistentDataType.STRING); Double scale = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENTITY_CULLING_VIEW_DISTANCE_SCALE), PersistentDataType.DOUBLE); + this.enableEntityCulling = Optional.ofNullable(player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENABLE_ENTITY_CULLING), PersistentDataType.BOOLEAN)).orElse(true); this.culling.setDistanceScale(Optional.ofNullable(scale).orElse(1.0)); this.selectedLocale = TranslationManager.parseLocale(locale); this.trackedChunks = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(512, 0.5f); @@ -597,38 +601,44 @@ public class BukkitServerPlayer extends Player { public void entityCullingTick() { this.culling.restoreTokenOnTick(); boolean useRayTracing = Config.entityCullingRayTracing(); - for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { - CullingData cullingData = cullableObject.cullable.cullingData(); - if (cullingData != null) { - boolean firstPersonVisible = this.culling.isVisible(cullingData, this.firstPersonCameraVec3, useRayTracing); - // 之前可见 - if (cullableObject.isShown) { - boolean thirdPersonVisible = this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing); - if (!firstPersonVisible && !thirdPersonVisible) { - cullableObject.setShown(this, false); + if (this.enableEntityCulling) { + for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { + CullingData cullingData = cullableObject.cullable.cullingData(); + if (cullingData != null) { + boolean firstPersonVisible = this.culling.isVisible(cullingData, this.firstPersonCameraVec3, useRayTracing); + // 之前可见 + if (cullableObject.isShown) { + boolean thirdPersonVisible = this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing); + if (!firstPersonVisible && !thirdPersonVisible) { + cullableObject.setShown(this, false); + } } - } - // 之前不可见 - else { - // 但是第一人称可见了 - if (firstPersonVisible) { - // 下次再说 - if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { + // 之前不可见 + else { + // 但是第一人称可见了 + if (firstPersonVisible) { + // 下次再说 + if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { + continue; + } + cullableObject.setShown(this, true); continue; } - cullableObject.setShown(this, true); - continue; - } - if (this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing)) { - // 下次再说 - if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { - continue; + if (this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing)) { + // 下次再说 + if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { + continue; + } + cullableObject.setShown(this, true); } - cullableObject.setShown(this, true); + // 仍然不可见 } - // 仍然不可见 + } else { + cullableObject.setShown(this, true); } - } else { + } + } else { + for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { cullableObject.setShown(this, true); } } @@ -1341,6 +1351,17 @@ public class BukkitServerPlayer extends Player { platformPlayer().getPersistentDataContainer().set(KeyUtils.toNamespacedKey(ENTITY_CULLING_VIEW_DISTANCE_SCALE), PersistentDataType.DOUBLE, value); } + @Override + public void setEnableEntityCulling(boolean enable) { + this.enableEntityCulling = enable; + platformPlayer().getPersistentDataContainer().set(KeyUtils.toNamespacedKey(ENABLE_ENTITY_CULLING), PersistentDataType.BOOLEAN, enable); + } + + @Override + public boolean enableEntityCulling() { + return enableEntityCulling; + } + @Override public void giveExperiencePoints(int xpPoints) { platformPlayer().giveExp(xpPoints); diff --git a/common-files/src/main/resources/commands.yml b/common-files/src/main/resources/commands.yml index ffd2eadee..41fbb153b 100644 --- a/common-files/src/main/resources/commands.yml +++ b/common-files/src/main/resources/commands.yml @@ -135,6 +135,12 @@ set_entity_view_distance_scale: usage: - /ce feature entity-view-distance-scale set +toggle_entity_culling: + enable: true + permission: ce.command.admin.toggle_entity_culling + usage: + - /ce feature toggle-entity-culling + # Debug commands debug_set_block: enable: true diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index fce2f7ea7..c7e538f21 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -72,9 +72,10 @@ command.upload.on_progress: "Started uploading progress. Check the consol command.send_resource_pack.success.single: "Sent resource pack to ." command.send_resource_pack.success.multiple: "Send resource packs to players." command.locale.set.failure: "Invalid locale format: " -command.locale.set.success: "Updated selected locale to for " +command.locale.set.success: "Selected locale has been set to for " command.locale.unset.success: "Cleared selected locale for " -command.entity_view_distance_scale.set.success: "Updated entity view distance scale to for " +command.entity_view_distance_scale.set.success: "Entity view distance scale updated to for " +command.entity_culling.toggle.success: "Entity culling status updated to for " 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." 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 847fe6d1c..689374c51 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 @@ -193,6 +193,10 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void setEntityCullingViewDistanceScale(double value); + public abstract void setEnableEntityCulling(boolean enable); + + public abstract boolean enableEntityCulling(); + public abstract void giveExperiencePoints(int xpPoints); public abstract void giveExperienceLevels(int levels); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java index 81174c5d0..f8cfbd946 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java @@ -42,4 +42,5 @@ public interface MessageConstants { TranslatableComponent.Builder COMMAND_ITEM_CLEAR_TEST_SINGLE = Component.translatable().key("command.item.clear.test.single"); TranslatableComponent.Builder COMMAND_ITEM_CLEAR_TEST_MULTIPLE = Component.translatable().key("command.item.clear.test.multiple"); TranslatableComponent.Builder COMMAND_ENTITY_VIEW_DISTANCE_SCALE_SET_SUCCESS = Component.translatable().key("command.entity_view_distance_scale.set.success"); + TranslatableComponent.Builder COMMAND_TOGGLE_ENTITY_CULLING_SUCCESS = Component.translatable().key("command.entity_culling.toggle.success"); } From d2b794c70d8d6dba3769607f20660fc37dfcfe01 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Tue, 2 Dec 2025 01:48:44 +0800 Subject: [PATCH 062/135] =?UTF-8?q?=E5=AE=B6=E5=85=B7=E9=87=8D=E6=9E=84par?= =?UTF-8?q?t1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/api/CraftEngineFurniture.java | 53 +- .../event/AsyncResourcePackCacheEvent.java | 2 +- .../api/event/FurnitureAttemptPlaceEvent.java | 8 +- .../api/event/FurnitureInteractEvent.java | 2 +- .../behavior/NearLiquidBlockBehavior.java | 2 +- .../block/behavior/OnLiquidBlockBehavior.java | 2 +- .../ItemDisplayBlockEntityElementConfig.java | 9 +- .../TextDisplayBlockEntityElementConfig.java | 2 +- .../bukkit/entity/BukkitItemEntity.java | 2 +- .../entity/data/ItemDisplayEntityData.java | 2 +- .../furniture/BukkitCustomFurniture.java | 74 --- .../entity/furniture/BukkitFurniture.java | 363 +++++--------- .../furniture/BukkitFurnitureElement.java | 177 ------- .../furniture/BukkitFurnitureManager.java | 452 +++++++++--------- .../furniture/FurnitureEventListener.java | 158 +++--- .../entity/furniture/ItemColorSource.java | 6 + .../BukkitFurnitureElementConfigs.java | 15 + .../element/ItemDisplayFurnitureElement.java | 22 + .../ItemDisplayFurnitureElementConfig.java | 169 +++++++ .../furniture/hitbox/BukkitHitBoxTypes.java | 2 +- .../furniture/hitbox/CustomHitBoxConfig.java | 60 +-- .../hitbox/HappyGhastHitBoxConfig.java | 3 +- .../hitbox/InteractionHitBoxConfig.java | 3 +- .../furniture/hitbox/ShulkerHitBoxConfig.java | 3 +- .../item/behavior/FurnitureItemBehavior.java | 259 +++++----- .../bukkit/plugin/BukkitCraftEngine.java | 2 + .../bukkit/plugin/BukkitPlatform.java | 4 +- .../command/feature/DebugItemDataCommand.java | 2 +- .../feature/DebugSpawnFurnitureCommand.java | 10 +- .../feature/ToggleEntityCullingCommand.java | 1 - .../plugin/network/BukkitNetworkManager.java | 16 +- .../handler/ProjectilePacketHandler.java | 2 +- .../plugin/network/payload/PayloadHelper.java | 4 +- .../default/configuration/furniture/bench.yml | 5 +- .../src/main/resources/translations/de.yml | 4 +- .../src/main/resources/translations/en.yml | 5 +- .../src/main/resources/translations/es.yml | 4 +- .../src/main/resources/translations/fr_fr.yml | 4 +- .../src/main/resources/translations/ru_ru.yml | 4 +- .../src/main/resources/translations/tr.yml | 4 +- .../src/main/resources/translations/zh_cn.yml | 4 +- .../core/block/AbstractBlockManager.java | 8 +- .../craftengine/core/block/BlockBehavior.java | 2 +- .../craftengine/core/block/BlockSettings.java | 23 +- .../behavior/IsPathFindableBlockBehavior.java | 2 +- .../core/block/entity/BlockEntity.java | 2 +- .../block/entity/BlockEntityTypeKeys.java | 1 + .../core/block/entity/BlockEntityTypes.java | 1 + .../block/entity/InactiveBlockEntity.java | 21 + .../craftengine/core/entity/CustomEntity.java | 96 ++++ .../core/entity/CustomEntityType.java | 14 + .../core/entity/CustomEntityTypeKeys.java | 10 + .../core/entity/CustomEntityTypes.java | 18 + .../core/entity/InactiveCustomEntity.java | 54 +++ .../core/entity/{ => display}/Billboard.java | 2 +- .../{ => display}/ItemDisplayContext.java | 2 +- .../furniture/AbstractCustomFurniture.java | 89 ---- .../furniture/AbstractFurnitureElement.java | 92 ---- .../furniture/AbstractFurnitureManager.java | 124 ++--- .../core/entity/furniture/AnchorType.java | 1 + .../entity/furniture/CustomFurniture.java | 60 --- .../core/entity/furniture/Furniture.java | 54 +-- .../entity/furniture/FurnitureConfig.java | 67 +++ .../entity/furniture/FurnitureConfigImpl.java | 145 ++++++ ...raData.java => FurnitureDataAccessor.java} | 95 ++-- .../entity/furniture/FurnitureElement.java | 59 --- .../entity/furniture/FurnitureManager.java | 6 +- .../entity/furniture/FurnitureSettings.java | 25 +- .../entity/furniture/FurnitureVariant.java | 13 + .../core/entity/furniture/HitBoxConfig.java | 34 -- .../behavior/EmptyFurnitureBehavior.java | 7 + .../furniture/behavior/FurnitureBehavior.java | 15 + .../furniture/element/FurnitureElement.java | 14 + .../element/FurnitureElementConfig.java | 9 + .../FurnitureElementConfigFactory.java | 8 + .../element/FurnitureElementConfigs.java | 31 ++ .../furniture/hitbox/AbstractHitBox.java | 26 +- .../{ => hitbox}/AbstractHitBoxConfig.java | 8 +- .../entity/furniture/{ => hitbox}/HitBox.java | 2 +- .../entity/furniture/hitbox/HitBoxConfig.java | 20 + .../{ => hitbox}/HitBoxConfigFactory.java | 2 +- .../furniture/{ => hitbox}/HitBoxPart.java | 2 +- .../furniture/{ => hitbox}/HitBoxTypes.java | 3 +- .../furniture/tick/FurnitureTicker.java | 8 + .../core/entity/{ => item}/ItemEntity.java | 2 +- .../entity/projectile/ProjectileMeta.java | 4 +- .../craftengine/core/item/Item.java | 2 +- .../craftengine/core/item/ItemSettings.java | 10 +- .../recipe/CustomSmithingTransformRecipe.java | 2 +- .../item/recipe/CustomSmithingTrimRecipe.java | 2 +- .../function/ApplyBonusCountFunction.java | 4 +- .../core/pack/model/ItemModels.java | 2 +- .../model/condition/ConditionProperties.java | 2 +- .../RangeDispatchProperties.java | 2 +- .../pack/model/select/SelectProperties.java | 2 +- .../pack/model/special/SignSpecialModel.java | 2 +- .../pack/model/special/SpecialModels.java | 2 +- .../core/pack/model/tint/Tints.java | 2 +- .../core/pack/obfuscation/ObfA.java | 2 +- .../plugin/command/sender/AbstractSender.java | 2 +- .../core/plugin/config/Config.java | 4 +- .../plugin/config/StringKeyConstructor.java | 10 +- .../argument/ExpressionTemplateArgument.java | 2 +- .../template/argument/TemplateArguments.java | 2 +- .../context/function/DamageFunction.java | 2 +- .../context/function/OpenWindowFunction.java | 2 +- .../function/RemoveFurnitureFunction.java | 2 +- .../function/ReplaceFurnitureFunction.java | 2 +- .../function/SpawnFurnitureFunction.java | 31 +- .../context/function/ToastFunction.java | 2 +- .../parameter/EntityParameterProvider.java | 2 +- .../core/plugin/locale/LangData.java | 2 +- .../core/registry/BuiltInRegistries.java | 8 +- .../craftengine/core/registry/Registries.java | 6 +- .../craftengine/core/sound/SoundData.java | 2 +- .../craftengine/core/util/Pair.java | 8 +- .../core/util/ReflectionUtils.java | 2 +- .../core/util/ResourceConfigUtils.java | 110 +++-- .../craftengine/core/util/SNBTReader.java | 2 +- .../craftengine/core/util/Tuple.java | 12 +- .../craftengine/core/util/TypeUtils.java | 14 +- .../craftengine/core/world/chunk/CEChunk.java | 34 +- .../DefaultBlockEntitySerializer.java | 7 +- .../serialization/DefaultChunkSerializer.java | 13 +- .../DefaultEntitySerializer.java | 55 +++ 125 files changed, 1827 insertions(+), 1753 deletions(-) delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCustomFurniture.java delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/block/entity/InactiveBlockEntity.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java rename core/src/main/java/net/momirealms/craftengine/core/entity/{ => display}/Billboard.java (80%) rename core/src/main/java/net/momirealms/craftengine/core/entity/{ => display}/ItemDisplayContext.java (87%) delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractCustomFurniture.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureElement.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/{FurnitureExtraData.java => FurnitureDataAccessor.java} (57%) delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureElement.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxConfig.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/EmptyFurnitureBehavior.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehavior.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java rename bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitHitBox.java => core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java (54%) rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/{ => hitbox}/AbstractHitBoxConfig.java (86%) rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/{ => hitbox}/HitBox.java (87%) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfig.java rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/{ => hitbox}/HitBoxConfigFactory.java (65%) rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/{ => hitbox}/HitBoxPart.java (73%) rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/{ => hitbox}/HitBoxTypes.java (91%) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/FurnitureTicker.java rename core/src/main/java/net/momirealms/craftengine/core/entity/{ => item}/ItemEntity.java (67%) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java 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 4a47cd615..8f61765d3 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 @@ -8,9 +8,8 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.entity.furniture.AnchorType; -import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData; +import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.LootTable; @@ -46,7 +45,7 @@ public final class CraftEngineFurniture { * @return a non-null map containing all loaded custom furniture */ @NotNull - public static Map loadedFurniture() { + public static Map loadedFurniture() { return BukkitFurnitureManager.instance().loadedFurniture(); } @@ -56,7 +55,7 @@ public final class CraftEngineFurniture { * @param id id * @return the custom furniture */ - public static CustomFurniture byId(@NotNull Key id) { + public static FurnitureConfig byId(@NotNull Key id) { return BukkitFurnitureManager.instance().furnitureById(id).orElse(null); } @@ -69,9 +68,11 @@ public final class CraftEngineFurniture { */ @Nullable public static BukkitFurniture place(Location location, Key furnitureId) { - CustomFurniture furniture = byId(furnitureId); + FurnitureConfig furniture = byId(furnitureId); if (furniture == null) return null; - return place(location, furnitureId, furniture.getAnyAnchorType()); + // fixme API +// return place(location, furnitureId, furniture.getAnyAnchorType()); + return null; } /** @@ -79,14 +80,17 @@ public final class CraftEngineFurniture { * * @param location location * @param furnitureId furniture to place - * @param anchorType anchor type + * @param anchorType anchor id * @return the loaded furniture */ @Nullable + @Deprecated(since = "0.0.66", forRemoval = true) public static BukkitFurniture place(Location location, Key furnitureId, AnchorType anchorType) { - CustomFurniture furniture = byId(furnitureId); + FurnitureConfig furniture = byId(furnitureId); if (furniture == null) return null; - return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), true); + // fixme API +// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), true); + return null; } /** @@ -94,12 +98,15 @@ public final class CraftEngineFurniture { * * @param location location * @param furniture furniture to place - * @param anchorType anchor type + * @param anchorType anchor id * @return the loaded furniture */ @NotNull - public static BukkitFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType) { - return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), true); + @Deprecated(since = "0.0.66", forRemoval = true) + public static BukkitFurniture place(Location location, FurnitureConfig furniture, AnchorType anchorType) { + // fixme API +// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), true); + return null; } /** @@ -107,15 +114,18 @@ public final class CraftEngineFurniture { * * @param location location * @param furnitureId furniture to place - * @param anchorType anchor type + * @param anchorType anchor id * @param playSound whether to play place sounds * @return the loaded furniture */ @Nullable + @Deprecated(since = "0.0.66", forRemoval = true) public static BukkitFurniture place(Location location, Key furnitureId, AnchorType anchorType, boolean playSound) { - CustomFurniture furniture = byId(furnitureId); + FurnitureConfig furniture = byId(furnitureId); if (furniture == null) return null; - return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), playSound); + // fixme API +// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), playSound); + return null; } /** @@ -123,13 +133,16 @@ public final class CraftEngineFurniture { * * @param location location * @param furniture furniture to place - * @param anchorType anchor type + * @param anchorType anchor id * @param playSound whether to play place sounds * @return the loaded furniture */ @NotNull - public static BukkitFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType, boolean playSound) { - return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), playSound); + @Deprecated(since = "0.0.66", forRemoval = true) + public static BukkitFurniture place(Location location, FurnitureConfig furniture, AnchorType anchorType, boolean playSound) { + // fixme API +// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), playSound); + return null; } /** @@ -286,7 +299,7 @@ public final class CraftEngineFurniture { boolean dropLoot, boolean playSound) { if (!furniture.isValid()) return; - Location location = ((BukkitFurniture) furniture).dropLocation(); + Location location = ((BukkitFurniture) furniture).getDropLocation(); furniture.destroy(); LootTable lootTable = (LootTable) furniture.config().lootTable(); World world = new BukkitWorld(location.getWorld()); @@ -295,7 +308,7 @@ public final class CraftEngineFurniture { ContextHolder.Builder builder = ContextHolder.builder() .withParameter(DirectContextParameters.POSITION, position) .withParameter(DirectContextParameters.FURNITURE, furniture) - .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.extraData().item().orElse(null)); + .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.dataAccessor().item().orElse(null)); if (player != null) { Item itemInHand = player.getItemInHand(InteractionHand.MAIN_HAND); builder.withParameter(DirectContextParameters.PLAYER, player) 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 index dfa064fa3..62099bba8 100644 --- 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 @@ -49,7 +49,7 @@ public final class AsyncResourcePackCacheEvent extends Event { * 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. + * The resource pack will be added to the appropriate cache collection based on its id. *

* * @param path the file system path to the resource pack. Must be either a .zip file or a directory. diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java index 6d0a54209..dd1920ca0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.bukkit.api.event; import net.momirealms.craftengine.core.entity.furniture.AnchorType; -import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.entity.player.InteractionHand; import org.bukkit.Location; import org.bukkit.block.Block; @@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull; public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Cancellable { private static final HandlerList HANDLER_LIST = new HandlerList(); private boolean cancelled; - private final CustomFurniture furniture; + private final FurnitureConfig furniture; private final Location location; private final AnchorType anchorType; private final BlockFace clickedFace; @@ -23,7 +23,7 @@ public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Can private final InteractionHand hand; public FurnitureAttemptPlaceEvent(@NotNull Player player, - @NotNull CustomFurniture furniture, + @NotNull FurnitureConfig furniture, @NotNull AnchorType anchorType, @NotNull Location location, @NotNull BlockFace clickedFace, @@ -69,7 +69,7 @@ public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Can } @NotNull - public CustomFurniture furniture() { + public FurnitureConfig furniture() { return furniture; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java index 1e1adcf47..ebe8850fb 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.bukkit.api.event; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; -import net.momirealms.craftengine.core.entity.furniture.HitBox; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBox; import net.momirealms.craftengine.core.entity.player.InteractionHand; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java index 9c9b02c65..060282f7d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java @@ -45,7 +45,7 @@ public class NearLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior { public static class Factory implements BlockBehaviorFactory { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water"))); + List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-id", List.of("water"))); boolean stackable = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("stackable", false), "stackable"); int delay = ResourceConfigUtils.getAsInt(arguments.getOrDefault("delay", 0), "delay"); List positionsToCheck = MiscUtils.getAsStringList(arguments.getOrDefault("positions", List.of())); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java index e71780c7c..3e8a3fed1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java @@ -40,7 +40,7 @@ public class OnLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior { public static class Factory implements BlockBehaviorFactory { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water"))); + List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-id", List.of("water"))); boolean stackable = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("stackable", false), "stackable"); int delay = ResourceConfigUtils.getAsInt(arguments.getOrDefault("delay", 0), "delay"); return new OnLiquidBlockBehavior(block, delay, stackable, liquidTypes.contains("water"), liquidTypes.contains("lava")); 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 index daa220d7d..dbda0e1e0 100644 --- 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 @@ -6,8 +6,8 @@ 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.display.Billboard; +import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.util.Key; @@ -19,7 +19,6 @@ 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; @@ -173,8 +172,8 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo 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)), + ResourceConfigUtils.getAsEnum(ResourceConfigUtils.get(arguments, "display-context", "display-transform"), ItemDisplayContext.class, ItemDisplayContext.NONE), + ResourceConfigUtils.getAsEnum(arguments.get("billboard"), Billboard.class, Billboard.FIXED), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength") ); 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 index 12012724d..dc450d1fe 100644 --- 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 @@ -7,7 +7,7 @@ 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.display.Billboard; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.util.AdventureHelper; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitItemEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitItemEntity.java index 449fc5ebb..7ee3115ae 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitItemEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/BukkitItemEntity.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.bukkit.entity; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; -import net.momirealms.craftengine.core.entity.ItemEntity; +import net.momirealms.craftengine.core.entity.item.ItemEntity; import org.bukkit.entity.Item; public class BukkitItemEntity extends BukkitEntity implements ItemEntity { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java index 09d39ebdc..091f8654e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java @@ -6,7 +6,7 @@ public class ItemDisplayEntityData extends DisplayEntityData { // Item display only public static final ItemDisplayEntityData DisplayedItem = new ItemDisplayEntityData<>(ItemDisplayEntityData.class, EntityDataValue.Serializers$ITEM_STACK, CoreReflections.instance$ItemStack$EMPTY); /** - * Display type: + * Display id: * 0 = NONE * 1 = THIRD_PERSON_LEFT_HAND * 2 = THIRD_PERSON_RIGHT_HAND diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCustomFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCustomFurniture.java deleted file mode 100644 index dcad38455..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCustomFurniture.java +++ /dev/null @@ -1,74 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture; - -import net.momirealms.craftengine.core.entity.furniture.AbstractCustomFurniture; -import net.momirealms.craftengine.core.entity.furniture.AnchorType; -import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; -import net.momirealms.craftengine.core.entity.furniture.FurnitureSettings; -import net.momirealms.craftengine.core.loot.LootTable; -import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; -import net.momirealms.craftengine.core.plugin.context.function.Function; -import net.momirealms.craftengine.core.util.Key; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.Map; - -public class BukkitCustomFurniture extends AbstractCustomFurniture { - - protected BukkitCustomFurniture(@NotNull Key id, - @NotNull FurnitureSettings settings, - @NotNull Map placements, - @NotNull Map>> events, - @Nullable LootTable lootTable) { - super(id, settings, placements, events, lootTable); - } - - public static Builder builder() { - return new BuilderImpl(); - } - - public static class BuilderImpl implements Builder { - private Key id; - private Map placements; - private FurnitureSettings settings; - private Map>> events; - private LootTable lootTable; - - @Override - public CustomFurniture build() { - return new BukkitCustomFurniture(id, settings, placements, events, lootTable); - } - - @Override - public Builder id(Key id) { - this.id = id; - return this; - } - - @Override - public Builder placement(Map placements) { - this.placements = placements; - return this; - } - - @Override - public Builder settings(FurnitureSettings settings) { - this.settings = settings; - return this; - } - - @Override - public Builder lootTable(LootTable lootTable) { - this.lootTable = lootTable; - return this; - } - - @Override - public Builder events(Map>> events) { - this.events = events; - return this; - } - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index 37d64bbf6..fe9bec331 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -1,184 +1,166 @@ package net.momirealms.craftengine.bukkit.entity.furniture; -import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import net.momirealms.craftengine.bukkit.entity.BukkitEntity; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.core.entity.furniture.*; -import net.momirealms.craftengine.core.entity.seat.Seat; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; +import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; +import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.util.QuaternionUtils; import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.sparrow.nbt.CompoundTag; import org.bukkit.Location; import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; -import java.io.IOException; import java.lang.ref.WeakReference; -import java.util.*; +import java.util.Optional; +import java.util.UUID; public class BukkitFurniture implements Furniture { - private final CustomFurniture furniture; - private final CustomFurniture.Placement placement; - private FurnitureExtraData extraData; - // location - private final Location location; - // base entity - private final WeakReference baseEntity; - private final int baseEntityId; - // colliders - private final Collider[] colliderEntities; - // cache - private final List fakeEntityIds; - private final List entityIds; - private final Map hitBoxes = new Int2ObjectArrayMap<>(); - private final Map hitBoxParts = new Int2ObjectArrayMap<>(); - private final boolean minimized; - private final boolean hasExternalModel; - // cached spawn packet - private Object cachedSpawnPacket; - private Object cachedMinimizedSpawnPacket; + private static final UUID INVALID_UUID = new UUID(0, 0); + @NotNull + private final FurnitureConfig config; + @NotNull + private final FurnitureDataAccessor dataAccessor; - public BukkitFurniture(Entity baseEntity, - CustomFurniture furniture, - FurnitureExtraData extraData) { - this.extraData = extraData; - this.baseEntityId = baseEntity.getEntityId(); - this.location = baseEntity.getLocation(); - this.baseEntity = new WeakReference<>(baseEntity); - this.furniture = furniture; - this.minimized = furniture.settings().minimized(); - this.placement = furniture.getValidPlacement(extraData.anchorType().orElseGet(furniture::getAnyAnchorType)); + private WeakReference baseEntity; + private FurnitureVariant currentVariant; + private Location location; + private boolean valid; + private UUID uuid; + private int entityId; - List fakeEntityIds = new IntArrayList(); - List mainEntityIds = new IntArrayList(); - mainEntityIds.add(this.baseEntityId); + private FurnitureElement[] elements; - // 绑定外部模型 - Optional optionalExternal = placement.externalModel(); - if (optionalExternal.isPresent()) { - try { - optionalExternal.get().bindModel(new BukkitEntity(baseEntity)); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to load external model for furniture " + id(), e); - } - this.hasExternalModel = true; - } else { - this.hasExternalModel = false; - } + public BukkitFurniture(@NotNull FurnitureConfig config, + @NotNull CompoundTag data) { + this.dataAccessor = FurnitureDataAccessor.of(data); + this.currentVariant = Optional.ofNullable(getVariant()).orElseGet(config::anyVariant); + this.config = config; + this.baseEntity = new WeakReference<>(null); + this.valid = false; + this.entityId = -1; + this.uuid = INVALID_UUID; + } - Quaternionf conjugated = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate(); - List packets = new ArrayList<>(); - List minimizedPackets = new ArrayList<>(); - List colliders = new ArrayList<>(4); + private void sync() { WorldPosition position = position(); - - - // 初始化家具的元素 - for (FurnitureElement element : placement.elements()) { - int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); - fakeEntityIds.add(entityId); - element.initPackets(this, entityId, conjugated, packet -> { - packets.add(packet); - if (this.minimized) minimizedPackets.add(packet); - }); + FurnitureElementConfig[] elementConfigs = this.currentVariant.elements(); + FurnitureElement[] elements = new FurnitureElement[elementConfigs.length]; + for (int i = 0; i < elementConfigs.length; i++) { + FurnitureElement o = elementConfigs[i].create(position); + elements[i] = o; } + this.elements = elements; + } - // 初始化碰撞箱 - for (HitBoxConfig hitBoxConfig : this.placement.hitBoxConfigs()) { - int[] ids = hitBoxConfig.acquireEntityIds(CoreReflections.instance$Entity$ENTITY_COUNTER::incrementAndGet); - List aabbs = new ArrayList<>(); - - hitBoxConfig.initPacketsAndColliders(ids, position, conjugated, (packet, canBeMinimized) -> { - packets.add(packet); - if (this.minimized && !canBeMinimized) { - minimizedPackets.add(packet); - } - }, colliders::add, part -> { - this.hitBoxParts.put(part.entityId(), part); - aabbs.add(part); - }); - - BukkitHitBox hitBox = new BukkitHitBox(this, hitBoxConfig, aabbs.toArray(new HitBoxPart[0])); - for (int entityId : ids) { - fakeEntityIds.add(entityId); - mainEntityIds.add(entityId); - this.hitBoxes.put(entityId, hitBox); - } - } - - // 初始化缓存的家具包 - try { - this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); - if (this.minimized) { - this.cachedMinimizedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(minimizedPackets); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to init spawn packets for furniture " + id(), e); - } - - - this.fakeEntityIds = fakeEntityIds; - this.entityIds = mainEntityIds; - this.colliderEntities = colliders.toArray(new Collider[0]); + public void addToWorld(Location location) { + this.setLocation(location); + this.valid = true; + Entity baseEntity = null; // fixme 处理生成 + this.baseEntity = new WeakReference<>(baseEntity); + this.uuid = baseEntity.getUniqueId(); + this.entityId = baseEntity.getEntityId(); + this.sync(); } @Override - public void initializeColliders() { - Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(this.location.getWorld()); - for (Collider entity : this.colliderEntities) { - FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(world, entity.handle()); - Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity.handle()); - bukkitEntity.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_COLLISION, PersistentDataType.BYTE, (byte) 1); + public void show(Player player) { + for (FurnitureElement element : this.elements) { + element.show(player); } } - @NotNull - public Object spawnPacket(Player player) { - // TODO hasPermission might be slow, can we use a faster way in the future? - // TODO Make it based on conditions. So we can dynamically control which furniture should be sent to the player - if (!this.minimized || player.hasPermission(FurnitureManager.FURNITURE_ADMIN_NODE)) { - return this.cachedSpawnPacket; - } else { - return this.cachedMinimizedSpawnPacket; + @Override + public void hide(Player player) { + for (FurnitureElement element : this.elements) { + element.hide(player); } } + @Nullable + @Override + public CullingData cullingData() { + return this.config.cullingData(); + } + + @NotNull + @Override + public FurnitureConfig config() { + return this.config; + } + + @NotNull + @Override + public FurnitureDataAccessor dataAccessor() { + return this.dataAccessor; + } + @Override public WorldPosition position() { return LocationUtils.toWorldPosition(this.location); } - @NotNull - public Location location() { - return this.location.clone(); - } - - @NotNull - public Entity baseEntity() { - Entity entity = this.baseEntity.get(); - if (entity == null) { - throw new RuntimeException("Base entity not found. It might be unloaded."); - } - return entity; + @Override + public boolean isValid() { + return this.valid; } @Override - public boolean isValid() { - return baseEntity().isValid(); + public void destroy() { + this.valid = false; + Optional.ofNullable(this.baseEntity.get()).ifPresent(Entity::remove); } - @NotNull - public Location dropLocation() { - Optional dropOffset = this.placement.dropOffset(); + @Override + public UUID uuid() { + return this.uuid; + } + + @Override + public int entityId() { + return this.entityId; + } + + public void teleport(WorldPosition position) { + Location newLocation = LocationUtils.toLocation(position); + if (newLocation.equals(this.location)) { + return; + } + this.setLocation(newLocation); + this.sync(); + } + + public Location location() { + return location; + } + + private void setLocation(Location location) { + this.location = location; + } + + public FurnitureVariant getVariant() { + return this.config.getVariant(this.dataAccessor.variant().orElseGet(this.config::anyVariantName)); + } + + public void setVariant(String variant) { + FurnitureVariant newVariant = this.config.getVariant(variant); + if (newVariant != this.currentVariant) { + this.currentVariant = newVariant; + this.sync(); + } + } + + // 获取掉落物的位置,受到家具变种的影响 + public Location getDropLocation() { + Optional dropOffset = this.getVariant().dropOffset(); if (dropOffset.isEmpty()) { return location(); } @@ -186,105 +168,4 @@ public class BukkitFurniture implements Furniture { Vector3f offset = conjugated.transform(new Vector3f(dropOffset.get())); return new Location(this.location.getWorld(), this.location.getX() + offset.x, this.location.getY() + offset.y, this.location.getZ() - offset.z); } - - @Override - public void destroy() { - if (!isValid()) { - return; - } - this.baseEntity().remove(); - this.destroyColliders(); - this.destroySeats(); - } - - @Override - public void destroyColliders() { - for (Collider entity : this.colliderEntities) { - if (entity != null) - entity.destroy(); - } - } - - @Override - public void destroySeats() { - for (HitBox hitBox : this.hitBoxes.values()) { - for (Seat seat : hitBox.seats()) { - seat.destroy(); - } - } - } - - @Override - public UUID uuid() { - return this.baseEntity().getUniqueId(); - } - - @Override - public int baseEntityId() { - return this.baseEntityId; - } - - @NotNull - public List entityIds() { - return Collections.unmodifiableList(this.entityIds); - } - - @NotNull - public List fakeEntityIds() { - return Collections.unmodifiableList(this.fakeEntityIds); - } - - public Collider[] collisionEntities() { - return this.colliderEntities; - } - - @Override - public @Nullable HitBox hitBoxByEntityId(int id) { - return this.hitBoxes.get(id); - } - - @Override - public @Nullable HitBoxPart hitBoxPartByEntityId(int id) { - return this.hitBoxParts.get(id); - } - - @Override - public @NotNull AnchorType anchorType() { - return this.placement.anchorType(); - } - - @Override - public @NotNull Key id() { - return this.furniture.id(); - } - - @Override - public @NotNull CustomFurniture config() { - return this.furniture; - } - - @Override - public boolean hasExternalModel() { - return hasExternalModel; - } - - @Override - public FurnitureExtraData extraData() { - return this.extraData; - } - - @Override - public void setExtraData(FurnitureExtraData extraData) { - this.extraData = extraData; - this.save(); - } - - @Override - public void save() { - try { - this.baseEntity().getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, this.extraData.toBytes()); - } catch (IOException e) { - CraftEngine.instance().logger().warn("Failed to save furniture data.", e); - } - } } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java deleted file mode 100644 index 83ca3fa4d..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureElement.java +++ /dev/null @@ -1,177 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture; - -import it.unimi.dsi.fastutil.ints.IntArrayList; -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.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; -import net.momirealms.craftengine.core.entity.Billboard; -import net.momirealms.craftengine.core.entity.ItemDisplayContext; -import net.momirealms.craftengine.core.entity.furniture.AbstractFurnitureElement; -import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.furniture.FurnitureElement; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.data.FireworkExplosion; -import net.momirealms.craftengine.core.util.Color; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.WorldPosition; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.function.Consumer; - -public class BukkitFurnitureElement extends AbstractFurnitureElement { - private final List commonValues; - - public BukkitFurnitureElement(Key item, - Billboard billboard, - ItemDisplayContext transform, - Vector3f scale, - Vector3f translation, - Vector3f position, - Quaternionf rotation, - float shadowRadius, - float shadowStrength, - boolean applyDyedColor) { - super(item, billboard, transform, scale, translation, position, rotation, shadowRadius, shadowStrength, applyDyedColor); - this.commonValues = new ArrayList<>(); - ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(scale(), this.commonValues); - ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(rotation(), this.commonValues); - ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(billboard().id(), this.commonValues); - ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(translation(), this.commonValues); - ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(transform().id(), this.commonValues); - ItemDisplayEntityData.ShadowRadius.addEntityDataIfNotDefaultValue(shadowRadius, this.commonValues); - ItemDisplayEntityData.ShadowStrength.addEntityDataIfNotDefaultValue(shadowStrength, this.commonValues); - } - - @Override - public void initPackets(Furniture furniture, int entityId, @NotNull Quaternionf conjugated, Consumer packets) { - WorldPosition position = furniture.position(); - Vector3f offset = conjugated.transform(new Vector3f(position())); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityId, UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.yRot(), - MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 - )); - if (applyDyedColor()) { - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, getCachedValues( - furniture.extraData().dyedColor().orElse(null), - furniture.extraData().fireworkExplosionColors().orElse(null) - ))); - } else { - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, getCachedValues(null, null))); - } - } - - private synchronized List getCachedValues(@Nullable Color color, int @Nullable [] colors) { - List cachedValues = new ArrayList<>(this.commonValues); - Item item = BukkitItemManager.instance().createWrappedItem(item(), null); - if (item == null) { - item = BukkitItemManager.instance().wrap(new ItemStack(Material.BARRIER)); - } else { - if (color != null) { - item.dyedColor(color); - } - if (colors != null) { - item.fireworkExplosion(new FireworkExplosion( - FireworkExplosion.Shape.SMALL_BALL, - new IntArrayList(colors), - new IntArrayList(), - false, - false - )); - } - } - ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(item.getLiteralObject(), cachedValues); - return cachedValues; - } - - public static Builder builder() { - return new BuilderImpl(); - } - - public static class BuilderImpl implements Builder { - private boolean applyDyedColor; - private Key item; - private Billboard billboard; - private ItemDisplayContext transform; - private Vector3f scale; - private Vector3f translation; - private Vector3f position; - private Quaternionf rotation; - private float shadowRadius; - private float shadowStrength; - - @Override - public Builder applyDyedColor(boolean applyDyedColor) { - this.applyDyedColor = applyDyedColor; - return this; - } - - @Override - public Builder item(Key item) { - this.item = item; - return this; - } - - @Override - public Builder billboard(Billboard billboard) { - this.billboard = billboard; - return this; - } - - @Override - public Builder transform(ItemDisplayContext transform) { - this.transform = transform; - return this; - } - - @Override - public Builder scale(Vector3f scale) { - this.scale = scale; - return this; - } - - @Override - public Builder translation(Vector3f translation) { - this.translation = translation; - return this; - } - - @Override - public Builder position(Vector3f position) { - this.position = position; - return this; - } - - @Override - public Builder rotation(Quaternionf rotation) { - this.rotation = rotation; - return this; - } - - @Override - public Builder shadowStrength(float shadowStrength) { - this.shadowStrength = shadowStrength; - return this; - } - - @Override - public Builder shadowRadius(float shadowRadius) { - this.shadowRadius = shadowRadius; - return this; - } - - @Override - public FurnitureElement build() { - return new BukkitFurnitureElement(item, billboard, transform, scale, translation, position, rotation, shadowRadius, shadowStrength, applyDyedColor); - } - } -} 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 f946e4888..969727e89 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 @@ -1,34 +1,23 @@ package net.momirealms.craftengine.bukkit.entity.furniture; -import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionHitBoxConfig; -import net.momirealms.craftengine.bukkit.nms.CollisionEntity; -import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.network.handler.FurniturePacketHandler; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; -import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; -import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; -import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.*; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; 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.VersionHelper; import net.momirealms.craftengine.core.world.WorldPosition; -import org.bukkit.*; -import org.bukkit.entity.*; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Boat; +import org.bukkit.entity.Interaction; import org.bukkit.event.HandlerList; -import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.Nullable; -import java.io.IOException; -import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BiConsumer; public class BukkitFurnitureManager extends AbstractFurnitureManager { public static final NamespacedKey FURNITURE_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_KEY); @@ -56,30 +45,32 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } @Override - public Furniture place(WorldPosition position, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) { - return this.place(LocationUtils.toLocation(position), furniture, extraData, playSound); + public Furniture place(WorldPosition position, FurnitureConfig furniture, FurnitureDataAccessor dataAccessor, boolean playSound) { +// return this.place(LocationUtils.toLocation(position), furniture, dataAccessor, playSound); + return null; } - public BukkitFurniture place(Location location, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) { - Optional optionalAnchorType = extraData.anchorType(); - if (optionalAnchorType.isEmpty() || !furniture.isAllowedPlacement(optionalAnchorType.get())) { - extraData.anchorType(furniture.getAnyAnchorType()); - } - Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> { - ItemDisplay display = (ItemDisplay) entity; - display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_KEY, PersistentDataType.STRING, furniture.id().toString()); - try { - display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, extraData.toBytes()); - } catch (IOException e) { - this.plugin.logger().warn("Failed to set furniture PDC for " + furniture.id().toString(), e); - } - handleBaseEntityLoadEarly(display); - }); - if (playSound) { - SoundData data = furniture.settings().sounds().placeSound(); - location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume().get(), data.pitch().get()); - } - return loadedFurnitureByRealEntityId(furnitureEntity.getEntityId()); + public BukkitFurniture place(Location location, FurnitureConfig furniture, FurnitureDataAccessor extraData, boolean playSound) { +// Optional optionalAnchorType = extraData.anchorType(); +// if (optionalAnchorType.isEmpty() || !furniture.isAllowedPlacement(optionalAnchorType.get())) { +// extraData.anchorType(furniture.getAnyAnchorType()); +// } +// Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> { +// ItemDisplay display = (ItemDisplay) entity; +// display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_KEY, PersistentDataType.STRING, furniture.id().toString()); +// try { +// display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, extraData.toBytes()); +// } catch (IOException e) { +// this.plugin.logger().warn("Failed to set furniture PDC for " + furniture.id().toString(), e); +// } +// handleBaseEntityLoadEarly(display); +// }); +// if (playSound) { +// SoundData data = furniture.settings().sounds().placeSound(); +// location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume().get(), data.pitch().get()); +// } +// return loadedFurnitureByRealEntityId(furnitureEntity.getEntityId()); + return null; } @Override @@ -88,34 +79,34 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { NMS_COLLISION_ENTITY_TYPE = Config.colliderType() == ColliderType.INTERACTION ? MEntityTypes.INTERACTION : MEntityTypes.OAK_BOAT; COLLISION_ENTITY_TYPE = Config.colliderType(); Bukkit.getPluginManager().registerEvents(this.furnitureEventListener, this.plugin.javaPlugin()); - if (VersionHelper.isFolia()) { - BiConsumer taskExecutor = (entity, runnable) -> entity.getScheduler().run(this.plugin.javaPlugin(), (t) -> runnable.run(), () -> {}); - for (World world : Bukkit.getWorlds()) { - List entities = world.getEntities(); - for (Entity entity : entities) { - if (entity instanceof ItemDisplay display) { - taskExecutor.accept(entity, () -> handleBaseEntityLoadEarly(display)); - } else if (entity instanceof Interaction interaction) { - taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(interaction)); - } else if (entity instanceof Boat boat) { - taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(boat)); - } - } - } - } else { - for (World world : Bukkit.getWorlds()) { - List entities = world.getEntities(); - for (Entity entity : entities) { - if (entity instanceof ItemDisplay display) { - handleBaseEntityLoadEarly(display); - } else if (entity instanceof Interaction interaction) { - handleCollisionEntityLoadOnEntitiesLoad(interaction); - } else if (entity instanceof Boat boat) { - handleCollisionEntityLoadOnEntitiesLoad(boat); - } - } - } - } +// if (VersionHelper.isFolia()) { +// BiConsumer taskExecutor = (entity, runnable) -> entity.getScheduler().run(this.plugin.javaPlugin(), (t) -> runnable.run(), () -> {}); +// for (World world : Bukkit.getWorlds()) { +// List entities = world.getEntities(); +// for (Entity entity : entities) { +// if (entity instanceof ItemDisplay display) { +// taskExecutor.accept(entity, () -> handleBaseEntityLoadEarly(display)); +// } else if (entity instanceof Interaction interaction) { +// taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(interaction)); +// } else if (entity instanceof Boat boat) { +// taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(boat)); +// } +// } +// } +// } else { +// for (World world : Bukkit.getWorlds()) { +// List entities = world.getEntities(); +// for (Entity entity : entities) { +// if (entity instanceof ItemDisplay display) { +// handleBaseEntityLoadEarly(display); +// } else if (entity instanceof Interaction interaction) { +// handleCollisionEntityLoadOnEntitiesLoad(interaction); +// } else if (entity instanceof Boat boat) { +// handleCollisionEntityLoadOnEntitiesLoad(boat); +// } +// } +// } +// } } @Override @@ -141,174 +132,165 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { return this.furnitureByEntityId.get(entityId); } - @Override - protected CustomFurniture.Builder furnitureBuilder() { - return BukkitCustomFurniture.builder(); - } - - @Override - protected FurnitureElement.Builder furnitureElementBuilder() { - return BukkitFurnitureElement.builder(); - } - - protected void handleBaseEntityUnload(Entity entity) { - int id = entity.getEntityId(); - BukkitFurniture furniture = this.furnitureByRealEntityId.remove(id); - if (furniture != null) { - Location location = entity.getLocation(); - boolean isPreventing = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); - if (!isPreventing) { - furniture.destroySeats(); - } - for (int sub : furniture.entityIds()) { - this.furnitureByEntityId.remove(sub); - } - } - } - - protected void handleCollisionEntityUnload(Entity entity) { - int id = entity.getEntityId(); - this.furnitureByRealEntityId.remove(id); - } - - @SuppressWarnings("deprecation") // just a misleading name `getTrackedPlayers` - protected void handleBaseEntityLoadLate(ItemDisplay display, int depth) { - // must be a furniture item - String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); - if (id == null) return; - - Key key = Key.of(id); - Optional optionalFurniture = furnitureById(key); - if (optionalFurniture.isEmpty()) return; - - CustomFurniture customFurniture = optionalFurniture.get(); - BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId()); - if (previous != null) return; - - Location location = display.getLocation(); - boolean above1_20_1 = VersionHelper.isOrAbove1_20_2(); - boolean preventChange = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); - if (above1_20_1) { - if (!preventChange) { - BukkitFurniture furniture = addNewFurniture(display, customFurniture); - furniture.initializeColliders(); - for (Player player : display.getTrackedPlayers()) { - BukkitAdaptors.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); - this.plugin.networkManager().sendPacket(BukkitAdaptors.adapt(player), furniture.spawnPacket(player)); - } - } - } else { - BukkitFurniture furniture = addNewFurniture(display, customFurniture); - for (Player player : display.getTrackedPlayers()) { - BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); - serverPlayer.entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); - this.plugin.networkManager().sendPacket(serverPlayer, furniture.spawnPacket(player)); - } - if (preventChange) { - this.plugin.scheduler().sync().runLater(furniture::initializeColliders, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); - } else { - furniture.initializeColliders(); - } - } - if (depth > 2) return; - this.plugin.scheduler().sync().runLater(() -> handleBaseEntityLoadLate(display, depth + 1), 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); - } - - protected void handleCollisionEntityLoadLate(Entity entity, int depth) { - // remove the entity if it's not a collision entity, it might be wrongly copied by WorldEdit - if (FastNMS.INSTANCE.method$CraftEntity$getHandle(entity) instanceof CollisionEntity) { - return; - } - // not a collision entity - Byte flag = entity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); - if (flag == null || flag != 1) { - return; - } - - Location location = entity.getLocation(); - World world = location.getWorld(); - int chunkX = location.getBlockX() >> 4; - int chunkZ = location.getBlockZ() >> 4; - if (!FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world), chunkX, chunkZ)) { - entity.remove(); - return; - } - - if (depth > 2) return; - plugin.scheduler().sync().runLater(() -> { - handleCollisionEntityLoadLate(entity, depth + 1); - }, 1, world, chunkX, chunkZ); - } - - public void handleBaseEntityLoadEarly(ItemDisplay display) { - String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); - if (id == null) return; - // Remove the entity if it's not a valid furniture - if (Config.handleInvalidFurniture()) { - String mapped = Config.furnitureMappings().get(id); - if (mapped != null) { - if (mapped.isEmpty()) { - display.remove(); - return; - } else { - id = mapped; - display.getPersistentDataContainer().set(FURNITURE_KEY, PersistentDataType.STRING, id); - } - } - } - - Key key = Key.of(id); - Optional optionalFurniture = furnitureById(key); - if (optionalFurniture.isPresent()) { - CustomFurniture customFurniture = optionalFurniture.get(); - BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId()); - if (previous != null) return; - BukkitFurniture furniture = addNewFurniture(display, customFurniture); - furniture.initializeColliders(); // safely do it here - } - } - - public void handleCollisionEntityLoadOnEntitiesLoad(Entity collisionEntity) { - // faster - if (FastNMS.INSTANCE.method$CraftEntity$getHandle(collisionEntity) instanceof CollisionEntity) { - collisionEntity.remove(); - return; - } - - // not a collision entity - Byte flag = collisionEntity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); - if (flag == null || flag != 1) { - return; - } - - collisionEntity.remove(); - } - - private FurnitureExtraData getFurnitureExtraData(Entity baseEntity) throws IOException { - byte[] extraData = baseEntity.getPersistentDataContainer().get(FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY); - if (extraData == null) return FurnitureExtraData.builder().build(); - return FurnitureExtraData.fromBytes(extraData); - } - - private synchronized BukkitFurniture addNewFurniture(ItemDisplay display, CustomFurniture furniture) { - FurnitureExtraData extraData; - try { - extraData = getFurnitureExtraData(display); - } catch (IOException e) { - extraData = FurnitureExtraData.builder().build(); - plugin.logger().warn("Furniture extra data could not be loaded", e); - } - BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, extraData); - this.furnitureByRealEntityId.put(bukkitFurniture.baseEntityId(), bukkitFurniture); - for (int entityId : bukkitFurniture.entityIds()) { - this.furnitureByEntityId.put(entityId, bukkitFurniture); - } - for (Collider collisionEntity : bukkitFurniture.collisionEntities()) { - int collisionEntityId = FastNMS.INSTANCE.method$Entity$getId(collisionEntity.handle()); - this.furnitureByRealEntityId.put(collisionEntityId, bukkitFurniture); - } - return bukkitFurniture; - } +// +// protected void handleBaseEntityUnload(Entity entity) { +// int id = entity.getEntityId(); +// BukkitFurniture furniture = this.furnitureByRealEntityId.remove(id); +// if (furniture != null) { +// Location location = entity.getLocation(); +// boolean isPreventing = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); +// if (!isPreventing) { +// furniture.destroySeats(); +// } +// for (int sub : furniture.entityIds()) { +// this.furnitureByEntityId.remove(sub); +// } +// } +// } +// +// protected void handleCollisionEntityUnload(Entity entity) { +// int id = entity.getEntityId(); +// this.furnitureByRealEntityId.remove(id); +// } +// +// @SuppressWarnings("deprecation") // just a misleading name `getTrackedPlayers` +// protected void handleBaseEntityLoadLate(ItemDisplay display, int depth) { +// // must be a furniture item +// String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); +// if (id == null) return; +// +// Key key = Key.of(id); +// Optional optionalFurniture = furnitureById(key); +// if (optionalFurniture.isEmpty()) return; +// +// FurnitureConfig customFurniture = optionalFurniture.get(); +// BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId()); +// if (previous != null) return; +// +// Location location = display.getLocation(); +// boolean above1_20_1 = VersionHelper.isOrAbove1_20_2(); +// boolean preventChange = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); +// if (above1_20_1) { +// if (!preventChange) { +// BukkitFurniture furniture = addNewFurniture(display, customFurniture); +// furniture.initializeColliders(); +// for (Player player : display.getTrackedPlayers()) { +// BukkitAdaptors.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); +// this.plugin.networkManager().sendPacket(BukkitAdaptors.adapt(player), furniture.spawnPacket(player)); +// } +// } +// } else { +// BukkitFurniture furniture = addNewFurniture(display, customFurniture); +// for (Player player : display.getTrackedPlayers()) { +// BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); +// serverPlayer.entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); +// this.plugin.networkManager().sendPacket(serverPlayer, furniture.spawnPacket(player)); +// } +// if (preventChange) { +// this.plugin.scheduler().sync().runLater(furniture::initializeColliders, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); +// } else { +// furniture.initializeColliders(); +// } +// } +// if (depth > 2) return; +// this.plugin.scheduler().sync().runLater(() -> handleBaseEntityLoadLate(display, depth + 1), 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); +// } +// +// protected void handleCollisionEntityLoadLate(Entity entity, int depth) { +// // remove the entity if it's not a collision entity, it might be wrongly copied by WorldEdit +// if (FastNMS.INSTANCE.method$CraftEntity$getHandle(entity) instanceof CollisionEntity) { +// return; +// } +// // not a collision entity +// Byte flag = entity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); +// if (flag == null || flag != 1) { +// return; +// } +// +// Location location = entity.getLocation(); +// World world = location.getWorld(); +// int chunkX = location.getBlockX() >> 4; +// int chunkZ = location.getBlockZ() >> 4; +// if (!FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world), chunkX, chunkZ)) { +// entity.remove(); +// return; +// } +// +// if (depth > 2) return; +// plugin.scheduler().sync().runLater(() -> { +// handleCollisionEntityLoadLate(entity, depth + 1); +// }, 1, world, chunkX, chunkZ); +// } +// +// public void handleBaseEntityLoadEarly(ItemDisplay display) { +// String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); +// if (id == null) return; +// // Remove the entity if it's not a valid furniture +// if (Config.handleInvalidFurniture()) { +// String mapped = Config.furnitureMappings().get(id); +// if (mapped != null) { +// if (mapped.isEmpty()) { +// display.remove(); +// return; +// } else { +// id = mapped; +// display.getPersistentDataContainer().set(FURNITURE_KEY, PersistentDataType.STRING, id); +// } +// } +// } +// +// Key key = Key.of(id); +// Optional optionalFurniture = furnitureById(key); +// if (optionalFurniture.isPresent()) { +// FurnitureConfig customFurniture = optionalFurniture.get(); +// BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId()); +// if (previous != null) return; +// BukkitFurniture furniture = addNewFurniture(display, customFurniture); +// furniture.initializeColliders(); // safely do it here +// } +// } +// +// public void handleCollisionEntityLoadOnEntitiesLoad(Entity collisionEntity) { +// // faster +// if (FastNMS.INSTANCE.method$CraftEntity$getHandle(collisionEntity) instanceof CollisionEntity) { +// collisionEntity.remove(); +// return; +// } +// +// // not a collision entity +// Byte flag = collisionEntity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); +// if (flag == null || flag != 1) { +// return; +// } +// +// collisionEntity.remove(); +// } +// +// private FurnitureDataAccessor getFurnitureExtraData(Entity baseEntity) throws IOException { +// byte[] extraData = baseEntity.getPersistentDataContainer().get(FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY); +// if (extraData == null) return FurnitureDataAccessor.builder().build(); +// return FurnitureDataAccessor.fromBytes(extraData); +// } +// +// private synchronized BukkitFurniture addNewFurniture(ItemDisplay display, FurnitureConfig furniture) { +// FurnitureDataAccessor extraData; +// try { +// extraData = getFurnitureExtraData(display); +// } catch (IOException e) { +// extraData = FurnitureDataAccessor.builder().build(); +// plugin.logger().warn("Furniture extra data could not be loaded", e); +// } +// BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, extraData); +// this.furnitureByRealEntityId.put(bukkitFurniture.baseEntityId(), bukkitFurniture); +// for (int entityId : bukkitFurniture.entityIds()) { +// this.furnitureByEntityId.put(entityId, bukkitFurniture); +// } +// for (Collider collisionEntity : bukkitFurniture.collisionEntities()) { +// int collisionEntityId = FastNMS.INSTANCE.method$Entity$getId(collisionEntity.handle()); +// this.furnitureByRealEntityId.put(collisionEntityId, bukkitFurniture); +// } +// return bukkitFurniture; +// } @Override protected HitBoxConfig defaultHitBox() { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java index ce4864b90..5fa382018 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java @@ -1,18 +1,6 @@ package net.momirealms.craftengine.bukkit.entity.furniture; -import com.destroystokyo.paper.event.entity.EntityAddToWorldEvent; -import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent; -import org.bukkit.entity.Entity; -import org.bukkit.entity.ItemDisplay; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.world.ChunkUnloadEvent; -import org.bukkit.event.world.EntitiesLoadEvent; -import org.bukkit.event.world.WorldLoadEvent; -import org.bukkit.event.world.WorldUnloadEvent; - -import java.util.List; public class FurnitureEventListener implements Listener { private final BukkitFurnitureManager manager; @@ -21,77 +9,77 @@ public class FurnitureEventListener implements Listener { this.manager = manager; } - /* - * Load Entities - */ - @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) - public void onEntitiesLoadEarly(EntitiesLoadEvent event) { - List entities = event.getEntities(); - for (Entity entity : entities) { - if (entity instanceof ItemDisplay itemDisplay) { - this.manager.handleBaseEntityLoadEarly(itemDisplay); - } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { - this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity); - } - } - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) - public void onWorldLoad(WorldLoadEvent event) { - List entities = event.getWorld().getEntities(); - for (Entity entity : entities) { - if (entity instanceof ItemDisplay itemDisplay) { - this.manager.handleBaseEntityLoadEarly(itemDisplay); - } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { - this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity); - } - } - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) - public void onEntityLoad(EntityAddToWorldEvent event) { - Entity entity = event.getEntity(); - if (entity instanceof ItemDisplay itemDisplay) { - this.manager.handleBaseEntityLoadLate(itemDisplay, 0); - } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { - this.manager.handleCollisionEntityLoadLate(entity, 0); - } - } - - /* - * Unload Entities - */ - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onChunkUnload(ChunkUnloadEvent event) { - Entity[] entities = event.getChunk().getEntities(); - for (Entity entity : entities) { - if (entity instanceof ItemDisplay) { - this.manager.handleBaseEntityUnload(entity); - } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { - this.manager.handleCollisionEntityUnload(entity); - } - } - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onWorldUnload(WorldUnloadEvent event) { - List entities = event.getWorld().getEntities(); - for (Entity entity : entities) { - if (entity instanceof ItemDisplay) { - this.manager.handleBaseEntityUnload(entity); - } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { - this.manager.handleCollisionEntityUnload(entity); - } - } - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onEntityUnload(EntityRemoveFromWorldEvent event) { - Entity entity = event.getEntity(); - if (entity instanceof ItemDisplay) { - this.manager.handleBaseEntityUnload(entity); - } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { - this.manager.handleCollisionEntityUnload(entity); - } - } +// /* +// * Load Entities +// */ +// @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) +// public void onEntitiesLoadEarly(EntitiesLoadEvent event) { +// List entities = event.getEntities(); +// for (Entity entity : entities) { +// if (entity instanceof ItemDisplay itemDisplay) { +// this.manager.handleBaseEntityLoadEarly(itemDisplay); +// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { +// this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity); +// } +// } +// } +// +// @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) +// public void onWorldLoad(WorldLoadEvent event) { +// List entities = event.getWorld().getEntities(); +// for (Entity entity : entities) { +// if (entity instanceof ItemDisplay itemDisplay) { +// this.manager.handleBaseEntityLoadEarly(itemDisplay); +// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { +// this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity); +// } +// } +// } +// +// @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) +// public void onEntityLoad(EntityAddToWorldEvent event) { +// Entity entity = event.getEntity(); +// if (entity instanceof ItemDisplay itemDisplay) { +// this.manager.handleBaseEntityLoadLate(itemDisplay, 0); +// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { +// this.manager.handleCollisionEntityLoadLate(entity, 0); +// } +// } +// +// /* +// * Unload Entities +// */ +// @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) +// public void onChunkUnload(ChunkUnloadEvent event) { +// Entity[] entities = event.getChunk().getEntities(); +// for (Entity entity : entities) { +// if (entity instanceof ItemDisplay) { +// this.manager.handleBaseEntityUnload(entity); +// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { +// this.manager.handleCollisionEntityUnload(entity); +// } +// } +// } +// +// @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) +// public void onWorldUnload(WorldUnloadEvent event) { +// List entities = event.getWorld().getEntities(); +// for (Entity entity : entities) { +// if (entity instanceof ItemDisplay) { +// this.manager.handleBaseEntityUnload(entity); +// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { +// this.manager.handleCollisionEntityUnload(entity); +// } +// } +// } +// +// @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) +// public void onEntityUnload(EntityRemoveFromWorldEvent event) { +// Entity entity = event.getEntity(); +// if (entity instanceof ItemDisplay) { +// this.manager.handleBaseEntityUnload(entity); +// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { +// this.manager.handleCollisionEntityUnload(entity); +// } +// } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java new file mode 100644 index 000000000..339dad09d --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.bukkit.entity.furniture; + +import net.momirealms.craftengine.core.util.Color; + +public record ItemColorSource(Color dyedColor, int[] fireworkColors) { +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java new file mode 100644 index 000000000..4ce2d57f2 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java @@ -0,0 +1,15 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.element; + +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs; + +public class BukkitFurnitureElementConfigs extends FurnitureElementConfigs { + + static { + register(ITEM_DISPLAY, ItemDisplayFurnitureElementConfig.FACTORY); + } + + private BukkitFurnitureElementConfigs() {} + + public static void init() { + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java new file mode 100644 index 000000000..5c92715cf --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java @@ -0,0 +1,22 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.element; + +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; +import net.momirealms.craftengine.core.entity.player.Player; + +public class ItemDisplayFurnitureElement implements FurnitureElement { + private final ItemDisplayFurnitureElementConfig config; + + public ItemDisplayFurnitureElement(ItemDisplayFurnitureElementConfig config) { + this.config = config; + } + + @Override + public void show(Player player) { + + } + + @Override + public void hide(Player player) { + + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java new file mode 100644 index 000000000..8edc5d489 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -0,0 +1,169 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.element; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; +import net.momirealms.craftengine.bukkit.entity.furniture.ItemColorSource; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.core.entity.display.Billboard; +import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; +import net.momirealms.craftengine.core.item.data.FireworkExplosion; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.WorldPosition; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiFunction; + +public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig { + public static final Factory FACTORY = new Factory(); + private final BiFunction> lazyMetadataPacket; + private final BiFunction> 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; + private final float shadowRadius; + private final float shadowStrength; + private final boolean applyDyedColor; + + public ItemDisplayFurnitureElementConfig(BiFunction> item, + Vector3f scale, + Vector3f position, + Vector3f translation, + float xRot, + float yRot, + Quaternionf rotation, + ItemDisplayContext displayContext, + Billboard billboard, + float shadowRadius, + float shadowStrength, + boolean applyDyedColor) { + 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.shadowRadius = shadowRadius; + this.shadowStrength = shadowStrength; + this.applyDyedColor = applyDyedColor; + this.item = item; + this.lazyMetadataPacket = (player, source) -> { + List dataValues = new ArrayList<>(); + ItemDisplayEntityData.DisplayedItem.addEntityData(item.apply(player, source).getLiteralObject(), dataValues); + ItemDisplayEntityData.Scale.addEntityData(this.scale, dataValues); + ItemDisplayEntityData.RotationLeft.addEntityData(this.rotation, dataValues); + ItemDisplayEntityData.BillboardConstraints.addEntityData(this.billboard.id(), dataValues); + ItemDisplayEntityData.Translation.addEntityData(this.translation, dataValues); + ItemDisplayEntityData.DisplayType.addEntityData(this.displayContext.id(), dataValues); + ItemDisplayEntityData.ShadowRadius.addEntityData(this.shadowRadius, dataValues); + ItemDisplayEntityData.ShadowStrength.addEntityData(this.shadowStrength, dataValues); + return dataValues; + }; + } + + public Vector3f scale() { + return scale; + } + + public Vector3f position() { + return position; + } + + public Vector3f translation() { + return translation; + } + + public float xRot() { + return xRot; + } + + public float yRot() { + return yRot; + } + + public Quaternionf rotation() { + return rotation; + } + + public ItemDisplayContext displayContext() { + return displayContext; + } + + public Billboard billboard() { + return billboard; + } + + public float shadowRadius() { + return shadowRadius; + } + + public float shadowStrength() { + return shadowStrength; + } + + public boolean applyDyedColor() { + return applyDyedColor; + } + + @Override + public ItemDisplayFurnitureElement create(@NotNull WorldPosition position) { + return new ItemDisplayFurnitureElement(this); + } + + public static class Factory implements FurnitureElementConfigFactory { + + @SuppressWarnings("unchecked") + @Override + public FurnitureElementConfig create(Map arguments) { + Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.furniture.element.item_display.missing_item")); + boolean applyDyedColor = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color"); + return (FurnitureElementConfig) new ItemDisplayFurnitureElementConfig( + (player, colorSource) -> { + Item wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player); + if (applyDyedColor && colorSource != null && wrappedItem != null) { + Optional.ofNullable(colorSource.dyedColor()).ifPresent(wrappedItem::dyedColor); + Optional.ofNullable(colorSource.fireworkColors()).ifPresent(colors -> wrappedItem.fireworkExplosion(new FireworkExplosion( + FireworkExplosion.Shape.SMALL_BALL, + new IntArrayList(colors), + new IntArrayList(), + false, + false + ))); + } + return Optional.ofNullable(wrappedItem).orElseGet(() -> BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null)); + }, + 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"), + ResourceConfigUtils.getAsEnum(ResourceConfigUtils.get(arguments, "display-context", "display-transform"), ItemDisplayContext.class, ItemDisplayContext.NONE), + ResourceConfigUtils.getAsEnum(arguments.get("billboard"), Billboard.class, Billboard.FIXED), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength"), + applyDyedColor + ); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java index 4d92bd7c8..017824a2a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; -import net.momirealms.craftengine.core.entity.furniture.HitBoxTypes; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxTypes; public class BukkitHitBoxTypes extends HitBoxTypes { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java index fa8e974a8..2b5abc32e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java @@ -1,42 +1,34 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; -import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; -import net.momirealms.craftengine.core.entity.furniture.*; +import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxTypes; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.craftengine.core.world.collision.AABB; import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.entity.EntityType; -import org.joml.Quaternionf; import org.joml.Vector3f; -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; +import java.util.Map; public class CustomHitBoxConfig extends AbstractHitBoxConfig { public static final Factory FACTORY = new Factory(); private final float scale; private final EntityType entityType; - private final List cachedValues = new ArrayList<>(); - public CustomHitBoxConfig(SeatConfig[] seats, Vector3f position, EntityType type, float scale, boolean blocksBuilding, boolean canBeHitByProjectile) { + public CustomHitBoxConfig(SeatConfig[] seats, + Vector3f position, + EntityType type, + float scale, + boolean blocksBuilding, + boolean canBeHitByProjectile) { super(seats, position, false, blocksBuilding, canBeHitByProjectile); this.scale = scale; this.entityType = type; - BaseEntityData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedValues); - BaseEntityData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedValues); - BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues); } public EntityType entityType() { @@ -52,41 +44,13 @@ public class CustomHitBoxConfig extends AbstractHitBoxConfig { return HitBoxTypes.CUSTOM; } - @Override - public void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, BiConsumer packets, Consumer collider, Consumer aabb) { - Vector3f offset = conjugated.transform(new Vector3f(position())); - try { - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityId[0], UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.yRot(), - FastNMS.INSTANCE.method$CraftEntityType$toNMSEntityType(this.entityType), 0, CoreReflections.instance$Vec3$Zero, 0 - ), true); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true); - if (VersionHelper.isOrAbove1_20_5() && this.scale != 1) { - Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); - CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale); - packets.accept(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityId[0], Collections.singletonList(attributeInstance)), false); - } - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Failed to construct custom hitbox spawn packet", e); - } - } - - @Override - public void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer aabbs) { - } - - @Override - public int[] acquireEntityIds(Supplier entityIdSupplier) { - return new int[] {entityIdSupplier.get()}; - } - public static class Factory implements HitBoxConfigFactory { @Override public HitBoxConfig create(Map arguments) { 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"); + String type = (String) arguments.getOrDefault("entity-id", "slime"); EntityType entityType = Registry.ENTITY_TYPE.get(new NamespacedKey("minecraft", type)); if (entityType == null) { throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.custom.invalid_entity", new IllegalArgumentException("EntityType not found: " + type), type); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java index 5663b9f87..df7d249fe 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java @@ -7,7 +7,8 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; -import net.momirealms.craftengine.core.entity.furniture.*; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.hitbox.*; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java index 60bec26d4..0d29c0ad2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java @@ -5,7 +5,8 @@ import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider; 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.entity.furniture.*; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.hitbox.*; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java index 57034cc9a..f24f79b10 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java @@ -9,7 +9,8 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeH import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.util.DirectionUtils; -import net.momirealms.craftengine.core.entity.furniture.*; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.hitbox.*; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.Vec3d; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index 81df0accb..b92b4440e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -1,43 +1,18 @@ package net.momirealms.craftengine.bukkit.item.behavior; -import net.momirealms.craftengine.bukkit.api.event.FurnitureAttemptPlaceEvent; -import net.momirealms.craftengine.bukkit.api.event.FurniturePlaceEvent; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.util.DirectionUtils; -import net.momirealms.craftengine.bukkit.util.EventUtils; -import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.core.entity.furniture.AnchorType; -import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; -import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData; -import net.momirealms.craftengine.core.entity.furniture.HitBoxConfig; import net.momirealms.craftengine.core.entity.player.InteractionResult; -import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.PendingConfigSection; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; -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.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.*; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.collision.AABB; -import org.bukkit.Location; -import org.bukkit.World; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; import java.util.Map; -import java.util.Optional; public class FurnitureItemBehavior extends ItemBehavior { public static final Factory FACTORY = new Factory(); @@ -57,121 +32,121 @@ public class FurnitureItemBehavior extends ItemBehavior { } public InteractionResult place(UseOnContext context) { - Optional optionalCustomFurniture = BukkitFurnitureManager.instance().furnitureById(this.id); - if (optionalCustomFurniture.isEmpty()) { - CraftEngine.instance().logger().warn("Furniture " + this.id + " not found"); - return InteractionResult.FAIL; - } - CustomFurniture customFurniture = optionalCustomFurniture.get(); - - Direction clickedFace = context.getClickedFace(); - AnchorType anchorType = switch (clickedFace) { - case EAST, WEST, NORTH, SOUTH -> AnchorType.WALL; - case UP -> AnchorType.GROUND; - case DOWN -> AnchorType.CEILING; - }; - - CustomFurniture.Placement placement = customFurniture.getPlacement(anchorType); - if (placement == null) { - return InteractionResult.FAIL; - } - - Player player = context.getPlayer(); - // todo adventure check - if (player != null && player.isAdventureMode()) { - return InteractionResult.FAIL; - } - - Vec3d clickedPosition = context.getClickLocation(); - - // trigger event - org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null; - World world = (World) context.getLevel().platformWorld(); - - // get position and rotation for placement - Vec3d finalPlacePosition; - double furnitureYaw; - if (anchorType == AnchorType.WALL) { - furnitureYaw = Direction.getYaw(clickedFace); - if (clickedFace == Direction.EAST || clickedFace == Direction.WEST) { - Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.y(), clickedPosition.z())); - finalPlacePosition = new Vec3d(clickedPosition.x(), xz.left(), xz.right()); - } else { - Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.y())); - finalPlacePosition = new Vec3d(xz.left(), xz.right(), clickedPosition.z()); - } - } else { - furnitureYaw = placement.rotationRule().apply(180 + (player != null ? player.yRot() : 0)); - Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.z())); - finalPlacePosition = new Vec3d(xz.left(), clickedPosition.y(), xz.right()); - } - - Location furnitureLocation = new Location(world, finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, 0); - - List aabbs = new ArrayList<>(); - for (HitBoxConfig hitBoxConfig : placement.hitBoxConfigs()) { - hitBoxConfig.initShapeForPlacement(finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - furnitureYaw), 0).conjugate(), aabbs::add); - } - if (!aabbs.isEmpty()) { - if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { - return InteractionResult.FAIL; - } - } - - if (!BukkitCraftEngine.instance().antiGriefProvider().canPlace(bukkitPlayer, furnitureLocation)) { - return InteractionResult.FAIL; - } - - if (player != null) { - FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, anchorType, furnitureLocation.clone(), - DirectionUtils.toBlockFace(clickedFace), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z())); - if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) { - return InteractionResult.FAIL; - } - } - - Item item = context.getItem(); - // 不可能 - if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL; - - BukkitFurniture bukkitFurniture = BukkitFurnitureManager.instance().place( - furnitureLocation.clone(), customFurniture, - FurnitureExtraData.builder() - .item(item.copyWithCount(1)) - .anchorType(anchorType) - .dyedColor(item.dyedColor().orElse(null)) - .fireworkExplosionColors(item.fireworkExplosion().map(explosion -> explosion.colors().toIntArray()).orElse(null)) - .build(), false); - - if (player != null) { - FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand()); - if (EventUtils.fireAndCheckCancel(placeEvent)) { - bukkitFurniture.destroy(); - return InteractionResult.FAIL; - } - } - - Cancellable dummy = Cancellable.dummy(); - PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder() - .withParameter(DirectContextParameters.FURNITURE, bukkitFurniture) - .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(furnitureLocation)) - .withParameter(DirectContextParameters.EVENT, dummy) - .withParameter(DirectContextParameters.HAND, context.getHand()) - .withParameter(DirectContextParameters.ITEM_IN_HAND, item) - ); - customFurniture.execute(functionContext, EventTrigger.PLACE); - if (dummy.isCancelled()) { - return InteractionResult.SUCCESS_AND_CANCEL; - } - - if (player != null) { - if (!player.canInstabuild()) { - item.count(item.count() - 1); - } - player.swingHand(context.getHand()); - } - - context.getLevel().playBlockSound(finalPlacePosition, customFurniture.settings().sounds().placeSound()); +// Optional optionalCustomFurniture = BukkitFurnitureManager.instance().furnitureById(this.id); +// if (optionalCustomFurniture.isEmpty()) { +// CraftEngine.instance().logger().warn("Furniture " + this.id + " not found"); +// return InteractionResult.FAIL; +// } +// FurnitureConfig customFurniture = optionalCustomFurniture.get(); +// +// Direction clickedFace = context.getClickedFace(); +// AnchorType anchorType = switch (clickedFace) { +// case EAST, WEST, NORTH, SOUTH -> AnchorType.WALL; +// case UP -> AnchorType.GROUND; +// case DOWN -> AnchorType.CEILING; +// }; +// +// FurnitureConfig.Variant placement = customFurniture.getPlacement(anchorType); +// if (placement == null) { +// return InteractionResult.FAIL; +// } +// +// Player player = context.getPlayer(); +// // todo adventure check +// if (player != null && player.isAdventureMode()) { +// return InteractionResult.FAIL; +// } +// +// Vec3d clickedPosition = context.getClickLocation(); +// +// // trigger event +// org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null; +// World world = (World) context.getLevel().platformWorld(); +// +// // get position and rotation for placement +// Vec3d finalPlacePosition; +// double furnitureYaw; +// if (anchorType == AnchorType.WALL) { +// furnitureYaw = Direction.getYaw(clickedFace); +// if (clickedFace == Direction.EAST || clickedFace == Direction.WEST) { +// Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.y(), clickedPosition.z())); +// finalPlacePosition = new Vec3d(clickedPosition.x(), xz.left(), xz.right()); +// } else { +// Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.y())); +// finalPlacePosition = new Vec3d(xz.left(), xz.right(), clickedPosition.z()); +// } +// } else { +// furnitureYaw = placement.rotationRule().apply(180 + (player != null ? player.yRot() : 0)); +// Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.z())); +// finalPlacePosition = new Vec3d(xz.left(), clickedPosition.y(), xz.right()); +// } +// +// Location furnitureLocation = new Location(world, finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, 0); +// +// List aabbs = new ArrayList<>(); +// for (HitBoxConfig hitBoxConfig : placement.hitBoxConfigs()) { +// hitBoxConfig.initShapeForPlacement(finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - furnitureYaw), 0).conjugate(), aabbs::add); +// } +// if (!aabbs.isEmpty()) { +// if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { +// return InteractionResult.FAIL; +// } +// } +// +// if (!BukkitCraftEngine.instance().antiGriefProvider().canPlace(bukkitPlayer, furnitureLocation)) { +// return InteractionResult.FAIL; +// } +// +// if (player != null) { +// FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, anchorType, furnitureLocation.clone(), +// DirectionUtils.toBlockFace(clickedFace), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z())); +// if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) { +// return InteractionResult.FAIL; +// } +// } +// +// Item item = context.getItem(); +// // 不可能 +// if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL; +// +// BukkitFurniture bukkitFurniture = BukkitFurnitureManager.instance().place( +// furnitureLocation.clone(), customFurniture, +// FurnitureDataAccessor.builder() +// .item(item.copyWithCount(1)) +// .anchorType(anchorType) +// .dyedColor(item.dyedColor().orElse(null)) +// .fireworkExplosionColors(item.fireworkExplosion().map(explosion -> explosion.colors().toIntArray()).orElse(null)) +// .build(), false); +// +// if (player != null) { +// FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand()); +// if (EventUtils.fireAndCheckCancel(placeEvent)) { +// bukkitFurniture.destroy(); +// return InteractionResult.FAIL; +// } +// } +// +// Cancellable dummy = Cancellable.dummy(); +// PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder() +// .withParameter(DirectContextParameters.FURNITURE, bukkitFurniture) +// .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(furnitureLocation)) +// .withParameter(DirectContextParameters.EVENT, dummy) +// .withParameter(DirectContextParameters.HAND, context.getHand()) +// .withParameter(DirectContextParameters.ITEM_IN_HAND, item) +// ); +// customFurniture.execute(functionContext, EventTrigger.PLACE); +// if (dummy.isCancelled()) { +// return InteractionResult.SUCCESS_AND_CANCEL; +// } +// +// if (player != null) { +// if (!player.canInstabuild()) { +// item.count(item.count() - 1); +// } +// player.swingHand(context.getHand()); +// } +// +// context.getLevel().playBlockSound(finalPlacePosition, customFurniture.settings().sounds().placeSound()); return InteractionResult.SUCCESS; } 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 ce298bf65..d312a801d 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 @@ -7,6 +7,7 @@ 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.element.BukkitFurnitureElementConfigs; import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BukkitHitBoxTypes; import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager; import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeatManager; @@ -162,6 +163,7 @@ public class BukkitCraftEngine extends CraftEngine { BukkitItemBehaviors.init(); BukkitHitBoxTypes.init(); BukkitBlockEntityElementConfigs.init(); + BukkitFurnitureElementConfigs.init(); // 初始化 onload 阶段的兼容性 super.compatibilityManager().onLoad(); // 创建网络管理器 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 9a6729b0a..352ffd619 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 @@ -39,7 +39,7 @@ public class BukkitPlatform implements Platform { Map map = (Map) MRegistryOps.NBT.convertTo(MRegistryOps.JAVA, tag); return map.get("root"); } catch (CommandSyntaxException e) { - throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e, nbt); + throw new LocalizedResourceConfigException("warning.config.id.snbt.invalid_syntax", e, nbt); } } @@ -55,7 +55,7 @@ public class BukkitPlatform implements Platform { CompoundTag map = (CompoundTag) MRegistryOps.NBT.convertTo(MRegistryOps.SPARROW_NBT, tag); return map.get("root"); } catch (CommandSyntaxException e) { - throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e, nbt); + throw new LocalizedResourceConfigException("warning.config.id.snbt.invalid_syntax", e, nbt); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java index 861c01eb5..1b4b1a942 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java @@ -115,7 +115,7 @@ public class DebugItemDataCommand extends BukkitCommandFeature { } else if (nbt instanceof short[]) { value = Arrays.toString((short[]) nbt); } else { - value = "Unknown array type"; + value = "Unknown array id"; } } else { value = nbt.toString(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java index a6333f2d5..e1d29f243 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java @@ -5,7 +5,7 @@ import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.core.entity.furniture.AnchorType; -import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.FlagKeys; @@ -42,19 +42,19 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature { NamespacedKey namespacedKey = context.get("id"); Key id = KeyUtils.namespacedKey2Key(namespacedKey); BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance(); - Optional optionalCustomFurniture = furnitureManager.furnitureById(id); + Optional optionalCustomFurniture = furnitureManager.furnitureById(id); if (optionalCustomFurniture.isEmpty()) { return; } Location location = context.get("location"); - CustomFurniture customFurniture = optionalCustomFurniture.get(); - AnchorType anchorType = (AnchorType) context.optional("anchor-type").orElse(customFurniture.getAnyAnchorType()); + FurnitureConfig customFurniture = optionalCustomFurniture.get(); + AnchorType anchorType = (AnchorType) context.optional("anchor-id").orElse(customFurniture.getAnyAnchorType()); boolean playSound = context.flags().hasFlag("silent"); CraftEngineFurniture.place(location, customFurniture, anchorType, playSound); }); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java index b46f47699..29971f70a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java @@ -15,7 +15,6 @@ import org.bukkit.entity.Player; import org.incendo.cloud.Command; import org.incendo.cloud.bukkit.parser.PlayerParser; import org.incendo.cloud.parser.standard.BooleanParser; -import org.incendo.cloud.parser.standard.DoubleParser; import java.util.Optional; 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 8ea7189f7..92686686d 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 @@ -59,8 +59,8 @@ import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; import net.momirealms.craftengine.core.advancement.network.AdvancementHolder; import net.momirealms.craftengine.core.advancement.network.AdvancementProgress; import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.entity.furniture.HitBox; -import net.momirealms.craftengine.core.entity.furniture.HitBoxPart; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBox; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxPart; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.font.FontManager; @@ -3711,11 +3711,11 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes if (actionType == 1) { // ATTACK boolean usingSecondaryAction = buf.readBoolean(); - if (entityId != furniture.baseEntityId()) { + if (entityId != furniture.entityId()) { event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeVarInt(furniture.baseEntityId()); + buf.writeVarInt(furniture.entityId()); buf.writeVarInt(actionType); buf.writeBoolean(usingSecondaryAction); } @@ -3765,11 +3765,11 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes float z = buf.readFloat(); InteractionHand hand = buf.readVarInt() == 0 ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND; boolean usingSecondaryAction = buf.readBoolean(); - if (entityId != furniture.baseEntityId()) { + if (entityId != furniture.entityId()) { event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeVarInt(furniture.baseEntityId()); + buf.writeVarInt(furniture.entityId()); buf.writeVarInt(actionType); buf.writeFloat(x).writeFloat(y).writeFloat(z); buf.writeVarInt(hand == InteractionHand.MAIN_HAND ? 0 : 1); @@ -3864,11 +3864,11 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } else if (actionType == 0) { int hand = buf.readVarInt(); boolean usingSecondaryAction = buf.readBoolean(); - if (entityId != furniture.baseEntityId()) { + if (entityId != furniture.entityId()) { event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); - buf.writeVarInt(furniture.baseEntityId()); + buf.writeVarInt(furniture.entityId()); buf.writeVarInt(actionType); buf.writeVarInt(hand); buf.writeBoolean(usingSecondaryAction); 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 81ad7c893..62390bef7 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 @@ -63,7 +63,7 @@ public class ProjectilePacketHandler implements EntityPacketHandler { public void convertAddCustomProjectilePacket(FriendlyByteBuf buf, ByteBufPacketEvent event) { UUID uuid = buf.readUUID(); - buf.readVarInt(); // type + buf.readVarInt(); // id double x = buf.readDouble(); double y = buf.readDouble(); double z = buf.readDouble(); 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 index 716ad2f0d..ba1b1804a 100644 --- 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 @@ -35,7 +35,7 @@ public class PayloadHelper { @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()); + CraftEngine.instance().logger().warn("Unknown data id class: " + data.getClass().getName()); return; } FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); @@ -65,7 +65,7 @@ public class PayloadHelper { @SuppressWarnings("unchecked") NetworkCodec codec = (NetworkCodec) BuiltInRegistries.MOD_PACKET.getValue(type); if (codec == null) { - Debugger.COMMON.debug(() -> "Unknown data type received: " + type); + Debugger.COMMON.debug(() -> "Unknown data id received: " + type); return; } 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 index be22a435c..06523d269 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml @@ -12,12 +12,9 @@ items: sounds: break: minecraft:block.bamboo_wood.break place: minecraft:block.bamboo_wood.place - placement: + variants: ground: loot-spawn-offset: 0.5,0.5,0 - rules: - rotation: FOUR - alignment: CENTER elements: - item: default:bench display-transform: NONE diff --git a/common-files/src/main/resources/translations/de.yml b/common-files/src/main/resources/translations/de.yml index 5ce8d8ef8..1d523d817 100644 --- a/common-files/src/main/resources/translations/de.yml +++ b/common-files/src/main/resources/translations/de.yml @@ -160,8 +160,8 @@ warning.config.sound.missing_name: "Problem in Datei gefunden - 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.missing_variants: "Problem in Datei gefunden - Beim Furniture '' fehlt das erforderliche 'variants'-Argument." +warning.config.furniture.element.item_display.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 ''." diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index c7e538f21..013865146 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -190,8 +190,9 @@ warning.config.sound.missing_name: "Issue found in file - The so warning.config.jukebox_song.duplicate: "Issue found in file - Duplicated jukebox song ''. Please check if there is the same configuration in other files." warning.config.jukebox_song.missing_sound: "Issue found in file - The jukebox song '' is missing the required 'sound' argument." warning.config.furniture.duplicate: "Issue found in file - Duplicated furniture ''. Please check if there is the same configuration in other files." -warning.config.furniture.missing_placement: "Issue found in file - The furniture '' is missing the required 'placement' argument." -warning.config.furniture.element.missing_item: "Issue found in file - The furniture '' is missing the required 'item' argument for one of its elements." +warning.config.furniture.missing_variants: "Issue found in file - The furniture '' is missing the required 'variants' argument." +warning.config.furniture.element.invalid_type: "Issue found in file - The furniture '' is using an invalid element type ''." +warning.config.furniture.element.item_display.missing_item: "Issue found in file - The furniture '' is missing the required 'item' argument for 'item_display' element." warning.config.furniture.settings.unknown: "Issue found in file - The furniture '' is using an unknown setting type ''." warning.config.furniture.hitbox.invalid_type: "Issue found in file - The furniture '' is using an invalid hitbox type ''." warning.config.furniture.hitbox.custom.invalid_entity: "Issue found in file - The furniture '' is using a custom hitbox with invalid entity type ''." diff --git a/common-files/src/main/resources/translations/es.yml b/common-files/src/main/resources/translations/es.yml index 0a262c0f3..c52e59568 100644 --- a/common-files/src/main/resources/translations/es.yml +++ b/common-files/src/main/resources/translations/es.yml @@ -111,8 +111,8 @@ warning.config.sound.missing_name: "Problema encontrado en el archivo Problema encontrado en el archivo - Canción de tocadiscos duplicada ''. Verifica si hay la misma configuración en otros archivos." warning.config.jukebox_song.missing_sound: "Problema encontrado en el archivo - La canción de tocadiscos '' carece del argumento requerido 'sound'." warning.config.furniture.duplicate: "Problema encontrado en el archivo - Mueble duplicado ''. Verifica si hay la misma configuración en otros archivos." -warning.config.furniture.missing_placement: "Problema encontrado en el archivo - El mueble '' carece del argumento requerido 'placement'." -warning.config.furniture.element.missing_item: "Problema encontrado en el archivo - El mueble '' carece del argumento requerido 'item' para uno de sus elementos." +warning.config.furniture.missing_variants: "Problema encontrado en el archivo - El mueble '' carece del argumento requerido 'variants'." +warning.config.furniture.element.item_display.missing_item: "Problema encontrado en el archivo - El mueble '' carece del argumento requerido 'item' para uno de sus elementos." warning.config.furniture.settings.unknown: "Problema encontrado en el archivo - El mueble '' está usando un tipo de configuración desconocido ''." warning.config.furniture.hitbox.invalid_type: "Problema encontrado en el archivo - El mueble '' está usando un tipo de hitbox inválido ''." warning.config.furniture.hitbox.custom.invalid_entity: "Problema encontrado en el archivo - El mueble '' está usando un hitbox personalizado con un tipo de entidad inválido ''." diff --git a/common-files/src/main/resources/translations/fr_fr.yml b/common-files/src/main/resources/translations/fr_fr.yml index 49bad7ee0..cf519f058 100644 --- a/common-files/src/main/resources/translations/fr_fr.yml +++ b/common-files/src/main/resources/translations/fr_fr.yml @@ -175,8 +175,8 @@ warning.config.sound.missing_name: "Problème trouvé dans le fichier Problème trouvé dans le fichier - Chanson de jukebox dupliquée ''. Vérifiez s’il existe la même configuration dans d’autres fichiers." warning.config.jukebox_song.missing_sound: "Problème trouvé dans le fichier - La chanson de jukebox '' manque l’argument obligatoire 'sound'." warning.config.furniture.duplicate: "Problème trouvé dans le fichier - Meuble dupliqué ''. Vérifiez s’il existe la même configuration dans d’autres fichiers." -warning.config.furniture.missing_placement: "Problème trouvé dans le fichier - Le meuble '' manque l’argument obligatoire 'placement'." -warning.config.furniture.element.missing_item: "Problème trouvé dans le fichier - Le meuble '' manque l’argument obligatoire 'item' pour un de ses éléments." +warning.config.furniture.missing_variants: "Problème trouvé dans le fichier - Le meuble '' manque l’argument obligatoire 'variants'." +warning.config.furniture.element.item_display.missing_item: "Problème trouvé dans le fichier - Le meuble '' manque l’argument obligatoire 'item' pour un de ses éléments." warning.config.furniture.settings.unknown: "Problème trouvé dans le fichier - Le meuble '' utilise un type de paramètre inconnu ''." warning.config.furniture.hitbox.invalid_type: "Problème trouvé dans le fichier - Le meuble '' utilise un type de hitbox invalide ''." warning.config.furniture.hitbox.custom.invalid_entity: "Problème trouvé dans le fichier - Le meuble '' utilise un hitbox personnalisé avec un type d’entité invalide ''." diff --git a/common-files/src/main/resources/translations/ru_ru.yml b/common-files/src/main/resources/translations/ru_ru.yml index af586317c..6e55efa40 100644 --- a/common-files/src/main/resources/translations/ru_ru.yml +++ b/common-files/src/main/resources/translations/ru_ru.yml @@ -148,8 +148,8 @@ warning.config.sound.missing_name: "Проблема найдена в warning.config.jukebox_song.duplicate: "Проблема найдена в файле - Дублированная песня музыкального автомата ''. Проверьте, есть ли такая же конфигурация в других файлах." warning.config.jukebox_song.missing_sound: "Проблема найдена в файле - В песне музыкального автомата '' отсутствует необходимый 'sound' аргумент." warning.config.furniture.duplicate: "Проблема найдена в файле - Дублированная мебель ''. Проверьте, есть ли такая же конфигурация в других файлах." -warning.config.furniture.missing_placement: "Проблема найдена в файле - В мебели '' отсутствует необходимый 'placement' аргумент." -warning.config.furniture.element.missing_item: "Проблема найдена в файле - В мебели '' отсутствует необходимый 'item' аргумент для одного из его элементов." +warning.config.furniture.missing_variants: "Проблема найдена в файле - В мебели '' отсутствует необходимый 'variants' аргумент." +warning.config.furniture.element.item_display.missing_item: "Проблема найдена в файле - В мебели '' отсутствует необходимый 'item' аргумент для одного из его элементов." warning.config.furniture.settings.unknown: "Проблема найдена в файле - Мебель '' использует неизвестный тип настройки ''." warning.config.furniture.hitbox.invalid_type: "Проблема найдена в файле - Мебель '' использует недопустимый тип хитбокса ''." warning.config.furniture.hitbox.custom.invalid_entity: "Проблема найдена в файле - Мебель '' использует пользовательский хитбокс с недопустимым типом сущности ''." diff --git a/common-files/src/main/resources/translations/tr.yml b/common-files/src/main/resources/translations/tr.yml index 94a56309f..f826f3cc7 100644 --- a/common-files/src/main/resources/translations/tr.yml +++ b/common-files/src/main/resources/translations/tr.yml @@ -110,8 +110,8 @@ warning.config.sound.missing_name: " dosyasında sorun bulundu - warning.config.jukebox_song.duplicate: " dosyasında sorun bulundu - Yinelenen müzik çalar şarkısı ''. Diğer dosyalarda aynı yapılandırmanın olup olmadığını kontrol edin." warning.config.jukebox_song.missing_sound: " dosyasında sorun bulundu - '' müzik çalar şarkısı gerekli 'sound' argümanı eksik." warning.config.furniture.duplicate: " dosyasında sorun bulundu - Yinelenen mobilya ''. Diğer dosyalarda aynı yapılandırmanın olup olmadığını kontrol edin." -warning.config.furniture.missing_placement: " dosyasında sorun bulundu - '' mobilyası gerekli 'placement' argümanı eksik." -warning.config.furniture.element.missing_item: " dosyasında sorun bulundu - '' mobilyası, elementlerinden biri için gerekli 'item' argümanı eksik." +warning.config.furniture.missing_variants: " dosyasında sorun bulundu - '' mobilyası gerekli 'variants' argümanı eksik." +warning.config.furniture.element.item_display.missing_item: " dosyasında sorun bulundu - '' mobilyası, elementlerinden biri için gerekli 'item' argümanı eksik." warning.config.furniture.settings.unknown: " dosyasında sorun bulundu - '' mobilyası bilinmeyen bir ayar türü '' kullanıyor." warning.config.furniture.hitbox.invalid_type: " dosyasında sorun bulundu - '' mobilyası geçersiz bir hitbox türü '' kullanıyor." warning.config.furniture.hitbox.custom.invalid_entity: " dosyasında sorun bulundu - '' mobilyası, geçersiz varlık türü '' olan özel bir hitbox kullanıyor." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index da06eeff2..1bcf6cb8f 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -183,8 +183,8 @@ warning.config.sound.missing_name: "在文件 发现问题 - 音 warning.config.jukebox_song.duplicate: "在文件 发现问题 - 重复的唱片机歌曲 '' 请检查其他文件中是否存在相同配置" warning.config.jukebox_song.missing_sound: "在文件 发现问题 - 唱片机歌曲 '' 缺少必需的 'sound' 参数" warning.config.furniture.duplicate: "在文件 发现问题 - 重复的家具 '' 请检查其他文件中是否存在相同配置" -warning.config.furniture.missing_placement: "在文件 发现问题 - 家具 '' 缺少必需的 'placement' 参数" -warning.config.furniture.element.missing_item: "在文件 发现问题 - 家具 '' 的某个元素缺少必需的 'item' 参数" +warning.config.furniture.missing_variants: "在文件 发现问题 - 家具 '' 缺少必需的 'variants' 参数" +warning.config.furniture.element.item_display.missing_item: "在文件 发现问题 - 家具 '' 的 'item_display' 元素缺少必需的 'item' 参数" warning.config.furniture.settings.unknown: "在文件 发现问题 - 家具 '' 使用了未知的设置类型 ''" warning.config.furniture.hitbox.invalid_type: "在文件 发现问题 - 家具 '' 使用了无效的碰撞箱类型 ''" warning.config.furniture.hitbox.custom.invalid_entity: "在文件 发现问题 - 家具 '' 的自定义碰撞箱使用了无效的实体类型 ''" 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 5b0dbf02b..5707d7b2f 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 @@ -711,12 +711,8 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem } private CullingData parseCullingData(Object arguments) { - if (arguments instanceof Boolean b && !b) { - return null; - } - if (!(arguments instanceof Map)) { - return new CullingData(DEFAULT_BLOCK_ENTITY_AABB, Config.entityCullingViewDistance(), 0.5, true); - } + if (arguments instanceof Boolean b && !b) return null; + if (!(arguments instanceof Map)) return new CullingData(DEFAULT_BLOCK_ENTITY_AABB, Config.entityCullingViewDistance(), 0.5, true); Map argumentsMap = ResourceConfigUtils.getAsMap(arguments, "entity-culling"); return new CullingData( ResourceConfigUtils.getAsAABB(argumentsMap.getOrDefault("aabb", 1), "aabb"), 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 6d794219f..b32258402 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 @@ -78,7 +78,7 @@ public abstract class BlockBehavior { return (boolean) superMethod.call(); } - // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType type + // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType id // 1.20.5+ BlockState state, PathComputationType pathComputationType public boolean isPathFindable(Object thisBlock, Object[] args, Callable superMethod) throws Exception { return (boolean) superMethod.call(); 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 e2e78a9a4..50a18c120 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 @@ -41,6 +41,7 @@ public class BlockSettings { float friction = 0.6f; float speedFactor = 1f; float jumpFactor = 1f; + Map, Object> customData = new IdentityHashMap<>(4); private BlockSettings() {} @@ -107,9 +108,29 @@ public class BlockSettings { newSettings.speedFactor = settings.speedFactor; newSettings.jumpFactor = settings.jumpFactor; newSettings.friction = settings.friction; + newSettings.customData = new IdentityHashMap<>(settings.customData); return newSettings; } + @SuppressWarnings("unchecked") + public T getCustomData(CustomDataType type) { + return (T) this.customData.get(type); + } + + public void clearCustomData() { + this.customData.clear(); + } + + @Nullable + @SuppressWarnings("unchecked") + public T removeCustomData(CustomDataType type) { + return (T) this.customData.remove(type); + } + + public void addCustomData(CustomDataType key, T value) { + this.customData.put(key, value); + } + public Set tags() { return tags; } @@ -542,7 +563,7 @@ public class BlockSettings { })); } - private static void registerFactory(String id, Modifier.Factory factory) { + public static void registerFactory(String id, Modifier.Factory factory) { FACTORIES.put(id, factory); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java index 7fe7d8e06..dca06a1bc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java @@ -4,7 +4,7 @@ import java.util.concurrent.Callable; public interface IsPathFindableBlockBehavior { - // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType type + // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType id // 1.20.5+ BlockState state, PathComputationType pathComputationType boolean isPathFindable(Object thisBlock, Object[] args, Callable superMethod) throws Exception; } 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 index 78dc7210a..fffeaaa89 100644 --- 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 @@ -24,7 +24,7 @@ public abstract class BlockEntity { this.type = type; } - public final CompoundTag saveAsTag() { + public CompoundTag saveAsTag() { CompoundTag tag = new CompoundTag(); this.saveId(tag); this.savePos(tag); 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 index d7ca645e6..9c5f1d2e3 100644 --- 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 @@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.util.Key; public final class BlockEntityTypeKeys { private BlockEntityTypeKeys() {} + public static final Key INACTIVE = Key.of("craftengine:inactive"); 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"); 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 index c4b725b06..46083cc4b 100644 --- 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 @@ -7,6 +7,7 @@ import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceKey; public abstract class BlockEntityTypes { + public static final BlockEntityType INACTIVE = register(BlockEntityTypeKeys.INACTIVE); public static BlockEntityType register(Key id) { BlockEntityType type = new BlockEntityType<>(id); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/InactiveBlockEntity.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/InactiveBlockEntity.java new file mode 100644 index 000000000..3aa928f83 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/InactiveBlockEntity.java @@ -0,0 +1,21 @@ +package net.momirealms.craftengine.core.block.entity; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.sparrow.nbt.CompoundTag; + +public class InactiveBlockEntity extends BlockEntity { + private final CompoundTag tag; + + public InactiveBlockEntity(BlockPos pos, + ImmutableBlockState blockState, + CompoundTag tag) { + super(BlockEntityTypes.INACTIVE, pos, blockState); + this.tag = tag; + } + + @Override + public CompoundTag saveAsTag() { + return this.tag; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java new file mode 100644 index 000000000..2ea1439a4 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java @@ -0,0 +1,96 @@ +package net.momirealms.craftengine.core.entity; + +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.Cullable; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.sparrow.nbt.CompoundTag; + +import java.util.UUID; + +public abstract class CustomEntity implements Cullable { + protected final CustomEntityType type; + protected final UUID uuid; + protected WorldPosition position; + protected boolean valid = true; + + protected CustomEntity(CustomEntityType type, WorldPosition position, UUID uuid) { + this.position = position; + this.type = type; + this.uuid = uuid; + } + + public CompoundTag saveAsTag() { + CompoundTag tag = new CompoundTag(); + this.saveId(tag); + this.savePos(tag); + this.saveCustomData(tag); + return tag; + } + + private void savePos(CompoundTag tag) { + tag.putDouble("x", this.position.x()); + tag.putDouble("y", this.position.y()); + tag.putDouble("z", this.position.z()); + tag.putFloat("x_rot", this.position.xRot()); + tag.putFloat("y_rot", this.position.yRot()); + } + + public boolean isValid() { + return this.valid; + } + + public UUID uuid() { + return uuid; + } + + public double x() { + return this.position.x(); + } + + public double y() { + return this.position.y(); + } + + public double z() { + return this.position.z(); + } + + public float yRot() { + return this.position.yRot(); + } + + public float xRot() { + return this.position.xRot(); + } + + public CustomEntityType entityType() { + return this.type; + } + + protected void saveCustomData(CompoundTag tag) { + } + + public void loadCustomData(CompoundTag tag) { + } + + private void saveId(CompoundTag tag) { + tag.putString("id", this.type.id().asString()); + } + + public void destroy() { + this.valid = false; + } + + public static UUID readUUID(CompoundTag tag) { + return tag.getUUID("uuid"); + } + + public static WorldPosition readPos(CEWorld world, CompoundTag tag) { + double x = tag.getDouble("x"); + double y = tag.getDouble("y"); + double z = tag.getDouble("z"); + float xRot = tag.getFloat("x_rot"); + float yRot = tag.getFloat("y_rot"); + return new WorldPosition(world.world, x, y, z, xRot, yRot); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java new file mode 100644 index 000000000..a7cd7cb33 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.core.entity; + +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.WorldPosition; + +import java.util.UUID; + +public record CustomEntityType(Key id, Factory factory) { + + public interface Factory { + + T create(UUID uuid, WorldPosition position); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java new file mode 100644 index 000000000..57e36fadc --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java @@ -0,0 +1,10 @@ +package net.momirealms.craftengine.core.entity; + +import net.momirealms.craftengine.core.util.Key; + +public final class CustomEntityTypeKeys { + private CustomEntityTypeKeys() {} + + public static final Key FURNITURE = Key.of("craftengine:furniture"); + public static final Key INACTIVE = Key.of("craftengine:inactive"); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java new file mode 100644 index 000000000..c5697d9c2 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java @@ -0,0 +1,18 @@ +package net.momirealms.craftengine.core.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 CustomEntityTypes { + public static final CustomEntityType INACTIVE = register(CustomEntityTypeKeys.INACTIVE, InactiveCustomEntity::new); + + public static CustomEntityType register(Key id, CustomEntityType.Factory factory) { + CustomEntityType type = new CustomEntityType<>(id, factory); + ((WritableRegistry>) BuiltInRegistries.ENTITY_TYPE) + .register(ResourceKey.create(Registries.ENTITY_TYPE.location(), id), type); + return type; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java new file mode 100644 index 000000000..1c870110a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java @@ -0,0 +1,54 @@ +package net.momirealms.craftengine.core.entity; + +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.NBT; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.UUID; + +public class InactiveCustomEntity extends CustomEntity { + public static final CompoundTag INVALID_TAG = new CompoundTag(Map.of("type", NBT.createString(CustomEntityTypes.INACTIVE.id().asMinimalString()))); + private final CompoundTag data; + + public InactiveCustomEntity(UUID uuid, WorldPosition position) { + super(CustomEntityTypes.INACTIVE, position, uuid); + this.data = INVALID_TAG; + } + + public InactiveCustomEntity(UUID uuid, WorldPosition position, CompoundTag data) { + super(CustomEntityTypes.INACTIVE, position, uuid); + this.data = data; + } + + @Override + public CompoundTag saveAsTag() { + return this.data; + } + + @Override + public boolean isValid() { + // 不正常的数据不要存储 + return this.data != INVALID_TAG; + } + + @Override + public void destroy() { + } + + @Override + public void show(Player player) { + } + + @Override + public void hide(Player player) { + } + + @Override + public @Nullable CullingData cullingData() { + return null; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/Billboard.java b/core/src/main/java/net/momirealms/craftengine/core/entity/display/Billboard.java similarity index 80% rename from core/src/main/java/net/momirealms/craftengine/core/entity/Billboard.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/display/Billboard.java index 7533b658b..a415a8e35 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/Billboard.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/display/Billboard.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity; +package net.momirealms.craftengine.core.entity.display; public enum Billboard { FIXED(0), diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/ItemDisplayContext.java b/core/src/main/java/net/momirealms/craftengine/core/entity/display/ItemDisplayContext.java similarity index 87% rename from core/src/main/java/net/momirealms/craftengine/core/entity/ItemDisplayContext.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/display/ItemDisplayContext.java index 239488019..fdbe09050 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/ItemDisplayContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/display/ItemDisplayContext.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity; +package net.momirealms.craftengine.core.entity.display; public enum ItemDisplayContext { NONE(0), diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractCustomFurniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractCustomFurniture.java deleted file mode 100644 index 3f4d4bae4..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractCustomFurniture.java +++ /dev/null @@ -1,89 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture; - -import net.momirealms.craftengine.core.loot.LootTable; -import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; -import net.momirealms.craftengine.core.plugin.context.function.Function; -import net.momirealms.craftengine.core.util.Key; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public abstract class AbstractCustomFurniture implements CustomFurniture { - private final Key id; - private final FurnitureSettings settings; - private final Map placements; - private final Map>> events; - @Nullable - private final LootTable lootTable; - - private final AnchorType anyType; - - protected AbstractCustomFurniture(@NotNull Key id, - @NotNull FurnitureSettings settings, - @NotNull Map placements, - @NotNull Map>> events, - @Nullable LootTable lootTable) { - this.id = id; - this.settings = settings; - this.placements = placements; - this.lootTable = lootTable; - this.events = events; - this.anyType = placements.keySet().stream().findFirst().orElse(null); - } - - @Override - public void execute(Context context, EventTrigger trigger) { - for (Function function : Optional.ofNullable(this.events.get(trigger)).orElse(Collections.emptyList())) { - function.run(context); - } - } - - @Override - public Key id() { - return this.id; - } - - @Override - public Map placements() { - return this.placements; - } - - @Override - public FurnitureSettings settings() { - return this.settings; - } - - @Override - public @Nullable LootTable lootTable() { - return this.lootTable; - } - - @Override - public AnchorType getAnyAnchorType() { - return this.anyType; - } - - @Override - public boolean isAllowedPlacement(AnchorType anchorType) { - return this.placements.containsKey(anchorType); - } - - @Override - public Placement getPlacement(AnchorType anchorType) { - return this.placements.get(anchorType); - } - - @Override - public Placement getValidPlacement(AnchorType anchorType) { - Placement placement = this.placements.get(anchorType); - if (placement == null) { - return this.placements.get(getAnyAnchorType()); - } - return placement; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureElement.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureElement.java deleted file mode 100644 index 9e470cb6d..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureElement.java +++ /dev/null @@ -1,92 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture; - -import net.momirealms.craftengine.core.entity.Billboard; -import net.momirealms.craftengine.core.entity.ItemDisplayContext; -import net.momirealms.craftengine.core.util.Key; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -public abstract class AbstractFurnitureElement implements FurnitureElement { - private final Key item; - private final Billboard billboard; - private final ItemDisplayContext transform; - private final Vector3f scale; - private final Vector3f translation; - private final Vector3f position; - private final Quaternionf rotation; - private final boolean applyDyedColor; - private final float shadowRadius; - private final float shadowStrength; - - public AbstractFurnitureElement(Key item, - Billboard billboard, - ItemDisplayContext transform, - Vector3f scale, - Vector3f translation, - Vector3f position, - Quaternionf rotation, - float shadowRadius, - float shadowStrength, - boolean applyDyedColor) { - this.billboard = billboard; - this.transform = transform; - this.scale = scale; - this.translation = translation; - this.item = item; - this.rotation = rotation; - this.position = position; - this.applyDyedColor = applyDyedColor; - this.shadowRadius = shadowRadius; - this.shadowStrength = shadowStrength; - } - - @Override - public float shadowRadius() { - return shadowRadius; - } - - @Override - public float shadowStrength() { - return shadowStrength; - } - - @Override - public boolean applyDyedColor() { - return applyDyedColor; - } - - @Override - public Quaternionf rotation() { - return rotation; - } - - @Override - public Key item() { - return item; - } - - @Override - public Billboard billboard() { - return billboard; - } - - @Override - public ItemDisplayContext transform() { - return transform; - } - - @Override - public Vector3f scale() { - return scale; - } - - @Override - public Vector3f translation() { - return translation; - } - - @Override - public Vector3f position() { - return position; - } -} 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 52ea8252d..dd67036f8 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 @@ -1,19 +1,24 @@ package net.momirealms.craftengine.core.entity.furniture; -import net.momirealms.craftengine.core.entity.Billboard; -import net.momirealms.craftengine.core.entity.ItemDisplayContext; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxTypes; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.PendingConfigSection; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser; import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.collision.AABB; import org.incendo.cloud.suggestion.Suggestion; import org.joml.Vector3f; @@ -21,7 +26,7 @@ import java.nio.file.Path; import java.util.*; public abstract class AbstractFurnitureManager implements FurnitureManager { - protected final Map byId = new HashMap<>(); + protected final Map byId = new HashMap<>(); private final CraftEngine plugin; private final FurnitureParser furnitureParser; // Cached command suggestions @@ -56,12 +61,12 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { } @Override - public Optional furnitureById(Key id) { + public Optional furnitureById(Key id) { return Optional.ofNullable(this.byId.get(id)); } @Override - public Map loadedFurniture() { + public Map loadedFurniture() { return Collections.unmodifiableMap(this.byId); } @@ -72,10 +77,6 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { protected abstract HitBoxConfig defaultHitBox(); - protected abstract FurnitureElement.Builder furnitureElementBuilder(); - - protected abstract CustomFurniture.Builder furnitureBuilder(); - public class FurnitureParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] { "furniture" }; private final List pendingConfigSections = new ArrayList<>(); @@ -107,91 +108,72 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { return LoadingSequence.FURNITURE; } - @SuppressWarnings("unchecked") @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (AbstractFurnitureManager.this.byId.containsKey(id)) { throw new LocalizedResourceConfigException("warning.config.furniture.duplicate"); } - EnumMap placements = new EnumMap<>(AnchorType.class); - Object placementObj = section.get("placement"); - Map placementMap = MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(placementObj, "warning.config.furniture.missing_placement"), false); - if (placementMap.isEmpty()) { - throw new LocalizedResourceConfigException("warning.config.furniture.missing_placement"); - } - for (Map.Entry entry : placementMap.entrySet()) { - // 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 -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset")); - // furniture display elements - List elements = new ArrayList<>(); - List> elementConfigs = (List>) placementArguments.getOrDefault("elements", List.of()); - for (Map element : elementConfigs) { - FurnitureElement furnitureElement = furnitureElementBuilder() - .item(Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(element.get("item"), "warning.config.furniture.element.missing_item"))) - .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(ResourceConfigUtils.get(element, "transform", "display-transform"), o -> ItemDisplayContext.valueOf(o.toString().toUpperCase(Locale.ENGLISH)), ItemDisplayContext.NONE)) - .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")) - .shadowRadius(ResourceConfigUtils.getAsFloat(element.getOrDefault("shadow-radius", 0f), "shadow-radius")) - .shadowStrength(ResourceConfigUtils.getAsFloat(element.getOrDefault("shadow-strength", 1f), "shadow-strength")) - .build(); - elements.add(furnitureElement); - } - // external model providers + Map variantsMap = ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "variants", "placement", "variant"), "warning.config.furniture.missing_variants"), "variants"); + if (variantsMap.isEmpty()) { + throw new LocalizedResourceConfigException("warning.config.furniture.missing_variants"); + } + + Map variants = new HashMap<>(); + for (Map.Entry e0 : variantsMap.entrySet()) { + String variantName = e0.getKey(); + Map variantArguments = ResourceConfigUtils.getAsMap(e0.getValue(), variantName); + Optional optionalLootSpawnOffset = Optional.ofNullable(variantArguments.get("loot-spawn-offset")).map(it -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset")); + List> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elements"), FurnitureElementConfigs::fromMap); + + // fixme 外部模型不应该在这 Optional externalModel; - if (placementArguments.containsKey("model-engine")) { - externalModel = Optional.of(plugin.compatibilityManager().createModel("ModelEngine", placementArguments.get("model-engine").toString())); - } else if (placementArguments.containsKey("better-model")) { - externalModel = Optional.of(plugin.compatibilityManager().createModel("BetterModel", placementArguments.get("better-model").toString())); + if (variantArguments.containsKey("model-engine")) { + externalModel = Optional.of(plugin.compatibilityManager().createModel("ModelEngine", variantArguments.get("model-engine").toString())); + } else if (variantArguments.containsKey("better-model")) { + externalModel = Optional.of(plugin.compatibilityManager().createModel("BetterModel", variantArguments.get("better-model").toString())); } else { externalModel = Optional.empty(); } - // add hitboxes - List hitboxes = ResourceConfigUtils.parseConfigAsList(placementArguments.get("hitboxes"), HitBoxTypes::fromMap); + List hitboxes = ResourceConfigUtils.parseConfigAsList(variantArguments.get("hitboxes"), HitBoxTypes::fromMap); if (hitboxes.isEmpty() && externalModel.isEmpty()) { hitboxes = List.of(defaultHitBox()); } - // rules - Map ruleSection = MiscUtils.castToMap(placementArguments.get("rules"), true); - if (ruleSection != null) { - placements.put(anchorType, new CustomFurniture.Placement( - anchorType, - elements.toArray(new FurnitureElement[0]), - hitboxes.toArray(new HitBoxConfig[0]), - ResourceConfigUtils.getOrDefault(ruleSection.get("rotation"), o -> RotationRule.valueOf(o.toString().toUpperCase(Locale.ENGLISH)), RotationRule.ANY), - ResourceConfigUtils.getOrDefault(ruleSection.get("alignment"), o -> AlignmentRule.valueOf(o.toString().toUpperCase(Locale.ENGLISH)), AlignmentRule.CENTER), - externalModel, - optionalLootSpawnOffset - )); - } else { - placements.put(anchorType, new CustomFurniture.Placement( - anchorType, - elements.toArray(new FurnitureElement[0]), - hitboxes.toArray(new HitBoxConfig[0]), - RotationRule.ANY, - AlignmentRule.CENTER, - externalModel, - optionalLootSpawnOffset - )); - } + variants.put(variantName, new FurnitureVariant( + elements.toArray(new FurnitureElementConfig[0]), + hitboxes.toArray(new HitBoxConfig[0]), + externalModel, + optionalLootSpawnOffset + )); } - CustomFurniture furniture = furnitureBuilder() + AABB maxAABB = null; + + FurnitureConfig furniture = FurnitureConfig.builder() .id(id) .settings(FurnitureSettings.fromMap(MiscUtils.castToMap(section.get("settings"), true))) - .placement(placements) + .variants(variants) .events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"))) .lootTable(LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), true))) + .cullingData(parseCullingData(section.get("entity-culling"), maxAABB)) .build(); AbstractFurnitureManager.this.byId.put(id, furniture); } + + private CullingData parseCullingData(Object arguments, AABB maxHitbox) { + if (arguments instanceof Boolean b && !b) + return null; + if (!(arguments instanceof Map)) + return new CullingData(maxHitbox, Config.entityCullingViewDistance(), 0.5, true); + Map argumentsMap = ResourceConfigUtils.getAsMap(arguments, "entity-culling"); + return new CullingData( + ResourceConfigUtils.getAsAABB(argumentsMap.getOrDefault("aabb", maxHitbox), "aabb"), + ResourceConfigUtils.getAsInt(argumentsMap.getOrDefault("view-distance", Config.entityCullingViewDistance()), "view-distance"), + ResourceConfigUtils.getAsDouble(argumentsMap.getOrDefault("aabb-expansion", 0.5), "aabb-expansion"), + ResourceConfigUtils.getAsBoolean(argumentsMap.getOrDefault("ray-tracing", true), "ray-tracing") + ); + } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java index d3a099b6f..cefebe26b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.entity.furniture; +@Deprecated(since = "0.0.66", forRemoval = true) public enum AnchorType { GROUND(0), WALL(1), diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java deleted file mode 100644 index 19afdf32f..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java +++ /dev/null @@ -1,60 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture; - -import net.momirealms.craftengine.core.loot.LootTable; -import net.momirealms.craftengine.core.plugin.context.Context; -import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; -import net.momirealms.craftengine.core.plugin.context.function.Function; -import net.momirealms.craftengine.core.util.Key; -import org.jetbrains.annotations.Nullable; -import org.joml.Vector3f; - -import java.util.List; -import java.util.Map; -import java.util.Optional; - -// TODO 家具的设计存在问题。家具也应该存在不同的状态,而不是根据放置规则直接决定状态类型 -public interface CustomFurniture { - - void execute(Context context, EventTrigger trigger); - - Key id(); - - Map placements(); - - FurnitureSettings settings(); - - @Nullable - LootTable lootTable(); - - AnchorType getAnyAnchorType(); - - boolean isAllowedPlacement(AnchorType anchorType); - - Placement getPlacement(AnchorType anchorType); - - Placement getValidPlacement(AnchorType anchorType); - - interface Builder { - - Builder id(Key id); - - Builder placement(Map placements); - - Builder settings(FurnitureSettings settings); - - Builder lootTable(LootTable lootTable); - - Builder events(Map>> events); - - CustomFurniture build(); - } - - record Placement(AnchorType anchorType, - FurnitureElement[] elements, - HitBoxConfig[] hitBoxConfigs, - RotationRule rotationRule, - AlignmentRule alignmentRule, - Optional externalModel, - Optional dropOffset) { - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index e5a2bcd59..1b4661ba1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -1,48 +1,28 @@ package net.momirealms.craftengine.core.entity.furniture; -import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.entity.CustomEntity; +import net.momirealms.craftengine.core.entity.CustomEntityType; import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.sparrow.nbt.CompoundTag; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.UUID; +public abstract class Furniture extends CustomEntity { + protected final FurnitureConfig config; + protected final FurnitureDataAccessor dataAccessor; -public interface Furniture { - void initializeColliders(); - - WorldPosition position(); - - boolean isValid(); - - void destroy(); - - void destroyColliders(); - - void destroySeats(); - - UUID uuid(); - - int baseEntityId(); - - @Nullable - HitBox hitBoxByEntityId(int id); - - @Nullable HitBoxPart hitBoxPartByEntityId(int id); + public Furniture(CustomEntityType type, WorldPosition position, FurnitureConfig config, CompoundTag data) { + super(type, position); + this.dataAccessor = new FurnitureDataAccessor(data); + this.config = config; + } @NotNull - AnchorType anchorType(); + public FurnitureConfig config() { + return this.config; + } @NotNull - Key id(); - - @NotNull - CustomFurniture config(); - - boolean hasExternalModel(); - - FurnitureExtraData extraData(); - - void setExtraData(FurnitureExtraData extraData); - - void save(); + public FurnitureDataAccessor dataAccessor() { + return this.dataAccessor; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java new file mode 100644 index 000000000..c6f075e60 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java @@ -0,0 +1,67 @@ +package net.momirealms.craftengine.core.entity.furniture; + +import net.momirealms.craftengine.core.entity.furniture.behavior.FurnitureBehavior; +import net.momirealms.craftengine.core.loot.LootTable; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; +import net.momirealms.craftengine.core.plugin.context.function.Function; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +public interface FurnitureConfig { + + void execute(Context context, EventTrigger trigger); + + Key id(); + + FurnitureSettings settings(); + + @Nullable + LootTable lootTable(); + + Map variants(); + + default FurnitureVariant anyVariant() { + return variants().values().stream().findFirst().get(); + } + + default String anyVariantName() { + return variants().keySet().stream().findFirst().get(); + } + + @Nullable + FurnitureVariant getVariant(String variantName); + + @NotNull + FurnitureBehavior behavior(); + + CullingData cullingData(); + + static Builder builder() { + return new FurnitureConfigImpl.BuilderImpl(); + } + + interface Builder { + + Builder id(Key id); + + Builder variants(Map variants); + + Builder settings(FurnitureSettings settings); + + Builder lootTable(LootTable lootTable); + + Builder events(Map>> events); + + Builder behavior(FurnitureBehavior behavior); + + Builder cullingData(CullingData cullingData); + + FurnitureConfig build(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java new file mode 100644 index 000000000..8d6fe94b8 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java @@ -0,0 +1,145 @@ +package net.momirealms.craftengine.core.entity.furniture; + +import com.google.common.collect.ImmutableMap; +import net.momirealms.craftengine.core.entity.furniture.behavior.EmptyFurnitureBehavior; +import net.momirealms.craftengine.core.entity.furniture.behavior.FurnitureBehavior; +import net.momirealms.craftengine.core.loot.LootTable; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; +import net.momirealms.craftengine.core.plugin.context.function.Function; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +class FurnitureConfigImpl implements FurnitureConfig { + private final Key id; + private final FurnitureSettings settings; + private final Map variants; + private final Map>> events; + private final FurnitureBehavior behavior; + private final CullingData cullingData; + @Nullable + private final LootTable lootTable; + + private FurnitureConfigImpl(@NotNull Key id, + @NotNull FurnitureSettings settings, + @NotNull Map variants, + @NotNull Map>> events, + @NotNull FurnitureBehavior behavior, + @Nullable CullingData cullingData, + @Nullable LootTable lootTable) { + this.id = id; + this.settings = settings; + this.variants = ImmutableMap.copyOf(variants); + this.lootTable = lootTable; + this.behavior = behavior; + this.cullingData = cullingData; + this.events = events; + } + + @Override + public void execute(Context context, EventTrigger trigger) { + for (Function function : Optional.ofNullable(this.events.get(trigger)).orElse(Collections.emptyList())) { + function.run(context); + } + } + + @Override + public Key id() { + return this.id; + } + + @Override + public FurnitureSettings settings() { + return this.settings; + } + + @Override + public @Nullable LootTable lootTable() { + return this.lootTable; + } + + @Override + public Map variants() { + return this.variants; + } + + @Override + public @NotNull FurnitureBehavior behavior() { + return this.behavior; + } + + @Override + public CullingData cullingData() { + return this.cullingData; + } + + @Nullable + @Override + public FurnitureVariant getVariant(String variantName) { + return this.variants.get(variantName); + } + + public static class BuilderImpl implements Builder { + private Key id; + private Map variants; + private FurnitureSettings settings; + private Map>> events; + private LootTable lootTable; + private FurnitureBehavior behavior = EmptyFurnitureBehavior.INSTANCE; + private CullingData cullingData; + + @Override + public FurnitureConfig build() { + return new FurnitureConfigImpl(this.id, this.settings, this.variants, this.events, this.behavior, this.cullingData, this.lootTable); + } + + @Override + public Builder id(Key id) { + this.id = id; + return this; + } + + @Override + public Builder variants(Map variants) { + this.variants = variants; + return this; + } + + @Override + public Builder settings(FurnitureSettings settings) { + this.settings = settings; + return this; + } + + @Override + public Builder lootTable(LootTable lootTable) { + this.lootTable = lootTable; + return this; + } + + @Override + public Builder events(Map>> events) { + this.events = events; + return this; + } + + @Override + public Builder cullingData(CullingData cullingData) { + this.cullingData = cullingData; + return this; + } + + @Override + public Builder behavior(FurnitureBehavior behavior) { + this.behavior = behavior; + return this; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java similarity index 57% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java index 72587f2a1..07a6ccbb5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureExtraData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java @@ -6,34 +6,53 @@ import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.util.Color; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.NBT; +import net.momirealms.sparrow.nbt.Tag; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.Optional; -public class FurnitureExtraData { +public class FurnitureDataAccessor { public static final String ITEM = "item"; public static final String DYED_COLOR = "dyed_color"; public static final String FIREWORK_EXPLOSION_COLORS = "firework_explosion_colors"; + public static final String VARIANT = "variant"; + @ApiStatus.Obsolete public static final String ANCHOR_TYPE = "anchor_type"; private final CompoundTag data; - public FurnitureExtraData(CompoundTag data) { + public FurnitureDataAccessor(CompoundTag data) { this.data = data; } - public static FurnitureExtraData of(CompoundTag data) { - return new FurnitureExtraData(data); + public static FurnitureDataAccessor of(CompoundTag data) { + return new FurnitureDataAccessor(data); } public CompoundTag copyTag() { return this.data.copy(); } + @ApiStatus.Internal public CompoundTag unsafeTag() { return this.data; } + public void addCustomData(String key, Tag value) { + this.data.put(key, value); + } + + @Nullable + public Tag getCustomData(String key) { + return this.data.get(key); + } + + public void removeCustomData(String key) { + this.data.remove(key); + } + public Optional> item() { byte[] data = this.data.getByteArray(ITEM); if (data == null) return Optional.empty(); @@ -45,73 +64,57 @@ public class FurnitureExtraData { } } + public void setItem(Item item) { + this.data.putByteArray(ITEM, item.toByteArray()); + } + public Optional fireworkExplosionColors() { if (this.data.containsKey(FIREWORK_EXPLOSION_COLORS)) return Optional.of(this.data.getIntArray(FIREWORK_EXPLOSION_COLORS)); return Optional.empty(); } + public void setFireworkExplosionColors(int[] colors) { + this.data.putIntArray(FIREWORK_EXPLOSION_COLORS, colors); + } + public Optional dyedColor() { if (this.data.containsKey(DYED_COLOR)) return Optional.of(Color.fromDecimal(this.data.getInt(DYED_COLOR))); return Optional.empty(); } + public void setDyedColor(Color color) { + this.data.putInt(DYED_COLOR, color.color()); + } + + public Optional variant() { + return Optional.ofNullable(this.data.getString(VARIANT)); + } + + public void setVariant(String variant) { + this.data.putString(VARIANT, variant); + } + + @ApiStatus.Obsolete public Optional anchorType() { if (this.data.containsKey(ANCHOR_TYPE)) return Optional.of(AnchorType.byId(this.data.getInt(ANCHOR_TYPE))); return Optional.empty(); } - public FurnitureExtraData anchorType(AnchorType type) { + @ApiStatus.Obsolete + public FurnitureDataAccessor anchorType(AnchorType type) { this.data.putInt(ANCHOR_TYPE, type.getId()); return this; } - public static Builder builder() { - return new Builder(); + public static FurnitureDataAccessor fromBytes(final byte[] data) throws IOException { + return new FurnitureDataAccessor(NBT.fromBytes(data)); } - public static FurnitureExtraData fromBytes(final byte[] data) throws IOException { - return new FurnitureExtraData(NBT.fromBytes(data)); - } - - public static byte[] toBytes(final FurnitureExtraData data) throws IOException { + public static byte[] toBytes(final FurnitureDataAccessor data) throws IOException { return NBT.toBytes(data.data); } public byte[] toBytes() throws IOException { return toBytes(this); } - - public static class Builder { - private final CompoundTag data; - - public Builder() { - this.data = new CompoundTag(); - } - - public Builder item(Item item) { - this.data.putByteArray(ITEM, item.toByteArray()); - return this; - } - - public Builder dyedColor(Color color) { - if (color == null) return this; - this.data.putInt(DYED_COLOR, color.color()); - return this; - } - - public Builder fireworkExplosionColors(int[] colors) { - if (colors == null) return this; - this.data.putIntArray(FIREWORK_EXPLOSION_COLORS, colors); - return this; - } - - public Builder anchorType(AnchorType type) { - this.data.putInt(ANCHOR_TYPE, type.getId()); - return this; - } - - public FurnitureExtraData build() { - return new FurnitureExtraData(data); - } - } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureElement.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureElement.java deleted file mode 100644 index 785b24c68..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureElement.java +++ /dev/null @@ -1,59 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture; - -import net.momirealms.craftengine.core.entity.Billboard; -import net.momirealms.craftengine.core.entity.ItemDisplayContext; -import net.momirealms.craftengine.core.util.Key; -import org.jetbrains.annotations.NotNull; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -import java.util.function.Consumer; - -public interface FurnitureElement { - Quaternionf rotation(); - - Key item(); - - Billboard billboard(); - - ItemDisplayContext transform(); - - float shadowRadius(); - - float shadowStrength(); - - boolean applyDyedColor(); - - Vector3f scale(); - - Vector3f translation(); - - Vector3f position(); - - void initPackets(Furniture furniture, int entityId, @NotNull Quaternionf conjugated, Consumer packets); - - interface Builder { - - Builder item(Key item); - - Builder billboard(Billboard billboard); - - Builder transform(ItemDisplayContext transform); - - Builder scale(Vector3f scale); - - Builder translation(Vector3f translation); - - Builder position(Vector3f position); - - Builder rotation(Quaternionf rotation); - - Builder applyDyedColor(boolean applyDyedColor); - - Builder shadowStrength(float shadowStrength); - - Builder shadowRadius(float shadowRadius); - - FurnitureElement build(); - } -} 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 e57baec7d..5f8ca181a 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 @@ -25,11 +25,11 @@ public interface FurnitureManager extends Manageable { Collection cachedSuggestions(); - Furniture place(WorldPosition position, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound); + Furniture place(WorldPosition position, FurnitureConfig furniture, FurnitureDataAccessor extraData, boolean playSound); - Optional furnitureById(Key id); + Optional furnitureById(Key id); - Map loadedFurniture(); + Map loadedFurniture(); boolean isFurnitureRealEntity(int entityId); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSettings.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSettings.java index 298dbc877..f6bf10314 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSettings.java @@ -1,11 +1,13 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.CustomDataType; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import org.jetbrains.annotations.Nullable; import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.Map; public class FurnitureSettings { @@ -13,6 +15,7 @@ public class FurnitureSettings { FurnitureSounds sounds = FurnitureSounds.EMPTY; @Nullable Key itemId; + Map, Object> customData = new IdentityHashMap<>(4); private FurnitureSettings() {} @@ -29,6 +32,7 @@ public class FurnitureSettings { newSettings.sounds = settings.sounds; newSettings.itemId = settings.itemId; newSettings.minimized = settings.minimized; + newSettings.customData = new IdentityHashMap<>(settings.customData); return newSettings; } @@ -45,6 +49,25 @@ public class FurnitureSettings { return settings; } + @SuppressWarnings("unchecked") + public T getCustomData(CustomDataType type) { + return (T) this.customData.get(type); + } + + public void clearCustomData() { + this.customData.clear(); + } + + @Nullable + @SuppressWarnings("unchecked") + public T removeCustomData(CustomDataType type) { + return (T) this.customData.remove(type); + } + + public void addCustomData(CustomDataType key, T value) { + this.customData.put(key, value); + } + public FurnitureSounds sounds() { return sounds; } @@ -103,7 +126,7 @@ public class FurnitureSettings { })); } - private static void registerFactory(String id, FurnitureSettings.Modifier.Factory factory) { + public static void registerFactory(String id, FurnitureSettings.Modifier.Factory factory) { FACTORIES.put(id, factory); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java new file mode 100644 index 000000000..946f201cd --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java @@ -0,0 +1,13 @@ +package net.momirealms.craftengine.core.entity.furniture; + +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; +import org.joml.Vector3f; + +import java.util.Optional; + +public record FurnitureVariant(FurnitureElementConfig[] elements, + HitBoxConfig[] hitBoxConfigs, + Optional externalModel, + Optional dropOffset) { +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxConfig.java deleted file mode 100644 index fdc574861..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture; - -import net.momirealms.craftengine.core.entity.seat.SeatConfig; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.craftengine.core.world.collision.AABB; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public interface HitBoxConfig { - - Key type(); - - void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, - BiConsumer packets, Consumer collider, Consumer aabb); - - void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer aabbs); - - int[] acquireEntityIds(Supplier entityIdSupplier); - - SeatConfig[] seats(); - - Vector3f position(); - - boolean blocksBuilding(); - - boolean canBeHitByProjectile(); - - boolean canUseItemOn(); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/EmptyFurnitureBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/EmptyFurnitureBehavior.java new file mode 100644 index 000000000..439348957 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/EmptyFurnitureBehavior.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.core.entity.furniture.behavior; + +public final class EmptyFurnitureBehavior implements FurnitureBehavior { + private EmptyFurnitureBehavior() {} + + public static final EmptyFurnitureBehavior INSTANCE = new EmptyFurnitureBehavior(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehavior.java new file mode 100644 index 000000000..655168a5a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehavior.java @@ -0,0 +1,15 @@ +package net.momirealms.craftengine.core.entity.furniture.behavior; + +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.tick.FurnitureTicker; + +public interface FurnitureBehavior { + + default FurnitureTicker createSyncFurnitureTicker(T furniture) { + return null; + } + + default FurnitureTicker createAsyncBlockEntityTicker(T furniture) { + return null; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java new file mode 100644 index 000000000..118d5bd92 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.core.entity.furniture.element; + +import net.momirealms.craftengine.core.entity.player.Player; + +public interface FurnitureElement { + + 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/entity/furniture/element/FurnitureElementConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java new file mode 100644 index 000000000..c291a43be --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java @@ -0,0 +1,9 @@ +package net.momirealms.craftengine.core.entity.furniture.element; + +import net.momirealms.craftengine.core.world.WorldPosition; +import org.jetbrains.annotations.NotNull; + +public interface FurnitureElementConfig { + + E create(@NotNull WorldPosition position); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java new file mode 100644 index 000000000..438124560 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.entity.furniture.element; + +import java.util.Map; + +public interface FurnitureElementConfigFactory { + + FurnitureElementConfig create(Map args); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java new file mode 100644 index 000000000..8e375a743 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java @@ -0,0 +1,31 @@ +package net.momirealms.craftengine.core.entity.furniture.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 FurnitureElementConfigs { + public static final Key ITEM_DISPLAY = Key.of("craftengine:item_display"); + public static final Key TEXT_DISPLAY = Key.of("craftengine:text_display"); + public static final Key ITEM = Key.of("craftengine:item"); + + public static void register(Key key, FurnitureElementConfigFactory type) { + ((WritableRegistry) BuiltInRegistries.FURNITURE_ELEMENT_TYPE) + .register(ResourceKey.create(Registries.FURNITURE_ELEMENT_TYPE.location(), key), type); + } + + public static FurnitureElementConfig fromMap(Map arguments) { + Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(it -> Key.withDefaultNamespace(it, "craftengine")).orElse(ITEM_DISPLAY); + FurnitureElementConfigFactory factory = BuiltInRegistries.FURNITURE_ELEMENT_TYPE.getValue(type); + if (factory == null) { + throw new LocalizedResourceConfigException("warning.config.furniture.element.invalid_type", type.toString()); + } + return factory.create(arguments); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitHitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java similarity index 54% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitHitBox.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java index 8f725a94b..185fec9de 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitHitBox.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java @@ -1,40 +1,26 @@ -package net.momirealms.craftengine.bukkit.entity.furniture; +package net.momirealms.craftengine.core.entity.furniture.hitbox; -import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat; import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.furniture.HitBox; -import net.momirealms.craftengine.core.entity.furniture.HitBoxConfig; -import net.momirealms.craftengine.core.entity.furniture.HitBoxPart; import net.momirealms.craftengine.core.entity.seat.Seat; -import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.world.EntityHitResult; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.sparrow.nbt.CompoundTag; import java.util.Optional; -public class BukkitHitBox implements HitBox { +public abstract class AbstractHitBox implements HitBox { private final Furniture furniture; private final HitBoxConfig config; private final HitBoxPart[] parts; - private final Seat[] seats; + private Seat[] seats; - public BukkitHitBox(Furniture furniture, HitBoxConfig config, HitBoxPart[] parts) { + public AbstractHitBox(Furniture furniture, HitBoxConfig config, HitBoxPart[] parts) { this.parts = parts; this.config = config; this.furniture = furniture; - this.seats = createSeats(config); } - @SuppressWarnings("unchecked") - private Seat[] createSeats(HitBoxConfig config) { - SeatConfig[] seatConfigs = config.seats(); - Seat[] seats = new Seat[seatConfigs.length]; - for (int i = 0; i < seatConfigs.length; i++) { - seats[i] = new BukkitSeat<>(this, seatConfigs[i]); - } - return seats; - } + protected abstract void createSeats(HitBoxConfig config); @Override public HitBoxPart[] parts() { @@ -65,6 +51,6 @@ public class BukkitHitBox implements HitBox { @Override public void saveCustomData(CompoundTag data) { data.putString("type", "furniture"); - data.putInt("entity_id", this.furniture.baseEntityId()); + data.putInt("entity_id", this.furniture.entityId()); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractHitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBoxConfig.java similarity index 86% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractHitBoxConfig.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBoxConfig.java index d7f8e3021..d9a5755f0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractHitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBoxConfig.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity.furniture; +package net.momirealms.craftengine.core.entity.furniture.hitbox; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import org.joml.Vector3f; @@ -30,16 +30,16 @@ public abstract class AbstractHitBoxConfig implements HitBoxConfig { @Override public boolean blocksBuilding() { - return blocksBuilding; + return this.blocksBuilding; } @Override public boolean canBeHitByProjectile() { - return canBeHitByProjectile; + return this.canBeHitByProjectile; } @Override public boolean canUseItemOn() { - return canUseItemOn; + return this.canUseItemOn; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBox.java similarity index 87% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBox.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBox.java index 961ea7420..32d31c3e1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBox.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBox.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity.furniture; +package net.momirealms.craftengine.core.entity.furniture.hitbox; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.entity.seat.SeatOwner; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfig.java new file mode 100644 index 000000000..f14f7a064 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfig.java @@ -0,0 +1,20 @@ +package net.momirealms.craftengine.core.entity.furniture.hitbox; + +import net.momirealms.craftengine.core.entity.seat.SeatConfig; +import net.momirealms.craftengine.core.util.Key; +import org.joml.Vector3f; + +public interface HitBoxConfig { + + Key type(); + + SeatConfig[] seats(); + + Vector3f position(); + + boolean blocksBuilding(); + + boolean canBeHitByProjectile(); + + boolean canUseItemOn(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxConfigFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfigFactory.java similarity index 65% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxConfigFactory.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfigFactory.java index 32fb5f355..df9a2b101 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxConfigFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfigFactory.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity.furniture; +package net.momirealms.craftengine.core.entity.furniture.hitbox; import java.util.Map; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxPart.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxPart.java similarity index 73% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxPart.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxPart.java index cfa336889..fe5619281 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxPart.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxPart.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity.furniture; +package net.momirealms.craftengine.core.entity.furniture.hitbox; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.collision.AABB; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxTypes.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxTypes.java similarity index 91% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxTypes.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxTypes.java index c005ee619..853fa8318 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxTypes.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity.furniture; +package net.momirealms.craftengine.core.entity.furniture.hitbox; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; @@ -14,6 +14,7 @@ public class HitBoxTypes { public static final Key INTERACTION = Key.of("minecraft:interaction"); public static final Key SHULKER = Key.of("minecraft:shulker"); public static final Key HAPPY_GHAST = Key.of("minecraft:happy_ghast"); + public static final Key VIRTUAL = Key.of("minecraft:virtual"); public static final Key CUSTOM = Key.of("minecraft:custom"); public static void register(Key key, HitBoxConfigFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/FurnitureTicker.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/FurnitureTicker.java new file mode 100644 index 000000000..1f034cbd9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/FurnitureTicker.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.entity.furniture.tick; + +import net.momirealms.craftengine.core.entity.furniture.Furniture; + +public interface FurnitureTicker { + + void tick(T furniture); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/ItemEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/item/ItemEntity.java similarity index 67% rename from core/src/main/java/net/momirealms/craftengine/core/entity/ItemEntity.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/item/ItemEntity.java index 77483b9c1..120c168f2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/ItemEntity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/item/ItemEntity.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.entity; +package net.momirealms.craftengine.core.entity.item; import net.momirealms.craftengine.core.item.Item; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java index 7801b8f9d..ca0c73679 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/projectile/ProjectileMeta.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.core.entity.projectile; -import net.momirealms.craftengine.core.entity.Billboard; -import net.momirealms.craftengine.core.entity.ItemDisplayContext; +import net.momirealms.craftengine.core.entity.display.Billboard; +import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; import net.momirealms.craftengine.core.util.Key; import org.joml.Quaternionf; import org.joml.Vector3f; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java index 5b7c2f563..f48a6bf39 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java @@ -28,7 +28,7 @@ import java.util.Optional; * This interface provides methods for managing item properties such as custom model data, * damage, display name, lore, enchantments, and tags. * - * @param the type of the item implementation + * @param the id of the item implementation */ public interface Item { 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 186588e32..0c125097d 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 @@ -1,7 +1,7 @@ package net.momirealms.craftengine.core.item; -import net.momirealms.craftengine.core.entity.Billboard; -import net.momirealms.craftengine.core.entity.ItemDisplayContext; +import net.momirealms.craftengine.core.entity.display.Billboard; +import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta; import net.momirealms.craftengine.core.item.equipment.ComponentBasedEquipment; import net.momirealms.craftengine.core.item.equipment.Equipment; @@ -134,6 +134,12 @@ public class ItemSettings { this.customData.clear(); } + @Nullable + @SuppressWarnings("unchecked") + public T removeCustomData(CustomDataType type) { + return (T) this.customData.remove(type); + } + public void addCustomData(CustomDataType key, T value) { this.customData.put(key, value); } 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 190ee8004..206655f05 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 @@ -190,7 +190,7 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip @Override public CustomSmithingTransformRecipe readMap(Key id, Map arguments) { List base = MiscUtils.getAsStringList(arguments.get("base")); - List template = MiscUtils.getAsStringList(arguments.get("template-type")); + List template = MiscUtils.getAsStringList(arguments.get("template-id")); List addition = MiscUtils.getAsStringList(arguments.get("addition")); boolean mergeComponents = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("merge-components", true), "merge-components"); boolean mergeEnchantments = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("merge-enchantments", false), "merge-enchantments"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java index 7119a5f48..c3a749435 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java @@ -141,7 +141,7 @@ public class CustomSmithingTrimRecipe extends AbstractRecipe @Override public CustomSmithingTrimRecipe readMap(Key id, Map arguments) { List base = MiscUtils.getAsStringList(arguments.get("base")); - List template = MiscUtils.getAsStringList(arguments.get("template-type")); + List template = MiscUtils.getAsStringList(arguments.get("template-id")); List addition = MiscUtils.getAsStringList(arguments.get("addition")); Key pattern = VersionHelper.isOrAbove1_21_5() ? Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("pattern"), "warning.config.recipe.smithing_trim.missing_pattern")) : null; return new CustomSmithingTrimRecipe<>(id, diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java index c0475500d..b5d118862 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java @@ -87,12 +87,12 @@ public class ApplyBonusCountFunction extends AbstractLootConditionalFunction< public static Formula fromMap(Map map) { String type = (String) map.get("type"); if (type == null) { - throw new NullPointerException("number type cannot be null"); + throw new NullPointerException("number id cannot be null"); } Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); FormulaFactory factory = BuiltInRegistries.FORMULA_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown formula type: " + type); + throw new IllegalArgumentException("Unknown formula id: " + type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java index e6d151250..c79c52a8a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java @@ -75,7 +75,7 @@ public class ItemModels { Key key = Key.withDefaultNamespace(type, "minecraft"); ItemModelReader reader = BuiltInRegistries.ITEM_MODEL_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid item model type: " + key); + throw new IllegalArgumentException("Invalid item model id: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java index f2d5022fa..ce19f4e75 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java @@ -80,7 +80,7 @@ public class ConditionProperties { Key key = Key.withDefaultNamespace(type, "minecraft"); ConditionPropertyReader reader = BuiltInRegistries.CONDITION_PROPERTY_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid condition property type: " + key); + throw new IllegalArgumentException("Invalid condition property id: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java index a8f09bcc0..0d98ca88b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java @@ -71,7 +71,7 @@ public class RangeDispatchProperties { Key key = Key.withDefaultNamespace(type, "minecraft"); RangeDispatchPropertyReader reader = BuiltInRegistries.RANGE_DISPATCH_PROPERTY_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid range dispatch property type: " + key); + throw new IllegalArgumentException("Invalid range dispatch property id: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java index e6f5b46e0..464c79fc3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java @@ -71,7 +71,7 @@ public class SelectProperties { Key key = Key.withDefaultNamespace(type, "minecraft"); SelectPropertyReader reader = BuiltInRegistries.SELECT_PROPERTY_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid select property type: " + key); + throw new IllegalArgumentException("Invalid select property id: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java index b6a83032c..cdfffd3bb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java @@ -45,7 +45,7 @@ public class SignSpecialModel implements SpecialModel { @Override public SpecialModel create(Map arguments) { Key type = Key.of(arguments.get("type").toString()); - String woodType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("wood-type"), "warning.config.item.model.special.sign.missing_wood_type"); + String woodType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("wood-id"), "warning.config.item.model.special.sign.missing_wood_type"); String texture = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.sign.missing_texture"); return new SignSpecialModel(type, woodType, texture); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java index c27c4bba3..8b222da2b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java @@ -80,7 +80,7 @@ public class SpecialModels { Key key = Key.withDefaultNamespace(type, "minecraft"); SpecialModelReader reader = BuiltInRegistries.SPECIAL_MODEL_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid special model type: " + key); + throw new IllegalArgumentException("Invalid special model id: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java index d44d3102d..8148873b5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java @@ -65,7 +65,7 @@ public class Tints { Key key = Key.withDefaultNamespace(type, "minecraft"); TintReader reader = BuiltInRegistries.TINT_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid tint type: " + type); + throw new IllegalArgumentException("Invalid tint id: " + type); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java b/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java index 9d545f67d..7833ea8fc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java @@ -36,7 +36,7 @@ public enum ObfA { return type; } } - throw new IllegalArgumentException("Unknown resource type: " + xclf); + throw new IllegalArgumentException("Unknown resource id: " + xclf); } public static final byte[] VALUES = new byte[] { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java index ff1e71fa1..96a87ede0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java @@ -9,7 +9,7 @@ import java.util.UUID; /** * Simple implementation of {@link Sender} using a {@link SenderFactory} * - * @param the command sender type + * @param the command sender id */ public final class AbstractSender implements Sender { private final Plugin plugin; 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 df09a8b08..2ec08dbdf 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 @@ -448,10 +448,10 @@ public class Config { // 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)); + furniture$collision_entity_type = ColliderType.valueOf(config.getString("furniture.collision-entity-id", "interaction").toUpperCase(Locale.ENGLISH)); // equipment - equipment$sacrificed_vanilla_armor$type = config.getString("equipment.sacrificed-vanilla-armor.type", "chainmail").toLowerCase(Locale.ENGLISH); + equipment$sacrificed_vanilla_armor$type = config.getString("equipment.sacrificed-vanilla-armor.id", "chainmail").toLowerCase(Locale.ENGLISH); if (!AbstractPackManager.ALLOWED_VANILLA_EQUIPMENT.contains(equipment$sacrificed_vanilla_armor$type)) { TranslationManager.instance().log("warning.config.equipment.invalid_sacrificed_armor", equipment$sacrificed_vanilla_armor$type); equipment$sacrificed_vanilla_armor$type = "chainmail"; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java index 10f7f8bea..4c3ea46fc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java @@ -259,7 +259,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.byteValue(); } - throw new RuntimeException("Unexpected type: " + value.getClass().getName()); + throw new RuntimeException("Unexpected id: " + value.getClass().getName()); } } @@ -271,7 +271,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.shortValue(); } - throw new RuntimeException("Unexpected type: " + value.getClass().getName()); + throw new RuntimeException("Unexpected id: " + value.getClass().getName()); } } @@ -283,7 +283,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.longValue(); } - throw new RuntimeException("Unexpected type: " + value.getClass().getName()); + throw new RuntimeException("Unexpected id: " + value.getClass().getName()); } } @@ -295,7 +295,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.floatValue(); } - throw new RuntimeException("Unexpected type: " + value.getClass().getName()); + throw new RuntimeException("Unexpected id: " + value.getClass().getName()); } } @@ -307,7 +307,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.doubleValue(); } - throw new RuntimeException("Unexpected type: " + value.getClass().getName()); + throw new RuntimeException("Unexpected id: " + value.getClass().getName()); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java index cf02eeb46..790641d5d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java @@ -61,7 +61,7 @@ public class ExpressionTemplateArgument implements TemplateArgument { public TemplateArgument create(Map arguments) { return new ExpressionTemplateArgument( arguments.getOrDefault("expression", "").toString(), - ValueType.valueOf(arguments.getOrDefault("value-type", "double").toString().toUpperCase(Locale.ROOT)) + ValueType.valueOf(arguments.getOrDefault("value-id", "double").toString().toUpperCase(Locale.ROOT)) ); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java index 9b1a8ef91..4c0e9baa7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java @@ -58,7 +58,7 @@ public class TemplateArguments { Key key = Key.withDefaultNamespace(type0, Key.DEFAULT_NAMESPACE); TemplateArgumentFactory factory = BuiltInRegistries.TEMPLATE_ARGUMENT_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown argument type: " + type); + throw new IllegalArgumentException("Unknown argument id: " + type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java index 97e197b55..fa33c8b7f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java @@ -43,7 +43,7 @@ public class DamageFunction extends AbstractConditionalFunc @Override public Function create(Map arguments) { PlayerSelector selector = PlayerSelectors.fromObject(arguments.getOrDefault("target", "self"), conditionFactory()); - Key damageType = Key.of(ResourceConfigUtils.getAsStringOrNull(arguments.getOrDefault("damage-type", "generic"))); + Key damageType = Key.of(ResourceConfigUtils.getAsStringOrNull(arguments.getOrDefault("damage-id", "generic"))); NumberProvider amount = NumberProviders.fromObject(arguments.getOrDefault("amount", 1f)); return new DamageFunction<>(selector, damageType, amount, getPredicates(arguments)); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java index bbe2fe3d0..1d1b17e4e 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 @@ -65,7 +65,7 @@ public class OpenWindowFunction extends AbstractConditional @Override public Function create(Map arguments) { String title = Optional.ofNullable(arguments.get("title")).map(String::valueOf).orElse(null); - String rawType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("gui-type"), "warning.config.function.open_window.missing_gui_type"); + String rawType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("gui-id"), "warning.config.function.open_window.missing_gui_type"); try { GuiType type = GuiType.valueOf(rawType.toUpperCase(Locale.ENGLISH)); return new OpenWindowFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), type, title); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java index 88a2eb2ff..796358ef3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java @@ -46,7 +46,7 @@ public class RemoveFurnitureFunction extends AbstractCondit ContextHolder.Builder builder = ContextHolder.builder() .withParameter(DirectContextParameters.POSITION, position) .withParameter(DirectContextParameters.FURNITURE, furniture) - .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.extraData().item().orElse(null)); + .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.dataAccessor().item().orElse(null)); Optional optionalPlayer = ctx.getOptionalParameter(DirectContextParameters.PLAYER); Player player = optionalPlayer.orElse(null); if (player != null) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java index 520863010..154c66687 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java @@ -94,7 +94,7 @@ public class ReplaceFurnitureFunction extends AbstractCondi NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); 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); + AnchorType anchorType = ResourceConfigUtils.getAsEnum(arguments.get("anchor-id"), AnchorType.class, null); boolean dropLoot = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("drop-loot", true), "drop-loot"); boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound"); return new ReplaceFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, anchorType, dropLoot, playSound, getPredicates(arguments)); 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 39a879c3e..1db66e416 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 @@ -1,8 +1,5 @@ package net.momirealms.craftengine.core.plugin.context.function; -import net.momirealms.craftengine.core.entity.furniture.AnchorType; -import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData; -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; @@ -15,7 +12,6 @@ import net.momirealms.craftengine.core.world.WorldPosition; import java.util.List; import java.util.Map; -import java.util.Optional; public class SpawnFurnitureFunction extends AbstractConditionalFunction { private final Key furnitureId; @@ -24,7 +20,7 @@ public class SpawnFurnitureFunction extends AbstractConditi private final NumberProvider z; private final NumberProvider pitch; private final NumberProvider yaw; - private final AnchorType anchorType; + private final String variant; private final boolean playSound; public SpawnFurnitureFunction( @@ -34,7 +30,7 @@ public class SpawnFurnitureFunction extends AbstractConditi NumberProvider z, NumberProvider pitch, NumberProvider yaw, - AnchorType anchorType, + String variant, boolean playSound, List> predicates ) { @@ -45,7 +41,7 @@ public class SpawnFurnitureFunction extends AbstractConditi this.z = z; this.pitch = pitch; this.yaw = yaw; - this.anchorType = anchorType; + this.variant = variant; this.playSound = playSound; } @@ -59,17 +55,18 @@ public class SpawnFurnitureFunction extends AbstractConditi float pitchValue = this.pitch.getFloat(ctx); float yawValue = this.yaw.getFloat(ctx); WorldPosition position = new WorldPosition(world, xPos, yPos, zPos, pitchValue, yawValue); - spawnFurniture(this.furnitureId, position, this.anchorType, this.playSound); + // fixme api +// spawnFurniture(this.furnitureId, position, this.anchorType, this.playSound); }); } - public static void spawnFurniture(Key furnitureId, WorldPosition position, AnchorType anchorType, boolean playSound) { - CraftEngine.instance().furnitureManager().furnitureById(furnitureId).ifPresent(furniture -> { - AnchorType anchor = Optional.ofNullable(anchorType).orElse(furniture.getAnyAnchorType()); - FurnitureExtraData extraData = FurnitureExtraData.builder().anchorType(anchor).build(); - CraftEngine.instance().furnitureManager().place(position, furniture, extraData, playSound); - }); - } +// public static void spawnFurniture(Key furnitureId, WorldPosition position, AnchorType anchorType, boolean playSound) { +// CraftEngine.instance().furnitureManager().furnitureById(furnitureId).ifPresent(furniture -> { +// AnchorType anchor = Optional.ofNullable(anchorType).orElse(furniture.getAnyAnchorType()); +// FurnitureDataAccessor extraData = FurnitureDataAccessor.builder().anchorType(anchor).build(); +// CraftEngine.instance().furnitureManager().place(position, furniture, extraData, playSound); +// }); +// } @Override public Key type() { @@ -90,9 +87,9 @@ public class SpawnFurnitureFunction extends AbstractConditi NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); 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); + String variant = ResourceConfigUtils.getAsStringOrNull(ResourceConfigUtils.get(arguments, "variant", "anchor-id")); boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound"); - return new SpawnFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, anchorType, playSound, getPredicates(arguments)); + return new SpawnFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, variant, playSound, getPredicates(arguments)); } } } 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 index 853cc7c28..ec63aa261 100644 --- 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 @@ -63,7 +63,7 @@ public class ToastFunction extends AbstractConditionalFunct @Override public Function create(Map arguments) { AdvancementType advancementType; - String advancementName = arguments.getOrDefault("advancement-type", "goal").toString(); + String advancementName = arguments.getOrDefault("advancement-id", "goal").toString(); try { advancementType = AdvancementType.valueOf(advancementName.toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java index 0fd065ee2..273e16b2b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/EntityParameterProvider.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.core.plugin.context.parameter; import net.momirealms.craftengine.core.entity.Entity; -import net.momirealms.craftengine.core.entity.ItemEntity; +import net.momirealms.craftengine.core.entity.item.ItemEntity; import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider; import net.momirealms.craftengine.core.plugin.context.ContextKey; import net.momirealms.craftengine.core.util.MiscUtils; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java index b677d18a5..b0f6d19dd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java @@ -53,7 +53,7 @@ public class LangData { temp.put(result, entry.getValue()); } }, - () -> CraftEngine.instance().logger().warn("Unknown lang type: " + key) + () -> CraftEngine.instance().logger().warn("Unknown lang id: " + key) ); } else { temp.put(key, entry.getValue()); 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 f09e3a159..8cab5409b 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 @@ -5,7 +5,9 @@ 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.HitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.CustomEntityType; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfigFactory; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.equipment.EquipmentFactory; @@ -88,9 +90,11 @@ public class BuiltInRegistries { 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_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_TYPE, 64); public static final Registry BLOCK_ENTITY_ELEMENT_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_ELEMENT_TYPE, 16); public static final Registry CRAFT_REMAINDER_FACTORY = createConstantBoundRegistry(Registries.CRAFT_REMAINDER_FACTORY, 16); + public static final Registry> ENTITY_TYPE = createConstantBoundRegistry(Registries.ENTITY_TYPE, 32); + public static final Registry FURNITURE_ELEMENT_TYPE = createConstantBoundRegistry(Registries.FURNITURE_ELEMENT_TYPE, 16); private static Registry createConstantBoundRegistry(ResourceKey> key, int expectedSize) { return new ConstantBoundRegistry<>(key, expectedSize); 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 33a34a9d9..5258d2e4b 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 @@ -5,7 +5,9 @@ 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.HitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.CustomEntityType; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; +import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfigFactory; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.equipment.EquipmentFactory; @@ -93,4 +95,6 @@ public class Registries { 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")); public static final ResourceKey> CRAFT_REMAINDER_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("craft_remainder_factory")); + public static final ResourceKey>> ENTITY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("entity_type")); + public static final ResourceKey> FURNITURE_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_element_type")); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java index af0356c6e..cc7919e9c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java @@ -22,7 +22,7 @@ public record SoundData(Key id, SoundValue volume, SoundValue pitch) { SoundValue pitchValue = Optional.ofNullable(SoundValue.of(map.get("pitch"))).orElse(volume); return new SoundData(id, volumeValue, pitchValue); } else { - throw new IllegalArgumentException("Illegal object type for sound data: " + obj.getClass()); + throw new IllegalArgumentException("Illegal object id for sound data: " + obj.getClass()); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java b/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java index de9774f5c..7d5e95ce6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java @@ -6,8 +6,8 @@ import java.util.Objects; * A generic class representing a pair of values. * This class provides methods to create and access pairs of values. * - * @param the type of the left value - * @param the type of the right value + * @param the id of the left value + * @param the id of the right value */ public record Pair(L left, R right) { @@ -16,8 +16,8 @@ public record Pair(L left, R right) { * * @param left the left value * @param right the right value - * @param the type of the left value - * @param the type of the right value + * @param the id of the left value + * @param the id of the right value * @return a new {@link Pair} with the specified values */ public static Pair of(final L left, final R right) { 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 703571af4..30472678c 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 @@ -439,7 +439,7 @@ public class ReflectionUtils { public static List getMethods(@NotNull Class clazz, @NotNull Class returnType, @NotNull Class... parameterTypes) { List list = new ArrayList<>(); for (Method method : clazz.getMethods()) { - if (!returnType.isAssignableFrom(method.getReturnType()) // check type + if (!returnType.isAssignableFrom(method.getReturnType()) // check id || method.getParameterCount() != parameterTypes.length // check length ) continue; Class[] types = method.getParameterTypes(); 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 1a98323d1..4461e5d93 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 @@ -137,13 +137,13 @@ public final class ResourceConfigUtils { try { return Integer.parseInt(s.replace("_", "")); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.type.int", e, s, option); + throw new LocalizedResourceConfigException("warning.config.id.int", e, s, option); } } case Boolean b -> { return b ? 1 : 0; } - default -> throw new LocalizedResourceConfigException("warning.config.type.int", o.toString(), option); + default -> throw new LocalizedResourceConfigException("warning.config.id.int", o.toString(), option); } } @@ -162,11 +162,11 @@ public final class ResourceConfigUtils { try { return Double.parseDouble(s); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.type.double", e, s, option); + throw new LocalizedResourceConfigException("warning.config.id.double", e, s, option); } } default -> { - throw new LocalizedResourceConfigException("warning.config.type.double", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.id.double", o.toString(), option); } } } @@ -183,14 +183,14 @@ public final class ResourceConfigUtils { try { return Float.parseFloat(s); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.type.float", e, s, option); + throw new LocalizedResourceConfigException("warning.config.id.float", e, s, option); } } case Number number -> { return number.floatValue(); } default -> { - throw new LocalizedResourceConfigException("warning.config.type.float", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.id.float", o.toString(), option); } } } @@ -206,15 +206,15 @@ public final class ResourceConfigUtils { case Number n -> { if (n.byteValue() == 0) return false; if (n.byteValue() == 1) return true; - throw new LocalizedResourceConfigException("warning.config.type.boolean", String.valueOf(n), option); + throw new LocalizedResourceConfigException("warning.config.id.boolean", String.valueOf(n), option); } case String s -> { if (s.equalsIgnoreCase("true")) return true; if (s.equalsIgnoreCase("false")) return false; - throw new LocalizedResourceConfigException("warning.config.type.boolean", s, option); + throw new LocalizedResourceConfigException("warning.config.id.boolean", s, option); } default -> { - throw new LocalizedResourceConfigException("warning.config.type.boolean", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.id.boolean", o.toString(), option); } } } @@ -234,11 +234,11 @@ public final class ResourceConfigUtils { try { return Long.parseLong(s.replace("_", "")); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.type.long", e, s, option); + throw new LocalizedResourceConfigException("warning.config.id.long", e, s, option); } } default -> { - throw new LocalizedResourceConfigException("warning.config.type.long", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.id.long", o.toString(), option); } } } @@ -248,7 +248,7 @@ public final class ResourceConfigUtils { if (obj instanceof Map map) { return (Map) map; } - throw new LocalizedResourceConfigException("warning.config.type.map", String.valueOf(obj), option); + throw new LocalizedResourceConfigException("warning.config.id.map", String.valueOf(obj), option); } @SuppressWarnings("unchecked") @@ -259,7 +259,7 @@ public final class ResourceConfigUtils { if (obj instanceof Map map) { return (Map) map; } - throw new LocalizedResourceConfigException("warning.config.type.map", String.valueOf(obj), option); + throw new LocalizedResourceConfigException("warning.config.id.map", String.valueOf(obj), option); } public static Vector3f getAsVector3f(Object o, String option) { @@ -274,7 +274,7 @@ public final class ResourceConfigUtils { } else if (split.length == 1) { return new Vector3f(Float.parseFloat(split[0])); } else { - throw new LocalizedResourceConfigException("warning.config.type.vector3f", stringFormat, option); + throw new LocalizedResourceConfigException("warning.config.id.vector3f", stringFormat, option); } } } @@ -293,7 +293,7 @@ public final class ResourceConfigUtils { } 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); + throw new LocalizedResourceConfigException("warning.config.id.quaternionf", stringFormat, option); } } } @@ -311,7 +311,7 @@ public final class ResourceConfigUtils { double d = Double.parseDouble(split[0]); return new Vec3d(d, d, d); } else { - throw new LocalizedResourceConfigException("warning.config.type.vec3d", stringFormat, option); + throw new LocalizedResourceConfigException("warning.config.id.vec3d", stringFormat, option); } } } @@ -344,50 +344,54 @@ public final class ResourceConfigUtils { } public static AABB getAsAABB(Object o, String option) { - if (o == null) { - throw new LocalizedResourceConfigException("warning.config.type.aabb", "null", option); - } - if (o instanceof Number number) { - double min = -(number.doubleValue() / 2); - double max = number.doubleValue() / 2; - return new AABB(min, min, min, max, max, max); - } else { - double[] args; - if (o instanceof List list) { - args = new double[list.size()]; - for (int i = 0; i < args.length; i++) { - if (list.get(i) instanceof Number number) { - args[i] = number.doubleValue(); - } else { + switch (o) { + case null -> throw new LocalizedResourceConfigException("warning.config.id.aabb", "null", option); + case AABB aabb -> { + return aabb; + } + case Number number -> { + double min = -(number.doubleValue() / 2); + double max = number.doubleValue() / 2; + return new AABB(min, min, min, max, max, max); + } + default -> { + double[] args; + if (o instanceof List list) { + args = new double[list.size()]; + for (int i = 0; i < args.length; i++) { + if (list.get(i) instanceof Number number) { + args[i] = number.doubleValue(); + } else { + try { + args[i] = Double.parseDouble(list.get(i).toString()); + } catch (NumberFormatException e) { + throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option); + } + } + } + } else { + String[] split = o.toString().split(","); + args = new double[split.length]; + for (int i = 0; i < args.length; i++) { try { - args[i] = Double.parseDouble(list.get(i).toString()); + args[i] = Double.parseDouble(split[i]); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option); } } } - } else { - String[] split = o.toString().split(","); - args = new double[split.length]; - for (int i = 0; i < args.length; i++) { - try { - args[i] = Double.parseDouble(split[i]); - } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); - } + if (args.length == 1) { + return new AABB(-args[0] / 2, -args[0] / 2, -args[0] / 2, args[0] / 2, args[0] / 2, args[0] / 2); + } else if (args.length == 2) { + return new AABB(-args[0] / 2, -args[1] / 2, -args[0] / 2, args[0] / 2, args[1] / 2, args[0] / 2); + } else if (args.length == 3) { + return new AABB(-args[0] / 2, -args[1] / 2, -args[2] / 2, args[0] / 2, args[1] / 2, args[2] / 2); + } else if (args.length == 6) { + return new AABB(args[0], args[1], args[2], args[3], args[4], args[5]); + } else { + throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option); } } - if (args.length == 1) { - return new AABB(-args[0]/2, -args[0]/2, -args[0]/2, args[0]/2, args[0]/2, args[0]/2); - } else if (args.length == 2) { - return new AABB(-args[0]/2, -args[1]/2, -args[0]/2, args[0]/2, args[1]/2, args[0]/2); - } else if (args.length == 3) { - return new AABB(-args[0]/2, -args[1]/2, -args[2]/2, args[0]/2, args[1]/2, args[2]/2); - } else if (args.length == 6) { - return new AABB(args[0], args[1], args[2], args[3], args[4], args[5]); - } else { - throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); - } } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java index 0be81388d..27fd39740 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java @@ -156,7 +156,7 @@ public final class SNBTReader extends DefaultStringReader { // 1.21.6的SNBT原版是支持 {key:[B;1,2b,0xFF]} 这种奇葩写法的, 越界部分会被自动舍弃, 如0xff的byte值为-1. // 如果需要和原版对齐, 那么只需要判断是否是数字就行了. // if (!(element instanceof Number number)) - // throw new IllegalArgumentException("Error element type at pos " + getCursor()); + // throw new IllegalArgumentException("Error element id at pos " + getCursor()); if (!(element instanceof Number number)) throw new IllegalArgumentException("Error parsing number at pos " + getCursor()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java b/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java index e4cf6baf5..d91a2afaf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java @@ -6,9 +6,9 @@ import java.util.Objects; * A generic class representing a tuple with three values. * This class provides methods for creating and accessing tuples with three values. * - * @param the type of the left value - * @param the type of the middle value - * @param the type of the right value + * @param the id of the left value + * @param the id of the middle value + * @param the id of the right value */ public record Tuple(L left, M mid, R right) { @@ -18,9 +18,9 @@ public record Tuple(L left, M mid, R right) { * @param left the left value * @param mid the middle value * @param right the right value - * @param the type of the left value - * @param the type of the middle value - * @param the type of the right value + * @param the id of the left value + * @param the id of the middle value + * @param the id of the right value * @return a new {@link Tuple} with the specified values */ public static Tuple of(final L left, final M mid, final R right) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java index 8e3b3dc1a..02687838f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java @@ -8,18 +8,18 @@ public class TypeUtils { private TypeUtils() {} /** - * Checks if the provided object is of the specified type. + * Checks if the provided object is of the specified id. * If not, throws an IllegalArgumentException with a detailed message. * * @param object The object to check. - * @param expectedType The expected class type. - * @param The type parameter for expectedType. - * @return The object cast to the expected type if it matches. - * @throws IllegalArgumentException if the object's type does not match the expected type. + * @param expectedType The expected class id. + * @param The id parameter for expectedType. + * @return The object cast to the expected id if it matches. + * @throws IllegalArgumentException if the object's id does not match the expected id. */ public static T checkType(Object object, Class expectedType) { if (!expectedType.isInstance(object)) { - throw new IllegalArgumentException("Expected type: " + expectedType.getName() + + throw new IllegalArgumentException("Expected id: " + expectedType.getName() + ", but got: " + (object == null ? "null" : object.getClass().getName())); } return expectedType.cast(object); @@ -48,7 +48,7 @@ public class TypeUtils { } yield bytes; } - default -> throw new IllegalStateException("Unsupported type: " + type.toLowerCase(Locale.ENGLISH)); + default -> throw new IllegalStateException("Unsupported id: " + type.toLowerCase(Locale.ENGLISH)); }; } 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 50eda3140..0aee4a4a1 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 @@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.block.entity.render.DynamicBlockEntityRen 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.CustomEntity; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.entityculling.CullingData; @@ -18,6 +19,7 @@ import net.momirealms.craftengine.core.world.*; import net.momirealms.craftengine.core.world.chunk.client.VirtualCullableObject; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntityRendererSerializer; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer; +import net.momirealms.craftengine.core.world.chunk.serialization.DefaultEntitySerializer; import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,11 +32,12 @@ public class CEChunk { public final ChunkPos chunkPos; public final CESection[] sections; public final WorldHeight worldHeightAccessor; - public final Map blockEntities; // 从区域线程上访问,安全 - public final Map tickingSyncBlockEntitiesByPos; // 从区域线程上访问,安全 - public final Map tickingAsyncBlockEntitiesByPos; // 从区域线程上访问,安全 - public final Map constantBlockEntityRenderers; // 会从区域线程上读写,netty线程上读取 - public final Map dynamicBlockEntityRenderers; // 会从区域线程上读写,netty线程上读取 + private final Map entities; // 从区域线程上访问,安全 + private final Map blockEntities; // 从区域线程上访问,安全 + private final Map tickingSyncBlockEntitiesByPos; // 从区域线程上访问,安全 + private final Map tickingAsyncBlockEntitiesByPos; // 从区域线程上访问,安全 + private final Map constantBlockEntityRenderers; // 会从区域线程上读写,netty线程上读取 + private final Map dynamicBlockEntityRenderers; // 会从区域线程上读写,netty线程上读取 private final ReentrantReadWriteLock renderLock = new ReentrantReadWriteLock(); private volatile boolean dirty; private volatile boolean loaded; @@ -50,10 +53,11 @@ public class CEChunk { this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.entities = new Object2ObjectOpenHashMap<>(10, 0.5f); this.fillEmptySection(); } - public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, @Nullable ListTag blockEntitiesTag, @Nullable ListTag itemDisplayBlockRenders) { + public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, @Nullable ListTag blockEntitiesTag, @Nullable ListTag blockEntityRenders, @Nullable ListTag entities) { this.world = world; this.chunkPos = chunkPos; this.worldHeightAccessor = world.worldHeight(); @@ -80,15 +84,27 @@ public class CEChunk { } 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); + if (blockEntityRenders != null) { + this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(Math.max(blockEntityRenders.size(), 10), 0.5f); + List blockEntityRendererPoses = DefaultBlockEntityRendererSerializer.deserialize(this.chunkPos, blockEntityRenders); for (BlockPos pos : blockEntityRendererPoses) { this.addConstantBlockEntityRenderer(pos); } } else { this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); } + if (entities != null) { + this.entities = new Object2ObjectOpenHashMap<>(Math.max(entities.size(), 10), 0.5f); + for (CustomEntity entity : DefaultEntitySerializer.deserialize(world, entities)) { + this.entities.put(entity.uuid(), entity); + } + } else { + this.entities = new Object2ObjectOpenHashMap<>(10, 0.5f); + } + } + + public Map entities() { + return Collections.unmodifiableMap(this.entities); } public void spawnBlockEntities(Player player) { 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 3c331d193..46d340b54 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 @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; import net.momirealms.craftengine.core.block.entity.BlockEntity; import net.momirealms.craftengine.core.block.entity.BlockEntityType; +import net.momirealms.craftengine.core.block.entity.InactiveBlockEntity; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.util.Key; @@ -37,11 +38,13 @@ public final class DefaultBlockEntitySerializer { CompoundTag data = tag.getCompound(i); Key id = Key.of(data.getString("id")); BlockEntityType type = BuiltInRegistries.BLOCK_ENTITY_TYPE.getValue(id); + BlockPos pos = BlockEntity.readPosAndVerify(data, chunk.chunkPos()); + ImmutableBlockState blockState = chunk.getBlockState(pos); if (type == null) { Debugger.BLOCK.debug(() -> "Unknown block entity type: " + id); + BlockEntity blockEntity = new InactiveBlockEntity(pos, blockState, data); + blockEntities.add(blockEntity); } else { - BlockPos pos = BlockEntity.readPosAndVerify(data, chunk.chunkPos()); - ImmutableBlockState blockState = chunk.getBlockState(pos); if (blockState.blockEntityType() == type) { Optional entityBlockBehavior = blockState.behavior().getAs(EntityBlockBehavior.class); if (entityBlockBehavior.isPresent()) { 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 2f851fe49..e98376e7e 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 @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.world.chunk.serialization; +import net.momirealms.craftengine.core.entity.CustomEntity; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.chunk.CEChunk; @@ -9,6 +10,9 @@ import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Map; +import java.util.UUID; + public final class DefaultChunkSerializer { private DefaultChunkSerializer() {} @@ -36,6 +40,10 @@ public final class DefaultChunkSerializer { if (!blockEntityRenders.isEmpty()) { chunkNbt.put("block_entity_renderers", blockEntityRenders); } + ListTag listTag = new ListTag(); + Map entities = chunk.entities(); + + return chunkNbt; } @@ -54,7 +62,8 @@ public final class DefaultChunkSerializer { } } ListTag blockEntities = chunkNbt.getList("block_entities"); - ListTag itemDisplayBlockRenders = chunkNbt.getList("block_entity_renderers"); - return new CEChunk(world, pos, sectionArray, blockEntities, itemDisplayBlockRenders); + ListTag blockEntityRenders = chunkNbt.getList("block_entity_renderers"); + ListTag entities = chunkNbt.getList("entities"); + return new CEChunk(world, pos, sectionArray, blockEntities, blockEntityRenders, entities); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java new file mode 100644 index 000000000..df24bb530 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java @@ -0,0 +1,55 @@ +package net.momirealms.craftengine.core.world.chunk.serialization; + +import net.momirealms.craftengine.core.entity.CustomEntity; +import net.momirealms.craftengine.core.entity.CustomEntityType; +import net.momirealms.craftengine.core.entity.InactiveCustomEntity; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.ListTag; +import net.momirealms.sparrow.nbt.Tag; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.UUID; + +public class DefaultEntitySerializer { + + public static ListTag serialize(@NotNull Collection entity) { + ListTag entities = new ListTag(); + for (CustomEntity customEntity : entity) { + if (customEntity.isValid()) { + entities.add(customEntity.saveAsTag()); + } + } + return entities; + } + + public static List deserialize(CEWorld world, ListTag entitiesTag) { + List entities = new ArrayList<>(entitiesTag.size()); + for (Tag tag : entitiesTag) { + if (tag instanceof CompoundTag entityTag) { + WorldPosition worldPosition = CustomEntity.readPos(world, entityTag); + UUID uuid = CustomEntity.readUUID(entityTag); + Key type = Key.of(entityTag.getString("type")); + CustomEntityType entityType = BuiltInRegistries.ENTITY_TYPE.getValue(type); + if (entityType == null) { + InactiveCustomEntity entity = new InactiveCustomEntity(uuid, worldPosition, entityTag); + entities.add(entity); + } else { + CustomEntity entity = entityType.factory().create(uuid, worldPosition); + entity.loadCustomData(entityTag); + // 加载时无效则直接放弃 + if (entity.isValid()) { + entities.add(entity); + } + } + } + } + return entities; + } +} From 13c76821d4b3e136b01fdf87121f5982c1716283 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Tue, 2 Dec 2025 01:52:52 +0800 Subject: [PATCH 063/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8E=89=E8=90=BD?= =?UTF-8?q?=E7=BB=8F=E9=AA=8C=E6=96=B9=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/behavior/BukkitBlockBehaviors.java | 2 + .../behavior/DropExperienceBlockBehavior.java | 104 ++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java 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 11133b67d..090512f4c 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 @@ -47,6 +47,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key SURFACE_SPREADING_BLOCK = Key.from("craftengine:surface_spreading_block"); public static final Key SNOWY_BLOCK = Key.from("craftengine:snowy_block"); public static final Key HANGABLE_BLOCK = Key.from("craftengine:hangable_block"); + public static final Key DROP_EXPERIENCE_BLOCK = Key.from("craftengine:drop_experience_block"); public static void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); @@ -92,5 +93,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(SURFACE_SPREADING_BLOCK, SurfaceSpreadingBlockBehavior.FACTORY); register(SNOWY_BLOCK, SnowyBlockBehavior.FACTORY); register(HANGABLE_BLOCK, HangableBlockBehavior.FACTORY); + register(DROP_EXPERIENCE_BLOCK, DropExperienceBlockBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java new file mode 100644 index 000000000..ef629c558 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java @@ -0,0 +1,104 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +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.BlockSettings; +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.loot.LootContext; +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.condition.AllOfCondition; +import net.momirealms.craftengine.core.plugin.context.event.EventConditions; +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.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +public class DropExperienceBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + private final NumberProvider amount; + private final Condition conditions; + + public DropExperienceBlockBehavior(CustomBlock customBlock, NumberProvider amount, Condition conditions) { + super(customBlock); + this.amount = amount; + this.conditions = conditions; + } + + @Override + public void spawnAfterBreak(Object thisBlock, Object[] args, Callable superMethod) { + boolean dropExperience = (boolean) args[4]; // 通常来说是 false + Item item = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(args[3])); + if (!dropExperience) { + ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null); + if (state == null) { + return; + } + BlockSettings settings = state.settings(); + if (settings.requireCorrectTool()) { + if (item.isEmpty()) { + return; + } + boolean cannotBreak = !settings.isCorrectTool(item.id()) + && (!settings.respectToolComponent() + || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(args[3], state.customBlockState().literalObject())); + if (cannotBreak) { + return; + } + } + } + World world = BukkitWorldManager.instance().wrap(FastNMS.INSTANCE.method$Level$getCraftWorld(args[1])); + BlockPos pos = LocationUtils.fromBlockPos(args[2]); + tryDropExperience(world, pos, item); + } + + private void tryDropExperience(World world, BlockPos pos, Item item) { + Vec3d dropPos = Vec3d.atCenterOf(pos); + ContextHolder holder = ContextHolder.builder() + .withParameter(DirectContextParameters.POSITION, new WorldPosition(world, dropPos)) + .withParameter(DirectContextParameters.ITEM_IN_HAND, item) + .build(); + LootContext context = new LootContext(world, null, 1.0f, holder); + if (this.conditions != null && !this.conditions.test(context)) { + return; + } + int finalAmount = this.amount.getInt(context); + if (finalAmount <= 0) { + return; + } + world.dropExp(dropPos, finalAmount); + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + NumberProvider amount = NumberProviders.fromObject(arguments.getOrDefault("amount", 0)); + Condition conditions = null; + List> conditionList = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(arguments, "conditions", "condition"), EventConditions::fromMap); + if (conditionList.size() == 1) { + conditions = conditionList.getFirst(); + } else if (!conditionList.isEmpty()) { + conditions = new AllOfCondition<>(conditionList); + } + return new DropExperienceBlockBehavior(block, amount, conditions); + } + } +} From db27567b12d8e41e2e9a74f9455982e82dc227e1 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Tue, 2 Dec 2025 01:58:07 +0800 Subject: [PATCH 064/135] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../default/configuration/blocks/topaz_ore.yml | 16 ++++++++++++---- .../configuration/templates/loot_tables.yml | 3 --- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/topaz_ore.yml b/common-files/src/main/resources/resources/default/configuration/blocks/topaz_ore.yml index 1a5a03297..2435ae04e 100644 --- a/common-files/src/main/resources/resources/default/configuration/blocks/topaz_ore.yml +++ b/common-files/src/main/resources/resources/default/configuration/blocks/topaz_ore.yml @@ -27,13 +27,17 @@ items: texture: minecraft:item/custom/topaz blocks: default:topaz_ore: + behavior: + type: drop_experience_block + amount: 3~7 + condition: + type: enchantment + predicate: minecraft:silk_touch<=0 loot: template: default:loot_table/ore arguments: ore_drop: default:topaz ore_block: default:topaz_ore - min_exp: 3 - max_exp: 7 settings: template: default:settings/ore arguments: @@ -45,13 +49,17 @@ blocks: arguments: path: minecraft:block/custom/topaz_ore default:deepslate_topaz_ore: + behavior: + type: drop_experience_block + amount: 3~7 + condition: + type: enchantment + predicate: minecraft:silk_touch<=0 loot: template: default:loot_table/ore arguments: ore_drop: default:topaz ore_block: default:deepslate_topaz_ore - min_exp: 3 - max_exp: 7 settings: template: default:settings/deepslate_ore arguments: diff --git a/common-files/src/main/resources/resources/default/configuration/templates/loot_tables.yml b/common-files/src/main/resources/resources/default/configuration/templates/loot_tables.yml index 24d3c1878..c089ddca6 100644 --- a/common-files/src/main/resources/resources/default/configuration/templates/loot_tables.yml +++ b/common-files/src/main/resources/resources/default/configuration/templates/loot_tables.yml @@ -180,7 +180,6 @@ templates: # ore_block: the ore block # ore_drop: the drops of the ore material # ore_drop_count: the amount of the ore materials - # exp: the exp to drop default:loot_table/ore: pools: - rolls: 1 @@ -203,8 +202,6 @@ templates: formula: type: ore_drops - type: explosion_decay - - type: drop_exp - count: ${exp:-2~4} # Using Silk Touch or shears will cause the leaves block itself to drop. # Using Fortune, however, increases the drop rates of sticks and saplings. From d020e5dfd2647749ff4c566b7dd75b1ca78ba595 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Tue, 2 Dec 2025 02:09:59 +0800 Subject: [PATCH 065/135] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=AE=9E=E4=BD=93=E6=9E=84=E9=80=A0=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/core/entity/CustomEntity.java | 2 +- .../core/entity/InactiveCustomEntity.java | 4 +- .../core/entity/furniture/Furniture.java | 51 ++++++++++++++++--- .../furniture/FurnitureDataAccessor.java | 2 +- 4 files changed, 48 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java index 2ea1439a4..424174dd5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java @@ -13,7 +13,7 @@ public abstract class CustomEntity implements Cullable { protected WorldPosition position; protected boolean valid = true; - protected CustomEntity(CustomEntityType type, WorldPosition position, UUID uuid) { + protected CustomEntity(CustomEntityType type, UUID uuid, WorldPosition position) { this.position = position; this.type = type; this.uuid = uuid; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java index 1c870110a..9a4633d37 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java @@ -15,12 +15,12 @@ public class InactiveCustomEntity extends CustomEntity { private final CompoundTag data; public InactiveCustomEntity(UUID uuid, WorldPosition position) { - super(CustomEntityTypes.INACTIVE, position, uuid); + super(CustomEntityTypes.INACTIVE, uuid, position); this.data = INVALID_TAG; } public InactiveCustomEntity(UUID uuid, WorldPosition position, CompoundTag data) { - super(CustomEntityTypes.INACTIVE, position, uuid); + super(CustomEntityTypes.INACTIVE, uuid, position); this.data = data; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index 1b4661ba1..236a014b8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -2,18 +2,51 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.entity.CustomEntity; import net.momirealms.craftengine.core.entity.CustomEntityType; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.sparrow.nbt.CompoundTag; import org.jetbrains.annotations.NotNull; -public abstract class Furniture extends CustomEntity { - protected final FurnitureConfig config; - protected final FurnitureDataAccessor dataAccessor; +import java.util.Optional; +import java.util.UUID; - public Furniture(CustomEntityType type, WorldPosition position, FurnitureConfig config, CompoundTag data) { - super(type, position); - this.dataAccessor = new FurnitureDataAccessor(data); - this.config = config; +public abstract class Furniture extends CustomEntity { + protected FurnitureConfig config; + protected FurnitureDataAccessor dataAccessor; + + protected Key furnitureId; + protected CompoundTag inactiveFurnitureData; + + protected Furniture(CustomEntityType type, UUID uuid, WorldPosition position) { + super(type, uuid, position); + } + + @Override + protected void saveCustomData(CompoundTag tag) { + tag.putString("id", this.config.id().asMinimalString()); + + } + + @Override + public void loadCustomData(CompoundTag tag) { + this.furnitureId = readFurnitureId(tag); + this.dataAccessor = new FurnitureDataAccessor(tag.getCompound("data")); + Optional furnitureConfig = CraftEngine.instance().furnitureManager().furnitureById(this.furnitureId); + if (furnitureConfig.isPresent()) { + this.config = furnitureConfig.get(); + } else { + this.inactiveFurnitureData = tag; + } + } + + @Override + public CompoundTag saveAsTag() { + // 如果家具数据只是不活跃,则返回不活跃家具数据 + if (this.inactiveFurnitureData != null) { + return this.inactiveFurnitureData; + } + return super.saveAsTag(); } @NotNull @@ -25,4 +58,8 @@ public abstract class Furniture extends CustomEntity { public FurnitureDataAccessor dataAccessor() { return this.dataAccessor; } + + protected final Key readFurnitureId(CompoundTag tag) { + return Key.of(tag.getString("id")); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java index 07a6ccbb5..34f862f4f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java @@ -24,7 +24,7 @@ public class FurnitureDataAccessor { private final CompoundTag data; public FurnitureDataAccessor(CompoundTag data) { - this.data = data; + this.data = data == null ? new CompoundTag() : data; } public static FurnitureDataAccessor of(CompoundTag data) { From 0a6bd37c3638e856d00c68c2507024483ae4f3be Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Tue, 2 Dec 2025 21:43:12 +0800 Subject: [PATCH 066/135] =?UTF-8?q?=E5=AE=B6=E5=85=B7=E9=87=8D=E6=9E=84par?= =?UTF-8?q?t2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BetterModelBlockEntityElementConfig.java | 8 +- .../ModelEngineBlockEntityElementConfig.java | 8 +- .../skript/event/EvtCustomClick.java | 2 +- .../skript/event/EvtCustomFurniture.java | 4 +- .../bukkit/api/CraftEngineFurniture.java | 4 +- .../api/event/FurnitureInteractEvent.java | 12 +- .../element/ItemBlockEntityElementConfig.java | 8 +- .../ItemDisplayBlockEntityElementConfig.java | 8 +- .../TextDisplayBlockEntityElementConfig.java | 8 +- .../bukkit/entity/BukkitEntity.java | 12 + .../bukkit/entity/data/BukkitEntityData.java | 2 +- .../entity/furniture/BukkitFurniture.java | 175 ++------ .../furniture/BukkitFurnitureManager.java | 417 +++++++++--------- .../furniture/FurnitureEventListener.java | 170 +++---- .../ItemDisplayFurnitureElementConfig.java | 12 +- .../hitbox/AbstractFurnitureHitBox.java | 57 +++ .../hitbox/BukkitFurnitureHitboxTypes.java | 15 + .../furniture/hitbox/BukkitHitBoxTypes.java | 15 - .../furniture/hitbox/CustomHitBoxConfig.java | 63 --- .../hitbox/HappyGhastHitBoxConfig.java | 139 ------ .../hitbox/InteractionFurnitureHitbox.java | 83 ++++ .../InteractionFurnitureHitboxConfig.java | 71 +++ .../hitbox/InteractionHitBoxConfig.java | 124 ------ .../furniture/hitbox/ShulkerHitBoxConfig.java | 361 --------------- .../bukkit/plugin/BukkitCraftEngine.java | 9 +- .../feature/DebugSpawnFurnitureCommand.java | 33 +- .../plugin/network/BukkitNetworkManager.java | 57 +-- .../bukkit/world/BukkitWorldManager.java | 8 + .../BlockEntityElementConfigFactory.java | 4 +- .../craftengine/core/entity/CustomEntity.java | 96 ---- .../core/entity/CustomEntityType.java | 14 - .../core/entity/CustomEntityTypeKeys.java | 10 - .../core/entity/CustomEntityTypes.java | 18 - .../craftengine/core/entity/Entity.java | 2 + .../core/entity/InactiveCustomEntity.java | 54 --- .../core/entity/data/EntityData.java | 4 +- .../furniture/AbstractFurnitureManager.java | 12 +- .../core/entity/furniture/AnchorType.java | 2 +- .../core/entity/furniture/Furniture.java | 198 +++++++-- .../entity/furniture/FurnitureConfig.java | 27 ++ .../entity/furniture/FurnitureVariant.java | 6 +- .../element/FurnitureElementConfig.java | 4 +- .../FurnitureElementConfigFactory.java | 4 +- ...ava => AbstractFurnitureHitBoxConfig.java} | 4 +- .../furniture/hitbox/AbstractHitBox.java | 56 --- .../furniture/hitbox/FurnitureHitBox.java | 29 ++ ...Config.java => FurnitureHitBoxConfig.java} | 6 +- .../hitbox/FurnitureHitBoxConfigFactory.java | 8 + ...oxTypes.java => FurnitureHitBoxTypes.java} | 14 +- .../core/entity/furniture/hitbox/HitBox.java | 19 - .../furniture/hitbox/HitBoxConfigFactory.java | 8 - .../entity/furniture/hitbox/HitBoxPart.java | 7 - .../core/entity/player/Player.java | 5 + .../core/pack/AbstractPackManager.java | 4 +- .../plugin/config/blockbench/BBModel.java | 2 +- .../function/RemoveFurnitureFunction.java | 4 +- .../function/ReplaceFurnitureFunction.java | 3 +- .../parameter/DirectContextParameters.java | 3 +- .../parameter/FurnitureParameterProvider.java | 4 +- .../core/registry/BuiltInRegistries.java | 6 +- .../craftengine/core/registry/Registries.java | 6 +- .../core/util/BlockEntityTickersList.java | 4 +- .../craftengine/core/world/WorldPosition.java | 38 +- .../craftengine/core/world/chunk/CEChunk.java | 51 +-- .../serialization/DefaultChunkSerializer.java | 11 +- .../DefaultEntitySerializer.java | 55 --- 66 files changed, 1011 insertions(+), 1676 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/{AbstractHitBoxConfig.java => AbstractFurnitureHitBoxConfig.java} (79%) delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/{HitBoxConfig.java => FurnitureHitBoxConfig.java} (64%) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfigFactory.java rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/{HitBoxTypes.java => FurnitureHitBoxTypes.java} (65%) delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBox.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfigFactory.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxPart.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java 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 index a6c936826..0ec8d06c9 100644 --- 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 @@ -1,6 +1,5 @@ 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; @@ -59,13 +58,12 @@ public class BetterModelBlockEntityElementConfig implements BlockEntityElementCo return BetterModelBlockEntityElement.class; } - public static class Factory implements BlockEntityElementConfigFactory { + public static class Factory implements BlockEntityElementConfigFactory { - @SuppressWarnings("unchecked") @Override - public BlockEntityElementConfig create(Map arguments) { + public BetterModelBlockEntityElementConfig create(Map arguments) { String model = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("model"), "warning.config.block.state.entity_renderer.better_model.missing_model"); - return (BlockEntityElementConfig) new BetterModelBlockEntityElementConfig( + return new BetterModelBlockEntityElementConfig( model, ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), 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 index 0a4c5c41c..c34ef8d12 100644 --- 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 @@ -1,6 +1,5 @@ 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; @@ -49,13 +48,12 @@ public class ModelEngineBlockEntityElementConfig implements BlockEntityElementCo return ModelEngineBlockEntityElement.class; } - public static class Factory implements BlockEntityElementConfigFactory { + public static class Factory implements BlockEntityElementConfigFactory { - @SuppressWarnings("unchecked") @Override - public BlockEntityElementConfig create(Map arguments) { + public ModelEngineBlockEntityElementConfig create(Map arguments) { String model = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("model"), "warning.config.block.state.entity_renderer.model_engine.missing_model"); - return (BlockEntityElementConfig) new ModelEngineBlockEntityElementConfig( + return new ModelEngineBlockEntityElementConfig( model, ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomClick.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomClick.java index 86b66d401..05ac9f064 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomClick.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomClick.java @@ -49,7 +49,7 @@ public class EvtCustomClick extends SkriptEvent { EventValues.registerEventValue(FurnitureInteractEvent.class, Location.class, FurnitureInteractEvent::location, EventValues.TIME_NOW); EventValues.registerEventValue(FurnitureInteractEvent.class, Player.class, FurnitureInteractEvent::player, EventValues.TIME_NOW); EventValues.registerEventValue(CustomBlockInteractEvent.class, Block.class, event -> null, EventValues.TIME_NOW); - EventValues.registerEventValue(FurnitureInteractEvent.class, Entity.class, event -> event.furniture().baseEntity(), EventValues.TIME_NOW); + EventValues.registerEventValue(FurnitureInteractEvent.class, Entity.class, event -> event.furniture().getBukkitEntity(), EventValues.TIME_NOW); EventValues.registerEventValue(FurnitureInteractEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW); } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomFurniture.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomFurniture.java index 2afbea401..41128d050 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomFurniture.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/event/EvtCustomFurniture.java @@ -30,14 +30,14 @@ public class EvtCustomFurniture extends SkriptEvent { .description("Called when a furniture is broken by a player."); EventValues.registerEventValue(FurnitureBreakEvent.class, Location.class, FurnitureBreakEvent::location, EventValues.TIME_NOW); EventValues.registerEventValue(FurnitureBreakEvent.class, Player.class, FurnitureBreakEvent::player, EventValues.TIME_NOW); - EventValues.registerEventValue(FurnitureBreakEvent.class, Entity.class, event -> event.furniture().baseEntity(), EventValues.TIME_NOW); + EventValues.registerEventValue(FurnitureBreakEvent.class, Entity.class, event -> event.furniture().getBukkitEntity(), EventValues.TIME_NOW); EventValues.registerEventValue(FurnitureBreakEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW); Skript.registerEvent("Place Furniture", EvtCustomFurniture.class, FurniturePlaceEvent.class, "(plac(e|ing)|build[ing]) of [(custom|ce|craft-engine)] furniture[s] [[of] %-strings%]") .description("Called when a player places a furniture."); EventValues.registerEventValue(FurniturePlaceEvent.class, Location.class, FurniturePlaceEvent::location, EventValues.TIME_NOW); EventValues.registerEventValue(FurniturePlaceEvent.class, Player.class, FurniturePlaceEvent::player, EventValues.TIME_NOW); - EventValues.registerEventValue(FurniturePlaceEvent.class, Entity.class, event -> event.furniture().baseEntity(), EventValues.TIME_NOW); + EventValues.registerEventValue(FurniturePlaceEvent.class, Entity.class, event -> event.furniture().getBukkitEntity(), EventValues.TIME_NOW); EventValues.registerEventValue(FurniturePlaceEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW); } 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 8f61765d3..39ae861f4 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 @@ -301,14 +301,14 @@ public final class CraftEngineFurniture { if (!furniture.isValid()) return; Location location = ((BukkitFurniture) furniture).getDropLocation(); furniture.destroy(); - LootTable lootTable = (LootTable) furniture.config().lootTable(); + LootTable lootTable = (LootTable) furniture.config.lootTable(); World world = new BukkitWorld(location.getWorld()); WorldPosition position = new WorldPosition(world, location.getX(), location.getY(), location.getZ()); if (dropLoot && lootTable != null) { ContextHolder.Builder builder = ContextHolder.builder() .withParameter(DirectContextParameters.POSITION, position) .withParameter(DirectContextParameters.FURNITURE, furniture) - .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.dataAccessor().item().orElse(null)); + .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.dataAccessor.item().orElse(null)); if (player != null) { Item itemInHand = player.getItemInHand(InteractionHand.MAIN_HAND); builder.withParameter(DirectContextParameters.PLAYER, player) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java index ebe8850fb..2fd4cc6bc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureInteractEvent.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.bukkit.api.event; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBox; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.player.InteractionHand; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -16,23 +16,23 @@ public final class FurnitureInteractEvent extends PlayerEvent implements Cancell private final BukkitFurniture furniture; private final InteractionHand hand; private final Location interactionPoint; - private final HitBox hitBox; + private final FurnitureHitBox furnitureHitBox; public FurnitureInteractEvent(@NotNull Player player, @NotNull BukkitFurniture furniture, @NotNull InteractionHand hand, @NotNull Location interactionPoint, - @NotNull HitBox hitBox) { + @NotNull FurnitureHitBox furnitureHitBox) { super(player); this.furniture = furniture; this.hand = hand; this.interactionPoint = interactionPoint; - this.hitBox = hitBox; + this.furnitureHitBox = furnitureHitBox; } @NotNull - public HitBox hitBox() { - return hitBox; + public FurnitureHitBox hitBox() { + return furnitureHitBox; } @NotNull diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java index 01151fab5..fc6ca3df7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java @@ -2,7 +2,6 @@ package net.momirealms.craftengine.bukkit.block.entity.renderer.element; import net.momirealms.craftengine.bukkit.entity.data.ItemEntityData; 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.player.Player; @@ -76,13 +75,12 @@ public class ItemBlockEntityElementConfig implements BlockEntityElementConfig { - @SuppressWarnings("unchecked") @Override - public BlockEntityElementConfig create(Map arguments) { + public ItemBlockEntityElementConfig 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 ItemBlockEntityElementConfig( + return new ItemBlockEntityElementConfig( player -> BukkitItemManager.instance().createWrappedItem(itemId, player), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position") ); 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 index dbda0e1e0..a8b02c0b0 100644 --- 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 @@ -3,7 +3,6 @@ package net.momirealms.craftengine.bukkit.block.entity.renderer.element; import com.google.common.base.Objects; 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.display.Billboard; @@ -158,13 +157,12 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo Objects.equal(rotation, that.rotation); } - public static class Factory implements BlockEntityElementConfigFactory { + public static class Factory implements BlockEntityElementConfigFactory { - @SuppressWarnings("unchecked") @Override - public BlockEntityElementConfig create(Map arguments) { + public ItemDisplayBlockEntityElementConfig 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( + return new ItemDisplayBlockEntityElementConfig( player -> BukkitItemManager.instance().createWrappedItem(itemId, player), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), 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 index dc450d1fe..5b96a9611 100644 --- 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 @@ -4,7 +4,6 @@ import com.google.common.base.Objects; 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.display.Billboard; @@ -135,13 +134,12 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo Objects.equal(rotation, that.rotation); } - public static class Factory implements BlockEntityElementConfigFactory { + public static class Factory implements BlockEntityElementConfigFactory { - @SuppressWarnings("unchecked") @Override - public BlockEntityElementConfig create(Map arguments) { + public TextDisplayBlockEntityElementConfig create(Map arguments) { String text = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("text"), "warning.config.block.state.entity_renderer.text_display.missing_text"); - return (BlockEntityElementConfig) new TextDisplayBlockEntityElementConfig( + return new TextDisplayBlockEntityElementConfig( text, ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), 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 76dfed5d0..3d4780635 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 @@ -2,12 +2,14 @@ package net.momirealms.craftengine.bukkit.entity; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.EntityUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.entity.data.EntityData; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; import java.lang.ref.WeakReference; import java.util.UUID; @@ -38,6 +40,11 @@ public class BukkitEntity extends AbstractEntity { public void tick() { } + @Override + public WorldPosition position() { + return LocationUtils.toWorldPosition(platformEntity().getLocation()); + } + @Override public int entityID() { return platformEntity().getEntityId(); @@ -78,6 +85,11 @@ public class BukkitEntity extends AbstractEntity { return EntityUtils.getEntityType(platformEntity()); } + @Override + public boolean isValid() { + return platformEntity().isValid(); + } + @Override public String name() { return platformEntity().getName(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/BukkitEntityData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/BukkitEntityData.java index 2e3ee3eae..814eacd5b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/BukkitEntityData.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/BukkitEntityData.java @@ -39,7 +39,7 @@ public class BukkitEntityData implements EntityData { } @Override - public Object create(Object entityDataAccessor, Object value) { + public Object create(Object entityDataAccessor, T value) { return EntityDataValue.create(entityDataAccessor, value); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index fe9bec331..7236d1ba0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -1,171 +1,68 @@ package net.momirealms.craftengine.bukkit.entity.furniture; -import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.entity.BukkitEntity; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.core.entity.furniture.Collider; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; -import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; -import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; -import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; -import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.util.QuaternionUtils; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.sparrow.nbt.CompoundTag; import org.bukkit.Location; import org.bukkit.entity.Entity; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.persistence.PersistentDataType; import org.joml.Quaternionf; import org.joml.Vector3f; import java.lang.ref.WeakReference; import java.util.Optional; -import java.util.UUID; -public class BukkitFurniture implements Furniture { - private static final UUID INVALID_UUID = new UUID(0, 0); - @NotNull - private final FurnitureConfig config; - @NotNull - private final FurnitureDataAccessor dataAccessor; - - private WeakReference baseEntity; - private FurnitureVariant currentVariant; +public class BukkitFurniture extends Furniture { + private final WeakReference metaEntity; private Location location; - private boolean valid; - private UUID uuid; - private int entityId; - private FurnitureElement[] elements; - - public BukkitFurniture(@NotNull FurnitureConfig config, - @NotNull CompoundTag data) { - this.dataAccessor = FurnitureDataAccessor.of(data); - this.currentVariant = Optional.ofNullable(getVariant()).orElseGet(config::anyVariant); - this.config = config; - this.baseEntity = new WeakReference<>(null); - this.valid = false; - this.entityId = -1; - this.uuid = INVALID_UUID; + public BukkitFurniture(ItemDisplay metaEntity, FurnitureConfig config, FurnitureDataAccessor data) { + super(new BukkitEntity(metaEntity), data, config); + this.metaEntity = new WeakReference<>(metaEntity); + this.location = metaEntity.getLocation(); + this.setVariant(super.config().getVariant(data)); } - private void sync() { - WorldPosition position = position(); - FurnitureElementConfig[] elementConfigs = this.currentVariant.elements(); - FurnitureElement[] elements = new FurnitureElement[elementConfigs.length]; - for (int i = 0; i < elementConfigs.length; i++) { - FurnitureElement o = elementConfigs[i].create(position); - elements[i] = o; + @Override + public void addCollidersToWorld() { + Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(this.location.getWorld()); + for (Collider entity : super.colliders) { + FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(world, entity.handle()); + Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity.handle()); + bukkitEntity.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_COLLISION, PersistentDataType.BYTE, (byte) 1); + bukkitEntity.setPersistent(false); } - this.elements = elements; - } - - public void addToWorld(Location location) { - this.setLocation(location); - this.valid = true; - Entity baseEntity = null; // fixme 处理生成 - this.baseEntity = new WeakReference<>(baseEntity); - this.uuid = baseEntity.getUniqueId(); - this.entityId = baseEntity.getEntityId(); - this.sync(); - } - - @Override - public void show(Player player) { - for (FurnitureElement element : this.elements) { - element.show(player); - } - } - - @Override - public void hide(Player player) { - for (FurnitureElement element : this.elements) { - element.hide(player); - } - } - - @Nullable - @Override - public CullingData cullingData() { - return this.config.cullingData(); - } - - @NotNull - @Override - public FurnitureConfig config() { - return this.config; - } - - @NotNull - @Override - public FurnitureDataAccessor dataAccessor() { - return this.dataAccessor; - } - - @Override - public WorldPosition position() { - return LocationUtils.toWorldPosition(this.location); - } - - @Override - public boolean isValid() { - return this.valid; } @Override public void destroy() { - this.valid = false; - Optional.ofNullable(this.baseEntity.get()).ifPresent(Entity::remove); - } - - @Override - public UUID uuid() { - return this.uuid; - } - - @Override - public int entityId() { - return this.entityId; - } - - public void teleport(WorldPosition position) { - Location newLocation = LocationUtils.toLocation(position); - if (newLocation.equals(this.location)) { - return; + Optional.ofNullable(this.metaEntity.get()).ifPresent(Entity::remove); + for (Collider entity : super.colliders) { + entity.destroy(); } - this.setLocation(newLocation); - this.sync(); + } + + // 获取掉落物的位置,受到家具变种的影响 + public Location getDropLocation() { + Optional dropOffset = this.getCurrentVariant().dropOffset(); + if (dropOffset.isEmpty()) { + return this.location; + } + Quaternionf conjugated = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate(); + Vector3f offset = conjugated.transform(new Vector3f(dropOffset.get())); + return new Location(this.location.getWorld(), this.location.getX() + offset.x, this.location.getY() + offset.y, this.location.getZ() - offset.z); } public Location location() { return location; } - private void setLocation(Location location) { - this.location = location; - } - - public FurnitureVariant getVariant() { - return this.config.getVariant(this.dataAccessor.variant().orElseGet(this.config::anyVariantName)); - } - - public void setVariant(String variant) { - FurnitureVariant newVariant = this.config.getVariant(variant); - if (newVariant != this.currentVariant) { - this.currentVariant = newVariant; - this.sync(); - } - } - - // 获取掉落物的位置,受到家具变种的影响 - public Location getDropLocation() { - Optional dropOffset = this.getVariant().dropOffset(); - if (dropOffset.isEmpty()) { - return location(); - } - Quaternionf conjugated = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate(); - Vector3f offset = conjugated.transform(new Vector3f(dropOffset.get())); - return new Location(this.location.getWorld(), this.location.getX() + offset.x, this.location.getY() + offset.y, this.location.getZ() - offset.z); + public Entity getBukkitEntity() { + return this.metaEntity.get(); } } \ No newline at end of file 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 969727e89..f2edfe2df 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 @@ -1,35 +1,52 @@ package net.momirealms.craftengine.bukkit.entity.furniture; -import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionHitBoxConfig; +import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionFurnitureHitboxConfig; +import net.momirealms.craftengine.bukkit.nms.CollisionEntity; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.core.entity.furniture.*; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.chunk.CEChunk; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.NamespacedKey; +import org.bukkit.World; import org.bukkit.entity.Boat; +import org.bukkit.entity.Entity; import org.bukkit.entity.Interaction; +import org.bukkit.entity.ItemDisplay; import org.bukkit.event.HandlerList; +import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.Nullable; +import java.io.IOException; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; public class BukkitFurnitureManager extends AbstractFurnitureManager { public static final NamespacedKey FURNITURE_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_KEY); public static final NamespacedKey FURNITURE_EXTRA_DATA_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_EXTRA_DATA_KEY); public static final NamespacedKey FURNITURE_COLLISION = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_COLLISION); + private static BukkitFurnitureManager instance; + public static Class COLLISION_ENTITY_CLASS = Interaction.class; public static Object NMS_COLLISION_ENTITY_TYPE = MEntityTypes.INTERACTION; public static ColliderType COLLISION_ENTITY_TYPE = ColliderType.INTERACTION; - private static BukkitFurnitureManager instance; + private final BukkitCraftEngine plugin; - private final Map furnitureByRealEntityId = new ConcurrentHashMap<>(256, 0.5f); - private final Map furnitureByEntityId = new ConcurrentHashMap<>(512, 0.5f); + + private final Map byMetaEntityId = new ConcurrentHashMap<>(256, 0.5f); + private final Map byEntityId = new ConcurrentHashMap<>(512, 0.5f); // Event listeners private final FurnitureEventListener furnitureEventListener; @@ -41,7 +58,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { super(plugin); instance = this; this.plugin = plugin; - this.furnitureEventListener = new FurnitureEventListener(this); + this.furnitureEventListener = new FurnitureEventListener(this, plugin.worldManager()); } @Override @@ -75,38 +92,43 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { @Override public void delayedInit() { + // 确定碰撞箱实体类型 + COLLISION_ENTITY_TYPE = Config.colliderType(); COLLISION_ENTITY_CLASS = Config.colliderType() == ColliderType.INTERACTION ? Interaction.class : Boat.class; NMS_COLLISION_ENTITY_TYPE = Config.colliderType() == ColliderType.INTERACTION ? MEntityTypes.INTERACTION : MEntityTypes.OAK_BOAT; - COLLISION_ENTITY_TYPE = Config.colliderType(); + + // 注册事件 Bukkit.getPluginManager().registerEvents(this.furnitureEventListener, this.plugin.javaPlugin()); -// if (VersionHelper.isFolia()) { -// BiConsumer taskExecutor = (entity, runnable) -> entity.getScheduler().run(this.plugin.javaPlugin(), (t) -> runnable.run(), () -> {}); -// for (World world : Bukkit.getWorlds()) { -// List entities = world.getEntities(); -// for (Entity entity : entities) { -// if (entity instanceof ItemDisplay display) { -// taskExecutor.accept(entity, () -> handleBaseEntityLoadEarly(display)); -// } else if (entity instanceof Interaction interaction) { -// taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(interaction)); -// } else if (entity instanceof Boat boat) { -// taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(boat)); -// } -// } -// } -// } else { -// for (World world : Bukkit.getWorlds()) { -// List entities = world.getEntities(); -// for (Entity entity : entities) { -// if (entity instanceof ItemDisplay display) { -// handleBaseEntityLoadEarly(display); -// } else if (entity instanceof Interaction interaction) { -// handleCollisionEntityLoadOnEntitiesLoad(interaction); -// } else if (entity instanceof Boat boat) { -// handleCollisionEntityLoadOnEntitiesLoad(boat); -// } -// } -// } -// } + + // 对世界上已有实体的记录 + if (VersionHelper.isFolia()) { + BiConsumer taskExecutor = (entity, runnable) -> entity.getScheduler().run(this.plugin.javaPlugin(), (t) -> runnable.run(), () -> {}); + for (World world : Bukkit.getWorlds()) { + List entities = world.getEntities(); + for (Entity entity : entities) { + if (entity instanceof ItemDisplay display) { + taskExecutor.accept(entity, () -> handleMetaEntityDuringChunkLoad(display)); + } else if (entity instanceof Interaction interaction) { + taskExecutor.accept(entity, () -> handleCollisionEntityDuringChunkLoad(interaction)); + } else if (entity instanceof Boat boat) { + taskExecutor.accept(entity, () -> handleCollisionEntityDuringChunkLoad(boat)); + } + } + } + } else { + for (World world : Bukkit.getWorlds()) { + List entities = world.getEntities(); + for (Entity entity : entities) { + if (entity instanceof ItemDisplay display) { + handleMetaEntityDuringChunkLoad(display); + } else if (entity instanceof Interaction interaction) { + handleCollisionEntityDuringChunkLoad(interaction); + } else if (entity instanceof Boat boat) { + handleCollisionEntityDuringChunkLoad(boat); + } + } + } + } } @Override @@ -117,183 +139,180 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { @Override public boolean isFurnitureRealEntity(int entityId) { - return this.furnitureByRealEntityId.containsKey(entityId); + return this.byMetaEntityId.containsKey(entityId); } @Nullable @Override public BukkitFurniture loadedFurnitureByRealEntityId(int entityId) { - return this.furnitureByRealEntityId.get(entityId); + return this.byMetaEntityId.get(entityId); } @Override @Nullable public BukkitFurniture loadedFurnitureByEntityId(int entityId) { - return this.furnitureByEntityId.get(entityId); + return this.byEntityId.get(entityId); } -// -// protected void handleBaseEntityUnload(Entity entity) { -// int id = entity.getEntityId(); -// BukkitFurniture furniture = this.furnitureByRealEntityId.remove(id); -// if (furniture != null) { -// Location location = entity.getLocation(); -// boolean isPreventing = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); -// if (!isPreventing) { -// furniture.destroySeats(); -// } -// for (int sub : furniture.entityIds()) { -// this.furnitureByEntityId.remove(sub); -// } -// } -// } -// -// protected void handleCollisionEntityUnload(Entity entity) { -// int id = entity.getEntityId(); -// this.furnitureByRealEntityId.remove(id); -// } -// -// @SuppressWarnings("deprecation") // just a misleading name `getTrackedPlayers` -// protected void handleBaseEntityLoadLate(ItemDisplay display, int depth) { -// // must be a furniture item -// String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); -// if (id == null) return; -// -// Key key = Key.of(id); -// Optional optionalFurniture = furnitureById(key); -// if (optionalFurniture.isEmpty()) return; -// -// FurnitureConfig customFurniture = optionalFurniture.get(); -// BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId()); -// if (previous != null) return; -// -// Location location = display.getLocation(); -// boolean above1_20_1 = VersionHelper.isOrAbove1_20_2(); -// boolean preventChange = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); -// if (above1_20_1) { -// if (!preventChange) { -// BukkitFurniture furniture = addNewFurniture(display, customFurniture); -// furniture.initializeColliders(); -// for (Player player : display.getTrackedPlayers()) { -// BukkitAdaptors.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); -// this.plugin.networkManager().sendPacket(BukkitAdaptors.adapt(player), furniture.spawnPacket(player)); -// } -// } -// } else { -// BukkitFurniture furniture = addNewFurniture(display, customFurniture); -// for (Player player : display.getTrackedPlayers()) { -// BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); -// serverPlayer.entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds())); -// this.plugin.networkManager().sendPacket(serverPlayer, furniture.spawnPacket(player)); -// } -// if (preventChange) { -// this.plugin.scheduler().sync().runLater(furniture::initializeColliders, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); -// } else { -// furniture.initializeColliders(); -// } -// } -// if (depth > 2) return; -// this.plugin.scheduler().sync().runLater(() -> handleBaseEntityLoadLate(display, depth + 1), 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); -// } -// -// protected void handleCollisionEntityLoadLate(Entity entity, int depth) { -// // remove the entity if it's not a collision entity, it might be wrongly copied by WorldEdit -// if (FastNMS.INSTANCE.method$CraftEntity$getHandle(entity) instanceof CollisionEntity) { -// return; -// } -// // not a collision entity -// Byte flag = entity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); -// if (flag == null || flag != 1) { -// return; -// } -// -// Location location = entity.getLocation(); -// World world = location.getWorld(); -// int chunkX = location.getBlockX() >> 4; -// int chunkZ = location.getBlockZ() >> 4; -// if (!FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world), chunkX, chunkZ)) { -// entity.remove(); -// return; -// } -// -// if (depth > 2) return; -// plugin.scheduler().sync().runLater(() -> { -// handleCollisionEntityLoadLate(entity, depth + 1); -// }, 1, world, chunkX, chunkZ); -// } -// -// public void handleBaseEntityLoadEarly(ItemDisplay display) { -// String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); -// if (id == null) return; -// // Remove the entity if it's not a valid furniture -// if (Config.handleInvalidFurniture()) { -// String mapped = Config.furnitureMappings().get(id); -// if (mapped != null) { -// if (mapped.isEmpty()) { -// display.remove(); -// return; -// } else { -// id = mapped; -// display.getPersistentDataContainer().set(FURNITURE_KEY, PersistentDataType.STRING, id); -// } -// } -// } -// -// Key key = Key.of(id); -// Optional optionalFurniture = furnitureById(key); -// if (optionalFurniture.isPresent()) { -// FurnitureConfig customFurniture = optionalFurniture.get(); -// BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId()); -// if (previous != null) return; -// BukkitFurniture furniture = addNewFurniture(display, customFurniture); -// furniture.initializeColliders(); // safely do it here -// } -// } -// -// public void handleCollisionEntityLoadOnEntitiesLoad(Entity collisionEntity) { -// // faster -// if (FastNMS.INSTANCE.method$CraftEntity$getHandle(collisionEntity) instanceof CollisionEntity) { -// collisionEntity.remove(); -// return; -// } -// -// // not a collision entity -// Byte flag = collisionEntity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); -// if (flag == null || flag != 1) { -// return; -// } -// -// collisionEntity.remove(); -// } -// -// private FurnitureDataAccessor getFurnitureExtraData(Entity baseEntity) throws IOException { -// byte[] extraData = baseEntity.getPersistentDataContainer().get(FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY); -// if (extraData == null) return FurnitureDataAccessor.builder().build(); -// return FurnitureDataAccessor.fromBytes(extraData); -// } -// -// private synchronized BukkitFurniture addNewFurniture(ItemDisplay display, FurnitureConfig furniture) { -// FurnitureDataAccessor extraData; -// try { -// extraData = getFurnitureExtraData(display); -// } catch (IOException e) { -// extraData = FurnitureDataAccessor.builder().build(); -// plugin.logger().warn("Furniture extra data could not be loaded", e); -// } -// BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, extraData); -// this.furnitureByRealEntityId.put(bukkitFurniture.baseEntityId(), bukkitFurniture); -// for (int entityId : bukkitFurniture.entityIds()) { -// this.furnitureByEntityId.put(entityId, bukkitFurniture); -// } -// for (Collider collisionEntity : bukkitFurniture.collisionEntities()) { -// int collisionEntityId = FastNMS.INSTANCE.method$Entity$getId(collisionEntity.handle()); -// this.furnitureByRealEntityId.put(collisionEntityId, bukkitFurniture); -// } -// return bukkitFurniture; -// } + protected void handleMetaEntityUnload(Entity entity) { + int id = entity.getEntityId(); + BukkitFurniture furniture = this.byMetaEntityId.remove(id); + if (furniture != null) { + Location location = entity.getLocation(); + // 区块还在加载的时候,就重复卸载了 + boolean isPreventing = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); + if (!isPreventing) { + furniture.destroySeats(); + } + for (int sub : furniture.entityIds()) { + this.byEntityId.remove(sub); + } + } + } + + protected void handleCollisionEntityUnload(Entity entity) { + int id = entity.getEntityId(); + this.byMetaEntityId.remove(id); + } + + private boolean isEntitiesLoaded(Location location) { + CEWorld ceWorld = this.plugin.worldManager().getWorld(location.getWorld()); + CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(location.getBlockX() >> 4, location.getBlockZ() >> 4); + if (ceChunk == null) return false; + return ceChunk.isEntitiesLoaded(); + } + + protected void handleMetaEntityDuringChunkLoad(ItemDisplay entity) { + // 实体可能不是持久的 + if (!entity.isPersistent()) { + return; + } + + // 获取家具pdc + String id = entity.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); + if (id == null) return; + + // 处理无效的家具 + if (Config.handleInvalidFurniture()) { + String mapped = Config.furnitureMappings().get(id); + if (mapped != null) { + if (mapped.isEmpty()) { + entity.remove(); + return; + } else { + id = mapped; + entity.getPersistentDataContainer().set(FURNITURE_KEY, PersistentDataType.STRING, id); + } + } + } + + // 获取家具配置 + Key key = Key.of(id); + Optional optionalFurniture = furnitureById(key); + if (optionalFurniture.isEmpty()) return; + + // 已经在其他事件里加载过了 + FurnitureConfig customFurniture = optionalFurniture.get(); + BukkitFurniture previous = this.byMetaEntityId.get(entity.getEntityId()); + if (previous != null) return; + + BukkitFurniture furniture = addNewFurniture(entity, customFurniture); + furniture.addCollidersToWorld(); + } + + protected void handleMetaEntityAfterChunkLoad(ItemDisplay entity) { + // 实体可能不是持久的 + if (!entity.isPersistent()) { + return; + } + + // 获取家具pdc + String id = entity.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); + if (id == null) return; + + // 这个区块还处于加载实体中,这个时候不处理 + if (!isEntitiesLoaded(entity.getLocation())) { + return; + } + + // 获取家具配置 + Key key = Key.of(id); + Optional optionalFurniture = furnitureById(key); + if (optionalFurniture.isEmpty()) return; + + // 已经在其他事件里加载过了 + FurnitureConfig customFurniture = optionalFurniture.get(); + BukkitFurniture previous = this.byMetaEntityId.get(entity.getEntityId()); + if (previous != null) return; + + BukkitFurniture furniture = addNewFurniture(entity, customFurniture); + furniture.addCollidersToWorld(); + } + + protected void handleCollisionEntityAfterChunkLoad(Entity entity) { + // 实体可能不是持久的 + if (!entity.isPersistent()) { + return; + } + // 如果是碰撞实体,那么就忽略 + if (FastNMS.INSTANCE.method$CraftEntity$getHandle(entity) instanceof CollisionEntity) { + return; + } + // 看看有没有碰撞实体的pdc + Byte flag = entity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); + if (flag == null || flag != 1) { + return; + } + // 实体未加载 + if (!isEntitiesLoaded(entity.getLocation())) { + return; + } + // 移除被WorldEdit错误复制的碰撞实体 + entity.remove(); + } + + public void handleCollisionEntityDuringChunkLoad(Entity collisionEntity) { + // faster + if (FastNMS.INSTANCE.method$CraftEntity$getHandle(collisionEntity) instanceof CollisionEntity) { + collisionEntity.remove(); + return; + } + + // not a collision entity + Byte flag = collisionEntity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE); + if (flag == null || flag != 1) { + return; + } + + collisionEntity.remove(); + } + + private FurnitureDataAccessor getFurnitureDataAccessor(Entity baseEntity) { + byte[] extraData = baseEntity.getPersistentDataContainer().get(FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY); + if (extraData == null) return new FurnitureDataAccessor(null); + try { + return FurnitureDataAccessor.fromBytes(extraData); + } catch (IOException e) { + // 损坏了?一般不会 + return new FurnitureDataAccessor(null); + } + } + + private synchronized BukkitFurniture addNewFurniture(ItemDisplay display, FurnitureConfig furniture) { + BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, getFurnitureDataAccessor(display)); + this.byMetaEntityId.put(display.getEntityId(), bukkitFurniture); + for (int entityId : bukkitFurniture.entityIds()) { + this.byEntityId.put(entityId, bukkitFurniture); + } + for (Collider collisionEntity : bukkitFurniture.colliders()) { + int collisionEntityId = FastNMS.INSTANCE.method$Entity$getId(collisionEntity.handle()); + this.byEntityId.put(collisionEntityId, bukkitFurniture); + } + return bukkitFurniture; + } @Override - protected HitBoxConfig defaultHitBox() { - return InteractionHitBoxConfig.DEFAULT; + protected FurnitureHitBoxConfig defaultHitBox() { + return InteractionFurnitureHitboxConfig.DEFAULT; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java index 5fa382018..a8b0e8da5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java @@ -1,85 +1,107 @@ package net.momirealms.craftengine.bukkit.entity.furniture; +import com.destroystokyo.paper.event.entity.EntityAddToWorldEvent; +import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent; +import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.chunk.CEChunk; +import org.bukkit.entity.Entity; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.world.ChunkUnloadEvent; +import org.bukkit.event.world.EntitiesLoadEvent; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.event.world.WorldUnloadEvent; + +import java.util.List; public class FurnitureEventListener implements Listener { private final BukkitFurnitureManager manager; + private final BukkitWorldManager worldManager; - public FurnitureEventListener(final BukkitFurnitureManager manager) { + public FurnitureEventListener(final BukkitFurnitureManager manager, final BukkitWorldManager worldManager) { this.manager = manager; + this.worldManager = worldManager; } -// /* -// * Load Entities -// */ -// @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) -// public void onEntitiesLoadEarly(EntitiesLoadEvent event) { -// List entities = event.getEntities(); -// for (Entity entity : entities) { -// if (entity instanceof ItemDisplay itemDisplay) { -// this.manager.handleBaseEntityLoadEarly(itemDisplay); -// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { -// this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity); -// } -// } -// } -// -// @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) -// public void onWorldLoad(WorldLoadEvent event) { -// List entities = event.getWorld().getEntities(); -// for (Entity entity : entities) { -// if (entity instanceof ItemDisplay itemDisplay) { -// this.manager.handleBaseEntityLoadEarly(itemDisplay); -// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { -// this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity); -// } -// } -// } -// -// @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) -// public void onEntityLoad(EntityAddToWorldEvent event) { -// Entity entity = event.getEntity(); -// if (entity instanceof ItemDisplay itemDisplay) { -// this.manager.handleBaseEntityLoadLate(itemDisplay, 0); -// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { -// this.manager.handleCollisionEntityLoadLate(entity, 0); -// } -// } -// -// /* -// * Unload Entities -// */ -// @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) -// public void onChunkUnload(ChunkUnloadEvent event) { -// Entity[] entities = event.getChunk().getEntities(); -// for (Entity entity : entities) { -// if (entity instanceof ItemDisplay) { -// this.manager.handleBaseEntityUnload(entity); -// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { -// this.manager.handleCollisionEntityUnload(entity); -// } -// } -// } -// -// @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) -// public void onWorldUnload(WorldUnloadEvent event) { -// List entities = event.getWorld().getEntities(); -// for (Entity entity : entities) { -// if (entity instanceof ItemDisplay) { -// this.manager.handleBaseEntityUnload(entity); -// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { -// this.manager.handleCollisionEntityUnload(entity); -// } -// } -// } -// -// @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) -// public void onEntityUnload(EntityRemoveFromWorldEvent event) { -// Entity entity = event.getEntity(); -// if (entity instanceof ItemDisplay) { -// this.manager.handleBaseEntityUnload(entity); -// } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { -// this.manager.handleCollisionEntityUnload(entity); -// } -// } + /* + * Load Entities + */ + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) + public void onEntitiesLoadEarly(EntitiesLoadEvent event) { + List entities = event.getEntities(); + for (Entity entity : entities) { + if (entity instanceof ItemDisplay itemDisplay) { + this.manager.handleMetaEntityDuringChunkLoad(itemDisplay); + } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { + this.manager.handleCollisionEntityDuringChunkLoad(entity); + } + } + CEWorld world = this.worldManager.getWorld(event.getWorld()); + CEChunk ceChunk = world.getChunkAtIfLoaded(event.getChunk().getChunkKey()); + if (ceChunk != null) { + ceChunk.setEntitiesLoaded(true); + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) + public void onWorldLoad(WorldLoadEvent event) { + List entities = event.getWorld().getEntities(); + for (Entity entity : entities) { + if (entity instanceof ItemDisplay itemDisplay) { + this.manager.handleMetaEntityDuringChunkLoad(itemDisplay); + } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { + this.manager.handleCollisionEntityDuringChunkLoad(entity); + } + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) + public void onEntityLoad(EntityAddToWorldEvent event) { + Entity entity = event.getEntity(); + if (entity instanceof ItemDisplay itemDisplay) { + this.manager.handleMetaEntityAfterChunkLoad(itemDisplay); + } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { + this.manager.handleCollisionEntityAfterChunkLoad(entity); + } + } + + /* + * Unload Entities + */ + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onChunkUnload(ChunkUnloadEvent event) { + Entity[] entities = event.getChunk().getEntities(); + for (Entity entity : entities) { + if (entity instanceof ItemDisplay) { + this.manager.handleMetaEntityUnload(entity); + } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { + this.manager.handleCollisionEntityUnload(entity); + } + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onWorldUnload(WorldUnloadEvent event) { + List entities = event.getWorld().getEntities(); + for (Entity entity : entities) { + if (entity instanceof ItemDisplay) { + this.manager.handleMetaEntityUnload(entity); + } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { + this.manager.handleCollisionEntityUnload(entity); + } + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onEntityUnload(EntityRemoveFromWorldEvent event) { + Entity entity = event.getEntity(); + if (entity instanceof ItemDisplay) { + this.manager.handleMetaEntityUnload(entity); + } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { + this.manager.handleCollisionEntityUnload(entity); + } + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java index 8edc5d489..fe52c4c83 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -6,7 +6,7 @@ import net.momirealms.craftengine.bukkit.entity.furniture.ItemColorSource; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.core.entity.display.Billboard; import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; -import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; +import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; import net.momirealms.craftengine.core.entity.player.Player; @@ -15,7 +15,6 @@ import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.item.data.FireworkExplosion; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.joml.Quaternionf; @@ -126,18 +125,17 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig } @Override - public ItemDisplayFurnitureElement create(@NotNull WorldPosition position) { + public ItemDisplayFurnitureElement create(@NotNull Furniture furniture) { return new ItemDisplayFurnitureElement(this); } - public static class Factory implements FurnitureElementConfigFactory { + public static class Factory implements FurnitureElementConfigFactory { - @SuppressWarnings("unchecked") @Override - public FurnitureElementConfig create(Map arguments) { + public ItemDisplayFurnitureElementConfig create(Map arguments) { Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.furniture.element.item_display.missing_item")); boolean applyDyedColor = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color"); - return (FurnitureElementConfig) new ItemDisplayFurnitureElementConfig( + return new ItemDisplayFurnitureElementConfig( (player, colorSource) -> { Item wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player); if (applyDyedColor && colorSource != null && wrappedItem != null) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java new file mode 100644 index 000000000..5ae471042 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java @@ -0,0 +1,57 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider; +import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.seat.Seat; +import net.momirealms.craftengine.core.entity.seat.SeatConfig; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.collision.AABB; +import net.momirealms.sparrow.nbt.CompoundTag; + +public abstract class AbstractFurnitureHitBox implements FurnitureHitBox { + protected final Furniture furniture; + protected Seat[] seats; + + public AbstractFurnitureHitBox(Furniture furniture, FurnitureHitBoxConfig config) { + this.furniture = furniture; + this.seats = createSeats(config); + } + + @SuppressWarnings("unchecked") + private Seat[] createSeats(FurnitureHitBoxConfig config) { + SeatConfig[] seatConfigs = config.seats(); + Seat[] seats = new Seat[seatConfigs.length]; + for (int i = 0; i < seatConfigs.length; i++) { + seats[i] = new BukkitSeat<>(this, seatConfigs[i]); + } + return seats; + } + + @Override + public void saveCustomData(CompoundTag data) { + data.putString("type", "furniture"); + // 用于通过座椅找到原始家具 + data.putInt("entity_id", this.furniture.entityId()); + } + + @Override + public Seat[] seats() { + return this.seats; + } + + protected Collider createCollider(World world, Vec3d position, AABB ceAABB, boolean canCollide, boolean blocksBuilding, boolean canBeHitByProjectile) { + Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); + return new BukkitCollider(world.serverWorld(), nmsAABB, position.x, position.y, position.z, canBeHitByProjectile, canCollide, blocksBuilding); + } + + protected Object createDespawnPacket(int[] entityIds) { + return FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList(entityIds)); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java new file mode 100644 index 000000000..c1ef83ddb --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java @@ -0,0 +1,15 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxTypes; + +public class BukkitFurnitureHitboxTypes extends FurnitureHitBoxTypes { + + public static void init() {} + + static { + register(INTERACTION, InteractionFurnitureHitboxConfig.FACTORY); +// register(SHULKER, ShulkerFurnitureHitboxConfig.FACTORY); +// register(HAPPY_GHAST, HappyGhastFurnitureHitboxConfig.FACTORY); +// register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java deleted file mode 100644 index 017824a2a..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; - -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxTypes; - -public class BukkitHitBoxTypes extends HitBoxTypes { - - public static void init() {} - - static { - register(INTERACTION, InteractionHitBoxConfig.FACTORY); - register(SHULKER, ShulkerHitBoxConfig.FACTORY); - register(HAPPY_GHAST, HappyGhastHitBoxConfig.FACTORY); - register(CUSTOM, CustomHitBoxConfig.FACTORY); - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java deleted file mode 100644 index 2b5abc32e..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomHitBoxConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; - -import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractHitBoxConfig; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfigFactory; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxTypes; -import net.momirealms.craftengine.core.entity.seat.SeatConfig; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import org.bukkit.NamespacedKey; -import org.bukkit.Registry; -import org.bukkit.entity.EntityType; -import org.joml.Vector3f; - -import java.util.Map; - -public class CustomHitBoxConfig extends AbstractHitBoxConfig { - public static final Factory FACTORY = new Factory(); - private final float scale; - private final EntityType entityType; - - public CustomHitBoxConfig(SeatConfig[] seats, - Vector3f position, - EntityType type, - float scale, - boolean blocksBuilding, - boolean canBeHitByProjectile) { - super(seats, position, false, blocksBuilding, canBeHitByProjectile); - this.scale = scale; - this.entityType = type; - } - - public EntityType entityType() { - return this.entityType; - } - - public float scale() { - return this.scale; - } - - @Override - public Key type() { - return HitBoxTypes.CUSTOM; - } - - public static class Factory implements HitBoxConfigFactory { - - @Override - public HitBoxConfig create(Map arguments) { - Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); - float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale"); - String type = (String) arguments.getOrDefault("entity-id", "slime"); - EntityType entityType = Registry.ENTITY_TYPE.get(new NamespacedKey("minecraft", type)); - if (entityType == null) { - throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.custom.invalid_entity", new IllegalArgumentException("EntityType not found: " + type), type); - } - boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile"); - boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); - return new CustomHitBoxConfig(SeatConfig.fromObj(arguments.get("seats")), position, entityType, scale, blocksBuilding, canBeHitByProjectile); - } - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java deleted file mode 100644 index df7d249fe..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastHitBoxConfig.java +++ /dev/null @@ -1,139 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; - -import net.momirealms.craftengine.bukkit.entity.data.HappyGhastData; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -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.MEntityTypes; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; -import net.momirealms.craftengine.core.entity.furniture.Collider; -import net.momirealms.craftengine.core.entity.furniture.hitbox.*; -import net.momirealms.craftengine.core.entity.seat.SeatConfig; -import net.momirealms.craftengine.core.util.Key; -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.World; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.craftengine.core.world.collision.AABB; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class HappyGhastHitBoxConfig extends AbstractHitBoxConfig { - public static final Factory FACTORY = new Factory(); - private final double scale; - private final boolean hardCollision; - private final List cachedValues = new ArrayList<>(); - - public HappyGhastHitBoxConfig(SeatConfig[] seats, Vector3f position, double scale, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile, boolean hardCollision) { - super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile); - this.scale = scale; - this.hardCollision = hardCollision; - HappyGhastData.StaysStill.addEntityDataIfNotDefaultValue(hardCollision, this.cachedValues); - HappyGhastData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedValues); // NO AI - HappyGhastData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues); // Invisible - } - - @Override - public Key type() { - return HitBoxTypes.HAPPY_GHAST; - } - - public double scale() { - return this.scale; - } - - public boolean hardCollision() { - return this.hardCollision; - } - - @Override - public void initPacketsAndColliders(int[] entityIds, - WorldPosition position, - Quaternionf conjugated, - BiConsumer packets, - Consumer collider, - Consumer aabb) { - Vector3f offset = conjugated.transform(new Vector3f(position())); - try { - double x = position.x(); - double y = position.y(); - double z = position.z(); - float yaw = position.yRot(); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityIds[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw, - MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0 - ), true); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[0], List.copyOf(this.cachedValues)), true); - if (VersionHelper.isOrAbove1_20_5() && this.scale != 1) { - Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); - CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale); - packets.accept(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[0], Collections.singletonList(attributeInstance)), false); - } - if (this.hardCollision) { - collider.accept(this.createCollider(position.world(), offset, x, y, z, entityIds[0], aabb)); - } - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Failed to construct custom hitbox spawn packet", e); - } - } - - public Collider createCollider(World world, Vector3f offset, double x, double y, double z, int entityId, Consumer aabb) { - AABB ceAABB = createAABB(offset, x, y, z); - Object level = world.serverWorld(); - Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); - aabb.accept(new HitBoxPart(entityId, ceAABB, new Vec3d(x, y, z))); - return new BukkitCollider(level, nmsAABB, x, y, z, this.canBeHitByProjectile(), true, this.blocksBuilding()); - } - - public AABB createAABB(Vector3f offset, double x, double y, double z) { - double baseSize = 4.0 * this.scale; - double halfSize = baseSize * 0.5; - double minX = x - halfSize + offset.x(); - double maxX = x + halfSize + offset.x(); - double minY = y + offset.y(); - double maxY = y + baseSize + offset.y(); - double minZ = z - halfSize - offset.z(); - double maxZ = z + halfSize - offset.z(); - return new AABB(minX, minY, minZ, maxX, maxY, maxZ); - } - - @Override - public void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer aabbs) { - if (!this.hardCollision) return; - Vector3f offset = conjugated.transform(new Vector3f(position())); - AABB aabb = createAABB(offset, x, y, z); - aabbs.accept(aabb); - } - - @Override - public int[] acquireEntityIds(Supplier entityIdSupplier) { - return new int[] {entityIdSupplier.get()}; - } - - public static class Factory implements HitBoxConfigFactory { - - @Override - public HitBoxConfig create(Map arguments) { - if (!VersionHelper.isOrAbove1_21_6()) { - throw new UnsupportedOperationException("HappyGhastHitBox is only supported on 1.21.6+"); - } - double scale = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("scale", 1), "scale"); - boolean hardCollision = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("hard-collision", true), "hard-collision"); - boolean canUseOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on"); - boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile"); - boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); - return new HappyGhastHitBoxConfig( - SeatConfig.fromObj(arguments.get("seats")), - 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/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java new file mode 100644 index 000000000..9f8bc0366 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -0,0 +1,83 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; +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.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { + private final InteractionFurnitureHitboxConfig config; + private final Vec3d position; + private final AABB aabb; + private final Collider collider; + private final int[] entityIds; + private final Object spawnPacket; + private final Object despawnPacket; + + public InteractionFurnitureHitbox(Furniture furniture, InteractionFurnitureHitboxConfig config) { + super(furniture, config); + this.config = config; + WorldPosition position = furniture.position(); + this.position = Furniture.getRelativePosition(position, config.position()); + this.aabb = AABB.fromInteraction(this.position, config.size.x, config.size.y); + this.collider = createCollider(furniture.world(), this.position, this.aabb, false, config.blocksBuilding(), config.canBeHitByProjectile()); + int interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + this.entityIds = new int[] {interactionId}; + List values = new ArrayList<>(3); + InteractionEntityData.Height.addEntityDataIfNotDefaultValue(config.size.y, values); + InteractionEntityData.Width.addEntityDataIfNotDefaultValue(config.size.x, values); + InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(config.responsive, values); + this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( + FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + interactionId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, + MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 + ), + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, values) + )); + this.despawnPacket = createDespawnPacket(this.entityIds); + } + + @Override + public void show(Player player) { + player.sendPacket(this.spawnPacket, false); + } + + @Override + public void hide(Player player) { + player.sendPacket(this.despawnPacket, false); + } + + @Override + public AABB aabb() { + return this.aabb; + } + + @Override + public Vec3d position() { + return this.position; + } + + @Override + public Collider collider() { + return this.collider; + } + + @Override + public int[] virtualEntityIds() { + return this.entityIds; + } + + public InteractionFurnitureHitboxConfig config() { + return this.config; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java new file mode 100644 index 000000000..2a1efd59f --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -0,0 +1,71 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.seat.SeatConfig; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.joml.Vector3f; + +import java.util.Map; + +public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig { + public static final Factory FACTORY = new Factory(); + public static final InteractionFurnitureHitboxConfig DEFAULT = new InteractionFurnitureHitboxConfig(new SeatConfig[0], new Vector3f(), false, false, false, new Vector3f(1,1,1), true); + + public final Vector3f size; + public final boolean responsive; + + public InteractionFurnitureHitboxConfig(SeatConfig[] seats, + Vector3f position, + boolean canUseItemOn, + boolean blocksBuilding, + boolean canBeHitByProjectile, + Vector3f size, + boolean responsive) { + super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile); + this.size = size; + this.responsive = responsive; + } + + public Vector3f size() { + return size; + } + + public boolean responsive() { + return responsive; + } + + @Override + public InteractionFurnitureHitbox create(Furniture furniture) { + return new InteractionFurnitureHitbox(furniture, this); + } + + public static class Factory implements FurnitureHitBoxConfigFactory { + + @Override + public InteractionFurnitureHitboxConfig create(Map arguments) { + Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); + float width; + float height; + if (arguments.containsKey("scale")) { + String[] split = arguments.get("scale").toString().split(","); + width = Float.parseFloat(split[0]); + height = Float.parseFloat(split[1]); + } else { + width = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("width", 1), "width"); + height = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("height", 1), "height"); + } + boolean canUseOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", false), "can-use-item-on"); + boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive"); + boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile"); + boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); + return new InteractionFurnitureHitboxConfig( + SeatConfig.fromObj(arguments.get("seats")), + position, canUseOn, blocksBuilding, canBeHitByProjectile, + new Vector3f(width, height, width), + interactive + ); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java deleted file mode 100644 index 0d29c0ad2..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionHitBoxConfig.java +++ /dev/null @@ -1,124 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; - -import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider; -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.entity.furniture.Collider; -import net.momirealms.craftengine.core.entity.furniture.hitbox.*; -import net.momirealms.craftengine.core.entity.seat.SeatConfig; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.craftengine.core.world.collision.AABB; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class InteractionHitBoxConfig extends AbstractHitBoxConfig { - public static final Factory FACTORY = new Factory(); - public static final InteractionHitBoxConfig DEFAULT = new InteractionHitBoxConfig(new SeatConfig[0], new Vector3f(), new Vector3f(1,1,1), true, false, false, false); - - private final Vector3f size; - private final boolean responsive; - private final List cachedValues = new ArrayList<>(); - - public InteractionHitBoxConfig(SeatConfig[] seats, Vector3f position, Vector3f size, boolean responsive, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile) { - super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile); - this.size = size; - this.responsive = responsive; - InteractionEntityData.Height.addEntityDataIfNotDefaultValue(size.y, cachedValues); - InteractionEntityData.Width.addEntityDataIfNotDefaultValue(size.x, cachedValues); - InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(responsive, cachedValues); - } - - public boolean responsive() { - return this.responsive; - } - - public Vector3f size() { - return this.size; - } - - @Override - public Key type() { - return HitBoxTypes.INTERACTION; - } - - @Override - public void initPacketsAndColliders(int[] entityId, - WorldPosition position, - Quaternionf conjugated, - BiConsumer packets, - Consumer collider, - Consumer aabb) { - Vector3f offset = conjugated.transform(new Vector3f(position())); - double x = position.x(); - double y = position.y(); - double z = position.z(); - float yaw = position.yRot(); - Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityId[0], UUID.randomUUID(), vec3d.x, vec3d.y, vec3d.z, 0, yaw, - MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 - ), true); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true); - aabb.accept(new HitBoxPart(entityId[0], AABB.fromInteraction(vec3d, this.size.x, this.size.y), vec3d)); - if (blocksBuilding() || this.canBeHitByProjectile()) { - AABB ceAABB = AABB.fromInteraction(vec3d, this.size.x, this.size.y); - Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); - collider.accept(new BukkitCollider(position.world().serverWorld(), nmsAABB, x, y, z, this.canBeHitByProjectile(), false, this.blocksBuilding())); - } - } - - @Override - public void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer aabbs) { - if (blocksBuilding()) { - Vector3f offset = conjugated.transform(new Vector3f(position())); - AABB ceAABB = AABB.fromInteraction(new Vec3d(x + offset.x, y + offset.y, z - offset.z), this.size.x, this.size.y); - aabbs.accept(ceAABB); - } - } - - @Override - public int[] acquireEntityIds(Supplier entityIdSupplier) { - return new int[] {entityIdSupplier.get()}; - } - - public static class Factory implements HitBoxConfigFactory { - - @Override - public HitBoxConfig create(Map arguments) { - Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); - float width; - float height; - if (arguments.containsKey("scale")) { - String[] split = arguments.get("scale").toString().split(","); - width = Float.parseFloat(split[0]); - height = Float.parseFloat(split[1]); - } else { - width = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("width", 1), "width"); - height = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("height", 1), "height"); - } - boolean canUseOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", false), "can-use-item-on"); - boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive"); - boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile"); - boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); - return new InteractionHitBoxConfig( - SeatConfig.fromObj(arguments.get("seats")), - position, - new Vector3f(width, height, width), - interactive, canUseOn, blocksBuilding, canBeHitByProjectile - ); - } - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java deleted file mode 100644 index f24f79b10..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBoxConfig.java +++ /dev/null @@ -1,361 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; - -import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; -import net.momirealms.craftengine.bukkit.entity.data.ShulkerData; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider; -import net.momirealms.craftengine.bukkit.nms.FastNMS; -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.MEntityTypes; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; -import net.momirealms.craftengine.bukkit.util.DirectionUtils; -import net.momirealms.craftengine.core.entity.furniture.Collider; -import net.momirealms.craftengine.core.entity.furniture.hitbox.*; -import net.momirealms.craftengine.core.entity.seat.SeatConfig; -import net.momirealms.craftengine.core.util.*; -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.collision.AABB; -import org.joml.Quaternionf; -import org.joml.Vector3f; - -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class ShulkerHitBoxConfig extends AbstractHitBoxConfig { - public static final Factory FACTORY = new Factory(); - // 1.20.6+ - private final float scale; - private final byte peek; - private final boolean interactive; - private final boolean interactionEntity; - private final Direction direction; - private final List cachedShulkerValues = new ArrayList<>(); - private final DirectionalShulkerSpawner spawner; - private final AABBCreator aabbCreator; - - public ShulkerHitBoxConfig(SeatConfig[] seats, Vector3f position, Direction direction, float scale, byte peek, boolean interactionEntity, boolean interactive, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile) { - super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile); - this.direction = direction; - this.scale = scale; - this.peek = peek; - this.interactive = interactive; - this.interactionEntity = interactionEntity; - - ShulkerData.Peek.addEntityDataIfNotDefaultValue(peek, this.cachedShulkerValues); - ShulkerData.Color.addEntityDataIfNotDefaultValue((byte) 0, this.cachedShulkerValues); - ShulkerData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedShulkerValues); - ShulkerData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedShulkerValues); - ShulkerData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedShulkerValues); // NO AI - ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedShulkerValues); // Invisible - - float shulkerHeight = (getPhysicalPeek(peek * 0.01F) + 1) * scale; - List cachedInteractionValues = new ArrayList<>(); - if (this.direction == Direction.UP) { - InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues); - InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues); - InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues); - this.spawner = (entityIds, world, x, y, z, yaw, offset, packets, collider, aabb) -> { - collider.accept(this.createCollider(Direction.UP, world, offset, x, y, z, entityIds[1], aabb)); - if (interactionEntity) { - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw, - MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 - ), true); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true); - if (canUseOn) { - Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z); - aabb.accept(new HitBoxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d)); - } - } - }; - this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.UP, offset, x, y, z); - } else if (this.direction == Direction.DOWN) { - InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues); - InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues); - InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues); - this.spawner = (entityIds, world, x, y, z, yaw, offset, packets, collider, aabb) -> { - collider.accept(this.createCollider(Direction.DOWN, world, offset, x, y, z, entityIds[1], aabb)); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.of(ShulkerData.AttachFace.createEntityDataIfNotDefaultValue(CoreReflections.instance$Direction$UP))), false); - if (interactionEntity) { - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f - shulkerHeight + scale, z - offset.z, 0, yaw, - MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 - ), true); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true); - if (canUseOn) { - Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z); - aabb.accept(new HitBoxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d)); - } - } - }; - this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.DOWN, offset, x, y, z); - } else { - InteractionEntityData.Height.addEntityDataIfNotDefaultValue(scale + 0.01f, cachedInteractionValues); - InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues); - InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues); - this.spawner = (entityIds, world, x, y, z, yaw, offset, packets, collider, aabb) -> { - Direction shulkerAnchor = getOriginalDirection(direction, Direction.fromYaw(yaw)); - Direction shulkerDirection = shulkerAnchor.opposite(); - collider.accept(this.createCollider(shulkerDirection, world, offset, x, y, z, entityIds[1], aabb)); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.of(ShulkerData.AttachFace.createEntityDataIfNotDefaultValue(DirectionUtils.toNMSDirection(shulkerAnchor)))), false); - if (interactionEntity) { - // first interaction - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw, - MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 - ), true); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true); - // second interaction - double distance = shulkerHeight - scale; - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityIds[3], UUID.randomUUID(), x + offset.x + shulkerDirection.stepX() * distance, y + offset.y - 0.005f, z - offset.z + shulkerDirection.stepZ() * distance, 0, yaw, - MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 - ), true); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues)), true); - if (canUseOn) { - Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z); - Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance); - aabb.accept(new HitBoxPart(entityIds[2], AABB.fromInteraction(vec3d1, scale, scale), vec3d1)); - aabb.accept(new HitBoxPart(entityIds[3], AABB.fromInteraction(vec3d2, scale, scale), vec3d2)); - } - } - }; - this.aabbCreator = (x, y, z, yaw, offset) -> { - Direction shulkerAnchor = getOriginalDirection(direction, Direction.fromYaw(yaw)); - Direction shulkerDirection = shulkerAnchor.opposite(); - return createAABB(shulkerDirection, offset, x, y, z); - }; - } - } - - public Collider createCollider(Direction direction, World world, Vector3f offset, double x, double y, double z, int entityId, Consumer aabb) { - AABB ceAABB = createAABB(direction, offset, x, y, z); - Object level = world.serverWorld(); - Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); - aabb.accept(new HitBoxPart(entityId, ceAABB, new Vec3d(x, y, z))); - return new BukkitCollider(level, nmsAABB, x, y, z, this.canBeHitByProjectile(), true, this.blocksBuilding()); - } - - public AABB createAABB(Direction direction, Vector3f offset, double x, double y, double z) { - float peek = getPhysicalPeek(this.peek() * 0.01F); - double x1 = -this.scale * 0.5; - double y1 = 0.0; - double z1 = -this.scale * 0.5; - double x2 = this.scale * 0.5; - double y2 = this.scale; - double z2 = this.scale * 0.5; - - double dx = (double) direction.stepX() * peek * (double) this.scale; - if (dx > 0) { - x2 += dx; - } else if (dx < 0) { - x1 += dx; - } - double dy = (double) direction.stepY() * peek * (double) this.scale; - if (dy > 0) { - y2 += dy; - } else if (dy < 0) { - y1 += dy; - } - double dz = (double) direction.stepZ() * peek * (double) this.scale; - if (dz > 0) { - z2 += dz; - } else if (dz < 0) { - z1 += dz; - } - double minX = x + x1 + offset.x(); - double maxX = x + x2 + offset.x(); - double minY = y + y1 + offset.y(); - double maxY = y + y2 + offset.y(); - double minZ = z + z1 - offset.z(); - double maxZ = z + z2 - offset.z(); - return new AABB(minX, minY, minZ, maxX, maxY, maxZ); - } - - private static float getPhysicalPeek(float peek) { - return 0.5F - MiscUtils.sin((0.5F + peek) * 3.1415927F) * 0.5F; - } - - public boolean interactionEntity() { - return interactionEntity; - } - - public boolean interactive() { - return interactive; - } - - public Direction direction() { - return direction; - } - - public byte peek() { - return peek; - } - - public float scale() { - return scale; - } - - @Override - public Key type() { - return HitBoxTypes.SHULKER; - } - - @Override - public void initPacketsAndColliders(int[] entityIds, - WorldPosition position, - Quaternionf conjugated, - BiConsumer packets, - Consumer collider, - Consumer aabb) { - Vector3f offset = conjugated.transform(new Vector3f(position())); - try { - double x = position.x(); - double y = position.y(); - double z = position.z(); - float yaw = position.yRot(); - double originalY = y + offset.y; - double integerPart = Math.floor(originalY); - double fractionalPart = originalY - integerPart; - double processedY = (fractionalPart >= 0.5) ? integerPart + 1 : originalY; - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityIds[0], UUID.randomUUID(), x + offset.x, originalY, z - offset.z, 0, yaw, - MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 - ), false); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityIds[1], UUID.randomUUID(), x + offset.x, processedY, z - offset.z, 0, yaw, - MEntityTypes.SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0 - ), false); - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(this.cachedShulkerValues)), false); - // add passengers - packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityIds[0], entityIds[1]), false); - // fix some special occasions - if (originalY != processedY) { - double deltaY = originalY - processedY; - short ya = (short) (deltaY * 8192); - packets.accept(NetworkReflections.constructor$ClientboundMoveEntityPacket$Pos.newInstance( - entityIds[1], (short) 0, ya, (short) 0, true - ), false); - } - // set shulker scale - if (VersionHelper.isOrAbove1_20_5() && this.scale != 1) { - Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); - CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale); - packets.accept(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[1], Collections.singletonList(attributeInstance)), false); - } - this.spawner.accept(entityIds, position.world(), x, y, z, yaw, offset, packets, collider, aabb); - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Failed to construct shulker hitbox spawn packet", e); - } - } - - @Override - public void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer aabbs) { - Vector3f offset = conjugated.transform(new Vector3f(position())); - aabbs.accept(this.aabbCreator.create(x, y, z, yaw, offset)); - } - - @FunctionalInterface - interface DirectionalShulkerSpawner { - - void accept(int[] entityIds, - World world, - double x, - double y, - double z, - float yaw, - Vector3f offset, - BiConsumer packets, - Consumer collider, - Consumer aabb); - } - - @FunctionalInterface - interface AABBCreator { - - AABB create(double x, double y, double z, float yaw, Vector3f offset); - } - - @Override - public int[] acquireEntityIds(Supplier entityIdSupplier) { - if (this.interactionEntity) { - if (this.direction.stepY() != 0) { - // 展示实体 // 潜影贝 // 交互实体 - return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()}; - } else { - // 展示实体 // 潜影贝 // 交互实体1 // 交互实体2 - return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()}; - } - } else { - // 展示实体 // 潜影贝 - return new int[] {entityIdSupplier.get(), entityIdSupplier.get()}; - } - } - - public static class Factory implements HitBoxConfigFactory { - - @Override - public HitBoxConfig create(Map arguments) { - 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); - boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive"); - boolean interactionEntity = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interaction-entity", true), "interaction-entity"); - boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on"); - boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile"); - boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); - return new ShulkerHitBoxConfig( - SeatConfig.fromObj(arguments.get("seats")), - position, directionEnum, - scale, peek, interactionEntity, interactive, canUseItemOn, blocksBuilding, canBeHitByProjectile - ); - } - } - - public static Direction getOriginalDirection(Direction newDirection, Direction oldDirection) { - switch (newDirection) { - case NORTH -> { - return switch (oldDirection) { - case NORTH -> Direction.NORTH; - case SOUTH -> Direction.SOUTH; - case WEST -> Direction.EAST; - case EAST -> Direction.WEST; - default -> throw new IllegalStateException("Unexpected value: " + oldDirection); - }; - } - case SOUTH -> { - return switch (oldDirection) { - case SOUTH -> Direction.NORTH; - case WEST -> Direction.WEST; - case EAST -> Direction.EAST; - case NORTH -> Direction.SOUTH; - default -> throw new IllegalStateException("Unexpected value: " + oldDirection); - }; - } - case WEST -> { - return switch (oldDirection) { - case SOUTH -> Direction.EAST; - case WEST -> Direction.NORTH; - case EAST -> Direction.SOUTH; - case NORTH -> Direction.WEST; - default -> throw new IllegalStateException("Unexpected value: " + oldDirection); - }; - } - case EAST -> { - return switch (oldDirection) { - case SOUTH -> Direction.WEST; - case WEST -> Direction.SOUTH; - case EAST -> Direction.NORTH; - case NORTH -> Direction.EAST; - default -> throw new IllegalStateException("Unexpected value: " + oldDirection); - }; - } - default -> throw new IllegalStateException("Unexpected value: " + newDirection); - } - } -} 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 d312a801d..e141aa000 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 @@ -8,7 +8,7 @@ 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.element.BukkitFurnitureElementConfigs; -import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BukkitHitBoxTypes; +import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BukkitFurnitureHitboxTypes; import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager; import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeatManager; import net.momirealms.craftengine.bukkit.font.BukkitFontManager; @@ -161,7 +161,7 @@ public class BukkitCraftEngine extends CraftEngine { super.onPluginLoad(); BukkitBlockBehaviors.init(); BukkitItemBehaviors.init(); - BukkitHitBoxTypes.init(); + BukkitFurnitureHitboxTypes.init(); BukkitBlockEntityElementConfigs.init(); BukkitFurnitureElementConfigs.init(); // 初始化 onload 阶段的兼容性 @@ -371,6 +371,11 @@ public class BukkitCraftEngine extends CraftEngine { return (BukkitFontManager) fontManager; } + @Override + public BukkitWorldManager worldManager() { + return (BukkitWorldManager) worldManager; + } + @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void saveResource(String resourcePath) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java index e1d29f243..c07060832 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java @@ -1,17 +1,10 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; -import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; -import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.core.entity.furniture.AnchorType; -import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.FlagKeys; -import net.momirealms.craftengine.core.util.Key; -import org.bukkit.Location; -import org.bukkit.NamespacedKey; import org.bukkit.command.CommandSender; import org.checkerframework.checker.nullness.qual.NonNull; import org.incendo.cloud.Command; @@ -23,7 +16,6 @@ import org.incendo.cloud.parser.standard.EnumParser; import org.incendo.cloud.suggestion.Suggestion; import org.incendo.cloud.suggestion.SuggestionProvider; -import java.util.Optional; import java.util.concurrent.CompletableFuture; public class DebugSpawnFurnitureCommand extends BukkitCommandFeature { @@ -45,18 +37,19 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature { - NamespacedKey namespacedKey = context.get("id"); - Key id = KeyUtils.namespacedKey2Key(namespacedKey); - BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance(); - Optional optionalCustomFurniture = furnitureManager.furnitureById(id); - if (optionalCustomFurniture.isEmpty()) { - return; - } - Location location = context.get("location"); - FurnitureConfig customFurniture = optionalCustomFurniture.get(); - AnchorType anchorType = (AnchorType) context.optional("anchor-id").orElse(customFurniture.getAnyAnchorType()); - boolean playSound = context.flags().hasFlag("silent"); - CraftEngineFurniture.place(location, customFurniture, anchorType, playSound); + // fixme 指令 +// NamespacedKey namespacedKey = context.get("id"); +// Key id = KeyUtils.namespacedKey2Key(namespacedKey); +// BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance(); +// Optional optionalCustomFurniture = furnitureManager.furnitureById(id); +// if (optionalCustomFurniture.isEmpty()) { +// return; +// } +// Location location = context.get("location"); +// FurnitureConfig customFurniture = optionalCustomFurniture.get(); +// AnchorType anchorType = (AnchorType) context.optional("anchor-id").orElse(customFurniture.getAnyAnchorType()); +// boolean playSound = context.flags().hasFlag("silent"); +// CraftEngineFurniture.place(location, customFurniture, anchorType, playSound); }); } 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 92686686d..9ab146c37 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 @@ -59,8 +59,7 @@ import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; import net.momirealms.craftengine.core.advancement.network.AdvancementHolder; import net.momirealms.craftengine.core.advancement.network.AdvancementProgress; import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBox; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxPart; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.font.FontManager; @@ -1274,7 +1273,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes private static void handlePickItemFromEntityOnMainThread(Player player, BukkitFurniture furniture) throws Throwable { Key itemId = furniture.config().settings().itemId(); if (itemId == null) return; - pickItem(player, itemId, null, FastNMS.INSTANCE.method$CraftEntity$getHandle(furniture.baseEntity())); + pickItem(player, itemId, null, FastNMS.INSTANCE.method$CraftEntity$getHandle(furniture.getBukkitEntity())); } } @@ -3705,7 +3704,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; if (serverPlayer.isSpectatorMode()) return; Player platformPlayer = serverPlayer.platformPlayer(); - Location location = furniture.baseEntity().getLocation(); + Location location = furniture.location(); Runnable mainThreadTask; if (actionType == 1) { @@ -3722,11 +3721,15 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes mainThreadTask = () -> { // todo 冒险模式破坏工具白名单 - if (serverPlayer.isAdventureMode() || - !furniture.isValid()) return; + if (serverPlayer.isAdventureMode() || !furniture.isValid()) return; - // todo 重构家具时候注意,需要准备加载好的hitbox类,以获取hitbox坐标 - if (!serverPlayer.canInteractPoint(new Vec3d(location.getX(), location.getY(), location.getZ()), 16d)) { + // 先检查碰撞箱部分是否存在 + FurnitureHitBox hitBox = furniture.hitboxByEntityId(entityId); + if (hitBox == null) return; + Vec3d pos = hitBox.position(); + + // 检查玩家是否能破坏此点 + if (!serverPlayer.canInteractPoint(pos, 16d)) { return; } @@ -3782,34 +3785,31 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } // 先检查碰撞箱部分是否存在 - HitBoxPart hitBoxPart = furniture.hitBoxPartByEntityId(entityId); - if (hitBoxPart == null) return; - Vec3d pos = hitBoxPart.pos(); + FurnitureHitBox hitBox = furniture.hitboxByEntityId(entityId); + if (hitBox == null) return; + Vec3d pos = hitBox.position(); + // 检测距离 if (!serverPlayer.canInteractPoint(pos, 16d)) { return; } - // 检测 + + // 检测能否交互碰撞箱 Location eyeLocation = platformPlayer.getEyeLocation(); Vector direction = eyeLocation.getDirection(); Location endLocation = eyeLocation.clone(); endLocation.add(direction.multiply(serverPlayer.getCachedInteractionRange())); - Optional result = hitBoxPart.aabb().clip(LocationUtils.toVec3d(eyeLocation), LocationUtils.toVec3d(endLocation)); + Optional result = hitBox.aabb().clip(LocationUtils.toVec3d(eyeLocation), LocationUtils.toVec3d(endLocation)); if (result.isEmpty()) { return; } EntityHitResult hitResult = result.get(); Vec3d hitLocation = hitResult.hitLocation(); + // 获取正确的交互点 Location interactionPoint = new Location(platformPlayer.getWorld(), hitLocation.x, hitLocation.y, hitLocation.z); - - HitBox hitbox = furniture.hitBoxByEntityId(entityId); - if (hitbox == null) { - return; - } - // 触发事件 - FurnitureInteractEvent interactEvent = new FurnitureInteractEvent(serverPlayer.platformPlayer(), furniture, hand, interactionPoint, hitbox); + FurnitureInteractEvent interactEvent = new FurnitureInteractEvent(serverPlayer.platformPlayer(), furniture, hand, interactionPoint, hitBox); if (EventUtils.fireAndCheckCancel(interactEvent)) { return; } @@ -3831,7 +3831,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } // 必须从网络包层面处理,否则无法获取交互的具体实体 - if (serverPlayer.isSecondaryUseActive() && !itemInHand.isEmpty() && hitbox.config().canUseItemOn()) { + if (serverPlayer.isSecondaryUseActive() && !itemInHand.isEmpty() && hitBox.config().canUseItemOn()) { Optional> optionalCustomItem = itemInHand.getCustomItem(); if (optionalCustomItem.isPresent() && !optionalCustomItem.get().behaviors().isEmpty()) { for (ItemBehavior behavior : optionalCustomItem.get().behaviors()) { @@ -3851,7 +3851,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes ); } else { if (!serverPlayer.isSecondaryUseActive()) { - for (Seat seat : hitbox.seats()) { + for (Seat seat : hitBox.seats()) { if (!seat.isOccupied()) { if (seat.spawnSeat(serverPlayer, furniture.position())) { break; @@ -3978,11 +3978,14 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes int id = buf.readVarInt(); BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); if (furniture != null) { - user.entityPacketHandlers().put(id, new FurniturePacketHandler(furniture.fakeEntityIds())); - user.sendPacket(furniture.spawnPacket((Player) user.platformPlayer()), false); - if (Config.hideBaseEntity() && !furniture.hasExternalModel()) { - event.setCancelled(true); - } + furniture.show((BukkitServerPlayer) user); + event.setCancelled(true); + // fixme 外部模型 +// user.entityPacketHandlers().put(id, new FurniturePacketHandler(furniture.fakeEntityIds())); +// user.sendPacket(furniture.spawnPacket((Player) user.platformPlayer()), false); +// if (Config.hideBaseEntity() && !furniture.hasExternalModel()) { +// event.setCancelled(true); +// } } else { user.entityPacketHandlers().put(id, ItemDisplayPacketHandler.INSTANCE); } 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 c54cb0d55..98ef141a8 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 @@ -101,6 +101,10 @@ public class BukkitWorldManager implements WorldManager, Listener { injectChunkGenerator(ceWorld); for (Chunk chunk : world.getLoadedChunks()) { handleChunkLoad(ceWorld, chunk, false); + CEChunk loadedChunk = ceWorld.getChunkAtIfLoaded(chunk.getChunkKey()); + if (loadedChunk != null) { + loadedChunk.setEntitiesLoaded(true); + } } ceWorld.setTicking(true); } catch (Exception e) { @@ -154,6 +158,10 @@ public class BukkitWorldManager implements WorldManager, Listener { CEWorld ceWorld = this.worlds.get(uuid); for (Chunk chunk : world.getLoadedChunks()) { handleChunkLoad(ceWorld, chunk, true); + CEChunk loadedChunk = ceWorld.getChunkAtIfLoaded(chunk.getChunkKey()); + if (loadedChunk != null) { + loadedChunk.setEntitiesLoaded(true); + } } ceWorld.setTicking(true); } else { 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 index 2aba16476..c4959f0aa 100644 --- 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 @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.block.entity.render.element; import java.util.Map; @FunctionalInterface -public interface BlockEntityElementConfigFactory { +public interface BlockEntityElementConfigFactory { - BlockEntityElementConfig create(Map args); + BlockEntityElementConfig create(Map args); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java deleted file mode 100644 index 424174dd5..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntity.java +++ /dev/null @@ -1,96 +0,0 @@ -package net.momirealms.craftengine.core.entity; - -import net.momirealms.craftengine.core.world.CEWorld; -import net.momirealms.craftengine.core.world.Cullable; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.sparrow.nbt.CompoundTag; - -import java.util.UUID; - -public abstract class CustomEntity implements Cullable { - protected final CustomEntityType type; - protected final UUID uuid; - protected WorldPosition position; - protected boolean valid = true; - - protected CustomEntity(CustomEntityType type, UUID uuid, WorldPosition position) { - this.position = position; - this.type = type; - this.uuid = uuid; - } - - public CompoundTag saveAsTag() { - CompoundTag tag = new CompoundTag(); - this.saveId(tag); - this.savePos(tag); - this.saveCustomData(tag); - return tag; - } - - private void savePos(CompoundTag tag) { - tag.putDouble("x", this.position.x()); - tag.putDouble("y", this.position.y()); - tag.putDouble("z", this.position.z()); - tag.putFloat("x_rot", this.position.xRot()); - tag.putFloat("y_rot", this.position.yRot()); - } - - public boolean isValid() { - return this.valid; - } - - public UUID uuid() { - return uuid; - } - - public double x() { - return this.position.x(); - } - - public double y() { - return this.position.y(); - } - - public double z() { - return this.position.z(); - } - - public float yRot() { - return this.position.yRot(); - } - - public float xRot() { - return this.position.xRot(); - } - - public CustomEntityType entityType() { - return this.type; - } - - protected void saveCustomData(CompoundTag tag) { - } - - public void loadCustomData(CompoundTag tag) { - } - - private void saveId(CompoundTag tag) { - tag.putString("id", this.type.id().asString()); - } - - public void destroy() { - this.valid = false; - } - - public static UUID readUUID(CompoundTag tag) { - return tag.getUUID("uuid"); - } - - public static WorldPosition readPos(CEWorld world, CompoundTag tag) { - double x = tag.getDouble("x"); - double y = tag.getDouble("y"); - double z = tag.getDouble("z"); - float xRot = tag.getFloat("x_rot"); - float yRot = tag.getFloat("y_rot"); - return new WorldPosition(world.world, x, y, z, xRot, yRot); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java deleted file mode 100644 index a7cd7cb33..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityType.java +++ /dev/null @@ -1,14 +0,0 @@ -package net.momirealms.craftengine.core.entity; - -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.WorldPosition; - -import java.util.UUID; - -public record CustomEntityType(Key id, Factory factory) { - - public interface Factory { - - T create(UUID uuid, WorldPosition position); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java deleted file mode 100644 index 57e36fadc..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypeKeys.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.momirealms.craftengine.core.entity; - -import net.momirealms.craftengine.core.util.Key; - -public final class CustomEntityTypeKeys { - private CustomEntityTypeKeys() {} - - public static final Key FURNITURE = Key.of("craftengine:furniture"); - public static final Key INACTIVE = Key.of("craftengine:inactive"); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java deleted file mode 100644 index c5697d9c2..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomEntityTypes.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.momirealms.craftengine.core.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 CustomEntityTypes { - public static final CustomEntityType INACTIVE = register(CustomEntityTypeKeys.INACTIVE, InactiveCustomEntity::new); - - public static CustomEntityType register(Key id, CustomEntityType.Factory factory) { - CustomEntityType type = new CustomEntityType<>(id, factory); - ((WritableRegistry>) BuiltInRegistries.ENTITY_TYPE) - .register(ResourceKey.create(Registries.ENTITY_TYPE.location(), id), type); - return type; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java index 5c52db4e2..81ab02cf7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java @@ -11,6 +11,8 @@ import java.util.UUID; public interface Entity { Key type(); + boolean isValid(); + double x(); double y(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java deleted file mode 100644 index 9a4633d37..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/InactiveCustomEntity.java +++ /dev/null @@ -1,54 +0,0 @@ -package net.momirealms.craftengine.core.entity; - -import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.plugin.entityculling.CullingData; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.NBT; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; -import java.util.UUID; - -public class InactiveCustomEntity extends CustomEntity { - public static final CompoundTag INVALID_TAG = new CompoundTag(Map.of("type", NBT.createString(CustomEntityTypes.INACTIVE.id().asMinimalString()))); - private final CompoundTag data; - - public InactiveCustomEntity(UUID uuid, WorldPosition position) { - super(CustomEntityTypes.INACTIVE, uuid, position); - this.data = INVALID_TAG; - } - - public InactiveCustomEntity(UUID uuid, WorldPosition position, CompoundTag data) { - super(CustomEntityTypes.INACTIVE, uuid, position); - this.data = data; - } - - @Override - public CompoundTag saveAsTag() { - return this.data; - } - - @Override - public boolean isValid() { - // 不正常的数据不要存储 - return this.data != INVALID_TAG; - } - - @Override - public void destroy() { - } - - @Override - public void show(Player player) { - } - - @Override - public void hide(Player player) { - } - - @Override - public @Nullable CullingData cullingData() { - return null; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/data/EntityData.java b/core/src/main/java/net/momirealms/craftengine/core/entity/data/EntityData.java index 1351831e6..896473f0f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/data/EntityData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/data/EntityData.java @@ -12,14 +12,14 @@ public interface EntityData { Object entityDataAccessor(); - Object create(Object entityDataAccessor, Object value); + Object create(Object entityDataAccessor, T value); default Object createEntityDataIfNotDefaultValue(T value) { if (defaultValue().equals(value)) return null; return create(entityDataAccessor(), value); } - default Object createEntityData(Object value) { + default Object createEntityData(T value) { return create(entityDataAccessor(), value); } 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 dd67036f8..092e70ea7 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 @@ -2,8 +2,8 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxTypes; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxTypes; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; @@ -75,7 +75,7 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { this.byId.clear(); } - protected abstract HitBoxConfig defaultHitBox(); + protected abstract FurnitureHitBoxConfig defaultHitBox(); public class FurnitureParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] { "furniture" }; @@ -124,7 +124,7 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { String variantName = e0.getKey(); Map variantArguments = ResourceConfigUtils.getAsMap(e0.getValue(), variantName); Optional optionalLootSpawnOffset = Optional.ofNullable(variantArguments.get("loot-spawn-offset")).map(it -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset")); - List> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elements"), FurnitureElementConfigs::fromMap); + List> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elementConfigs"), FurnitureElementConfigs::fromMap); // fixme 外部模型不应该在这 Optional externalModel; @@ -136,14 +136,14 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { externalModel = Optional.empty(); } - List hitboxes = ResourceConfigUtils.parseConfigAsList(variantArguments.get("hitboxes"), HitBoxTypes::fromMap); + List> hitboxes = ResourceConfigUtils.parseConfigAsList(variantArguments.get("hitboxes"), FurnitureHitBoxTypes::fromMap); if (hitboxes.isEmpty() && externalModel.isEmpty()) { hitboxes = List.of(defaultHitBox()); } variants.put(variantName, new FurnitureVariant( elements.toArray(new FurnitureElementConfig[0]), - hitboxes.toArray(new HitBoxConfig[0]), + hitboxes.toArray(new FurnitureHitBoxConfig[0]), externalModel, optionalLootSpawnOffset )); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java index cefebe26b..b389f52f7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.core.entity.furniture; -@Deprecated(since = "0.0.66", forRemoval = true) +@Deprecated(since = "0.0.66") public enum AnchorType { GROUND(0), WALL(1), diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index 236a014b8..d50a23ea0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -1,65 +1,187 @@ package net.momirealms.craftengine.core.entity.furniture; -import net.momirealms.craftengine.core.entity.CustomEntity; -import net.momirealms.craftengine.core.entity.CustomEntityType; -import net.momirealms.craftengine.core.plugin.CraftEngine; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.entity.seat.Seat; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.QuaternionUtils; +import net.momirealms.craftengine.core.world.Cullable; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.sparrow.nbt.CompoundTag; -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Quaternionf; +import org.joml.Vector3f; -import java.util.Optional; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; -public abstract class Furniture extends CustomEntity { - protected FurnitureConfig config; - protected FurnitureDataAccessor dataAccessor; +public abstract class Furniture implements Cullable { + public final FurnitureConfig config; + public final FurnitureDataAccessor dataAccessor; + public final WeakReference metaDataEntity; - protected Key furnitureId; - protected CompoundTag inactiveFurnitureData; + protected FurnitureVariant currentVariant; + protected FurnitureElement[] elements; + protected Collider[] colliders; + protected FurnitureHitBox[] hitboxes; - protected Furniture(CustomEntityType type, UUID uuid, WorldPosition position) { - super(type, uuid, position); + protected Int2ObjectMap hitboxMap; + protected int[] entityIds; + + protected Furniture(Entity metaDataEntity, FurnitureDataAccessor data, FurnitureConfig config) { + this.config = config; + this.dataAccessor = data; + this.metaDataEntity = new WeakReference<>(metaDataEntity); + } + + public WeakReference metaDataEntity() { + return this.metaDataEntity; + } + + public FurnitureVariant getCurrentVariant() { + return currentVariant; + } + + public String getCurrentVariantName() { + return null; + } + + public void setVariant(FurnitureVariant variant) { + this.currentVariant = variant; + WorldPosition position = this.position(); + // 初始化家具元素 + FurnitureElementConfig[] elementConfigs = variant.elementConfigs(); + this.elements = new FurnitureElement[elementConfigs.length]; + for (int i = 0; i < elementConfigs.length; i++) { + this.elements[i] = elementConfigs[i].create(this); + } + // 初始化碰撞箱 + FurnitureHitBoxConfig[] furnitureHitBoxConfigs = variant.furnitureHitBoxConfigs(); + List colliders = new ArrayList<>(furnitureHitBoxConfigs.length); + this.hitboxes = new FurnitureHitBox[furnitureHitBoxConfigs.length]; + for (int i = 0; i < furnitureHitBoxConfigs.length; i++) { + FurnitureHitBox hitbox = furnitureHitBoxConfigs[i].create(this); + this.hitboxes[i] = hitbox; + for (int hitboxEntityId : hitbox.virtualEntityIds()) { + this.hitboxMap.put(hitboxEntityId, hitbox); + } + Collider collider = hitbox.collider(); + if (collider != null) { + colliders.add(collider); + } + } + this.colliders = colliders.toArray(new Collider[0]); + } + + @Nullable + public FurnitureHitBox hitboxByEntityId(int entityId) { + return this.hitboxMap.get(entityId); + } + + @Nullable + @Override + public CullingData cullingData() { + return this.config.cullingData(); + } + + public Key id() { + return this.config.id(); + } + + public int[] entityIds() { + return this.entityIds; + } + + public UUID uuid() { + Entity entity = this.metaDataEntity.get(); + if (entity == null) return null; + return entity.uuid(); } @Override - protected void saveCustomData(CompoundTag tag) { - tag.putString("id", this.config.id().asMinimalString()); - - } - - @Override - public void loadCustomData(CompoundTag tag) { - this.furnitureId = readFurnitureId(tag); - this.dataAccessor = new FurnitureDataAccessor(tag.getCompound("data")); - Optional furnitureConfig = CraftEngine.instance().furnitureManager().furnitureById(this.furnitureId); - if (furnitureConfig.isPresent()) { - this.config = furnitureConfig.get(); - } else { - this.inactiveFurnitureData = tag; + public void show(Player player) { + for (FurnitureElement element : this.elements) { + element.show(player); + } + for (FurnitureHitBox hitbox : this.hitboxes) { + hitbox.show(player); } } @Override - public CompoundTag saveAsTag() { - // 如果家具数据只是不活跃,则返回不活跃家具数据 - if (this.inactiveFurnitureData != null) { - return this.inactiveFurnitureData; + public void hide(Player player) { + for (FurnitureElement element : this.elements) { + element.hide(player); + } + for (FurnitureHitBox hitbox : this.hitboxes) { + hitbox.hide(player); } - return super.saveAsTag(); } - @NotNull + public abstract void addCollidersToWorld(); + + public void destroySeats() { + for (FurnitureHitBox hitbox : this.hitboxes) { + for (Seat seat : hitbox.seats()) { + seat.destroy(); + } + } + } + + public boolean isValid() { + Entity entity = this.metaDataEntity.get(); + if (entity == null) return false; + return entity.isValid(); + } + + public abstract void destroy(); + public FurnitureConfig config() { - return this.config; + return config; } - @NotNull public FurnitureDataAccessor dataAccessor() { - return this.dataAccessor; + return dataAccessor; } - protected final Key readFurnitureId(CompoundTag tag) { - return Key.of(tag.getString("id")); + public Collider[] colliders() { + return this.colliders; + } + + public WorldPosition position() { + Entity entity = this.metaDataEntity.get(); + if (entity == null) return null; + return entity.position(); + } + + public int entityId() { + Entity entity = this.metaDataEntity.get(); + if (entity == null) return -1; + return entity.entityID(); + } + + public Vec3d getRelativePosition(Vector3f position) { + return getRelativePosition(this.position(), position); + } + + public static Vec3d getRelativePosition(WorldPosition location, Vector3f position) { + Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - location.yRot()), 0f).conjugate(); + Vector3f offset = conjugated.transform(new Vector3f(position)); + return new Vec3d(location.x + offset.x, location.y + offset.y, location.z - offset.z); + } + + public World world() { + Entity entity = this.metaDataEntity.get(); + if (entity == null) return null; + return entity.world(); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java index c6f075e60..6e70376e6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java @@ -11,7 +11,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Optional; public interface FurnitureConfig { @@ -40,6 +42,31 @@ public interface FurnitureConfig { @NotNull FurnitureBehavior behavior(); + @NotNull + default FurnitureVariant getVariant(FurnitureDataAccessor accessor) { + Optional optionalVariant = accessor.variant(); + String variantName = null; + if (optionalVariant.isPresent()) { + variantName = optionalVariant.get(); + } else { + Optional optionalAnchorType = accessor.anchorType(); + if (optionalAnchorType.isPresent()) { + variantName = optionalAnchorType.get().name().toLowerCase(Locale.ROOT); + accessor.setVariant(variantName); + accessor.removeCustomData(FurnitureDataAccessor.ANCHOR_TYPE); + } + } + if (variantName == null) { + return anyVariant(); + } + FurnitureVariant variant = getVariant(variantName); + if (variant == null) { + return anyVariant(); + } + return variant; + + } + CullingData cullingData(); static Builder builder() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java index 946f201cd..056a8f47f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java @@ -1,13 +1,13 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import org.joml.Vector3f; import java.util.Optional; -public record FurnitureVariant(FurnitureElementConfig[] elements, - HitBoxConfig[] hitBoxConfigs, +public record FurnitureVariant(FurnitureElementConfig[] elementConfigs, + FurnitureHitBoxConfig[] furnitureHitBoxConfigs, Optional externalModel, Optional dropOffset) { } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java index c291a43be..31ab640f8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfig.java @@ -1,9 +1,9 @@ package net.momirealms.craftengine.core.entity.furniture.element; -import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.entity.furniture.Furniture; import org.jetbrains.annotations.NotNull; public interface FurnitureElementConfig { - E create(@NotNull WorldPosition position); + E create(@NotNull Furniture furniture); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java index 438124560..5172948f3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigFactory.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.entity.furniture.element; import java.util.Map; -public interface FurnitureElementConfigFactory { +public interface FurnitureElementConfigFactory { - FurnitureElementConfig create(Map args); + FurnitureElementConfig create(Map args); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractFurnitureHitBoxConfig.java similarity index 79% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBoxConfig.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractFurnitureHitBoxConfig.java index d9a5755f0..f977145e7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractFurnitureHitBoxConfig.java @@ -3,14 +3,14 @@ package net.momirealms.craftengine.core.entity.furniture.hitbox; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import org.joml.Vector3f; -public abstract class AbstractHitBoxConfig implements HitBoxConfig { +public abstract class AbstractFurnitureHitBoxConfig implements FurnitureHitBoxConfig { protected final SeatConfig[] seats; protected final Vector3f position; protected final boolean canUseItemOn; protected final boolean blocksBuilding; protected final boolean canBeHitByProjectile; - public AbstractHitBoxConfig(SeatConfig[] seats, Vector3f position, boolean canUseItemOn, boolean blocksBuilding, boolean canBeHitByProjectile) { + public AbstractFurnitureHitBoxConfig(SeatConfig[] seats, Vector3f position, boolean canUseItemOn, boolean blocksBuilding, boolean canBeHitByProjectile) { this.seats = seats; this.position = position; this.canUseItemOn = canUseItemOn; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java deleted file mode 100644 index 185fec9de..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractHitBox.java +++ /dev/null @@ -1,56 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture.hitbox; - -import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.seat.Seat; -import net.momirealms.craftengine.core.world.EntityHitResult; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.sparrow.nbt.CompoundTag; - -import java.util.Optional; - -public abstract class AbstractHitBox implements HitBox { - private final Furniture furniture; - private final HitBoxConfig config; - private final HitBoxPart[] parts; - private Seat[] seats; - - public AbstractHitBox(Furniture furniture, HitBoxConfig config, HitBoxPart[] parts) { - this.parts = parts; - this.config = config; - this.furniture = furniture; - } - - protected abstract void createSeats(HitBoxConfig config); - - @Override - public HitBoxPart[] parts() { - return this.parts; - } - - @Override - public HitBoxConfig config() { - return this.config; - } - - @Override - public Seat[] seats() { - return this.seats; - } - - @Override - public Optional clip(Vec3d min, Vec3d max) { - for (HitBoxPart hbe : this.parts) { - Optional result = hbe.aabb().clip(min, max); - if (result.isPresent()) { - return result; - } - } - return Optional.empty(); - } - - @Override - public void saveCustomData(CompoundTag data) { - data.putString("type", "furniture"); - data.putInt("entity_id", this.furniture.entityId()); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java new file mode 100644 index 000000000..7fbb4ca70 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java @@ -0,0 +1,29 @@ +package net.momirealms.craftengine.core.entity.furniture.hitbox; + +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.entity.seat.Seat; +import net.momirealms.craftengine.core.entity.seat.SeatOwner; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.collision.AABB; +import org.jetbrains.annotations.Nullable; + +public interface FurnitureHitBox extends SeatOwner { + + Seat[] seats(); + + AABB aabb(); + + Vec3d position(); + + @Nullable + Collider collider(); + + int[] virtualEntityIds(); + + void show(Player player); + + void hide(Player player); + + FurnitureHitBoxConfig config(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java similarity index 64% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfig.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java index f14f7a064..e1730c7f2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java @@ -1,12 +1,12 @@ package net.momirealms.craftengine.core.entity.furniture.hitbox; +import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.seat.SeatConfig; -import net.momirealms.craftengine.core.util.Key; import org.joml.Vector3f; -public interface HitBoxConfig { +public interface FurnitureHitBoxConfig { - Key type(); + H create(Furniture furniture); SeatConfig[] seats(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfigFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfigFactory.java new file mode 100644 index 000000000..4d867d1ec --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfigFactory.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.entity.furniture.hitbox; + +import java.util.Map; + +public interface FurnitureHitBoxConfigFactory { + + FurnitureHitBoxConfig create(Map arguments); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxTypes.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxTypes.java similarity index 65% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxTypes.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxTypes.java index 853fa8318..d4f002a9c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxTypes.java @@ -10,21 +10,21 @@ import net.momirealms.craftengine.core.util.ResourceKey; import java.util.Map; import java.util.Optional; -public class HitBoxTypes { +public class FurnitureHitBoxTypes { public static final Key INTERACTION = Key.of("minecraft:interaction"); public static final Key SHULKER = Key.of("minecraft:shulker"); public static final Key HAPPY_GHAST = Key.of("minecraft:happy_ghast"); public static final Key VIRTUAL = Key.of("minecraft:virtual"); public static final Key CUSTOM = Key.of("minecraft:custom"); - public static void register(Key key, HitBoxConfigFactory factory) { - ((WritableRegistry) BuiltInRegistries.HITBOX_FACTORY) - .register(ResourceKey.create(Registries.HITBOX_FACTORY.location(), key), factory); + public static void register(Key key, FurnitureHitBoxConfigFactory factory) { + ((WritableRegistry) BuiltInRegistries.FURNITURE_HITBOX_TYPE) + .register(ResourceKey.create(Registries.FURNITURE_HITBOX_TYPE.location(), key), factory); } - public static HitBoxConfig fromMap(Map arguments) { - Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(Key::of).orElse(HitBoxTypes.INTERACTION); - HitBoxConfigFactory factory = BuiltInRegistries.HITBOX_FACTORY.getValue(type); + public static FurnitureHitBoxConfig fromMap(Map arguments) { + Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(Key::of).orElse(FurnitureHitBoxTypes.INTERACTION); + FurnitureHitBoxConfigFactory factory = BuiltInRegistries.FURNITURE_HITBOX_TYPE.getValue(type); if (factory == null) { throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.invalid_type", type.toString()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBox.java deleted file mode 100644 index 32d31c3e1..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBox.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture.hitbox; - -import net.momirealms.craftengine.core.entity.seat.Seat; -import net.momirealms.craftengine.core.entity.seat.SeatOwner; -import net.momirealms.craftengine.core.world.EntityHitResult; -import net.momirealms.craftengine.core.world.Vec3d; - -import java.util.Optional; - -public interface HitBox extends SeatOwner { - - Seat[] seats(); - - Optional clip(Vec3d min, Vec3d max); - - HitBoxPart[] parts(); - - HitBoxConfig config(); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfigFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfigFactory.java deleted file mode 100644 index df9a2b101..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxConfigFactory.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture.hitbox; - -import java.util.Map; - -public interface HitBoxConfigFactory { - - HitBoxConfig create(Map arguments); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxPart.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxPart.java deleted file mode 100644 index fe5619281..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/HitBoxPart.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.momirealms.craftengine.core.entity.furniture.hitbox; - -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.collision.AABB; - -public record HitBoxPart(int entityId, AABB aabb, Vec3d pos) { -} 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 689374c51..ae13804e6 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 @@ -224,4 +224,9 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { } public abstract WorldPosition eyePosition(); + + @Override + public boolean isValid() { + return this.isOnline(); + } } 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 1aa740527..1c9f0bd85 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 @@ -90,7 +90,7 @@ public abstract class AbstractPackManager implements PackManager { public static final String NEW_TRIM_MATERIAL = "custom"; public static final Set ALLOWED_VANILLA_EQUIPMENT = Set.of("chainmail", "diamond", "gold", "iron", "netherite"); - public static final Set ALLOWED_MODEL_TAGS = Set.of("parent", "ambientocclusion", "display", "textures", "elements", "gui_light", "overrides"); + public static final Set ALLOWED_MODEL_TAGS = Set.of("parent", "ambientocclusion", "display", "textures", "elementConfigs", "gui_light", "overrides"); private static final byte[] EMPTY_1X1_IMAGE; private static final byte[] EMPTY_EQUIPMENT_IMAGE; @@ -2136,7 +2136,7 @@ public abstract class AbstractPackManager implements PackManager { .resolve("empty.png"); try { Files.createDirectories(modelPath.getParent()); - Files.writeString(modelPath, "{\"textures\":{\"particle\":\"block/empty\"},\"elements\":[{\"from\":[0,0,0],\"to\":[0,0,0],\"color\":0,\"faces\":{\"north\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"east\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"south\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"west\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"up\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"down\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"}}}]}"); + Files.writeString(modelPath, "{\"textures\":{\"particle\":\"block/empty\"},\"elementConfigs\":[{\"from\":[0,0,0],\"to\":[0,0,0],\"color\":0,\"faces\":{\"north\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"east\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"south\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"west\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"up\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"down\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"}}}]}"); } catch (IOException e) { this.plugin.logger().severe("Error writing empty block model", e); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java index 8c4b0e176..942a704a4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java @@ -11,7 +11,7 @@ public class BBModel { private String model_identifier; @SerializedName("visible_box") private int[] visible_box; - @SerializedName("elements") + @SerializedName("elementConfigs") private Element[] elements; @SerializedName("outliner") private OutLiner[] outliner; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java index 796358ef3..518b061cd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java @@ -41,12 +41,12 @@ public class RemoveFurnitureFunction extends AbstractCondit WorldPosition position = furniture.position(); World world = position.world(); furniture.destroy(); - LootTable lootTable = furniture.config().lootTable(); + LootTable lootTable = furniture.config.lootTable(); if (dropLoot && lootTable != null) { ContextHolder.Builder builder = ContextHolder.builder() .withParameter(DirectContextParameters.POSITION, position) .withParameter(DirectContextParameters.FURNITURE, furniture) - .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.dataAccessor().item().orElse(null)); + .withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.dataAccessor.item().orElse(null)); Optional optionalPlayer = ctx.getOptionalParameter(DirectContextParameters.PLAYER); Player player = optionalPlayer.orElse(null); if (player != null) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java index 154c66687..20dee5084 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java @@ -71,7 +71,8 @@ public class ReplaceFurnitureFunction extends AbstractCondi RemoveFurnitureFunction.removeFurniture(ctx, oldFurniture, dropLoot, playSound); // Place the new furniture - SpawnFurnitureFunction.spawnFurniture(this.newFurnitureId, newPosition, this.anchorType, this.playSound); + // fixme function +// SpawnFurnitureFunction.spawnFurniture(this.newFurnitureId, newPosition, this.anchorType, this.playSound); } } 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 2191c4da9..376550f22 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 @@ -3,7 +3,6 @@ package net.momirealms.craftengine.core.plugin.context.parameter; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.entity.Entity; -import net.momirealms.craftengine.core.entity.furniture.AnchorType; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.player.GameMode; import net.momirealms.craftengine.core.entity.player.InteractionHand; @@ -55,7 +54,7 @@ public final class DirectContextParameters { public static final ContextKey ID = ContextKey.direct("id"); public static final ContextKey CUSTOM_MODEL_DATA = ContextKey.direct("custom_model_data"); public static final ContextKey FURNITURE = ContextKey.direct("furniture"); - public static final ContextKey ANCHOR_TYPE = ContextKey.direct("anchor_type"); + public static final ContextKey VARIANT = ContextKey.direct("variant"); public static final ContextKey HAND = ContextKey.direct("hand"); public static final ContextKey EVENT = ContextKey.direct("event"); public static final ContextKey IS_SNEAKING = ContextKey.direct("is_sneaking"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java index dc4774853..dcf69a5e4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java @@ -12,9 +12,9 @@ import java.util.function.Function; public class FurnitureParameterProvider implements ChainParameterProvider { private static final Map, Function> CONTEXT_FUNCTIONS = new HashMap<>(); static { - CONTEXT_FUNCTIONS.put(DirectContextParameters.ID, Furniture::id); + CONTEXT_FUNCTIONS.put(DirectContextParameters.ID, f -> f.config().id()); CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, Furniture::uuid); - CONTEXT_FUNCTIONS.put(DirectContextParameters.ANCHOR_TYPE, Furniture::anchorType); + CONTEXT_FUNCTIONS.put(DirectContextParameters.VARIANT, Furniture::getCurrentVariantName); CONTEXT_FUNCTIONS.put(DirectContextParameters.X, furniture -> furniture.position().x()); CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, furniture -> furniture.position().y()); CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, furniture -> furniture.position().z()); 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 8cab5409b..62bf71645 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 @@ -5,9 +5,8 @@ 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.CustomEntityType; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.equipment.EquipmentFactory; @@ -78,7 +77,6 @@ public class BuiltInRegistries { 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); @@ -93,8 +91,8 @@ public class BuiltInRegistries { public static final Registry> BLOCK_ENTITY_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_TYPE, 64); public static final Registry BLOCK_ENTITY_ELEMENT_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_ELEMENT_TYPE, 16); public static final Registry CRAFT_REMAINDER_FACTORY = createConstantBoundRegistry(Registries.CRAFT_REMAINDER_FACTORY, 16); - public static final Registry> ENTITY_TYPE = createConstantBoundRegistry(Registries.ENTITY_TYPE, 32); public static final Registry FURNITURE_ELEMENT_TYPE = createConstantBoundRegistry(Registries.FURNITURE_ELEMENT_TYPE, 16); + public static final Registry FURNITURE_HITBOX_TYPE = createConstantBoundRegistry(Registries.FURNITURE_HITBOX_TYPE, 16); private static Registry createConstantBoundRegistry(ResourceKey> key, int expectedSize) { return new ConstantBoundRegistry<>(key, expectedSize); 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 5258d2e4b..9b728d316 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 @@ -5,9 +5,8 @@ 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.CustomEntityType; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; -import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.equipment.EquipmentFactory; @@ -80,7 +79,6 @@ public class Registries { public static final ResourceKey>> PATH_MATCHER_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("path_matcher_factory")); public static final ResourceKey> RESOLUTION_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("resolution_factory")); public static final ResourceKey> SMITHING_RESULT_PROCESSOR_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("smithing_result_processor_factory")); - public static final ResourceKey> HITBOX_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("hitbox_factory")); public static final ResourceKey> RESOURCE_PACK_HOST_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("resource_pack_host_factory")); public static final ResourceKey>> EVENT_FUNCTION_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("event_function_factory")); public static final ResourceKey>> EVENT_CONDITION_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("event_condition_factory")); @@ -95,6 +93,6 @@ public class Registries { 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")); public static final ResourceKey> CRAFT_REMAINDER_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("craft_remainder_factory")); - public static final ResourceKey>> ENTITY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("entity_type")); public static final ResourceKey> FURNITURE_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_element_type")); + public static final ResourceKey> FURNITURE_HITBOX_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_hitbox_type")); } 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 index 4ff57411a..639bd31b2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java @@ -54,7 +54,7 @@ public final class BlockEntityTickersList extends ObjectArrayList because we want to avoid autoboxing when using contains final int requiredMatches = c.size(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/WorldPosition.java b/core/src/main/java/net/momirealms/craftengine/core/world/WorldPosition.java index 868538ea6..fb0b9dd02 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/WorldPosition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/WorldPosition.java @@ -1,12 +1,14 @@ package net.momirealms.craftengine.core.world; +import java.util.Objects; + public class WorldPosition implements Position { - private final World world; - private final double x; - private final double y; - private final double z; - private final float xRot; - private final float yRot; + public final World world; + public final double x; + public final double y; + public final double z; + public final float xRot; + public final float yRot; public WorldPosition(World world, Position position) { this.x = position.x(); @@ -70,4 +72,28 @@ public class WorldPosition implements Position { public float yRot() { return yRot; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WorldPosition that = (WorldPosition) o; + return Double.compare(that.x, this.x) == 0 && + Double.compare(that.y, this.y) == 0 && + Double.compare(that.z, this.z) == 0 && + Float.compare(that.xRot, this.xRot) == 0 && + Float.compare(that.yRot, this.yRot) == 0 && + Objects.equals(this.world, that.world); + } + + @Override + public int hashCode() { + int result = Objects.hashCode(this.world); + result = 31 * result + Double.hashCode(this.x); + result = 31 * result + Double.hashCode(this.y); + result = 31 * result + Double.hashCode(this.z); + result = 31 * result + Float.floatToIntBits(this.xRot); + result = 31 * result + Float.floatToIntBits(this.yRot); + return result; + } } 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 0aee4a4a1..d278ed0cd 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 @@ -10,7 +10,6 @@ import net.momirealms.craftengine.core.block.entity.render.DynamicBlockEntityRen 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.CustomEntity; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.entityculling.CullingData; @@ -19,7 +18,6 @@ import net.momirealms.craftengine.core.world.*; import net.momirealms.craftengine.core.world.chunk.client.VirtualCullableObject; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntityRendererSerializer; import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer; -import net.momirealms.craftengine.core.world.chunk.serialization.DefaultEntitySerializer; import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -28,11 +26,11 @@ import java.util.*; import java.util.concurrent.locks.ReentrantReadWriteLock; public class CEChunk { + private static final int DEFAULT_MAP_SIZE = 8; public final CEWorld world; public final ChunkPos chunkPos; public final CESection[] sections; public final WorldHeight worldHeightAccessor; - private final Map entities; // 从区域线程上访问,安全 private final Map blockEntities; // 从区域线程上访问,安全 private final Map tickingSyncBlockEntitiesByPos; // 从区域线程上访问,安全 private final Map tickingAsyncBlockEntitiesByPos; // 从区域线程上访问,安全 @@ -42,18 +40,18 @@ public class CEChunk { private volatile boolean dirty; private volatile boolean loaded; private volatile boolean activated; + private boolean isEntitiesLoaded; 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 Object2ObjectOpenHashMap<>(10, 0.5f); - this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.entities = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.blockEntities = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); + this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); + this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); + this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); + this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); this.fillEmptySection(); } @@ -61,9 +59,9 @@ public class CEChunk { this.world = world; this.chunkPos = chunkPos; this.worldHeightAccessor = world.worldHeight(); - this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); + this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); + this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); int sectionCount = this.worldHeightAccessor.getSectionsCount(); this.sections = new CESection[sectionCount]; if (sections != null) { @@ -76,35 +74,23 @@ public class CEChunk { } this.fillEmptySection(); if (blockEntitiesTag != null) { - this.blockEntities = new Object2ObjectOpenHashMap<>(Math.max(blockEntitiesTag.size(), 10), 0.5f); + this.blockEntities = new Object2ObjectOpenHashMap<>(Math.max(blockEntitiesTag.size(), DEFAULT_MAP_SIZE), 0.5f); List blockEntities = DefaultBlockEntitySerializer.deserialize(this, blockEntitiesTag); for (BlockEntity blockEntity : blockEntities) { this.setBlockEntity(blockEntity); } } else { - this.blockEntities = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.blockEntities = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); } if (blockEntityRenders != null) { - this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(Math.max(blockEntityRenders.size(), 10), 0.5f); + this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(Math.max(blockEntityRenders.size(), DEFAULT_MAP_SIZE), 0.5f); List blockEntityRendererPoses = DefaultBlockEntityRendererSerializer.deserialize(this.chunkPos, blockEntityRenders); for (BlockPos pos : blockEntityRendererPoses) { this.addConstantBlockEntityRenderer(pos); } } else { - this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(DEFAULT_MAP_SIZE, 0.5f); } - if (entities != null) { - this.entities = new Object2ObjectOpenHashMap<>(Math.max(entities.size(), 10), 0.5f); - for (CustomEntity entity : DefaultEntitySerializer.deserialize(world, entities)) { - this.entities.put(entity.uuid(), entity); - } - } else { - this.entities = new Object2ObjectOpenHashMap<>(10, 0.5f); - } - } - - public Map entities() { - return Collections.unmodifiableMap(this.entities); } public void spawnBlockEntities(Player player) { @@ -680,6 +666,14 @@ public class CEChunk { return this.sections; } + public boolean isEntitiesLoaded() { + return this.isEntitiesLoaded; + } + + public void setEntitiesLoaded(boolean entitiesLoaded) { + this.isEntitiesLoaded = entitiesLoaded; + } + public boolean isLoaded() { return this.loaded; } @@ -694,5 +688,6 @@ public class CEChunk { if (!this.loaded) return; this.world.removeLoadedChunk(this); this.loaded = false; + this.isEntitiesLoaded = false; } } 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 e98376e7e..030d8fc3e 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 @@ -1,6 +1,5 @@ package net.momirealms.craftengine.core.world.chunk.serialization; -import net.momirealms.craftengine.core.entity.CustomEntity; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.chunk.CEChunk; @@ -10,9 +9,6 @@ import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Map; -import java.util.UUID; - public final class DefaultChunkSerializer { private DefaultChunkSerializer() {} @@ -40,10 +36,6 @@ public final class DefaultChunkSerializer { if (!blockEntityRenders.isEmpty()) { chunkNbt.put("block_entity_renderers", blockEntityRenders); } - ListTag listTag = new ListTag(); - Map entities = chunk.entities(); - - return chunkNbt; } @@ -63,7 +55,6 @@ public final class DefaultChunkSerializer { } ListTag blockEntities = chunkNbt.getList("block_entities"); ListTag blockEntityRenders = chunkNbt.getList("block_entity_renderers"); - ListTag entities = chunkNbt.getList("entities"); - return new CEChunk(world, pos, sectionArray, blockEntities, blockEntityRenders, entities); + return new CEChunk(world, pos, sectionArray, blockEntities, blockEntityRenders, null); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java deleted file mode 100644 index df24bb530..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultEntitySerializer.java +++ /dev/null @@ -1,55 +0,0 @@ -package net.momirealms.craftengine.core.world.chunk.serialization; - -import net.momirealms.craftengine.core.entity.CustomEntity; -import net.momirealms.craftengine.core.entity.CustomEntityType; -import net.momirealms.craftengine.core.entity.InactiveCustomEntity; -import net.momirealms.craftengine.core.registry.BuiltInRegistries; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.CEWorld; -import net.momirealms.craftengine.core.world.WorldPosition; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.ListTag; -import net.momirealms.sparrow.nbt.Tag; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.UUID; - -public class DefaultEntitySerializer { - - public static ListTag serialize(@NotNull Collection entity) { - ListTag entities = new ListTag(); - for (CustomEntity customEntity : entity) { - if (customEntity.isValid()) { - entities.add(customEntity.saveAsTag()); - } - } - return entities; - } - - public static List deserialize(CEWorld world, ListTag entitiesTag) { - List entities = new ArrayList<>(entitiesTag.size()); - for (Tag tag : entitiesTag) { - if (tag instanceof CompoundTag entityTag) { - WorldPosition worldPosition = CustomEntity.readPos(world, entityTag); - UUID uuid = CustomEntity.readUUID(entityTag); - Key type = Key.of(entityTag.getString("type")); - CustomEntityType entityType = BuiltInRegistries.ENTITY_TYPE.getValue(type); - if (entityType == null) { - InactiveCustomEntity entity = new InactiveCustomEntity(uuid, worldPosition, entityTag); - entities.add(entity); - } else { - CustomEntity entity = entityType.factory().create(uuid, worldPosition); - entity.loadCustomData(entityTag); - // 加载时无效则直接放弃 - if (entity.isValid()) { - entities.add(entity); - } - } - } - } - return entities; - } -} From c5bc8d8e3b014c809dfa70d0681705e81f282094 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Tue, 2 Dec 2025 21:47:47 +0800 Subject: [PATCH 067/135] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/translations/en.yml | 1 + .../src/main/resources/translations/zh_cn.yml | 1 + .../recipe/CustomSmithingTransformRecipe.java | 45 ++++++++++++++++--- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index fce2f7ea7..b53305b6e 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -161,6 +161,7 @@ warning.config.recipe.smithing_transform.post_processor.missing_type: "I warning.config.recipe.smithing_transform.post_processor.invalid_type: "Issue found in file - The smithing transform recipe '' is using an invalid post processor type ''." warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "Issue found in file - The smithing transform recipe '' is missing the required argument 'components' for post-processors 'keep_components'." warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "Issue found in file - The smithing transform recipe '' is missing the required argument 'tags' for post-processors 'keep_tags'." +warning.config.recipe.smithing_transform.post_processor.keep_custom_data.missing_paths: "Issue found in file - The smithing transform recipe '' is missing the required argument 'paths' for post-processors 'keep_custom_data'." warning.config.recipe.smithing_transform.missing_base: "Issue found in file - The smithing transform recipe '' is missing the required 'base' argument." warning.config.recipe.smithing_trim.missing_base: "Issue found in file - The smithing trim recipe '' is missing the required 'base' argument." warning.config.recipe.smithing_trim.missing_template_type: "Issue found in file - The smithing trim recipe '' is missing the required 'template-type' argument." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index da06eeff2..bbada1743 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -159,6 +159,7 @@ warning.config.recipe.smithing_transform.post_processor.missing_type: " warning.config.recipe.smithing_transform.post_processor.invalid_type: "在文件 发现问题 - 锻造升级配方 '' 使用了无效的后处理器类型 ''" warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "在文件 发现问题 - 锻造升级配方 '' 的 'keep_components' 后处理器缺少必需的 'components' 参数" warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "在文件 发现问题 - 锻造升级配方 '' 的 'keep_tags' 后处理器缺少必需的 'tags' 参数" +warning.config.recipe.smithing_transform.post_processor.keep_custom_data.missing_paths: "在文件 发现问题 - 锻造升级配方 '' 的 'keep_custom_data' 后处理器缺少必需的 'paths' 参数" warning.config.recipe.smithing_transform.missing_base: "在文件 发现问题 - 锻造升级配方 '' 缺少必需的 'base' 参数" warning.config.recipe.smithing_trim.missing_base: "在文件 发现问题 - 锻造纹饰配方 '' 缺少必需的 'base' 参数" warning.config.recipe.smithing_trim.missing_template_type: "在文件 发现问题 - 锻造纹饰配方 '' 缺少必需的 'template-type' 参数" 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 cad232493..87730aa0e 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 @@ -155,7 +155,6 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip private T createSmithingResult(Item base, T result) { Item wrappedResult = (Item) CraftEngine.instance().itemManager().wrap(result); Item finalResult = wrappedResult; - Optional customId = finalResult.customId(); // 修复在保留custom_data组件的时候意外保留前物品的id if (this.mergeComponents) { finalResult = base.mergeCopy(wrappedResult); } @@ -164,9 +163,6 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip processor.accept(base, wrappedResult, finalResult); } } - if (customId.isPresent()) { - finalResult.customId(customId.get()); - } return finalResult.getItem(); } @@ -233,10 +229,12 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip public static final Key KEEP_COMPONENTS = Key.of("craftengine:keep_components"); public static final Key KEEP_TAGS = Key.of("craftengine:keep_tags"); public static final Key MERGE_ENCHANTMENTS = Key.of("craftengine:merge_enchantments"); + public static final Key KEEP_CUSTOM_DATA = Key.of("craftengine:keep_custom_data"); static { if (VersionHelper.isOrAbove1_20_5()) { register(KEEP_COMPONENTS, KeepComponents.FACTORY); + register(KEEP_CUSTOM_DATA, KeepCustomData.FACTORY); } else { register(KEEP_TAGS, KeepTags.FACTORY); } @@ -316,6 +314,42 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip } } + public static class KeepCustomData implements ItemDataProcessor { + public static final Factory FACTORY = new Factory(); + private final List paths; + + public KeepCustomData(List data) { + this.paths = data; + } + + @Override + public void accept(Item item1, Item item2, Item item3) { + for (String[] path : this.paths) { + Object dataObj = item1.getJavaTag((Object[]) path); + if (dataObj != null) { + item3.setTag(dataObj, (Object[]) path); + } + } + } + + @Override + public Key type() { + return ItemDataProcessors.KEEP_CUSTOM_DATA; + } + + public static class Factory implements ProcessorFactory { + + @Override + public ItemDataProcessor create(Map arguments) { + List paths = MiscUtils.getAsStringList(ResourceConfigUtils.requireNonNullOrThrow( + arguments.get("paths"), + "warning.config.recipe.smithing_transform.post_processor.keep_custom_data.missing_paths") + ); + return new KeepCustomData(paths.stream().map(it -> it.split("\\.")).toList()); + } + } + } + public static class KeepComponents implements ItemDataProcessor { public static final Factory FACTORY = new Factory(); private final List components; @@ -340,6 +374,7 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip } public static class Factory implements ProcessorFactory { + private static final Key CUSTOM_DATA = Key.of("minecraft", "custom_data"); @Override public ItemDataProcessor create(Map arguments) { @@ -348,7 +383,7 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip throw new LocalizedResourceConfigException("warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components"); } List components = MiscUtils.getAsStringList(componentsObj); - return new KeepComponents(components.stream().map(Key::of).toList()); + return new KeepComponents(components.stream().map(Key::of).filter(it -> !CUSTOM_DATA.equals(it)).toList()); } } } From 20e1a42f6256292fe50e09768e241cad441e64ca Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Tue, 2 Dec 2025 23:19:33 +0800 Subject: [PATCH 068/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=AE=80=E5=8D=95?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E5=AE=B9=E5=99=A8=E5=85=B3=E4=B8=8D=E4=B8=8A?= =?UTF-8?q?=E9=97=A8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/block/entity/SimpleStorageBlockEntity.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 index 625818617..5649bccc4 100644 --- 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 @@ -173,7 +173,9 @@ public class SimpleStorageBlockEntity extends BlockEntity { 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(); + SimpleStorageBlockBehavior behavior = state.behavior().getAs(SimpleStorageBlockBehavior.class).orElse(null); + if (behavior == null) return; + Property property = behavior.openProperty(); if (property == null) return; super.world.world().setBlockState(this.pos.x(), this.pos.y(), this.pos.z(), state.with(property, open), UpdateOption.UPDATE_ALL.flags()); } From d2bb9103bd0583eb311641552aff8cf74550c4b6 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 3 Dec 2025 01:30:29 +0800 Subject: [PATCH 069/135] =?UTF-8?q?=E5=AE=B6=E5=85=B7=E9=87=8D=E6=9E=84par?= =?UTF-8?q?t3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/api/CraftEngineFurniture.java | 12 +- .../api/event/FurnitureAttemptPlaceEvent.java | 19 +- .../entity/furniture/BukkitFurniture.java | 1 - .../furniture/BukkitFurnitureManager.java | 109 ++++---- .../entity/furniture/ItemColorSource.java | 6 - .../element/ItemDisplayFurnitureElement.java | 43 +++- .../ItemDisplayFurnitureElementConfig.java | 114 +++++---- .../hitbox/AbstractFurnitureHitBox.java | 6 + .../hitbox/InteractionFurnitureHitbox.java | 38 +-- .../InteractionFurnitureHitboxConfig.java | 26 +- .../item/behavior/FurnitureItemBehavior.java | 239 +++++++++--------- .../plugin/network/BukkitNetworkManager.java | 41 +-- .../handler/FurniturePacketHandler.java | 23 +- .../plugin/user/BukkitServerPlayer.java | 96 ++++--- .../furniture/AbstractFurnitureManager.java | 11 +- .../core/entity/furniture/Furniture.java | 64 +++-- .../furniture/FurnitureColorSource.java | 6 + .../entity/furniture/FurnitureConfig.java | 4 - .../entity/furniture/FurnitureConfigImpl.java | 17 +- .../furniture/FurnitureDataAccessor.java | 14 +- .../entity/furniture/FurnitureManager.java | 11 +- .../entity/furniture/FurnitureVariant.java | 8 +- .../furniture/element/FurnitureElement.java | 6 + .../hitbox/AbstractFurnitureHitBoxConfig.java | 6 +- .../furniture/hitbox/FurnitureHitBox.java | 29 ++- .../hitbox/FurnitureHitBoxConfig.java | 6 + .../core/entity/player/Player.java | 7 + .../parameter/FurnitureParameterProvider.java | 2 +- .../plugin/network/EntityPacketHandler.java | 2 +- .../core/util/ResourceConfigUtils.java | 2 + 30 files changed, 580 insertions(+), 388 deletions(-) delete mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureColorSource.java 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 39ae861f4..5d5c4532f 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 @@ -185,7 +185,7 @@ public final class CraftEngineFurniture { */ @Nullable public static BukkitFurniture getLoadedFurnitureByBaseEntity(@NotNull Entity baseEntity) { - return BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(baseEntity.getEntityId()); + return BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(baseEntity.getEntityId()); } /** @@ -199,7 +199,7 @@ public final class CraftEngineFurniture { if (isSeat(seat)) { CompoundTag seatExtraData = BukkitSeatManager.instance().getSeatExtraData(seat); int entityId = seatExtraData.getInt("entity_id"); - BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId); + BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(entityId); } return null; } @@ -212,7 +212,7 @@ public final class CraftEngineFurniture { */ public static boolean remove(@NotNull Entity entity) { if (!isFurniture(entity)) return false; - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entity.getEntityId()); + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(entity.getEntityId()); if (furniture == null) return false; furniture.destroy(); return true; @@ -230,7 +230,7 @@ public final class CraftEngineFurniture { boolean dropLoot, boolean playSound) { if (!isFurniture(entity)) return false; - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entity.getEntityId()); + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(entity.getEntityId()); if (furniture == null) return false; remove(furniture, (net.momirealms.craftengine.core.entity.player.Player) null, dropLoot, playSound); return true; @@ -250,7 +250,7 @@ public final class CraftEngineFurniture { boolean dropLoot, boolean playSound) { if (!isFurniture(entity)) return false; - Furniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entity.getEntityId()); + Furniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(entity.getEntityId()); if (furniture == null) return false; remove(furniture, player, dropLoot, playSound); return true; @@ -320,7 +320,7 @@ public final class CraftEngineFurniture { } } if (playSound) { - world.playBlockSound(position, furniture.config().settings().sounds().breakSound()); + world.playBlockSound(position, furniture.config.settings().sounds().breakSound()); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java index dd1920ca0..d7d053413 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.api.event; import net.momirealms.craftengine.core.entity.furniture.AnchorType; import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; +import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; import net.momirealms.craftengine.core.entity.player.InteractionHand; import org.bukkit.Location; import org.bukkit.block.Block; @@ -17,23 +18,20 @@ public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Can private boolean cancelled; private final FurnitureConfig furniture; private final Location location; - private final AnchorType anchorType; - private final BlockFace clickedFace; + private final FurnitureVariant variant; private final Block clickedBlock; private final InteractionHand hand; public FurnitureAttemptPlaceEvent(@NotNull Player player, @NotNull FurnitureConfig furniture, - @NotNull AnchorType anchorType, + @NotNull FurnitureVariant variant, @NotNull Location location, - @NotNull BlockFace clickedFace, @NotNull InteractionHand hand, @NotNull Block clickedBlock) { super(player); this.furniture = furniture; this.location = location; - this.anchorType = anchorType; - this.clickedFace = clickedFace; + this.variant = variant; this.clickedBlock = clickedBlock; this.hand = hand; } @@ -48,19 +46,14 @@ public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Can return hand; } - @NotNull - public BlockFace clickedFace() { - return clickedFace; - } - @NotNull public Player player() { return getPlayer(); } @NotNull - public AnchorType anchorType() { - return anchorType; + public FurnitureVariant variant() { + return variant; } @NotNull diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index 7236d1ba0..4455884dc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -25,7 +25,6 @@ public class BukkitFurniture extends Furniture { super(new BukkitEntity(metaEntity), data, config); this.metaEntity = new WeakReference<>(metaEntity); this.location = metaEntity.getLocation(); - this.setVariant(super.config().getVariant(data)); } @Override 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 f2edfe2df..a1dee6c88 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 @@ -5,23 +5,20 @@ import net.momirealms.craftengine.bukkit.nms.CollisionEntity; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; 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.VersionHelper; import net.momirealms.craftengine.core.world.CEWorld; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.chunk.CEChunk; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.NamespacedKey; -import org.bukkit.World; -import org.bukkit.entity.Boat; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Interaction; -import org.bukkit.entity.ItemDisplay; +import org.bukkit.*; +import org.bukkit.entity.*; import org.bukkit.event.HandlerList; import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.Nullable; @@ -46,7 +43,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { private final BukkitCraftEngine plugin; private final Map byMetaEntityId = new ConcurrentHashMap<>(256, 0.5f); - private final Map byEntityId = new ConcurrentHashMap<>(512, 0.5f); + private final Map byVirtualEntityId = new ConcurrentHashMap<>(512, 0.5f); + private final Map byColliderEntityId = new ConcurrentHashMap<>(512, 0.5f); // Event listeners private final FurnitureEventListener furnitureEventListener; @@ -63,31 +61,25 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { @Override public Furniture place(WorldPosition position, FurnitureConfig furniture, FurnitureDataAccessor dataAccessor, boolean playSound) { -// return this.place(LocationUtils.toLocation(position), furniture, dataAccessor, playSound); - return null; + return this.place(LocationUtils.toLocation(position), furniture, dataAccessor, playSound); } - public BukkitFurniture place(Location location, FurnitureConfig furniture, FurnitureDataAccessor extraData, boolean playSound) { -// Optional optionalAnchorType = extraData.anchorType(); -// if (optionalAnchorType.isEmpty() || !furniture.isAllowedPlacement(optionalAnchorType.get())) { -// extraData.anchorType(furniture.getAnyAnchorType()); -// } -// Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> { -// ItemDisplay display = (ItemDisplay) entity; -// display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_KEY, PersistentDataType.STRING, furniture.id().toString()); -// try { -// display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, extraData.toBytes()); -// } catch (IOException e) { -// this.plugin.logger().warn("Failed to set furniture PDC for " + furniture.id().toString(), e); -// } -// handleBaseEntityLoadEarly(display); -// }); -// if (playSound) { -// SoundData data = furniture.settings().sounds().placeSound(); -// location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume().get(), data.pitch().get()); -// } -// return loadedFurnitureByRealEntityId(furnitureEntity.getEntityId()); - return null; + public BukkitFurniture place(Location location, FurnitureConfig furniture, FurnitureDataAccessor data, boolean playSound) { + Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> { + ItemDisplay display = (ItemDisplay) entity; + display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_KEY, PersistentDataType.STRING, furniture.id().toString()); + try { + display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, data.toBytes()); + } catch (IOException e) { + this.plugin.logger().warn("Failed to set furniture PDC for " + furniture.id().toString(), e); + } + handleMetaEntityAfterChunkLoad(display); + }); + if (playSound) { + SoundData sound = furniture.settings().sounds().placeSound(); + location.getWorld().playSound(location, sound.id().toString(), SoundCategory.BLOCKS, sound.volume().get(), sound.pitch().get()); + } + return loadedFurnitureByMetaEntityId(furnitureEntity.getEntityId()); } @Override @@ -138,43 +130,59 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } @Override - public boolean isFurnitureRealEntity(int entityId) { + public boolean isFurnitureMetaEntity(int entityId) { return this.byMetaEntityId.containsKey(entityId); } @Nullable @Override - public BukkitFurniture loadedFurnitureByRealEntityId(int entityId) { + public BukkitFurniture loadedFurnitureByMetaEntityId(int entityId) { return this.byMetaEntityId.get(entityId); } - @Override @Nullable - public BukkitFurniture loadedFurnitureByEntityId(int entityId) { - return this.byEntityId.get(entityId); + @Override + public BukkitFurniture loadedFurnitureByVirtualEntityId(int entityId) { + return this.byVirtualEntityId.get(entityId); } + @Nullable + @Override + public BukkitFurniture loadedFurnitureByColliderEntityId(int entityId) { + return this.byColliderEntityId.get(entityId); + } + + // 当元数据实体被卸载了 protected void handleMetaEntityUnload(Entity entity) { + // 不是持久化的 + if (!entity.isPersistent()) { + return; + } int id = entity.getEntityId(); BukkitFurniture furniture = this.byMetaEntityId.remove(id); if (furniture != null) { Location location = entity.getLocation(); - // 区块还在加载的时候,就重复卸载了 + // 区块还在加载的时候,就重复卸载了。为极其特殊情况 boolean isPreventing = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); if (!isPreventing) { furniture.destroySeats(); } - for (int sub : furniture.entityIds()) { - this.byEntityId.remove(sub); + for (int sub : furniture.virtualEntityIds()) { + this.byVirtualEntityId.remove(sub); + } + for (int sub : furniture.colliderEntityIds()) { + this.byColliderEntityId.remove(sub); } } } + // 保险起见,collision实体卸载也移除一下 protected void handleCollisionEntityUnload(Entity entity) { int id = entity.getEntityId(); - this.byMetaEntityId.remove(id); + this.byColliderEntityId.remove(id); } + // 检查这个区块的实体是否已经被加载了 private boolean isEntitiesLoaded(Location location) { CEWorld ceWorld = this.plugin.worldManager().getWorld(location.getWorld()); CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(location.getBlockX() >> 4, location.getBlockZ() >> 4); @@ -216,8 +224,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { BukkitFurniture previous = this.byMetaEntityId.get(entity.getEntityId()); if (previous != null) return; - BukkitFurniture furniture = addNewFurniture(entity, customFurniture); - furniture.addCollidersToWorld(); + // 创建新的家具 + createFurnitureInstance(entity, customFurniture); } protected void handleMetaEntityAfterChunkLoad(ItemDisplay entity) { @@ -245,8 +253,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { BukkitFurniture previous = this.byMetaEntityId.get(entity.getEntityId()); if (previous != null) return; - BukkitFurniture furniture = addNewFurniture(entity, customFurniture); - furniture.addCollidersToWorld(); + createFurnitureInstance(entity, customFurniture); } protected void handleCollisionEntityAfterChunkLoad(Entity entity) { @@ -298,17 +305,17 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } } - private synchronized BukkitFurniture addNewFurniture(ItemDisplay display, FurnitureConfig furniture) { + // 创建家具实例,并初始化碰撞实体 + private void createFurnitureInstance(ItemDisplay display, FurnitureConfig furniture) { BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, getFurnitureDataAccessor(display)); this.byMetaEntityId.put(display.getEntityId(), bukkitFurniture); - for (int entityId : bukkitFurniture.entityIds()) { - this.byEntityId.put(entityId, bukkitFurniture); + for (int entityId : bukkitFurniture.virtualEntityIds()) { + this.byVirtualEntityId.put(entityId, bukkitFurniture); } for (Collider collisionEntity : bukkitFurniture.colliders()) { - int collisionEntityId = FastNMS.INSTANCE.method$Entity$getId(collisionEntity.handle()); - this.byEntityId.put(collisionEntityId, bukkitFurniture); + this.byColliderEntityId.put(collisionEntity.entityId(), bukkitFurniture); } - return bukkitFurniture; + bukkitFurniture.addCollidersToWorld(); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java deleted file mode 100644 index 339dad09d..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/ItemColorSource.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.momirealms.craftengine.bukkit.entity.furniture; - -import net.momirealms.craftengine.core.util.Color; - -public record ItemColorSource(Color dyedColor, int[] fireworkColors) { -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java index 5c92715cf..ae884d0c7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java @@ -1,22 +1,61 @@ package net.momirealms.craftengine.bukkit.entity.furniture.element; +import it.unimi.dsi.fastutil.ints.IntArrayList; +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.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; + +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; public class ItemDisplayFurnitureElement implements FurnitureElement { private final ItemDisplayFurnitureElementConfig config; + private final WorldPosition position; + private final int entityId; + private final Object despawnPacket; + private final FurnitureColorSource colorSource; - public ItemDisplayFurnitureElement(ItemDisplayFurnitureElementConfig config) { + public ItemDisplayFurnitureElement(Furniture furniture, ItemDisplayFurnitureElementConfig config) { this.config = config; + this.entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + WorldPosition furniturePos = furniture.position(); + Vec3d position = Furniture.getRelativePosition(furniturePos, config.position()); + this.position = new WorldPosition(furniturePos.world, position.x, position.y, position.z, furniturePos.xRot, furniturePos.yRot); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(entityId); }}); + this.colorSource = furniture.dataAccessor.getColorSource(); } @Override public void show(Player player) { - + player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( + FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + this.entityId, UUID.randomUUID(), + this.position.x, this.position.y, this.position.z, 0, this.position.yRot, + MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 + ), + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadata.apply(player, this.colorSource)) + )), false); } @Override public void hide(Player player) { + player.sendPacket(this.despawnPacket, false); + } + @Override + public int[] virtualEntityIds() { + return new int[] {this.entityId}; + } + + @Override + public void collectVirtualEntityId(Consumer collector) { + collector.accept(this.entityId); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java index fe52c4c83..cf58886a5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.bukkit.entity.furniture.element; import it.unimi.dsi.fastutil.ints.IntArrayList; import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; -import net.momirealms.craftengine.bukkit.entity.furniture.ItemColorSource; +import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.core.entity.display.Billboard; import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; @@ -28,21 +28,21 @@ import java.util.function.BiFunction; public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig { public static final Factory FACTORY = new Factory(); - private final BiFunction> lazyMetadataPacket; - private final BiFunction> 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; - private final float shadowRadius; - private final float shadowStrength; - private final boolean applyDyedColor; + public final BiFunction> metadata; + public final Key itemId; + public final Vector3f scale; + public final Vector3f position; + public final Vector3f translation; + public final float xRot; + public final float yRot; + public final Quaternionf rotation; + public final ItemDisplayContext displayContext; + public final Billboard billboard; + public final float shadowRadius; + public final float shadowStrength; + public final boolean applyDyedColor; - public ItemDisplayFurnitureElementConfig(BiFunction> item, + public ItemDisplayFurnitureElementConfig(Key itemId, Vector3f scale, Vector3f position, Vector3f translation, @@ -65,10 +65,24 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig this.shadowRadius = shadowRadius; this.shadowStrength = shadowStrength; this.applyDyedColor = applyDyedColor; - this.item = item; - this.lazyMetadataPacket = (player, source) -> { + this.itemId = itemId; + BiFunction> itemFunction = (player, colorSource) -> { + Item wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player); + if (applyDyedColor && colorSource != null && wrappedItem != null) { + Optional.ofNullable(colorSource.dyedColor()).ifPresent(wrappedItem::dyedColor); + Optional.ofNullable(colorSource.fireworkColors()).ifPresent(colors -> wrappedItem.fireworkExplosion(new FireworkExplosion( + FireworkExplosion.Shape.SMALL_BALL, + new IntArrayList(colors), + new IntArrayList(), + false, + false + ))); + } + return Optional.ofNullable(wrappedItem).orElseGet(() -> BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null)); + }; + this.metadata = (player, source) -> { List dataValues = new ArrayList<>(); - ItemDisplayEntityData.DisplayedItem.addEntityData(item.apply(player, source).getLiteralObject(), dataValues); + ItemDisplayEntityData.DisplayedItem.addEntityData(itemFunction.apply(player, source).getLiteralObject(), dataValues); ItemDisplayEntityData.Scale.addEntityData(this.scale, dataValues); ItemDisplayEntityData.RotationLeft.addEntityData(this.rotation, dataValues); ItemDisplayEntityData.BillboardConstraints.addEntityData(this.billboard.id(), dataValues); @@ -81,52 +95,56 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig } public Vector3f scale() { - return scale; + return this.scale; } public Vector3f position() { - return position; + return this.position; } public Vector3f translation() { - return translation; + return this.translation; } public float xRot() { - return xRot; + return this.xRot; } public float yRot() { - return yRot; + return this.yRot; } public Quaternionf rotation() { - return rotation; + return this.rotation; } public ItemDisplayContext displayContext() { - return displayContext; + return this.displayContext; } public Billboard billboard() { - return billboard; + return this.billboard; } public float shadowRadius() { - return shadowRadius; + return this.shadowRadius; } public float shadowStrength() { - return shadowStrength; + return this.shadowStrength; } public boolean applyDyedColor() { - return applyDyedColor; + return this.applyDyedColor; + } + + public Key itemId() { + return this.itemId; } @Override public ItemDisplayFurnitureElement create(@NotNull Furniture furniture) { - return new ItemDisplayFurnitureElement(this); + return new ItemDisplayFurnitureElement(furniture, this); } public static class Factory implements FurnitureElementConfigFactory { @@ -136,22 +154,9 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.furniture.element.item_display.missing_item")); boolean applyDyedColor = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color"); return new ItemDisplayFurnitureElementConfig( - (player, colorSource) -> { - Item wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player); - if (applyDyedColor && colorSource != null && wrappedItem != null) { - Optional.ofNullable(colorSource.dyedColor()).ifPresent(wrappedItem::dyedColor); - Optional.ofNullable(colorSource.fireworkColors()).ifPresent(colors -> wrappedItem.fireworkExplosion(new FireworkExplosion( - FireworkExplosion.Shape.SMALL_BALL, - new IntArrayList(colors), - new IntArrayList(), - false, - false - ))); - } - return Optional.ofNullable(wrappedItem).orElseGet(() -> BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null)); - }, + itemId, ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), - ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0f), "position"), ResourceConfigUtils.getAsVector3f(arguments.get("translation"), "translation"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), @@ -164,4 +169,23 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig ); } } + + @Override + public String toString() { + return "ItemDisplayFurnitureElementConfig{" + + "metadata=" + metadata + + ", itemId=" + itemId + + ", scale=" + scale + + ", position=" + position + + ", translation=" + translation + + ", xRot=" + xRot + + ", yRot=" + yRot + + ", rotation=" + rotation + + ", displayContext=" + displayContext + + ", billboard=" + billboard + + ", shadowRadius=" + shadowRadius + + ", shadowStrength=" + shadowStrength + + ", applyDyedColor=" + applyDyedColor + + '}'; + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java index 5ae471042..2789d4347 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.entity.furniture.Collider; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.world.Vec3d; @@ -54,4 +55,9 @@ public abstract class AbstractFurnitureHitBox implements FurnitureHitBox { protected Object createDespawnPacket(int[] entityIds) { return FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList(entityIds)); } + + @Override + public void hide(Player player) { + player.sendPacket(createDespawnPacket(this.virtualEntityIds()), false); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index 9f8bc0366..65836a4b5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; +import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; @@ -14,15 +15,15 @@ import net.momirealms.craftengine.core.world.collision.AABB; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.function.Consumer; public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { private final InteractionFurnitureHitboxConfig config; private final Vec3d position; private final AABB aabb; private final Collider collider; - private final int[] entityIds; + private final int interactionId; private final Object spawnPacket; - private final Object despawnPacket; public InteractionFurnitureHitbox(Furniture furniture, InteractionFurnitureHitboxConfig config) { super(furniture, config); @@ -31,20 +32,21 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { this.position = Furniture.getRelativePosition(position, config.position()); this.aabb = AABB.fromInteraction(this.position, config.size.x, config.size.y); this.collider = createCollider(furniture.world(), this.position, this.aabb, false, config.blocksBuilding(), config.canBeHitByProjectile()); - int interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); - this.entityIds = new int[] {interactionId}; - List values = new ArrayList<>(3); + this.interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + List values = new ArrayList<>(4); InteractionEntityData.Height.addEntityDataIfNotDefaultValue(config.size.y, values); InteractionEntityData.Width.addEntityDataIfNotDefaultValue(config.size.x, values); InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(config.responsive, values); + if (config.invisible) { + BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, values); + } this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - interactionId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, + this.interactionId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 ), - FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, values) + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.interactionId, values) )); - this.despawnPacket = createDespawnPacket(this.entityIds); } @Override @@ -53,13 +55,8 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { } @Override - public void hide(Player player) { - player.sendPacket(this.despawnPacket, false); - } - - @Override - public AABB aabb() { - return this.aabb; + public AABB[] aabb() { + return new AABB[] { this.aabb }; } @Override @@ -68,13 +65,18 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { } @Override - public Collider collider() { - return this.collider; + public List colliders() { + return List.of(this.collider); } @Override public int[] virtualEntityIds() { - return this.entityIds; + return new int[] { this.interactionId }; + } + + @Override + public void collectVirtualEntityIds(Consumer collector) { + collector.accept(this.interactionId); } public InteractionFurnitureHitboxConfig config() { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java index 2a1efd59f..7711218c2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -5,27 +5,34 @@ import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurniture import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; import org.joml.Vector3f; import java.util.Map; +import java.util.function.Consumer; public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig { public static final Factory FACTORY = new Factory(); - public static final InteractionFurnitureHitboxConfig DEFAULT = new InteractionFurnitureHitboxConfig(new SeatConfig[0], new Vector3f(), false, false, false, new Vector3f(1,1,1), true); + public static final InteractionFurnitureHitboxConfig DEFAULT = new InteractionFurnitureHitboxConfig(new SeatConfig[0], new Vector3f(), false, false, false, false, new Vector3f(1,1,1), true); public final Vector3f size; public final boolean responsive; + public final boolean invisible; public InteractionFurnitureHitboxConfig(SeatConfig[] seats, Vector3f position, boolean canUseItemOn, boolean blocksBuilding, boolean canBeHitByProjectile, + boolean invisible, Vector3f size, boolean responsive) { super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile); this.size = size; this.responsive = responsive; + this.invisible = invisible; } public Vector3f size() { @@ -36,6 +43,18 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon return responsive; } + public boolean invisible() { + return invisible; + } + + @Override + public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { + if (this.blocksBuilding) { + Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); + aabbConsumer.accept(AABB.fromInteraction(relativePosition, size.x, size.y)); + } + } + @Override public InteractionFurnitureHitbox create(Furniture furniture) { return new InteractionFurnitureHitbox(furniture, this); @@ -45,7 +64,7 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon @Override public InteractionFurnitureHitboxConfig create(Map arguments) { - Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); + Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0), "position"); float width; float height; if (arguments.containsKey("scale")) { @@ -60,9 +79,10 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive"); boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile"); boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); + boolean invisible = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("invisible", false), "invisible"); return new InteractionFurnitureHitboxConfig( SeatConfig.fromObj(arguments.get("seats")), - position, canUseOn, blocksBuilding, canBeHitByProjectile, + position, canUseOn, blocksBuilding, canBeHitByProjectile, invisible, new Vector3f(width, height, width), interactive ); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index b92b4440e..3b85c0ef7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -1,18 +1,46 @@ package net.momirealms.craftengine.bukkit.item.behavior; +import net.momirealms.craftengine.bukkit.api.event.FurnitureAttemptPlaceEvent; +import net.momirealms.craftengine.bukkit.api.event.FurniturePlaceEvent; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.util.DirectionUtils; +import net.momirealms.craftengine.bukkit.util.EventUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; +import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; +import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.PendingConfigSection; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.ContextHolder; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +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.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.*; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; +import net.momirealms.sparrow.nbt.CompoundTag; +import org.bukkit.Location; +import org.bukkit.World; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.Map; +import java.util.Optional; public class FurnitureItemBehavior extends ItemBehavior { public static final Factory FACTORY = new Factory(); @@ -23,7 +51,7 @@ public class FurnitureItemBehavior extends ItemBehavior { } public Key furnitureId() { - return id; + return this.id; } @Override @@ -32,121 +60,96 @@ public class FurnitureItemBehavior extends ItemBehavior { } public InteractionResult place(UseOnContext context) { -// Optional optionalCustomFurniture = BukkitFurnitureManager.instance().furnitureById(this.id); -// if (optionalCustomFurniture.isEmpty()) { -// CraftEngine.instance().logger().warn("Furniture " + this.id + " not found"); -// return InteractionResult.FAIL; -// } -// FurnitureConfig customFurniture = optionalCustomFurniture.get(); -// -// Direction clickedFace = context.getClickedFace(); -// AnchorType anchorType = switch (clickedFace) { -// case EAST, WEST, NORTH, SOUTH -> AnchorType.WALL; -// case UP -> AnchorType.GROUND; -// case DOWN -> AnchorType.CEILING; -// }; -// -// FurnitureConfig.Variant placement = customFurniture.getPlacement(anchorType); -// if (placement == null) { -// return InteractionResult.FAIL; -// } -// -// Player player = context.getPlayer(); -// // todo adventure check -// if (player != null && player.isAdventureMode()) { -// return InteractionResult.FAIL; -// } -// -// Vec3d clickedPosition = context.getClickLocation(); -// -// // trigger event -// org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null; -// World world = (World) context.getLevel().platformWorld(); -// -// // get position and rotation for placement -// Vec3d finalPlacePosition; -// double furnitureYaw; -// if (anchorType == AnchorType.WALL) { -// furnitureYaw = Direction.getYaw(clickedFace); -// if (clickedFace == Direction.EAST || clickedFace == Direction.WEST) { -// Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.y(), clickedPosition.z())); -// finalPlacePosition = new Vec3d(clickedPosition.x(), xz.left(), xz.right()); -// } else { -// Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.y())); -// finalPlacePosition = new Vec3d(xz.left(), xz.right(), clickedPosition.z()); -// } -// } else { -// furnitureYaw = placement.rotationRule().apply(180 + (player != null ? player.yRot() : 0)); -// Pair xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.z())); -// finalPlacePosition = new Vec3d(xz.left(), clickedPosition.y(), xz.right()); -// } -// -// Location furnitureLocation = new Location(world, finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, 0); -// -// List aabbs = new ArrayList<>(); -// for (HitBoxConfig hitBoxConfig : placement.hitBoxConfigs()) { -// hitBoxConfig.initShapeForPlacement(finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - furnitureYaw), 0).conjugate(), aabbs::add); -// } -// if (!aabbs.isEmpty()) { -// if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { -// return InteractionResult.FAIL; -// } -// } -// -// if (!BukkitCraftEngine.instance().antiGriefProvider().canPlace(bukkitPlayer, furnitureLocation)) { -// return InteractionResult.FAIL; -// } -// -// if (player != null) { -// FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, anchorType, furnitureLocation.clone(), -// DirectionUtils.toBlockFace(clickedFace), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z())); -// if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) { -// return InteractionResult.FAIL; -// } -// } -// -// Item item = context.getItem(); -// // 不可能 -// if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL; -// -// BukkitFurniture bukkitFurniture = BukkitFurnitureManager.instance().place( -// furnitureLocation.clone(), customFurniture, -// FurnitureDataAccessor.builder() -// .item(item.copyWithCount(1)) -// .anchorType(anchorType) -// .dyedColor(item.dyedColor().orElse(null)) -// .fireworkExplosionColors(item.fireworkExplosion().map(explosion -> explosion.colors().toIntArray()).orElse(null)) -// .build(), false); -// -// if (player != null) { -// FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand()); -// if (EventUtils.fireAndCheckCancel(placeEvent)) { -// bukkitFurniture.destroy(); -// return InteractionResult.FAIL; -// } -// } -// -// Cancellable dummy = Cancellable.dummy(); -// PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder() -// .withParameter(DirectContextParameters.FURNITURE, bukkitFurniture) -// .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(furnitureLocation)) -// .withParameter(DirectContextParameters.EVENT, dummy) -// .withParameter(DirectContextParameters.HAND, context.getHand()) -// .withParameter(DirectContextParameters.ITEM_IN_HAND, item) -// ); -// customFurniture.execute(functionContext, EventTrigger.PLACE); -// if (dummy.isCancelled()) { -// return InteractionResult.SUCCESS_AND_CANCEL; -// } -// -// if (player != null) { -// if (!player.canInstabuild()) { -// item.count(item.count() - 1); -// } -// player.swingHand(context.getHand()); -// } -// -// context.getLevel().playBlockSound(finalPlacePosition, customFurniture.settings().sounds().placeSound()); + Optional optionalCustomFurniture = BukkitFurnitureManager.instance().furnitureById(this.id); + if (optionalCustomFurniture.isEmpty()) { + CraftEngine.instance().logger().warn("Furniture " + this.id + " not found"); + return InteractionResult.FAIL; + } + + FurnitureConfig customFurniture = optionalCustomFurniture.get(); + FurnitureVariant variant = customFurniture.anyVariant(); + if (variant == null) { + return InteractionResult.FAIL; + } + + Player player = context.getPlayer(); + if (player != null && player.isAdventureMode()) { + return InteractionResult.FAIL; + } + + Vec3d clickedPosition = context.getClickLocation(); + + // trigger event + org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null; + World world = (World) context.getLevel().platformWorld(); + + // get position and rotation for placement + Vec3d finalPlacePosition = clickedPosition; + double furnitureYaw = 180 + (player != null ? player.yRot() : 0); + + Location furnitureLocation = new Location(world, finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, 0); + WorldPosition furniturePos = LocationUtils.toWorldPosition(furnitureLocation); + List aabbs = new ArrayList<>(); + // 收集阻挡的碰撞箱 + for (FurnitureHitBoxConfig hitBoxConfig : variant.hitBoxConfigs()) { + hitBoxConfig.prepareForPlacement(furniturePos, aabbs::add); + } + // 检查方块、实体阻挡 + if (!aabbs.isEmpty()) { + if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { + return InteractionResult.FAIL; + } + } + // 检查其他插件兼容性 + if (!BukkitCraftEngine.instance().antiGriefProvider().canPlace(bukkitPlayer, furnitureLocation)) { + return InteractionResult.FAIL; + } + // 触发尝试放置的事件 + if (player != null) { + FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, variant, furnitureLocation.clone(), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z())); + if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) { + return InteractionResult.FAIL; + } + } + Item item = context.getItem(); + if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL; + // 获取家具物品的一些属性 + FurnitureDataAccessor dataAccessor = FurnitureDataAccessor.of(new CompoundTag()); + dataAccessor.setVariant(variant.name()); + dataAccessor.setItem(item.copyWithCount(1)); + dataAccessor.setDyedColor(item.dyedColor().orElse(null)); + dataAccessor.setFireworkExplosionColors(item.fireworkExplosion().map(explosion -> explosion.colors().toIntArray()).orElse(null)); + // 放置家具 + BukkitFurniture bukkitFurniture = BukkitFurnitureManager.instance().place(furnitureLocation.clone(), customFurniture, dataAccessor, false); + // 触发放置事件 + if (player != null) { + FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand()); + if (EventUtils.fireAndCheckCancel(placeEvent)) { + bukkitFurniture.destroy(); + return InteractionResult.FAIL; + } + } + // 触发ce事件 + Cancellable dummy = Cancellable.dummy(); + PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder() + .withParameter(DirectContextParameters.FURNITURE, bukkitFurniture) + .withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(furnitureLocation)) + .withParameter(DirectContextParameters.EVENT, dummy) + .withParameter(DirectContextParameters.HAND, context.getHand()) + .withParameter(DirectContextParameters.ITEM_IN_HAND, item) + ); + customFurniture.execute(functionContext, EventTrigger.PLACE); + if (dummy.isCancelled()) { + return InteractionResult.SUCCESS_AND_CANCEL; + } + // 后续处理 + if (player != null) { + if (!player.canInstabuild()) { + item.count(item.count() - 1); + } + player.swingHand(context.getHand()); + } + context.getLevel().playBlockSound(finalPlacePosition, customFurniture.settings().sounds().placeSound()); return InteractionResult.SUCCESS; } 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 9ab146c37..57a5f59cc 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 @@ -1247,7 +1247,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes CraftEngine.instance().logger().warn("Failed to get entityId from ServerboundPickItemFromEntityPacket", e); return; } - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId); + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByVirtualEntityId(entityId); if (furniture == null) return; Player player = (Player) user.platformPlayer(); if (player == null) return; @@ -1469,6 +1469,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes player.setClientSideWorld(BukkitAdaptors.adapt(world)); player.clearTrackedChunks(); player.clearTrackedBlockEntities(); + player.clearTrackedFurniture(); } else { CraftEngine.instance().logger().warn("Failed to handle ClientboundRespawnPacket: World " + location + " does not exist"); } @@ -1745,9 +1746,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes @Override public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { int entityId = ProtectedFieldVisitor.get().field$ClientboundMoveEntityPacket$entityId(packet); - if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { - event.setCancelled(true); - } EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); if (handler != null) { handler.handleMoveAndRotate(user, event, packet); @@ -1778,7 +1776,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes CraftEngine.instance().logger().warn("Failed to get entity id from ClientboundRotateHeadPacket", t); return; } - if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { + if (BukkitFurnitureManager.instance().isFurnitureMetaEntity(entityId)) { + System.out.println("RotateHeadListener"); event.setCancelled(true); } } @@ -1796,7 +1795,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes CraftEngine.instance().logger().warn("Failed to get entity id from ClientboundSetEntityMotionPacket", t); return; } - if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { + if (BukkitFurnitureManager.instance().isFurnitureMetaEntity(entityId)) { + System.out.println("SetEntityMotionListener"); event.setCancelled(true); } } @@ -3349,7 +3349,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes for (int i = 0, size = intList.size(); i < size; i++) { int entityId = intList.getInt(i); EntityPacketHandler handler = user.entityPacketHandlers().remove(entityId); - if (handler != null && handler.handleEntitiesRemove(intList)) { + if (handler != null && handler.handleEntitiesRemove(user, intList)) { changed = true; } } @@ -3698,7 +3698,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes public void onPacketReceive(NetWorkUser user, ByteBufPacketEvent event) { FriendlyByteBuf buf = event.getBuffer(); int entityId = hasModelEngine() ? plugin.compatibilityManager().interactionToBaseEntity(buf.readVarInt()) : buf.readVarInt(); - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId); + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByVirtualEntityId(entityId); if (furniture == null) return; int actionType = buf.readVarInt(); BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; @@ -3799,7 +3799,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes Vector direction = eyeLocation.getDirection(); Location endLocation = eyeLocation.clone(); endLocation.add(direction.multiply(serverPlayer.getCachedInteractionRange())); - Optional result = hitBox.aabb().clip(LocationUtils.toVec3d(eyeLocation), LocationUtils.toVec3d(endLocation)); + Optional result = hitBox.clip(LocationUtils.toVec3d(eyeLocation), LocationUtils.toVec3d(endLocation)); if (result.isEmpty()) { return; } @@ -3976,16 +3976,19 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes this.handlers[MEntityTypes.ITEM_DISPLAY$registryId] = (user, event) -> { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); + BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(id); if (furniture != null) { - furniture.show((BukkitServerPlayer) user); - event.setCancelled(true); + serverPlayer.entityPacketHandlers().put(id, new FurniturePacketHandler(id, furniture.virtualEntityIds())); + if (Config.enableEntityCulling()) { + serverPlayer.addTrackedFurniture(id, furniture); + } else { + furniture.show(serverPlayer); + } // fixme 外部模型 -// user.entityPacketHandlers().put(id, new FurniturePacketHandler(furniture.fakeEntityIds())); -// user.sendPacket(furniture.spawnPacket((Player) user.platformPlayer()), false); -// if (Config.hideBaseEntity() && !furniture.hasExternalModel()) { -// event.setCancelled(true); -// } + if (Config.hideBaseEntity() && true) { + event.setCancelled(true); + } } else { user.entityPacketHandlers().put(id, ItemDisplayPacketHandler.INSTANCE); } @@ -3995,7 +3998,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); // Cancel collider entity packet - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByColliderEntityId(id); if (furniture != null) { event.setCancelled(true); user.entityPacketHandlers().put(id, FurnitureCollisionPacketHandler.INSTANCE); @@ -4006,7 +4009,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); // Cancel collider entity packet - BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(id); + BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByColliderEntityId(id); if (furniture != null) { event.setCancelled(true); user.entityPacketHandlers().put(id, FurnitureCollisionPacketHandler.INSTANCE); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java index 0a60cfc3a..3e7277429 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java @@ -1,22 +1,26 @@ package net.momirealms.craftengine.bukkit.plugin.network.handler; import it.unimi.dsi.fastutil.ints.IntList; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; import net.momirealms.craftengine.core.plugin.network.NMSPacketEvent; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; -import java.util.List; - public class FurniturePacketHandler implements EntityPacketHandler { - private final List fakeEntities; + private final int metaEntityId; + private final int[] virtualHitboxEntities; - public FurniturePacketHandler(List fakeEntities) { - this.fakeEntities = fakeEntities; + public FurniturePacketHandler(int metaEntityId, int[] virtualHitboxEntities) { + this.virtualHitboxEntities = virtualHitboxEntities; + this.metaEntityId = metaEntityId; } @Override - public boolean handleEntitiesRemove(IntList entityIds) { - entityIds.addAll(this.fakeEntities); + public boolean handleEntitiesRemove(NetWorkUser user, IntList entityIds) { + ((Player) user).removeTrackedFurniture(this.metaEntityId); + for (int entityId : this.virtualHitboxEntities) { + entityIds.add(entityId); + } return true; } @@ -29,4 +33,9 @@ public class FurniturePacketHandler implements EntityPacketHandler { public void handleMove(NetWorkUser user, NMSPacketEvent event, Object packet) { event.setCancelled(true); } + + @Override + public void handleMoveAndRotate(NetWorkUser user, NMSPacketEvent event, Object packet) { + event.setCancelled(true); + } } 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 e7bdc3bea..a67b52a51 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 @@ -25,6 +25,7 @@ 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.entity.data.EntityData; +import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.player.GameMode; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.Player; @@ -144,6 +145,7 @@ public class BukkitServerPlayer extends Player { private int lastStopMiningTick; // 跟踪到的方块实体渲染器 private final Map trackedBlockEntityRenderers = new ConcurrentHashMap<>(); + private final Map trackedFurniture = new ConcurrentHashMap<>(); private final EntityCulling culling; private Vec3d firstPersonCameraVec3; private Vec3d thirdPersonCameraVec3; @@ -603,44 +605,54 @@ public class BukkitServerPlayer extends Player { boolean useRayTracing = Config.entityCullingRayTracing(); if (this.enableEntityCulling) { for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { - CullingData cullingData = cullableObject.cullable.cullingData(); - if (cullingData != null) { - boolean firstPersonVisible = this.culling.isVisible(cullingData, this.firstPersonCameraVec3, useRayTracing); - // 之前可见 - if (cullableObject.isShown) { - boolean thirdPersonVisible = this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing); - if (!firstPersonVisible && !thirdPersonVisible) { - cullableObject.setShown(this, false); - } - } - // 之前不可见 - else { - // 但是第一人称可见了 - if (firstPersonVisible) { - // 下次再说 - if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { - continue; - } - cullableObject.setShown(this, true); - continue; - } - if (this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing)) { - // 下次再说 - if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { - continue; - } - cullableObject.setShown(this, true); - } - // 仍然不可见 - } - } else { - cullableObject.setShown(this, true); - } + cullEntity(useRayTracing, cullableObject); + } + for (VirtualCullableObject cullableObject : this.trackedFurniture.values()) { + cullEntity(useRayTracing, cullableObject); } } else { for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { cullableObject.setShown(this, true); } + for (VirtualCullableObject cullableObject : this.trackedFurniture.values()) { + cullableObject.setShown(this, true); + } + } + } + + private void cullEntity(boolean useRayTracing, VirtualCullableObject cullableObject) { + CullingData cullingData = cullableObject.cullable.cullingData(); + if (cullingData != null) { + boolean firstPersonVisible = this.culling.isVisible(cullingData, this.firstPersonCameraVec3, useRayTracing); + // 之前可见 + if (cullableObject.isShown) { + boolean thirdPersonVisible = this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing); + if (!firstPersonVisible && !thirdPersonVisible) { + cullableObject.setShown(this, false); + } + } + // 之前不可见 + else { + // 但是第一人称可见了 + if (firstPersonVisible) { + // 下次再说 + if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { + return; + } + cullableObject.setShown(this, true); + return; + } + if (this.culling.isVisible(cullingData, this.thirdPersonCameraVec3, useRayTracing)) { + // 下次再说 + if (Config.enableEntityCullingRateLimiting() && !this.culling.takeToken()) { + return; + } + cullableObject.setShown(this, true); + } + // 仍然不可见 + } + } else { + cullableObject.setShown(this, true); } } @@ -1427,6 +1439,24 @@ public class BukkitServerPlayer extends Player { this.trackedBlockEntityRenderers.clear(); } + @Override + public void addTrackedFurniture(int entityId, Furniture furniture) { + this.trackedFurniture.put(entityId, new VirtualCullableObject(furniture)); + } + + @Override + public void removeTrackedFurniture(int entityId) { + VirtualCullableObject remove = this.trackedFurniture.remove(entityId); + if (remove != null && remove.isShown()) { + remove.cullable().hide(this); + } + } + + @Override + public void clearTrackedFurniture() { + this.trackedFurniture.clear(); + } + @Override public WorldPosition eyePosition() { return LocationUtils.toWorldPosition(this.getEyeLocation()); 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 092e70ea7..c0569fd87 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 @@ -124,7 +124,7 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { String variantName = e0.getKey(); Map variantArguments = ResourceConfigUtils.getAsMap(e0.getValue(), variantName); Optional optionalLootSpawnOffset = Optional.ofNullable(variantArguments.get("loot-spawn-offset")).map(it -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset")); - List> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elementConfigs"), FurnitureElementConfigs::fromMap); + List> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elements"), FurnitureElementConfigs::fromMap); // fixme 外部模型不应该在这 Optional externalModel; @@ -141,7 +141,12 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { hitboxes = List.of(defaultHitBox()); } + // fixme 动态计算aabb,因为家具具有朝向 + AABB maxAABB = new AABB(0,0,0,1,1,1); + variants.put(variantName, new FurnitureVariant( + variantName, + parseCullingData(section.get("entity-culling"), maxAABB), elements.toArray(new FurnitureElementConfig[0]), hitboxes.toArray(new FurnitureHitBoxConfig[0]), externalModel, @@ -149,15 +154,13 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { )); } - AABB maxAABB = null; - FurnitureConfig furniture = FurnitureConfig.builder() .id(id) .settings(FurnitureSettings.fromMap(MiscUtils.castToMap(section.get("settings"), true))) .variants(variants) .events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"))) .lootTable(LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), true))) - .cullingData(parseCullingData(section.get("entity-culling"), maxAABB)) + .build(); AbstractFurnitureManager.this.byId.put(id, furniture); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index d50a23ea0..32f0b7c4a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -1,6 +1,10 @@ package net.momirealms.craftengine.core.entity.furniture; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.momirealms.craftengine.core.entity.Entity; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; @@ -15,13 +19,12 @@ import net.momirealms.craftengine.core.world.Cullable; 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.collision.AABB; import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; public abstract class Furniture implements Cullable { @@ -29,18 +32,20 @@ public abstract class Furniture implements Cullable { public final FurnitureDataAccessor dataAccessor; public final WeakReference metaDataEntity; + protected CullingData cullingData; protected FurnitureVariant currentVariant; protected FurnitureElement[] elements; protected Collider[] colliders; protected FurnitureHitBox[] hitboxes; - protected Int2ObjectMap hitboxMap; - protected int[] entityIds; + protected int[] virtualEntityIds; + protected int[] colliderEntityIds; protected Furniture(Entity metaDataEntity, FurnitureDataAccessor data, FurnitureConfig config) { this.config = config; this.dataAccessor = data; this.metaDataEntity = new WeakReference<>(metaDataEntity); + this.setVariant(config.getVariant(data)); } public WeakReference metaDataEntity() { @@ -48,25 +53,24 @@ public abstract class Furniture implements Cullable { } public FurnitureVariant getCurrentVariant() { - return currentVariant; - } - - public String getCurrentVariantName() { - return null; + return this.currentVariant; } public void setVariant(FurnitureVariant variant) { this.currentVariant = variant; - WorldPosition position = this.position(); + this.hitboxMap = new Int2ObjectOpenHashMap<>(); // 初始化家具元素 + IntList virtualEntityIds = new IntArrayList(); FurnitureElementConfig[] elementConfigs = variant.elementConfigs(); this.elements = new FurnitureElement[elementConfigs.length]; for (int i = 0; i < elementConfigs.length; i++) { - this.elements[i] = elementConfigs[i].create(this); + FurnitureElement element = elementConfigs[i].create(this); + this.elements[i] = element; + element.collectVirtualEntityId(virtualEntityIds::addLast); } // 初始化碰撞箱 - FurnitureHitBoxConfig[] furnitureHitBoxConfigs = variant.furnitureHitBoxConfigs(); - List colliders = new ArrayList<>(furnitureHitBoxConfigs.length); + FurnitureHitBoxConfig[] furnitureHitBoxConfigs = variant.hitBoxConfigs(); + ObjectArrayList colliders = new ObjectArrayList<>(furnitureHitBoxConfigs.length); this.hitboxes = new FurnitureHitBox[furnitureHitBoxConfigs.length]; for (int i = 0; i < furnitureHitBoxConfigs.length; i++) { FurnitureHitBox hitbox = furnitureHitBoxConfigs[i].create(this); @@ -74,12 +78,23 @@ public abstract class Furniture implements Cullable { for (int hitboxEntityId : hitbox.virtualEntityIds()) { this.hitboxMap.put(hitboxEntityId, hitbox); } - Collider collider = hitbox.collider(); - if (collider != null) { - colliders.add(collider); - } + colliders.addAll(hitbox.colliders()); + hitbox.collectVirtualEntityIds(virtualEntityIds::addLast); } + // 虚拟碰撞箱的实体id + this.virtualEntityIds = virtualEntityIds.toIntArray(); this.colliders = colliders.toArray(new Collider[0]); + this.colliderEntityIds = colliders.stream().mapToInt(Collider::entityId).toArray(); + this.cullingData = createCullingData(variant.cullingData()); + } + + private CullingData createCullingData(CullingData parent) { + if (parent == null) return null; + AABB aabb = parent.aabb; + WorldPosition position = position(); + Vec3d pos1 = getRelativePosition(position, new Vector3f((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ)); + Vec3d pos2 = getRelativePosition(position, new Vector3f((float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ)); + return new CullingData(new AABB(pos1.x, pos1.y, pos1.z, pos2.x, pos2.y, pos2.z), parent.maxDistance, parent.aabbExpansion, parent.rayTracing); } @Nullable @@ -90,15 +105,20 @@ public abstract class Furniture implements Cullable { @Nullable @Override public CullingData cullingData() { - return this.config.cullingData(); + return this.cullingData; } public Key id() { return this.config.id(); } - public int[] entityIds() { - return this.entityIds; + // 会发给玩家的包 + public int[] virtualEntityIds() { + return this.virtualEntityIds; + } + + public int[] colliderEntityIds() { + return colliderEntityIds; } public UUID uuid() { @@ -146,11 +166,11 @@ public abstract class Furniture implements Cullable { public abstract void destroy(); public FurnitureConfig config() { - return config; + return this.config; } public FurnitureDataAccessor dataAccessor() { - return dataAccessor; + return this.dataAccessor; } public Collider[] colliders() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureColorSource.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureColorSource.java new file mode 100644 index 000000000..2a6427080 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureColorSource.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.core.entity.furniture; + +import net.momirealms.craftengine.core.util.Color; + +public record FurnitureColorSource(Color dyedColor, int[] fireworkColors) { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java index 6e70376e6..7ef9ffd4c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java @@ -67,8 +67,6 @@ public interface FurnitureConfig { } - CullingData cullingData(); - static Builder builder() { return new FurnitureConfigImpl.BuilderImpl(); } @@ -87,8 +85,6 @@ public interface FurnitureConfig { Builder behavior(FurnitureBehavior behavior); - Builder cullingData(CullingData cullingData); - FurnitureConfig build(); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java index 8d6fe94b8..632e4fe80 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java @@ -23,7 +23,6 @@ class FurnitureConfigImpl implements FurnitureConfig { private final Map variants; private final Map>> events; private final FurnitureBehavior behavior; - private final CullingData cullingData; @Nullable private final LootTable lootTable; @@ -32,14 +31,12 @@ class FurnitureConfigImpl implements FurnitureConfig { @NotNull Map variants, @NotNull Map>> events, @NotNull FurnitureBehavior behavior, - @Nullable CullingData cullingData, @Nullable LootTable lootTable) { this.id = id; this.settings = settings; this.variants = ImmutableMap.copyOf(variants); this.lootTable = lootTable; this.behavior = behavior; - this.cullingData = cullingData; this.events = events; } @@ -75,11 +72,6 @@ class FurnitureConfigImpl implements FurnitureConfig { return this.behavior; } - @Override - public CullingData cullingData() { - return this.cullingData; - } - @Nullable @Override public FurnitureVariant getVariant(String variantName) { @@ -93,11 +85,10 @@ class FurnitureConfigImpl implements FurnitureConfig { private Map>> events; private LootTable lootTable; private FurnitureBehavior behavior = EmptyFurnitureBehavior.INSTANCE; - private CullingData cullingData; @Override public FurnitureConfig build() { - return new FurnitureConfigImpl(this.id, this.settings, this.variants, this.events, this.behavior, this.cullingData, this.lootTable); + return new FurnitureConfigImpl(this.id, this.settings, this.variants, this.events, this.behavior, this.lootTable); } @Override @@ -130,12 +121,6 @@ class FurnitureConfigImpl implements FurnitureConfig { return this; } - @Override - public Builder cullingData(CullingData cullingData) { - this.cullingData = cullingData; - return this; - } - @Override public Builder behavior(FurnitureBehavior behavior) { this.behavior = behavior; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java index 34f862f4f..e1bb1bd0e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java @@ -68,12 +68,20 @@ public class FurnitureDataAccessor { this.data.putByteArray(ITEM, item.toByteArray()); } + public FurnitureColorSource getColorSource() { + return new FurnitureColorSource(dyedColor().orElse(null), fireworkExplosionColors().orElse(null)); + } + public Optional fireworkExplosionColors() { if (this.data.containsKey(FIREWORK_EXPLOSION_COLORS)) return Optional.of(this.data.getIntArray(FIREWORK_EXPLOSION_COLORS)); return Optional.empty(); } public void setFireworkExplosionColors(int[] colors) { + if (colors == null) { + this.data.remove(FIREWORK_EXPLOSION_COLORS); + return; + } this.data.putIntArray(FIREWORK_EXPLOSION_COLORS, colors); } @@ -82,7 +90,11 @@ public class FurnitureDataAccessor { return Optional.empty(); } - public void setDyedColor(Color color) { + public void setDyedColor(@Nullable Color color) { + if (color == null) { + this.data.remove(DYED_COLOR); + return; + } this.data.putInt(DYED_COLOR, color.color()); } 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 5f8ca181a..99d4ee3a0 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 @@ -1,6 +1,5 @@ package net.momirealms.craftengine.core.entity.furniture; -import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.util.Key; @@ -31,16 +30,14 @@ public interface FurnitureManager extends Manageable { Map loadedFurniture(); - boolean isFurnitureRealEntity(int entityId); + boolean isFurnitureMetaEntity(int entityId); @Nullable - Furniture loadedFurnitureByRealEntityId(int entityId); + Furniture loadedFurnitureByMetaEntityId(int entityId); @Nullable - default Furniture loadedFurnitureByRealEntity(AbstractEntity entity) { - return loadedFurnitureByRealEntityId(entity.entityID()); - } + Furniture loadedFurnitureByVirtualEntityId(int entityId); @Nullable - Furniture loadedFurnitureByEntityId(int entityId); + Furniture loadedFurnitureByColliderEntityId(int entityId); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java index 056a8f47f..206a5ddf7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureVariant.java @@ -2,12 +2,16 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.plugin.entityculling.CullingData; +import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; import java.util.Optional; -public record FurnitureVariant(FurnitureElementConfig[] elementConfigs, - FurnitureHitBoxConfig[] furnitureHitBoxConfigs, +public record FurnitureVariant(String name, + @Nullable CullingData cullingData, + FurnitureElementConfig[] elementConfigs, + FurnitureHitBoxConfig[] hitBoxConfigs, Optional externalModel, Optional dropOffset) { } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java index 118d5bd92..6a625f941 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElement.java @@ -2,8 +2,14 @@ package net.momirealms.craftengine.core.entity.furniture.element; import net.momirealms.craftengine.core.entity.player.Player; +import java.util.function.Consumer; + public interface FurnitureElement { + int[] virtualEntityIds(); + + void collectVirtualEntityId(Consumer collector); + void show(Player player); void hide(Player player); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractFurnitureHitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractFurnitureHitBoxConfig.java index f977145e7..b121fbe02 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractFurnitureHitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/AbstractFurnitureHitBoxConfig.java @@ -10,7 +10,11 @@ public abstract class AbstractFurnitureHitBoxConfig i protected final boolean blocksBuilding; protected final boolean canBeHitByProjectile; - public AbstractFurnitureHitBoxConfig(SeatConfig[] seats, Vector3f position, boolean canUseItemOn, boolean blocksBuilding, boolean canBeHitByProjectile) { + public AbstractFurnitureHitBoxConfig(SeatConfig[] seats, + Vector3f position, + boolean canUseItemOn, + boolean blocksBuilding, + boolean canBeHitByProjectile) { this.seats = seats; this.position = position; this.canUseItemOn = canUseItemOn; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java index 7fbb4ca70..7f3d894c4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java @@ -4,26 +4,41 @@ import net.momirealms.craftengine.core.entity.furniture.Collider; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.entity.seat.SeatOwner; +import net.momirealms.craftengine.core.world.EntityHitResult; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.collision.AABB; -import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; public interface FurnitureHitBox extends SeatOwner { - Seat[] seats(); - - AABB aabb(); - Vec3d position(); - @Nullable - Collider collider(); + Seat[] seats(); + + AABB[] aabb(); + + List colliders(); int[] virtualEntityIds(); + void collectVirtualEntityIds(Consumer collector); + void show(Player player); void hide(Player player); FurnitureHitBoxConfig config(); + + default Optional clip(Vec3d min, Vec3d max) { + for (AABB value : aabb()) { + Optional clip = value.clip(min, max); + if (clip.isPresent()) { + return clip; + } + } + return Optional.empty(); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java index e1730c7f2..689cb130c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java @@ -2,8 +2,12 @@ package net.momirealms.craftengine.core.entity.furniture.hitbox; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.seat.SeatConfig; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; import org.joml.Vector3f; +import java.util.function.Consumer; + public interface FurnitureHitBoxConfig { H create(Furniture furniture); @@ -17,4 +21,6 @@ public interface FurnitureHitBoxConfig { boolean canBeHitByProjectile(); boolean canUseItemOn(); + + void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer); } 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 ae13804e6..2f89bf90b 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 @@ -4,6 +4,7 @@ import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.advancement.AdvancementType; import net.momirealms.craftengine.core.block.entity.render.ConstantBlockEntityRenderer; import net.momirealms.craftengine.core.entity.AbstractEntity; +import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.CooldownData; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; @@ -217,12 +218,18 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void removeTrackedBlockEntities(Collection renders); + public abstract void addTrackedFurniture(int entityId, Furniture furniture); + public abstract void clearTrackedBlockEntities(); @Override public void remove() { } + public abstract void removeTrackedFurniture(int entityId); + + public abstract void clearTrackedFurniture(); + public abstract WorldPosition eyePosition(); @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java index dcf69a5e4..c7dd3f082 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/parameter/FurnitureParameterProvider.java @@ -14,7 +14,7 @@ public class FurnitureParameterProvider implements ChainParameterProvider f.config().id()); CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, Furniture::uuid); - CONTEXT_FUNCTIONS.put(DirectContextParameters.VARIANT, Furniture::getCurrentVariantName); + CONTEXT_FUNCTIONS.put(DirectContextParameters.VARIANT, f -> f.getCurrentVariant().name()); CONTEXT_FUNCTIONS.put(DirectContextParameters.X, furniture -> furniture.position().x()); CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, furniture -> furniture.position().y()); CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, furniture -> furniture.position().z()); 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 57fb85de9..233b09cf5 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 @@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.entity.player.Player; public interface EntityPacketHandler { - default boolean handleEntitiesRemove(IntList entityIds) { + default boolean handleEntitiesRemove(NetWorkUser user, IntList entityIds) { return false; } 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 4461e5d93..08308a8fa 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 @@ -266,6 +266,8 @@ public final class ResourceConfigUtils { 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 if (o instanceof Number number) { + return new Vector3f(number.floatValue()); } else { String stringFormat = o.toString(); String[] split = stringFormat.split(","); From 34cb3936b084fd76d13bd8bbf47237b1a8e2304b Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 3 Dec 2025 04:45:29 +0800 Subject: [PATCH 070/135] =?UTF-8?q?=E5=AE=B6=E5=85=B7=E9=87=8D=E6=9E=84par?= =?UTF-8?q?t4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/event/FurnitureAttemptPlaceEvent.java | 2 - .../furniture/BukkitFurnitureManager.java | 2 +- .../furniture/FurnitureEventListener.java | 12 +- .../ItemDisplayFurnitureElementConfig.java | 2 +- .../hitbox/AbstractFurnitureHitBox.java | 11 - .../hitbox/BukkitFurnitureHitboxTypes.java | 2 +- .../hitbox/InteractionFurnitureHitbox.java | 65 ++-- .../hitbox/ShulkerFurnitureHitbox.java | 131 ++++++++ .../hitbox/ShulkerFurnitureHitboxConfig.java | 317 ++++++++++++++++++ .../item/behavior/FurnitureItemBehavior.java | 7 +- .../plugin/network/BukkitNetworkManager.java | 70 ++-- .../handler/FurniturePacketHandler.java | 5 - .../core/entity/furniture/Furniture.java | 34 +- .../entity/furniture/FurnitureConfig.java | 1 - .../entity/furniture/FurnitureConfigImpl.java | 1 - .../furniture/hitbox/FurnitureHitBox.java | 14 +- .../furniture/hitbox/FurnitureHitboxPart.java | 7 + 17 files changed, 533 insertions(+), 150 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java index d7d053413..7e492239e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java @@ -1,12 +1,10 @@ package net.momirealms.craftengine.bukkit.api.event; -import net.momirealms.craftengine.core.entity.furniture.AnchorType; import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; import net.momirealms.craftengine.core.entity.player.InteractionHand; import org.bukkit.Location; import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; 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 a1dee6c88..21693ed18 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 @@ -153,7 +153,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } // 当元数据实体被卸载了 - protected void handleMetaEntityUnload(Entity entity) { + protected void handleMetaEntityUnload(ItemDisplay entity) { // 不是持久化的 if (!entity.isPersistent()) { return; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java index a8b0e8da5..534a3b841 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java @@ -75,8 +75,8 @@ public class FurnitureEventListener implements Listener { public void onChunkUnload(ChunkUnloadEvent event) { Entity[] entities = event.getChunk().getEntities(); for (Entity entity : entities) { - if (entity instanceof ItemDisplay) { - this.manager.handleMetaEntityUnload(entity); + if (entity instanceof ItemDisplay itemDisplay) { + this.manager.handleMetaEntityUnload(itemDisplay); } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { this.manager.handleCollisionEntityUnload(entity); } @@ -87,8 +87,8 @@ public class FurnitureEventListener implements Listener { public void onWorldUnload(WorldUnloadEvent event) { List entities = event.getWorld().getEntities(); for (Entity entity : entities) { - if (entity instanceof ItemDisplay) { - this.manager.handleMetaEntityUnload(entity); + if (entity instanceof ItemDisplay itemDisplay) { + this.manager.handleMetaEntityUnload(itemDisplay); } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { this.manager.handleCollisionEntityUnload(entity); } @@ -98,8 +98,8 @@ public class FurnitureEventListener implements Listener { @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onEntityUnload(EntityRemoveFromWorldEvent event) { Entity entity = event.getEntity(); - if (entity instanceof ItemDisplay) { - this.manager.handleMetaEntityUnload(entity); + if (entity instanceof ItemDisplay itemDisplay) { + this.manager.handleMetaEntityUnload(itemDisplay); } else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) { this.manager.handleCollisionEntityUnload(entity); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java index cf58886a5..caa972e9b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -2,11 +2,11 @@ package net.momirealms.craftengine.bukkit.entity.furniture.element; import it.unimi.dsi.fastutil.ints.IntArrayList; import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; -import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.core.entity.display.Billboard; import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; import net.momirealms.craftengine.core.entity.player.Player; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java index 2789d4347..f40ead283 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/AbstractFurnitureHitBox.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; -import it.unimi.dsi.fastutil.ints.IntArrayList; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider; import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat; import net.momirealms.craftengine.bukkit.nms.FastNMS; @@ -8,7 +7,6 @@ import net.momirealms.craftengine.core.entity.furniture.Collider; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; -import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.entity.seat.SeatConfig; import net.momirealms.craftengine.core.world.Vec3d; @@ -51,13 +49,4 @@ public abstract class AbstractFurnitureHitBox implements FurnitureHitBox { Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); return new BukkitCollider(world.serverWorld(), nmsAABB, position.x, position.y, position.z, canBeHitByProjectile, canCollide, blocksBuilding); } - - protected Object createDespawnPacket(int[] entityIds) { - return FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList(entityIds)); - } - - @Override - public void hide(Player player) { - player.sendPacket(createDespawnPacket(this.virtualEntityIds()), false); - } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java index c1ef83ddb..b930275bc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java @@ -8,7 +8,7 @@ public class BukkitFurnitureHitboxTypes extends FurnitureHitBoxTypes { static { register(INTERACTION, InteractionFurnitureHitboxConfig.FACTORY); -// register(SHULKER, ShulkerFurnitureHitboxConfig.FACTORY); + register(SHULKER, ShulkerFurnitureHitboxConfig.FACTORY); // register(HAPPY_GHAST, HappyGhastFurnitureHitboxConfig.FACTORY); // register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index 65836a4b5..836434808 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; +import it.unimi.dsi.fastutil.ints.IntArrayList; import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; import net.momirealms.craftengine.bukkit.nms.FastNMS; @@ -7,6 +8,7 @@ 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.Collider; import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; @@ -15,24 +17,22 @@ import net.momirealms.craftengine.core.world.collision.AABB; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import java.util.function.Consumer; public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { private final InteractionFurnitureHitboxConfig config; - private final Vec3d position; - private final AABB aabb; private final Collider collider; - private final int interactionId; private final Object spawnPacket; + private final Object despawnPacket; + private final FurnitureHitboxPart part; public InteractionFurnitureHitbox(Furniture furniture, InteractionFurnitureHitboxConfig config) { super(furniture, config); this.config = config; WorldPosition position = furniture.position(); - this.position = Furniture.getRelativePosition(position, config.position()); - this.aabb = AABB.fromInteraction(this.position, config.size.x, config.size.y); - this.collider = createCollider(furniture.world(), this.position, this.aabb, false, config.blocksBuilding(), config.canBeHitByProjectile()); - this.interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + Vec3d pos = Furniture.getRelativePosition(position, config.position()); + AABB aabb = AABB.fromInteraction(pos, config.size.x, config.size.y); + this.collider = createCollider(furniture.world(), pos, aabb, false, config.blocksBuilding(), config.canBeHitByProjectile()); + int interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); List values = new ArrayList<>(4); InteractionEntityData.Height.addEntityDataIfNotDefaultValue(config.size.y, values); InteractionEntityData.Width.addEntityDataIfNotDefaultValue(config.size.x, values); @@ -42,11 +42,28 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { } this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - this.interactionId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, + interactionId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 ), - FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.interactionId, values) + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, values) )); + this.part = new FurnitureHitboxPart(interactionId, aabb, pos); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(interactionId); }}); + } + + @Override + public List colliders() { + return List.of(this.collider); + } + + @Override + public List parts() { + return List.of(this.part); + } + + @Override + public InteractionFurnitureHitboxConfig config() { + return this.config; } @Override @@ -55,31 +72,7 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { } @Override - public AABB[] aabb() { - return new AABB[] { this.aabb }; - } - - @Override - public Vec3d position() { - return this.position; - } - - @Override - public List colliders() { - return List.of(this.collider); - } - - @Override - public int[] virtualEntityIds() { - return new int[] { this.interactionId }; - } - - @Override - public void collectVirtualEntityIds(Consumer collector) { - collector.accept(this.interactionId); - } - - public InteractionFurnitureHitboxConfig config() { - return this.config; + public void hide(Player player) { + player.sendPacket(this.despawnPacket, false); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java new file mode 100644 index 000000000..69842ded6 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java @@ -0,0 +1,131 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +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.MEntityTypes; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.QuaternionUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.WorldPosition; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { + private final ShulkerFurnitureHitboxConfig config; + private final List parts; + private final List colliders; + private final Object spawnPacket; + private final Object despawnPacket; + + public ShulkerFurnitureHitbox(Furniture furniture, ShulkerFurnitureHitboxConfig config) { + super(furniture, config); + this.config = config; + int[] entityIds = acquireEntityIds(CoreReflections.instance$Entity$ENTITY_COUNTER::incrementAndGet); + WorldPosition position = furniture.position(); + Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - position.yRot()), 0f).conjugate(); + Vector3f offset = conjugated.transform(new Vector3f(config.position())); + double x = position.x(); + double y = position.y(); + double z = position.z(); + float yaw = position.yRot(); + double originalY = y + offset.y; + double integerPart = Math.floor(originalY); + double fractionalPart = originalY - integerPart; + double processedY = (fractionalPart >= 0.5) ? integerPart + 1 : originalY; + List packets = new ArrayList<>(); + List colliders = new ArrayList<>(); + List parts = new ArrayList<>(); + + packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityIds[0], UUID.randomUUID(), x + offset.x, originalY, z - offset.z, 0, yaw, + MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityIds[1], UUID.randomUUID(), x + offset.x, processedY, z - offset.z, 0, yaw, + MEntityTypes.SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(config.cachedShulkerValues))); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityIds[0], entityIds[1])); + + // fix some special occasions + if (originalY != processedY) { + double deltaY = originalY - processedY; + short ya = (short) (deltaY * 8192); + try { + packets.add(NetworkReflections.constructor$ClientboundMoveEntityPacket$Pos.newInstance( + entityIds[1], (short) 0, ya, (short) 0, true + )); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to construct ClientboundMoveEntityPacket$Pos", e); + } + } + if (VersionHelper.isOrAbove1_20_5() && config.scale != 1) { + try { + Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); + CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale); + packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[1], Collections.singletonList(attributeInstance))); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to apply scale attribute", e); + } + } + config.spawner.accept(entityIds, position.world(), x, y, z, yaw, offset, packets::add, colliders::add, parts::add); + this.parts = parts; + this.colliders = colliders; + this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList(entityIds)); + } + + @Override + public List colliders() { + return this.colliders; + } + + @Override + public List parts() { + return this.parts; + } + + @Override + public void show(Player player) { + player.sendPacket(this.spawnPacket, false); + } + + @Override + public void hide(Player player) { + player.sendPacket(this.despawnPacket, false); + } + + @Override + public ShulkerFurnitureHitboxConfig config() { + return this.config; + } + + public int[] acquireEntityIds(Supplier entityIdSupplier) { + if (config.interactionEntity) { + if (config.direction.stepY() != 0) { + // 展示实体 // 潜影贝 // 交互实体 + return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()}; + } else { + // 展示实体 // 潜影贝 // 交互实体1 // 交互实体2 + return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()}; + } + } else { + // 展示实体 // 潜影贝 + return new int[] {entityIdSupplier.get(), entityIdSupplier.get()}; + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java new file mode 100644 index 000000000..4a86ba359 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -0,0 +1,317 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; +import net.momirealms.craftengine.bukkit.entity.data.ShulkerData; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider; +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.bukkit.util.DirectionUtils; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; +import net.momirealms.craftengine.core.entity.seat.SeatConfig; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.QuaternionUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +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.collision.AABB; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; + +public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig { + public static final Factory FACTORY = new Factory(); + public final float scale; + public final byte peek; + public final boolean interactive; + public final boolean interactionEntity; + public final Direction direction; + public final DirectionalShulkerSpawner spawner; + public final List cachedShulkerValues = new ArrayList<>(); + public final AABBCreator aabbCreator; + + public ShulkerFurnitureHitboxConfig(SeatConfig[] seats, + Vector3f position, + boolean canUseItemOn, + boolean blocksBuilding, + boolean canBeHitByProjectile, + float scale, + byte peek, + boolean interactive, + boolean interactionEntity, + Direction direction) { + super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile); + this.scale = scale; + this.peek = peek; + this.interactive = interactive; + this.interactionEntity = interactionEntity; + this.direction = direction; + + ShulkerData.Peek.addEntityDataIfNotDefaultValue(peek, this.cachedShulkerValues); + ShulkerData.Color.addEntityDataIfNotDefaultValue((byte) 0, this.cachedShulkerValues); + ShulkerData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedShulkerValues); + ShulkerData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedShulkerValues); + ShulkerData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedShulkerValues); // NO AI + ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedShulkerValues); // Invisible + + List cachedInteractionValues = new ArrayList<>(); + float shulkerHeight = (getPhysicalPeek(peek * 0.01F) + 1) * scale; + if (direction == Direction.UP) { + InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues); + InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues); + InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues); + this.spawner = (entityIds, world, x, y, z, yaw, offset, packets, collider, aabb) -> { + collider.accept(this.createCollider(Direction.UP, world, offset, x, y, z, entityIds[1], aabb)); + if (interactionEntity) { + packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw, + MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); + if (canUseItemOn) { + Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d)); + } + } + }; + this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.UP, offset, x, y, z); + } else if (direction == Direction.DOWN) { + InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues); + InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues); + InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues); + this.spawner = (entityIds, world, x, y, z, yaw, offset, packets, collider, aabb) -> { + collider.accept(this.createCollider(Direction.DOWN, world, offset, x, y, z, entityIds[1], aabb)); + packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.of(ShulkerData.AttachFace.createEntityDataIfNotDefaultValue(CoreReflections.instance$Direction$UP)))); + if (interactionEntity) { + packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f - shulkerHeight + scale, z - offset.z, 0, yaw, + MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); + if (canUseItemOn) { + Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d)); + } + } + }; + this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.DOWN, offset, x, y, z); + } else { + InteractionEntityData.Height.addEntityDataIfNotDefaultValue(scale + 0.01f, cachedInteractionValues); + InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues); + InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues); + this.spawner = (entityIds, world, x, y, z, yaw, offset, packets, collider, aabb) -> { + Direction shulkerAnchor = getOriginalDirection(direction, Direction.fromYaw(yaw)); + Direction shulkerDirection = shulkerAnchor.opposite(); + collider.accept(this.createCollider(shulkerDirection, world, offset, x, y, z, entityIds[1], aabb)); + packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.of(ShulkerData.AttachFace.createEntityDataIfNotDefaultValue(DirectionUtils.toNMSDirection(shulkerAnchor))))); + if (interactionEntity) { + // first interaction + packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw, + MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); + // second interaction + double distance = shulkerHeight - scale; + packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityIds[3], UUID.randomUUID(), x + offset.x + shulkerDirection.stepX() * distance, y + offset.y - 0.005f, z - offset.z + shulkerDirection.stepZ() * distance, 0, yaw, + MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues))); + if (canUseItemOn) { + Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z); + Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d1, scale, scale), vec3d1)); + aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.fromInteraction(vec3d2, scale, scale), vec3d2)); + } + } + }; + this.aabbCreator = (x, y, z, yaw, offset) -> { + Direction shulkerAnchor = getOriginalDirection(direction, Direction.fromYaw(yaw)); + Direction shulkerDirection = shulkerAnchor.opposite(); + return createAABB(shulkerDirection, offset, x, y, z); + }; + } + } + + public static float getPhysicalPeek(float peek) { + return 0.5F - MiscUtils.sin((0.5F + peek) * 3.1415927F) * 0.5F; + } + + @Override + public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { + if (this.blocksBuilding) { + Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - targetPos.yRot()), 0f).conjugate(); + Vector3f offset = conjugated.transform(new Vector3f(position())); + aabbConsumer.accept(this.aabbCreator.create(targetPos.x, targetPos.y, targetPos.z, targetPos.yRot, offset)); + } + } + + public float scale() { + return scale; + } + + public byte peek() { + return peek; + } + + public boolean interactive() { + return interactive; + } + + public boolean interactionEntity() { + return interactionEntity; + } + + public Direction direction() { + return direction; + } + + @Override + public ShulkerFurnitureHitbox create(Furniture furniture) { + return new ShulkerFurnitureHitbox(furniture, this); + } + + @FunctionalInterface + public interface AABBCreator { + + AABB create(double x, double y, double z, float yaw, Vector3f offset); + } + + @FunctionalInterface + public interface DirectionalShulkerSpawner { + + void accept(int[] entityIds, + World world, + double x, + double y, + double z, + float yaw, + Vector3f offset, + Consumer packets, + Consumer collider, + Consumer aabb); + } + + public Collider createCollider(Direction direction, World world, + Vector3f offset, double x, double y, double z, + int entityId, + Consumer aabb) { + AABB ceAABB = createAABB(direction, offset, x, y, z); + Object level = world.serverWorld(); + Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); + aabb.accept(new FurnitureHitboxPart(entityId, ceAABB, new Vec3d(x, y, z))); + return new BukkitCollider(level, nmsAABB, x, y, z, this.canBeHitByProjectile(), true, this.blocksBuilding()); + } + + public AABB createAABB(Direction direction, Vector3f relativePos, double x, double y, double z) { + float peek = getPhysicalPeek(this.peek * 0.01F); + double x1 = -this.scale * 0.5; + double y1 = 0.0; + double z1 = -this.scale * 0.5; + double x2 = this.scale * 0.5; + double y2 = this.scale; + double z2 = this.scale * 0.5; + + double dx = (double) direction.stepX() * peek * (double) this.scale; + if (dx > 0) { + x2 += dx; + } else if (dx < 0) { + x1 += dx; + } + double dy = (double) direction.stepY() * peek * (double) this.scale; + if (dy > 0) { + y2 += dy; + } else if (dy < 0) { + y1 += dy; + } + double dz = (double) direction.stepZ() * peek * (double) this.scale; + if (dz > 0) { + z2 += dz; + } else if (dz < 0) { + z1 += dz; + } + double minX = x + x1 + relativePos.x(); + double maxX = x + x2 + relativePos.x(); + double minY = y + y1 + relativePos.y(); + double maxY = y + y2 + relativePos.y(); + double minZ = z + z1 - relativePos.z(); + double maxZ = z + z2 - relativePos.z(); + return new AABB(minX, minY, minZ, maxX, maxY, maxZ); + } + + public static Direction getOriginalDirection(Direction newDirection, Direction oldDirection) { + switch (newDirection) { + case NORTH -> { + return switch (oldDirection) { + case NORTH -> Direction.NORTH; + case SOUTH -> Direction.SOUTH; + case WEST -> Direction.EAST; + case EAST -> Direction.WEST; + default -> throw new IllegalStateException("Unexpected value: " + oldDirection); + }; + } + case SOUTH -> { + return switch (oldDirection) { + case SOUTH -> Direction.NORTH; + case WEST -> Direction.WEST; + case EAST -> Direction.EAST; + case NORTH -> Direction.SOUTH; + default -> throw new IllegalStateException("Unexpected value: " + oldDirection); + }; + } + case WEST -> { + return switch (oldDirection) { + case SOUTH -> Direction.EAST; + case WEST -> Direction.NORTH; + case EAST -> Direction.SOUTH; + case NORTH -> Direction.WEST; + default -> throw new IllegalStateException("Unexpected value: " + oldDirection); + }; + } + case EAST -> { + return switch (oldDirection) { + case SOUTH -> Direction.WEST; + case WEST -> Direction.SOUTH; + case EAST -> Direction.NORTH; + case NORTH -> Direction.EAST; + default -> throw new IllegalStateException("Unexpected value: " + oldDirection); + }; + } + default -> throw new IllegalStateException("Unexpected value: " + newDirection); + } + } + + public static class Factory implements FurnitureHitBoxConfigFactory { + + @Override + public ShulkerFurnitureHitboxConfig create(Map arguments) { + 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 = ResourceConfigUtils.getAsEnum(arguments.get("direction"), Direction.class, Direction.UP); + boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive"); + boolean interactionEntity = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interaction-entity", true), "interaction-entity"); + boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on"); + boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile"); + boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); + return new ShulkerFurnitureHitboxConfig( + SeatConfig.fromObj(arguments.get("seats")), + position, + canUseItemOn, blocksBuilding, canBeHitByProjectile, + scale, peek, interactive, interactionEntity, directionEnum + ); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index 3b85c0ef7..19a9aea16 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -6,13 +6,11 @@ import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.util.DirectionUtils; import net.momirealms.craftengine.bukkit.util.EventUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; -import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.entity.player.Player; @@ -28,7 +26,10 @@ 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.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.*; +import net.momirealms.craftengine.core.util.Cancellable; +import net.momirealms.craftengine.core.util.ItemUtils; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; 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 57a5f59cc..578925913 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 @@ -60,6 +60,7 @@ import net.momirealms.craftengine.core.advancement.network.AdvancementHolder; import net.momirealms.craftengine.core.advancement.network.AdvancementProgress; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.font.FontManager; @@ -358,8 +359,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerNMSPacketConsumer(new EntityEventListener(), NetworkReflections.clazz$ClientboundEntityEventPacket); registerNMSPacketConsumer(new MovePosAndRotateEntityListener(), NetworkReflections.clazz$ClientboundMoveEntityPacket$PosRot); registerNMSPacketConsumer(new MovePosEntityListener(), NetworkReflections.clazz$ClientboundMoveEntityPacket$Pos); - registerNMSPacketConsumer(new RotateHeadListener(), NetworkReflections.clazz$ClientboundRotateHeadPacket); - registerNMSPacketConsumer(new SetEntityMotionListener(), NetworkReflections.clazz$ClientboundSetEntityMotionPacket); registerNMSPacketConsumer(new FinishConfigurationListener(), NetworkReflections.clazz$ClientboundFinishConfigurationPacket); registerNMSPacketConsumer(new LoginFinishedListener(), NetworkReflections.clazz$ClientboundLoginFinishedPacket); registerNMSPacketConsumer(new UpdateTagsListener(), NetworkReflections.clazz$ClientboundUpdateTagsPacket); @@ -1765,43 +1764,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } } - public static class RotateHeadListener implements NMSPacketListener { - - @Override - public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { - int entityId; - try { - entityId = (int) NetworkReflections.methodHandle$ClientboundRotateHeadPacket$entityIdGetter.invokeExact(packet); - } catch (Throwable t) { - CraftEngine.instance().logger().warn("Failed to get entity id from ClientboundRotateHeadPacket", t); - return; - } - if (BukkitFurnitureManager.instance().isFurnitureMetaEntity(entityId)) { - System.out.println("RotateHeadListener"); - event.setCancelled(true); - } - } - } - - public static class SetEntityMotionListener implements NMSPacketListener { - - @Override - public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) { - if (!VersionHelper.isOrAbove1_21_6()) return; - int entityId; - try { - entityId = (int) NetworkReflections.methodHandle$ClientboundSetEntityMotionPacket$idGetter.invokeExact(packet); - } catch (Throwable t) { - CraftEngine.instance().logger().warn("Failed to get entity id from ClientboundSetEntityMotionPacket", t); - return; - } - if (BukkitFurnitureManager.instance().isFurnitureMetaEntity(entityId)) { - System.out.println("SetEntityMotionListener"); - event.setCancelled(true); - } - } - } - public static class FinishConfigurationListener implements NMSPacketListener { @SuppressWarnings("unchecked") @@ -3726,11 +3688,13 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes // 先检查碰撞箱部分是否存在 FurnitureHitBox hitBox = furniture.hitboxByEntityId(entityId); if (hitBox == null) return; - Vec3d pos = hitBox.position(); - - // 检查玩家是否能破坏此点 - if (!serverPlayer.canInteractPoint(pos, 16d)) { - return; + for (FurnitureHitboxPart part : hitBox.parts()) { + if (part.entityId() == entityId) { + // 检查玩家是否能破坏此点 + if (!serverPlayer.canInteractPoint(part.pos(), 16d)) { + return; + } + } } FurnitureAttemptBreakEvent preBreakEvent = new FurnitureAttemptBreakEvent(serverPlayer.platformPlayer(), furniture); @@ -3787,10 +3751,18 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes // 先检查碰撞箱部分是否存在 FurnitureHitBox hitBox = furniture.hitboxByEntityId(entityId); if (hitBox == null) return; - Vec3d pos = hitBox.position(); - - // 检测距离 - if (!serverPlayer.canInteractPoint(pos, 16d)) { + FurnitureHitboxPart part = null; + for (FurnitureHitboxPart p : hitBox.parts()) { + if (p.entityId() == entityId) { + Vec3d pos = p.pos(); + // 检测距离 + if (!serverPlayer.canInteractPoint(pos, 16d)) { + return; + } + part = p; + } + } + if (part == null) { return; } @@ -3799,7 +3771,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes Vector direction = eyeLocation.getDirection(); Location endLocation = eyeLocation.clone(); endLocation.add(direction.multiply(serverPlayer.getCachedInteractionRange())); - Optional result = hitBox.clip(LocationUtils.toVec3d(eyeLocation), LocationUtils.toVec3d(endLocation)); + Optional result = part.aabb().clip(LocationUtils.toVec3d(eyeLocation), LocationUtils.toVec3d(endLocation)); if (result.isEmpty()) { return; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java index 3e7277429..a73ebc3e2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java @@ -33,9 +33,4 @@ public class FurniturePacketHandler implements EntityPacketHandler { public void handleMove(NetWorkUser user, NMSPacketEvent event, Object packet) { event.setCancelled(true); } - - @Override - public void handleMoveAndRotate(NetWorkUser user, NMSPacketEvent event, Object packet) { - event.setCancelled(true); - } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index 32f0b7c4a..a69ccd26f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.plugin.entityculling.CullingData; @@ -24,13 +25,12 @@ import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; -import java.lang.ref.WeakReference; import java.util.UUID; public abstract class Furniture implements Cullable { public final FurnitureConfig config; public final FurnitureDataAccessor dataAccessor; - public final WeakReference metaDataEntity; + public final Entity metaDataEntity; protected CullingData cullingData; protected FurnitureVariant currentVariant; @@ -44,11 +44,11 @@ public abstract class Furniture implements Cullable { protected Furniture(Entity metaDataEntity, FurnitureDataAccessor data, FurnitureConfig config) { this.config = config; this.dataAccessor = data; - this.metaDataEntity = new WeakReference<>(metaDataEntity); + this.metaDataEntity = metaDataEntity; this.setVariant(config.getVariant(data)); } - public WeakReference metaDataEntity() { + public Entity metaDataEntity() { return this.metaDataEntity; } @@ -75,11 +75,11 @@ public abstract class Furniture implements Cullable { for (int i = 0; i < furnitureHitBoxConfigs.length; i++) { FurnitureHitBox hitbox = furnitureHitBoxConfigs[i].create(this); this.hitboxes[i] = hitbox; - for (int hitboxEntityId : hitbox.virtualEntityIds()) { - this.hitboxMap.put(hitboxEntityId, hitbox); + for (FurnitureHitboxPart part : hitbox.parts()) { + this.hitboxMap.put(part.entityId(), hitbox); + virtualEntityIds.add(part.entityId()); } colliders.addAll(hitbox.colliders()); - hitbox.collectVirtualEntityIds(virtualEntityIds::addLast); } // 虚拟碰撞箱的实体id this.virtualEntityIds = virtualEntityIds.toIntArray(); @@ -122,9 +122,7 @@ public abstract class Furniture implements Cullable { } public UUID uuid() { - Entity entity = this.metaDataEntity.get(); - if (entity == null) return null; - return entity.uuid(); + return this.metaDataEntity.uuid(); } @Override @@ -158,9 +156,7 @@ public abstract class Furniture implements Cullable { } public boolean isValid() { - Entity entity = this.metaDataEntity.get(); - if (entity == null) return false; - return entity.isValid(); + return this.metaDataEntity.isValid(); } public abstract void destroy(); @@ -178,15 +174,11 @@ public abstract class Furniture implements Cullable { } public WorldPosition position() { - Entity entity = this.metaDataEntity.get(); - if (entity == null) return null; - return entity.position(); + return this.metaDataEntity.position(); } public int entityId() { - Entity entity = this.metaDataEntity.get(); - if (entity == null) return -1; - return entity.entityID(); + return this.metaDataEntity.entityID(); } public Vec3d getRelativePosition(Vector3f position) { @@ -200,8 +192,6 @@ public abstract class Furniture implements Cullable { } public World world() { - Entity entity = this.metaDataEntity.get(); - if (entity == null) return null; - return entity.world(); + return this.metaDataEntity.world(); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java index 7ef9ffd4c..b9f34c864 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java @@ -5,7 +5,6 @@ import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.function.Function; -import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java index 632e4fe80..a68613fef 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java @@ -7,7 +7,6 @@ import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.function.Function; -import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java index 7f3d894c4..0a249acd4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java @@ -6,25 +6,17 @@ import net.momirealms.craftengine.core.entity.seat.Seat; import net.momirealms.craftengine.core.entity.seat.SeatOwner; import net.momirealms.craftengine.core.world.EntityHitResult; import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.collision.AABB; import java.util.List; import java.util.Optional; -import java.util.function.Consumer; public interface FurnitureHitBox extends SeatOwner { - Vec3d position(); - Seat[] seats(); - AABB[] aabb(); - List colliders(); - int[] virtualEntityIds(); - - void collectVirtualEntityIds(Consumer collector); + List parts(); void show(Player player); @@ -33,8 +25,8 @@ public interface FurnitureHitBox extends SeatOwner { FurnitureHitBoxConfig config(); default Optional clip(Vec3d min, Vec3d max) { - for (AABB value : aabb()) { - Optional clip = value.clip(min, max); + for (FurnitureHitboxPart value : parts()) { + Optional clip = value.aabb().clip(min, max); if (clip.isPresent()) { return clip; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java new file mode 100644 index 000000000..e69a747f5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.core.entity.furniture.hitbox; + +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.collision.AABB; + +public record FurnitureHitboxPart(int entityId, AABB aabb, Vec3d pos) { +} From 7082020220fd4c1e98f598649c7b41cd8abc7cce Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Wed, 3 Dec 2025 07:38:36 +0800 Subject: [PATCH 071/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=BF=AB=E4=B9=90?= =?UTF-8?q?=E6=81=B6=E9=AD=82=E7=A2=B0=E6=92=9E=E7=AE=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/api/CraftEngineFurniture.java | 8 +- .../event/AsyncResourcePackCacheEvent.java | 2 +- .../behavior/NearLiquidBlockBehavior.java | 2 +- .../block/behavior/OnLiquidBlockBehavior.java | 2 +- .../entity/data/ItemDisplayEntityData.java | 2 +- .../hitbox/BukkitFurnitureHitboxTypes.java | 7 +- .../hitbox/HappyGhastFurnitureHitbox.java | 88 +++++++++++++++++++ .../HappyGhastFurnitureHitboxConfig.java | 83 +++++++++++++++++ .../hitbox/InteractionFurnitureHitbox.java | 14 +-- .../InteractionFurnitureHitboxConfig.java | 30 ++++++- .../hitbox/ShulkerFurnitureHitbox.java | 12 +-- .../hitbox/ShulkerFurnitureHitboxConfig.java | 28 +++--- .../bukkit/plugin/BukkitPlatform.java | 4 +- .../command/feature/DebugItemDataCommand.java | 2 +- .../feature/DebugSpawnFurnitureCommand.java | 5 +- .../handler/ProjectilePacketHandler.java | 2 +- .../plugin/network/payload/PayloadHelper.java | 4 +- .../craftengine/core/block/BlockBehavior.java | 2 +- .../behavior/IsPathFindableBlockBehavior.java | 2 +- .../element/BlockEntityElementConfigs.java | 7 +- .../entity/furniture/FurnitureConfig.java | 1 + .../furniture/FurnitureDataAccessor.java | 3 +- .../element/FurnitureElementConfigs.java | 7 +- .../hitbox/FurnitureHitBoxTypes.java | 7 +- .../craftengine/core/item/Item.java | 2 +- .../craftengine/core/item/ItemSettings.java | 4 +- .../recipe/CustomSmithingTransformRecipe.java | 2 +- .../item/recipe/CustomSmithingTrimRecipe.java | 2 +- .../function/ApplyBonusCountFunction.java | 4 +- .../core/pack/model/ItemModels.java | 2 +- .../model/condition/ConditionProperties.java | 2 +- .../RangeDispatchProperties.java | 2 +- .../pack/model/select/SelectProperties.java | 2 +- .../pack/model/special/SignSpecialModel.java | 2 +- .../pack/model/special/SpecialModels.java | 2 +- .../core/pack/model/tint/Tints.java | 2 +- .../core/pack/obfuscation/ObfA.java | 2 +- .../plugin/command/sender/AbstractSender.java | 2 +- .../core/plugin/config/Config.java | 4 +- .../plugin/config/StringKeyConstructor.java | 10 +-- .../argument/ExpressionTemplateArgument.java | 2 +- .../template/argument/TemplateArguments.java | 2 +- .../context/function/DamageFunction.java | 2 +- .../context/function/OpenWindowFunction.java | 2 +- .../function/ReplaceFurnitureFunction.java | 3 +- .../function/SpawnFurnitureFunction.java | 2 +- .../context/function/ToastFunction.java | 2 +- .../core/plugin/locale/LangData.java | 2 +- .../core/registry/BuiltInRegistries.java | 6 +- .../craftengine/core/registry/Registries.java | 6 +- .../craftengine/core/sound/SoundData.java | 2 +- .../craftengine/core/util/Pair.java | 8 +- .../core/util/ReflectionUtils.java | 2 +- .../core/util/ResourceConfigUtils.java | 72 ++++++++------- .../craftengine/core/util/SNBTReader.java | 2 +- .../craftengine/core/util/Tuple.java | 12 +-- .../craftengine/core/util/TypeUtils.java | 14 +-- 57 files changed, 359 insertions(+), 152 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java 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 5d5c4532f..7f2a1cabe 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 @@ -80,7 +80,7 @@ public final class CraftEngineFurniture { * * @param location location * @param furnitureId furniture to place - * @param anchorType anchor id + * @param anchorType anchor type * @return the loaded furniture */ @Nullable @@ -98,7 +98,7 @@ public final class CraftEngineFurniture { * * @param location location * @param furniture furniture to place - * @param anchorType anchor id + * @param anchorType anchor type * @return the loaded furniture */ @NotNull @@ -114,7 +114,7 @@ public final class CraftEngineFurniture { * * @param location location * @param furnitureId furniture to place - * @param anchorType anchor id + * @param anchorType anchor type * @param playSound whether to play place sounds * @return the loaded furniture */ @@ -133,7 +133,7 @@ public final class CraftEngineFurniture { * * @param location location * @param furniture furniture to place - * @param anchorType anchor id + * @param anchorType anchor type * @param playSound whether to play place sounds * @return the loaded furniture */ 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 index 62099bba8..dfa064fa3 100644 --- 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 @@ -49,7 +49,7 @@ public final class AsyncResourcePackCacheEvent extends Event { * 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 id. + * 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. diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java index 060282f7d..9c9b02c65 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/NearLiquidBlockBehavior.java @@ -45,7 +45,7 @@ public class NearLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior { public static class Factory implements BlockBehaviorFactory { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-id", List.of("water"))); + List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water"))); boolean stackable = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("stackable", false), "stackable"); int delay = ResourceConfigUtils.getAsInt(arguments.getOrDefault("delay", 0), "delay"); List positionsToCheck = MiscUtils.getAsStringList(arguments.getOrDefault("positions", List.of())); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java index 3e8a3fed1..e71780c7c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java @@ -40,7 +40,7 @@ public class OnLiquidBlockBehavior extends AbstractCanSurviveBlockBehavior { public static class Factory implements BlockBehaviorFactory { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-id", List.of("water"))); + List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water"))); boolean stackable = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("stackable", false), "stackable"); int delay = ResourceConfigUtils.getAsInt(arguments.getOrDefault("delay", 0), "delay"); return new OnLiquidBlockBehavior(block, delay, stackable, liquidTypes.contains("water"), liquidTypes.contains("lava")); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java index 091f8654e..09d39ebdc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ItemDisplayEntityData.java @@ -6,7 +6,7 @@ public class ItemDisplayEntityData extends DisplayEntityData { // Item display only public static final ItemDisplayEntityData DisplayedItem = new ItemDisplayEntityData<>(ItemDisplayEntityData.class, EntityDataValue.Serializers$ITEM_STACK, CoreReflections.instance$ItemStack$EMPTY); /** - * Display id: + * Display type: * 0 = NONE * 1 = THIRD_PERSON_LEFT_HAND * 2 = THIRD_PERSON_RIGHT_HAND diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java index b930275bc..10c1a33d2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxTypes; +import net.momirealms.craftengine.core.util.VersionHelper; public class BukkitFurnitureHitboxTypes extends FurnitureHitBoxTypes { @@ -9,7 +10,9 @@ public class BukkitFurnitureHitboxTypes extends FurnitureHitBoxTypes { static { register(INTERACTION, InteractionFurnitureHitboxConfig.FACTORY); register(SHULKER, ShulkerFurnitureHitboxConfig.FACTORY); -// register(HAPPY_GHAST, HappyGhastFurnitureHitboxConfig.FACTORY); -// register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); + // register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); + if (VersionHelper.isOrAbove1_21_6()) { + register(HAPPY_GHAST, HappyGhastFurnitureHitboxConfig.FACTORY); + } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java new file mode 100644 index 000000000..8905b7c2f --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java @@ -0,0 +1,88 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +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.MEntityTypes; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.QuaternionUtils; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +public class HappyGhastFurnitureHitbox extends AbstractFurnitureHitBox { + private final HappyGhastFurnitureHitboxConfig config; + private final Collider collider; + private final Object spawnPacket; + private final Object despawnPacket; + private final FurnitureHitboxPart part; + + public HappyGhastFurnitureHitbox(Furniture furniture, HappyGhastFurnitureHitboxConfig config) { + super(furniture, config); + this.config = config; + WorldPosition position = furniture.position(); + Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - position.yRot()), 0f).conjugate(); + Vector3f offset = conjugated.transform(new Vector3f(config.position())); + Vec3d pos = Furniture.getRelativePosition(position, config.position()); + AABB aabb = AABB.fromInteraction(pos, 3 * config.scale(), 3 * config.scale()); + int happyGhastId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + List packets = new ArrayList<>(3); + packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + happyGhastId, UUID.randomUUID(), position.x + offset.x, position.y + offset.y, position.z + offset.z, 0, position.yRot, + MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(happyGhastId, config.cachedValues())); + if (config.scale() != 1) { + try { + Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); + CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale()); + packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(happyGhastId, Collections.singletonList(attributeInstance))); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to apply scale attribute", e); + } + } + this.collider = createCollider(furniture.world(), pos, aabb, config.hardCollision(), config.blocksBuilding(), config.canBeHitByProjectile()); + this.part = new FurnitureHitboxPart(happyGhastId, aabb, pos); + this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(happyGhastId); }}); + } + + @Override + public List colliders() { + return List.of(this.collider); + } + + @Override + public List parts() { + return List.of(this.part); + } + + @Override + public void show(Player player) { + player.sendPacket(this.spawnPacket, false); + } + + @Override + public void hide(Player player) { + player.sendPacket(this.despawnPacket, false); + } + + @Override + public HappyGhastFurnitureHitboxConfig config() { + return this.config; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java new file mode 100644 index 000000000..7e28b68da --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java @@ -0,0 +1,83 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import net.momirealms.craftengine.bukkit.entity.data.HappyGhastData; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.seat.SeatConfig; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class HappyGhastFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig { + public static final Factory FACTORY = new Factory(); + private final double scale; + private final boolean hardCollision; + private final List cachedValues = new ArrayList<>(3); + + public HappyGhastFurnitureHitboxConfig(SeatConfig[] seats, + Vector3f position, + boolean canUseItemOn, + boolean blocksBuilding, + boolean canBeHitByProjectile, + double scale, + boolean hardCollision) { + super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile); + this.scale = scale; + this.hardCollision = hardCollision; + HappyGhastData.StaysStill.addEntityDataIfNotDefaultValue(hardCollision, this.cachedValues); + HappyGhastData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedValues); // NO AI + HappyGhastData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues); // Invisible + } + + public double scale() { + return scale; + } + + public boolean hardCollision() { + return hardCollision; + } + + public List cachedValues() { + return cachedValues; + } + + @Override + public HappyGhastFurnitureHitbox create(Furniture furniture) { + return new HappyGhastFurnitureHitbox(furniture, this); + } + + @Override + public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { + if (this.blocksBuilding) { + Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); + aabbConsumer.accept(AABB.fromInteraction(relativePosition, 3 * this.scale, 3 * this.scale)); + } + } + + public static class Factory implements FurnitureHitBoxConfigFactory { + + @Override + public FurnitureHitBoxConfig create(Map arguments) { + Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0), "position"); + boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on"); + boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); + boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile"); + double scale = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("scale", 1), "scale"); + boolean hardCollision = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("hard-collision", true), "hard-collision"); + return new HappyGhastFurnitureHitboxConfig( + SeatConfig.fromObj(arguments.get("seats")), + position, canUseItemOn, blocksBuilding, canBeHitByProjectile, + scale, hardCollision + ); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index 836434808..44380525c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -1,8 +1,6 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; import it.unimi.dsi.fastutil.ints.IntArrayList; -import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; -import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; 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; @@ -14,7 +12,6 @@ import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; -import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -30,22 +27,15 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { this.config = config; WorldPosition position = furniture.position(); Vec3d pos = Furniture.getRelativePosition(position, config.position()); - AABB aabb = AABB.fromInteraction(pos, config.size.x, config.size.y); + AABB aabb = AABB.fromInteraction(pos, config.size().x, config.size().y); this.collider = createCollider(furniture.world(), pos, aabb, false, config.blocksBuilding(), config.canBeHitByProjectile()); int interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); - List values = new ArrayList<>(4); - InteractionEntityData.Height.addEntityDataIfNotDefaultValue(config.size.y, values); - InteractionEntityData.Width.addEntityDataIfNotDefaultValue(config.size.x, values); - InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(config.responsive, values); - if (config.invisible) { - BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, values); - } this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( interactionId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 ), - FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, values) + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, config.cachedValues()) )); this.part = new FurnitureHitboxPart(interactionId, aabb, pos); this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(interactionId); }}); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java index 7711218c2..a3e853d17 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -1,5 +1,7 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; +import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; +import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; @@ -10,16 +12,19 @@ import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import org.joml.Vector3f; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.function.Consumer; public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig { public static final Factory FACTORY = new Factory(); - public static final InteractionFurnitureHitboxConfig DEFAULT = new InteractionFurnitureHitboxConfig(new SeatConfig[0], new Vector3f(), false, false, false, false, new Vector3f(1,1,1), true); + public static final InteractionFurnitureHitboxConfig DEFAULT = new InteractionFurnitureHitboxConfig(); - public final Vector3f size; - public final boolean responsive; - public final boolean invisible; + private final Vector3f size; + private final boolean responsive; + private final boolean invisible; + private final List cachedValues = new ArrayList<>(4); public InteractionFurnitureHitboxConfig(SeatConfig[] seats, Vector3f position, @@ -33,6 +38,19 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon this.size = size; this.responsive = responsive; this.invisible = invisible; + InteractionEntityData.Height.addEntityDataIfNotDefaultValue(size.y, cachedValues); + InteractionEntityData.Width.addEntityDataIfNotDefaultValue(size.x, cachedValues); + InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(responsive, cachedValues); + if (invisible) { + BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, cachedValues); + } + } + + private InteractionFurnitureHitboxConfig() { + super(new SeatConfig[0], new Vector3f(), false, false, false); + this.size = new Vector3f(1); + this.responsive = true; + this.invisible = false; } public Vector3f size() { @@ -47,6 +65,10 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon return invisible; } + public List cachedValues() { + return cachedValues; + } + @Override public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { if (this.blocksBuilding) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java index 69842ded6..9a6c39d43 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java @@ -58,7 +58,7 @@ public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { entityIds[1], UUID.randomUUID(), x + offset.x, processedY, z - offset.z, 0, yaw, MEntityTypes.SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0 )); - packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(config.cachedShulkerValues))); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(config.cachedShulkerValues()))); packets.add(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityIds[0], entityIds[1])); // fix some special occasions @@ -73,16 +73,16 @@ public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { CraftEngine.instance().logger().warn("Failed to construct ClientboundMoveEntityPacket$Pos", e); } } - if (VersionHelper.isOrAbove1_20_5() && config.scale != 1) { + if (VersionHelper.isOrAbove1_20_5() && config.scale() != 1) { try { Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); - CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale); + CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale()); packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[1], Collections.singletonList(attributeInstance))); } catch (ReflectiveOperationException e) { CraftEngine.instance().logger().warn("Failed to apply scale attribute", e); } } - config.spawner.accept(entityIds, position.world(), x, y, z, yaw, offset, packets::add, colliders::add, parts::add); + config.spawner().accept(entityIds, position.world(), x, y, z, yaw, offset, packets::add, colliders::add, parts::add); this.parts = parts; this.colliders = colliders; this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); @@ -115,8 +115,8 @@ public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { } public int[] acquireEntityIds(Supplier entityIdSupplier) { - if (config.interactionEntity) { - if (config.direction.stepY() != 0) { + if (config.interactionEntity()) { + if (config.direction().stepY() != 0) { // 展示实体 // 潜影贝 // 交互实体 return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()}; } else { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java index 4a86ba359..004b73d26 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -32,14 +32,14 @@ import java.util.function.Consumer; public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig { public static final Factory FACTORY = new Factory(); - public final float scale; - public final byte peek; - public final boolean interactive; - public final boolean interactionEntity; - public final Direction direction; - public final DirectionalShulkerSpawner spawner; - public final List cachedShulkerValues = new ArrayList<>(); - public final AABBCreator aabbCreator; + private final float scale; + private final byte peek; + private final boolean interactive; + private final boolean interactionEntity; + private final Direction direction; + private final DirectionalShulkerSpawner spawner; + private final List cachedShulkerValues = new ArrayList<>(6); + private final AABBCreator aabbCreator; public ShulkerFurnitureHitboxConfig(SeatConfig[] seats, Vector3f position, @@ -178,6 +178,14 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< return direction; } + public DirectionalShulkerSpawner spawner() { + return spawner; + } + + public List cachedShulkerValues() { + return cachedShulkerValues; + } + @Override public ShulkerFurnitureHitbox create(Furniture furniture) { return new ShulkerFurnitureHitbox(furniture, this); @@ -297,8 +305,8 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< @Override public ShulkerFurnitureHitboxConfig create(Map arguments) { - Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); - float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", "1"), "scale"); + 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 = ResourceConfigUtils.getAsEnum(arguments.get("direction"), Direction.class, Direction.UP); boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive"); 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 352ffd619..9a6729b0a 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 @@ -39,7 +39,7 @@ public class BukkitPlatform implements Platform { Map map = (Map) MRegistryOps.NBT.convertTo(MRegistryOps.JAVA, tag); return map.get("root"); } catch (CommandSyntaxException e) { - throw new LocalizedResourceConfigException("warning.config.id.snbt.invalid_syntax", e, nbt); + throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e, nbt); } } @@ -55,7 +55,7 @@ public class BukkitPlatform implements Platform { CompoundTag map = (CompoundTag) MRegistryOps.NBT.convertTo(MRegistryOps.SPARROW_NBT, tag); return map.get("root"); } catch (CommandSyntaxException e) { - throw new LocalizedResourceConfigException("warning.config.id.snbt.invalid_syntax", e, nbt); + throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e, nbt); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java index 1b4b1a942..861c01eb5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugItemDataCommand.java @@ -115,7 +115,7 @@ public class DebugItemDataCommand extends BukkitCommandFeature { } else if (nbt instanceof short[]) { value = Arrays.toString((short[]) nbt); } else { - value = "Unknown array id"; + value = "Unknown array type"; } } else { value = nbt.toString(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java index c07060832..7b32aeba1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java @@ -24,6 +24,7 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder @@ -34,7 +35,7 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature { // fixme 指令 @@ -47,7 +48,7 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature codec = (NetworkCodec) BuiltInRegistries.MOD_PACKET.getValue(data.type()); if (codec == null) { - CraftEngine.instance().logger().warn("Unknown data id class: " + data.getClass().getName()); + CraftEngine.instance().logger().warn("Unknown data type class: " + data.getClass().getName()); return; } FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); @@ -65,7 +65,7 @@ public class PayloadHelper { @SuppressWarnings("unchecked") NetworkCodec codec = (NetworkCodec) BuiltInRegistries.MOD_PACKET.getValue(type); if (codec == null) { - Debugger.COMMON.debug(() -> "Unknown data id received: " + type); + Debugger.COMMON.debug(() -> "Unknown data type received: " + type); return; } 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 b32258402..6d794219f 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 @@ -78,7 +78,7 @@ public abstract class BlockBehavior { return (boolean) superMethod.call(); } - // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType id + // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType type // 1.20.5+ BlockState state, PathComputationType pathComputationType public boolean isPathFindable(Object thisBlock, Object[] args, Callable superMethod) throws Exception { return (boolean) superMethod.call(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java index dca06a1bc..7fe7d8e06 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/IsPathFindableBlockBehavior.java @@ -4,7 +4,7 @@ import java.util.concurrent.Callable; public interface IsPathFindableBlockBehavior { - // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType id + // 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType type // 1.20.5+ BlockState state, PathComputationType pathComputationType boolean isPathFindable(Object thisBlock, Object[] args, Callable superMethod) throws Exception; } 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 index 1dabfa3be..c355c4eb2 100644 --- 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 @@ -15,14 +15,15 @@ public abstract class BlockEntityElementConfigs { public static final Key TEXT_DISPLAY = Key.of("craftengine:text_display"); public static final Key ITEM = Key.of("craftengine:item"); - public static void register(Key key, BlockEntityElementConfigFactory type) { - ((WritableRegistry) BuiltInRegistries.BLOCK_ENTITY_ELEMENT_TYPE) + 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); + @SuppressWarnings("unchecked") + BlockEntityElementConfigFactory factory = (BlockEntityElementConfigFactory) BuiltInRegistries.BLOCK_ENTITY_ELEMENT_TYPE.getValue(type); if (factory == null) { throw new LocalizedResourceConfigException("warning.config.block.state.entity_renderer.invalid_type", type.toString()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java index b9f34c864..cd5634893 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java @@ -48,6 +48,7 @@ public interface FurnitureConfig { if (optionalVariant.isPresent()) { variantName = optionalVariant.get(); } else { + @SuppressWarnings("deprecation") Optional optionalAnchorType = accessor.anchorType(); if (optionalAnchorType.isPresent()) { variantName = optionalAnchorType.get().name().toLowerCase(Locale.ROOT); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java index e1bb1bd0e..c6bc0793a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java @@ -106,6 +106,7 @@ public class FurnitureDataAccessor { this.data.putString(VARIANT, variant); } + @SuppressWarnings("deprecation") @ApiStatus.Obsolete public Optional anchorType() { if (this.data.containsKey(ANCHOR_TYPE)) return Optional.of(AnchorType.byId(this.data.getInt(ANCHOR_TYPE))); @@ -113,7 +114,7 @@ public class FurnitureDataAccessor { } @ApiStatus.Obsolete - public FurnitureDataAccessor anchorType(AnchorType type) { + public FurnitureDataAccessor anchorType(@SuppressWarnings("deprecation") AnchorType type) { this.data.putInt(ANCHOR_TYPE, type.getId()); return this; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java index 8e375a743..7247cdf0e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java @@ -15,14 +15,15 @@ public class FurnitureElementConfigs { public static final Key TEXT_DISPLAY = Key.of("craftengine:text_display"); public static final Key ITEM = Key.of("craftengine:item"); - public static void register(Key key, FurnitureElementConfigFactory type) { - ((WritableRegistry) BuiltInRegistries.FURNITURE_ELEMENT_TYPE) + public static void register(Key key, FurnitureElementConfigFactory type) { + ((WritableRegistry>) BuiltInRegistries.FURNITURE_ELEMENT_TYPE) .register(ResourceKey.create(Registries.FURNITURE_ELEMENT_TYPE.location(), key), type); } public static FurnitureElementConfig fromMap(Map arguments) { Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(it -> Key.withDefaultNamespace(it, "craftengine")).orElse(ITEM_DISPLAY); - FurnitureElementConfigFactory factory = BuiltInRegistries.FURNITURE_ELEMENT_TYPE.getValue(type); + @SuppressWarnings("unchecked") + FurnitureElementConfigFactory factory = (FurnitureElementConfigFactory) BuiltInRegistries.FURNITURE_ELEMENT_TYPE.getValue(type); if (factory == null) { throw new LocalizedResourceConfigException("warning.config.furniture.element.invalid_type", type.toString()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxTypes.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxTypes.java index d4f002a9c..61fc5b871 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxTypes.java @@ -17,14 +17,15 @@ public class FurnitureHitBoxTypes { public static final Key VIRTUAL = Key.of("minecraft:virtual"); public static final Key CUSTOM = Key.of("minecraft:custom"); - public static void register(Key key, FurnitureHitBoxConfigFactory factory) { - ((WritableRegistry) BuiltInRegistries.FURNITURE_HITBOX_TYPE) + public static void register(Key key, FurnitureHitBoxConfigFactory factory) { + ((WritableRegistry>) BuiltInRegistries.FURNITURE_HITBOX_TYPE) .register(ResourceKey.create(Registries.FURNITURE_HITBOX_TYPE.location(), key), factory); } public static FurnitureHitBoxConfig fromMap(Map arguments) { Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(Key::of).orElse(FurnitureHitBoxTypes.INTERACTION); - FurnitureHitBoxConfigFactory factory = BuiltInRegistries.FURNITURE_HITBOX_TYPE.getValue(type); + @SuppressWarnings("unchecked") + FurnitureHitBoxConfigFactory factory = (FurnitureHitBoxConfigFactory) BuiltInRegistries.FURNITURE_HITBOX_TYPE.getValue(type); if (factory == null) { throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.invalid_type", type.toString()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java index f48a6bf39..5b7c2f563 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java @@ -28,7 +28,7 @@ import java.util.Optional; * This interface provides methods for managing item properties such as custom model data, * damage, display name, lore, enchantments, and tags. * - * @param the id of the item implementation + * @param the type of the item implementation */ public interface Item { 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 0c125097d..64a67c8dd 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 @@ -446,8 +446,8 @@ 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 = ResourceConfigUtils.getAsVector3f(args.getOrDefault("translation", "0"), "translation"); - Vector3f scale = ResourceConfigUtils.getAsVector3f(args.getOrDefault("scale", "1"), "scale"); + Vector3f translation = ResourceConfigUtils.getAsVector3f(args.getOrDefault("translation", 0), "translation"); + Vector3f scale = ResourceConfigUtils.getAsVector3f(args.getOrDefault("scale", 1), "scale"); Quaternionf rotation = ResourceConfigUtils.getAsQuaternionf(ResourceConfigUtils.get(args, "rotation"), "rotation"); double range = ResourceConfigUtils.getAsDouble(args.getOrDefault("range", 1), "range"); return settings -> settings.projectileMeta(new ProjectileMeta(customTridentItemId, displayType, billboard, scale, translation, rotation, range)); 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 206655f05..190ee8004 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 @@ -190,7 +190,7 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip @Override public CustomSmithingTransformRecipe readMap(Key id, Map arguments) { List base = MiscUtils.getAsStringList(arguments.get("base")); - List template = MiscUtils.getAsStringList(arguments.get("template-id")); + List template = MiscUtils.getAsStringList(arguments.get("template-type")); List addition = MiscUtils.getAsStringList(arguments.get("addition")); boolean mergeComponents = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("merge-components", true), "merge-components"); boolean mergeEnchantments = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("merge-enchantments", false), "merge-enchantments"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java index c3a749435..7119a5f48 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java @@ -141,7 +141,7 @@ public class CustomSmithingTrimRecipe extends AbstractRecipe @Override public CustomSmithingTrimRecipe readMap(Key id, Map arguments) { List base = MiscUtils.getAsStringList(arguments.get("base")); - List template = MiscUtils.getAsStringList(arguments.get("template-id")); + List template = MiscUtils.getAsStringList(arguments.get("template-type")); List addition = MiscUtils.getAsStringList(arguments.get("addition")); Key pattern = VersionHelper.isOrAbove1_21_5() ? Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("pattern"), "warning.config.recipe.smithing_trim.missing_pattern")) : null; return new CustomSmithingTrimRecipe<>(id, diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java index b5d118862..c0475500d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/function/ApplyBonusCountFunction.java @@ -87,12 +87,12 @@ public class ApplyBonusCountFunction extends AbstractLootConditionalFunction< public static Formula fromMap(Map map) { String type = (String) map.get("type"); if (type == null) { - throw new NullPointerException("number id cannot be null"); + throw new NullPointerException("number type cannot be null"); } Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); FormulaFactory factory = BuiltInRegistries.FORMULA_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown formula id: " + type); + throw new IllegalArgumentException("Unknown formula type: " + type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java index c79c52a8a..e6d151250 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java @@ -75,7 +75,7 @@ public class ItemModels { Key key = Key.withDefaultNamespace(type, "minecraft"); ItemModelReader reader = BuiltInRegistries.ITEM_MODEL_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid item model id: " + key); + throw new IllegalArgumentException("Invalid item model type: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java index ce19f4e75..f2d5022fa 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java @@ -80,7 +80,7 @@ public class ConditionProperties { Key key = Key.withDefaultNamespace(type, "minecraft"); ConditionPropertyReader reader = BuiltInRegistries.CONDITION_PROPERTY_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid condition property id: " + key); + throw new IllegalArgumentException("Invalid condition property type: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java index 0d98ca88b..a8f09bcc0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java @@ -71,7 +71,7 @@ public class RangeDispatchProperties { Key key = Key.withDefaultNamespace(type, "minecraft"); RangeDispatchPropertyReader reader = BuiltInRegistries.RANGE_DISPATCH_PROPERTY_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid range dispatch property id: " + key); + throw new IllegalArgumentException("Invalid range dispatch property type: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java index 464c79fc3..e6f5b46e0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java @@ -71,7 +71,7 @@ public class SelectProperties { Key key = Key.withDefaultNamespace(type, "minecraft"); SelectPropertyReader reader = BuiltInRegistries.SELECT_PROPERTY_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid select property id: " + key); + throw new IllegalArgumentException("Invalid select property type: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java index cdfffd3bb..b6a83032c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java @@ -45,7 +45,7 @@ public class SignSpecialModel implements SpecialModel { @Override public SpecialModel create(Map arguments) { Key type = Key.of(arguments.get("type").toString()); - String woodType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("wood-id"), "warning.config.item.model.special.sign.missing_wood_type"); + String woodType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("wood-type"), "warning.config.item.model.special.sign.missing_wood_type"); String texture = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.sign.missing_texture"); return new SignSpecialModel(type, woodType, texture); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java index 8b222da2b..c27c4bba3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java @@ -80,7 +80,7 @@ public class SpecialModels { Key key = Key.withDefaultNamespace(type, "minecraft"); SpecialModelReader reader = BuiltInRegistries.SPECIAL_MODEL_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid special model id: " + key); + throw new IllegalArgumentException("Invalid special model type: " + key); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java index 8148873b5..d44d3102d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java @@ -65,7 +65,7 @@ public class Tints { Key key = Key.withDefaultNamespace(type, "minecraft"); TintReader reader = BuiltInRegistries.TINT_READER.getValue(key); if (reader == null) { - throw new IllegalArgumentException("Invalid tint id: " + type); + throw new IllegalArgumentException("Invalid tint type: " + type); } return reader.read(json); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java b/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java index 7833ea8fc..9d545f67d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfA.java @@ -36,7 +36,7 @@ public enum ObfA { return type; } } - throw new IllegalArgumentException("Unknown resource id: " + xclf); + throw new IllegalArgumentException("Unknown resource type: " + xclf); } public static final byte[] VALUES = new byte[] { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java index 96a87ede0..ff1e71fa1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/AbstractSender.java @@ -9,7 +9,7 @@ import java.util.UUID; /** * Simple implementation of {@link Sender} using a {@link SenderFactory} * - * @param the command sender id + * @param the command sender type */ public final class AbstractSender implements Sender { private final Plugin plugin; 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 2ec08dbdf..df09a8b08 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 @@ -448,10 +448,10 @@ public class Config { // furniture furniture$hide_base_entity = config.getBoolean("furniture.hide-base-entity", true); - furniture$collision_entity_type = ColliderType.valueOf(config.getString("furniture.collision-entity-id", "interaction").toUpperCase(Locale.ENGLISH)); + furniture$collision_entity_type = ColliderType.valueOf(config.getString("furniture.collision-entity-type", "interaction").toUpperCase(Locale.ENGLISH)); // equipment - equipment$sacrificed_vanilla_armor$type = config.getString("equipment.sacrificed-vanilla-armor.id", "chainmail").toLowerCase(Locale.ENGLISH); + equipment$sacrificed_vanilla_armor$type = config.getString("equipment.sacrificed-vanilla-armor.type", "chainmail").toLowerCase(Locale.ENGLISH); if (!AbstractPackManager.ALLOWED_VANILLA_EQUIPMENT.contains(equipment$sacrificed_vanilla_armor$type)) { TranslationManager.instance().log("warning.config.equipment.invalid_sacrificed_armor", equipment$sacrificed_vanilla_armor$type); equipment$sacrificed_vanilla_armor$type = "chainmail"; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java index 4c3ea46fc..10f7f8bea 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java @@ -259,7 +259,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.byteValue(); } - throw new RuntimeException("Unexpected id: " + value.getClass().getName()); + throw new RuntimeException("Unexpected type: " + value.getClass().getName()); } } @@ -271,7 +271,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.shortValue(); } - throw new RuntimeException("Unexpected id: " + value.getClass().getName()); + throw new RuntimeException("Unexpected type: " + value.getClass().getName()); } } @@ -283,7 +283,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.longValue(); } - throw new RuntimeException("Unexpected id: " + value.getClass().getName()); + throw new RuntimeException("Unexpected type: " + value.getClass().getName()); } } @@ -295,7 +295,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.floatValue(); } - throw new RuntimeException("Unexpected id: " + value.getClass().getName()); + throw new RuntimeException("Unexpected type: " + value.getClass().getName()); } } @@ -307,7 +307,7 @@ public class StringKeyConstructor extends SafeConstructor { if (value instanceof Number number) { return number.doubleValue(); } - throw new RuntimeException("Unexpected id: " + value.getClass().getName()); + throw new RuntimeException("Unexpected type: " + value.getClass().getName()); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java index 790641d5d..cf02eeb46 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/ExpressionTemplateArgument.java @@ -61,7 +61,7 @@ public class ExpressionTemplateArgument implements TemplateArgument { public TemplateArgument create(Map arguments) { return new ExpressionTemplateArgument( arguments.getOrDefault("expression", "").toString(), - ValueType.valueOf(arguments.getOrDefault("value-id", "double").toString().toUpperCase(Locale.ROOT)) + ValueType.valueOf(arguments.getOrDefault("value-type", "double").toString().toUpperCase(Locale.ROOT)) ); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java index 4c0e9baa7..9b1a8ef91 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/argument/TemplateArguments.java @@ -58,7 +58,7 @@ public class TemplateArguments { Key key = Key.withDefaultNamespace(type0, Key.DEFAULT_NAMESPACE); TemplateArgumentFactory factory = BuiltInRegistries.TEMPLATE_ARGUMENT_FACTORY.getValue(key); if (factory == null) { - throw new IllegalArgumentException("Unknown argument id: " + type); + throw new IllegalArgumentException("Unknown argument type: " + type); } return factory.create(map); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java index fa33c8b7f..97e197b55 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java @@ -43,7 +43,7 @@ public class DamageFunction extends AbstractConditionalFunc @Override public Function create(Map arguments) { PlayerSelector selector = PlayerSelectors.fromObject(arguments.getOrDefault("target", "self"), conditionFactory()); - Key damageType = Key.of(ResourceConfigUtils.getAsStringOrNull(arguments.getOrDefault("damage-id", "generic"))); + Key damageType = Key.of(ResourceConfigUtils.getAsStringOrNull(arguments.getOrDefault("damage-type", "generic"))); NumberProvider amount = NumberProviders.fromObject(arguments.getOrDefault("amount", 1f)); return new DamageFunction<>(selector, damageType, amount, getPredicates(arguments)); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java index 1d1b17e4e..bbe2fe3d0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/OpenWindowFunction.java @@ -65,7 +65,7 @@ public class OpenWindowFunction extends AbstractConditional @Override public Function create(Map arguments) { String title = Optional.ofNullable(arguments.get("title")).map(String::valueOf).orElse(null); - String rawType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("gui-id"), "warning.config.function.open_window.missing_gui_type"); + String rawType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("gui-type"), "warning.config.function.open_window.missing_gui_type"); try { GuiType type = GuiType.valueOf(rawType.toUpperCase(Locale.ENGLISH)); return new OpenWindowFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), type, title); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java index 20dee5084..61eec5c6c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +@SuppressWarnings("deprecation") public class ReplaceFurnitureFunction extends AbstractConditionalFunction { private final Key newFurnitureId; private final NumberProvider x; @@ -95,7 +96,7 @@ public class ReplaceFurnitureFunction extends AbstractCondi NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", "")); NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", "")); - AnchorType anchorType = ResourceConfigUtils.getAsEnum(arguments.get("anchor-id"), AnchorType.class, null); + AnchorType anchorType = ResourceConfigUtils.getAsEnum(arguments.get("anchor-type"), AnchorType.class, null); boolean dropLoot = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("drop-loot", true), "drop-loot"); boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound"); return new ReplaceFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, anchorType, dropLoot, playSound, getPredicates(arguments)); 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 1db66e416..d29b7758e 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 @@ -87,7 +87,7 @@ public class SpawnFurnitureFunction extends AbstractConditi NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", "")); NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", "")); - String variant = ResourceConfigUtils.getAsStringOrNull(ResourceConfigUtils.get(arguments, "variant", "anchor-id")); + String variant = ResourceConfigUtils.getAsStringOrNull(ResourceConfigUtils.get(arguments, "variant", "anchor-type")); boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound"); return new SpawnFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, variant, playSound, getPredicates(arguments)); } 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 index ec63aa261..853cc7c28 100644 --- 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 @@ -63,7 +63,7 @@ public class ToastFunction extends AbstractConditionalFunct @Override public Function create(Map arguments) { AdvancementType advancementType; - String advancementName = arguments.getOrDefault("advancement-id", "goal").toString(); + String advancementName = arguments.getOrDefault("advancement-type", "goal").toString(); try { advancementType = AdvancementType.valueOf(advancementName.toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java index b0f6d19dd..b677d18a5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/LangData.java @@ -53,7 +53,7 @@ public class LangData { temp.put(result, entry.getValue()); } }, - () -> CraftEngine.instance().logger().warn("Unknown lang id: " + key) + () -> CraftEngine.instance().logger().warn("Unknown lang type: " + key) ); } else { temp.put(key, entry.getValue()); 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 62bf71645..287ca2752 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 @@ -89,10 +89,10 @@ public class BuiltInRegistries { 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, 64); - public static final Registry BLOCK_ENTITY_ELEMENT_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_ELEMENT_TYPE, 16); + public static final Registry> BLOCK_ENTITY_ELEMENT_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_ELEMENT_TYPE, 16); public static final Registry CRAFT_REMAINDER_FACTORY = createConstantBoundRegistry(Registries.CRAFT_REMAINDER_FACTORY, 16); - public static final Registry FURNITURE_ELEMENT_TYPE = createConstantBoundRegistry(Registries.FURNITURE_ELEMENT_TYPE, 16); - public static final Registry FURNITURE_HITBOX_TYPE = createConstantBoundRegistry(Registries.FURNITURE_HITBOX_TYPE, 16); + public static final Registry> FURNITURE_ELEMENT_TYPE = createConstantBoundRegistry(Registries.FURNITURE_ELEMENT_TYPE, 16); + public static final Registry> FURNITURE_HITBOX_TYPE = createConstantBoundRegistry(Registries.FURNITURE_HITBOX_TYPE, 16); private static Registry createConstantBoundRegistry(ResourceKey> key, int expectedSize) { return new ConstantBoundRegistry<>(key, expectedSize); 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 9b728d316..4afecdcd7 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 @@ -91,8 +91,8 @@ public class Registries { 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")); + public static final ResourceKey>> BLOCK_ENTITY_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_entity_element_type")); public static final ResourceKey> CRAFT_REMAINDER_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("craft_remainder_factory")); - public static final ResourceKey> FURNITURE_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_element_type")); - public static final ResourceKey> FURNITURE_HITBOX_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_hitbox_type")); + public static final ResourceKey>> FURNITURE_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_element_type")); + public static final ResourceKey>> FURNITURE_HITBOX_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_hitbox_type")); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java index cc7919e9c..af0356c6e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/SoundData.java @@ -22,7 +22,7 @@ public record SoundData(Key id, SoundValue volume, SoundValue pitch) { SoundValue pitchValue = Optional.ofNullable(SoundValue.of(map.get("pitch"))).orElse(volume); return new SoundData(id, volumeValue, pitchValue); } else { - throw new IllegalArgumentException("Illegal object id for sound data: " + obj.getClass()); + throw new IllegalArgumentException("Illegal object type for sound data: " + obj.getClass()); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java b/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java index 7d5e95ce6..de9774f5c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Pair.java @@ -6,8 +6,8 @@ import java.util.Objects; * A generic class representing a pair of values. * This class provides methods to create and access pairs of values. * - * @param the id of the left value - * @param the id of the right value + * @param the type of the left value + * @param the type of the right value */ public record Pair(L left, R right) { @@ -16,8 +16,8 @@ public record Pair(L left, R right) { * * @param left the left value * @param right the right value - * @param the id of the left value - * @param the id of the right value + * @param the type of the left value + * @param the type of the right value * @return a new {@link Pair} with the specified values */ public static Pair of(final L left, final R right) { 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 30472678c..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 @@ -439,7 +439,7 @@ public class ReflectionUtils { public static List getMethods(@NotNull Class clazz, @NotNull Class returnType, @NotNull Class... parameterTypes) { List list = new ArrayList<>(); for (Method method : clazz.getMethods()) { - if (!returnType.isAssignableFrom(method.getReturnType()) // check id + if (!returnType.isAssignableFrom(method.getReturnType()) // check type || method.getParameterCount() != parameterTypes.length // check length ) continue; Class[] types = method.getParameterTypes(); 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 08308a8fa..43a35d7dc 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 @@ -137,13 +137,13 @@ public final class ResourceConfigUtils { try { return Integer.parseInt(s.replace("_", "")); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.id.int", e, s, option); + throw new LocalizedResourceConfigException("warning.config.type.int", e, s, option); } } case Boolean b -> { return b ? 1 : 0; } - default -> throw new LocalizedResourceConfigException("warning.config.id.int", o.toString(), option); + default -> throw new LocalizedResourceConfigException("warning.config.type.int", o.toString(), option); } } @@ -162,11 +162,11 @@ public final class ResourceConfigUtils { try { return Double.parseDouble(s); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.id.double", e, s, option); + throw new LocalizedResourceConfigException("warning.config.type.double", e, s, option); } } default -> { - throw new LocalizedResourceConfigException("warning.config.id.double", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.type.double", o.toString(), option); } } } @@ -183,14 +183,14 @@ public final class ResourceConfigUtils { try { return Float.parseFloat(s); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.id.float", e, s, option); + throw new LocalizedResourceConfigException("warning.config.type.float", e, s, option); } } case Number number -> { return number.floatValue(); } default -> { - throw new LocalizedResourceConfigException("warning.config.id.float", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.type.float", o.toString(), option); } } } @@ -206,15 +206,15 @@ public final class ResourceConfigUtils { case Number n -> { if (n.byteValue() == 0) return false; if (n.byteValue() == 1) return true; - throw new LocalizedResourceConfigException("warning.config.id.boolean", String.valueOf(n), option); + throw new LocalizedResourceConfigException("warning.config.type.boolean", String.valueOf(n), option); } case String s -> { if (s.equalsIgnoreCase("true")) return true; if (s.equalsIgnoreCase("false")) return false; - throw new LocalizedResourceConfigException("warning.config.id.boolean", s, option); + throw new LocalizedResourceConfigException("warning.config.type.boolean", s, option); } default -> { - throw new LocalizedResourceConfigException("warning.config.id.boolean", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.type.boolean", o.toString(), option); } } } @@ -234,11 +234,11 @@ public final class ResourceConfigUtils { try { return Long.parseLong(s.replace("_", "")); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.id.long", e, s, option); + throw new LocalizedResourceConfigException("warning.config.type.long", e, s, option); } } default -> { - throw new LocalizedResourceConfigException("warning.config.id.long", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.type.long", o.toString(), option); } } } @@ -248,7 +248,7 @@ public final class ResourceConfigUtils { if (obj instanceof Map map) { return (Map) map; } - throw new LocalizedResourceConfigException("warning.config.id.map", String.valueOf(obj), option); + throw new LocalizedResourceConfigException("warning.config.type.map", String.valueOf(obj), option); } @SuppressWarnings("unchecked") @@ -259,24 +259,30 @@ public final class ResourceConfigUtils { if (obj instanceof Map map) { return (Map) map; } - throw new LocalizedResourceConfigException("warning.config.id.map", String.valueOf(obj), option); + 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 if (o instanceof Number number) { - return new Vector3f(number.floatValue()); - } 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.id.vector3f", stringFormat, option); + switch (o) { + case null -> { + return new Vector3f(); + } + case List list when list.size() == 3 -> { + return new Vector3f(Float.parseFloat(list.get(0).toString()), Float.parseFloat(list.get(1).toString()), Float.parseFloat(list.get(2).toString())); + } + case Number number -> { + return new Vector3f(number.floatValue()); + } + default -> { + 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); + } } } } @@ -295,7 +301,7 @@ public final class ResourceConfigUtils { } else if (split.length == 1) { return QuaternionUtils.toQuaternionf(0, (float) -Math.toRadians(Float.parseFloat(split[0])), 0); } else { - throw new LocalizedResourceConfigException("warning.config.id.quaternionf", stringFormat, option); + throw new LocalizedResourceConfigException("warning.config.type.quaternionf", stringFormat, option); } } } @@ -313,7 +319,7 @@ public final class ResourceConfigUtils { double d = Double.parseDouble(split[0]); return new Vec3d(d, d, d); } else { - throw new LocalizedResourceConfigException("warning.config.id.vec3d", stringFormat, option); + throw new LocalizedResourceConfigException("warning.config.type.vec3d", stringFormat, option); } } } @@ -347,7 +353,7 @@ public final class ResourceConfigUtils { public static AABB getAsAABB(Object o, String option) { switch (o) { - case null -> throw new LocalizedResourceConfigException("warning.config.id.aabb", "null", option); + case null -> throw new LocalizedResourceConfigException("warning.config.type.aabb", "null", option); case AABB aabb -> { return aabb; } @@ -367,7 +373,7 @@ public final class ResourceConfigUtils { try { args[i] = Double.parseDouble(list.get(i).toString()); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); } } } @@ -378,7 +384,7 @@ public final class ResourceConfigUtils { try { args[i] = Double.parseDouble(split[i]); } catch (NumberFormatException e) { - throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); } } } @@ -391,7 +397,7 @@ public final class ResourceConfigUtils { } else if (args.length == 6) { return new AABB(args[0], args[1], args[2], args[3], args[4], args[5]); } else { - throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option); + throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java index 27fd39740..0be81388d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java @@ -156,7 +156,7 @@ public final class SNBTReader extends DefaultStringReader { // 1.21.6的SNBT原版是支持 {key:[B;1,2b,0xFF]} 这种奇葩写法的, 越界部分会被自动舍弃, 如0xff的byte值为-1. // 如果需要和原版对齐, 那么只需要判断是否是数字就行了. // if (!(element instanceof Number number)) - // throw new IllegalArgumentException("Error element id at pos " + getCursor()); + // throw new IllegalArgumentException("Error element type at pos " + getCursor()); if (!(element instanceof Number number)) throw new IllegalArgumentException("Error parsing number at pos " + getCursor()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java b/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java index d91a2afaf..e4cf6baf5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Tuple.java @@ -6,9 +6,9 @@ import java.util.Objects; * A generic class representing a tuple with three values. * This class provides methods for creating and accessing tuples with three values. * - * @param the id of the left value - * @param the id of the middle value - * @param the id of the right value + * @param the type of the left value + * @param the type of the middle value + * @param the type of the right value */ public record Tuple(L left, M mid, R right) { @@ -18,9 +18,9 @@ public record Tuple(L left, M mid, R right) { * @param left the left value * @param mid the middle value * @param right the right value - * @param the id of the left value - * @param the id of the middle value - * @param the id of the right value + * @param the type of the left value + * @param the type of the middle value + * @param the type of the right value * @return a new {@link Tuple} with the specified values */ public static Tuple of(final L left, final M mid, final R right) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java index 02687838f..8e3b3dc1a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/TypeUtils.java @@ -8,18 +8,18 @@ public class TypeUtils { private TypeUtils() {} /** - * Checks if the provided object is of the specified id. + * Checks if the provided object is of the specified type. * If not, throws an IllegalArgumentException with a detailed message. * * @param object The object to check. - * @param expectedType The expected class id. - * @param The id parameter for expectedType. - * @return The object cast to the expected id if it matches. - * @throws IllegalArgumentException if the object's id does not match the expected id. + * @param expectedType The expected class type. + * @param The type parameter for expectedType. + * @return The object cast to the expected type if it matches. + * @throws IllegalArgumentException if the object's type does not match the expected type. */ public static T checkType(Object object, Class expectedType) { if (!expectedType.isInstance(object)) { - throw new IllegalArgumentException("Expected id: " + expectedType.getName() + + throw new IllegalArgumentException("Expected type: " + expectedType.getName() + ", but got: " + (object == null ? "null" : object.getClass().getName())); } return expectedType.cast(object); @@ -48,7 +48,7 @@ public class TypeUtils { } yield bytes; } - default -> throw new IllegalStateException("Unsupported id: " + type.toLowerCase(Locale.ENGLISH)); + default -> throw new IllegalStateException("Unsupported type: " + type.toLowerCase(Locale.ENGLISH)); }; } From 5e79d18cf8cb1c5f7657470d4a2f90765b3cb989 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 3 Dec 2025 16:27:31 +0800 Subject: [PATCH 072/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../momirealms/craftengine/core/pack/AbstractPackManager.java | 4 ++-- .../craftengine/core/plugin/config/blockbench/BBModel.java | 2 +- .../craftengine/core/util/BlockEntityTickersList.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) 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 1c9f0bd85..1aa740527 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 @@ -90,7 +90,7 @@ public abstract class AbstractPackManager implements PackManager { public static final String NEW_TRIM_MATERIAL = "custom"; public static final Set ALLOWED_VANILLA_EQUIPMENT = Set.of("chainmail", "diamond", "gold", "iron", "netherite"); - public static final Set ALLOWED_MODEL_TAGS = Set.of("parent", "ambientocclusion", "display", "textures", "elementConfigs", "gui_light", "overrides"); + public static final Set ALLOWED_MODEL_TAGS = Set.of("parent", "ambientocclusion", "display", "textures", "elements", "gui_light", "overrides"); private static final byte[] EMPTY_1X1_IMAGE; private static final byte[] EMPTY_EQUIPMENT_IMAGE; @@ -2136,7 +2136,7 @@ public abstract class AbstractPackManager implements PackManager { .resolve("empty.png"); try { Files.createDirectories(modelPath.getParent()); - Files.writeString(modelPath, "{\"textures\":{\"particle\":\"block/empty\"},\"elementConfigs\":[{\"from\":[0,0,0],\"to\":[0,0,0],\"color\":0,\"faces\":{\"north\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"east\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"south\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"west\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"up\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"down\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"}}}]}"); + Files.writeString(modelPath, "{\"textures\":{\"particle\":\"block/empty\"},\"elements\":[{\"from\":[0,0,0],\"to\":[0,0,0],\"color\":0,\"faces\":{\"north\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"east\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"south\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"west\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"up\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"},\"down\":{\"uv\":[0,0,0,0],\"texture\":\"#particle\"}}}]}"); } catch (IOException e) { this.plugin.logger().severe("Error writing empty block model", e); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java index 942a704a4..8c4b0e176 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/blockbench/BBModel.java @@ -11,7 +11,7 @@ public class BBModel { private String model_identifier; @SerializedName("visible_box") private int[] visible_box; - @SerializedName("elementConfigs") + @SerializedName("elements") private Element[] elements; @SerializedName("outliner") private OutLiner[] outliner; 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 index 639bd31b2..4ff57411a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java @@ -54,7 +54,7 @@ public final class BlockEntityTickersList extends ObjectArrayList because we want to avoid autoboxing when using contains final int requiredMatches = c.size(); From fb4ae9357bc4a6d7ef68536d08559be3559d8921 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 3 Dec 2025 17:19:16 +0800 Subject: [PATCH 073/135] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BA=A7=E6=A4=85?= =?UTF-8?q?=E4=BA=A4=E4=BA=92=E4=B8=8E=E4=B9=90=E9=AD=82=E7=9A=84=E7=94=9F?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hitbox/HappyGhastFurnitureHitbox.java | 44 ++++++++++--------- .../HappyGhastFurnitureHitboxConfig.java | 2 +- .../hitbox/InteractionFurnitureHitbox.java | 2 +- .../InteractionFurnitureHitboxConfig.java | 6 +-- .../hitbox/ShulkerFurnitureHitbox.java | 2 +- .../hitbox/ShulkerFurnitureHitboxConfig.java | 25 ++++++----- .../plugin/network/BukkitNetworkManager.java | 3 ++ .../configuration/furniture/wooden_chair.yml | 1 + .../furniture/hitbox/FurnitureHitboxPart.java | 2 +- 9 files changed, 47 insertions(+), 40 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java index 8905b7c2f..fcf530b95 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java @@ -11,12 +11,9 @@ import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.QuaternionUtils; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; -import org.joml.Quaternionf; -import org.joml.Vector3f; import java.util.ArrayList; import java.util.Collections; @@ -27,38 +24,37 @@ import java.util.function.Consumer; public class HappyGhastFurnitureHitbox extends AbstractFurnitureHitBox { private final HappyGhastFurnitureHitboxConfig config; private final Collider collider; - private final Object spawnPacket; private final Object despawnPacket; private final FurnitureHitboxPart part; + private final Vec3d pos; + private final List packets; + private final int entityId; + private final float yaw; public HappyGhastFurnitureHitbox(Furniture furniture, HappyGhastFurnitureHitboxConfig config) { super(furniture, config); this.config = config; WorldPosition position = furniture.position(); - Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - position.yRot()), 0f).conjugate(); - Vector3f offset = conjugated.transform(new Vector3f(config.position())); - Vec3d pos = Furniture.getRelativePosition(position, config.position()); - AABB aabb = AABB.fromInteraction(pos, 3 * config.scale(), 3 * config.scale()); - int happyGhastId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); - List packets = new ArrayList<>(3); - packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - happyGhastId, UUID.randomUUID(), position.x + offset.x, position.y + offset.y, position.z + offset.z, 0, position.yRot, - MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0 - )); - packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(happyGhastId, config.cachedValues())); + this.pos = Furniture.getRelativePosition(position, config.position()); + double bbSize = 4 * config.scale(); + AABB aabb = AABB.fromInteraction(this.pos, bbSize, bbSize); + this.yaw = position.yRot; + this.entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + this.packets = new ArrayList<>(3); + this.packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, config.cachedValues())); if (config.scale() != 1) { try { Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale()); - packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(happyGhastId, Collections.singletonList(attributeInstance))); + this.packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(this.entityId, Collections.singletonList(attributeInstance))); } catch (ReflectiveOperationException e) { CraftEngine.instance().logger().warn("Failed to apply scale attribute", e); } } - this.collider = createCollider(furniture.world(), pos, aabb, config.hardCollision(), config.blocksBuilding(), config.canBeHitByProjectile()); - this.part = new FurnitureHitboxPart(happyGhastId, aabb, pos); - this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); - this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(happyGhastId); }}); + this.packets.add(FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(this.entityId, this.pos.x, this.pos.y, this.pos.z, 0, position.yRot, false)); + this.collider = createCollider(furniture.world(), this.pos, aabb, config.hardCollision(), config.blocksBuilding(), config.canBeHitByProjectile()); + this.part = new FurnitureHitboxPart(this.entityId, aabb, this.pos, false); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(entityId); }}); } @Override @@ -73,7 +69,13 @@ public class HappyGhastFurnitureHitbox extends AbstractFurnitureHitBox { @Override public void show(Player player) { - player.sendPacket(this.spawnPacket, false); + List packets = new ArrayList<>(); + packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + this.entityId, UUID.randomUUID(), this.pos.x, player.y() - (this.config.scale() * 4 + 16), this.pos.z, 0, this.yaw, + MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.addAll(this.packets); + player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets), false); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java index 7e28b68da..1d2cffb8a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java @@ -59,7 +59,7 @@ public class HappyGhastFurnitureHitboxConfig extends AbstractFurnitureHitBoxConf public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { if (this.blocksBuilding) { Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); - aabbConsumer.accept(AABB.fromInteraction(relativePosition, 3 * this.scale, 3 * this.scale)); + aabbConsumer.accept(AABB.fromInteraction(relativePosition, 4 * this.scale, 4 * this.scale)); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index 44380525c..c44037500 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -37,7 +37,7 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { ), FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, config.cachedValues()) )); - this.part = new FurnitureHitboxPart(interactionId, aabb, pos); + this.part = new FurnitureHitboxPart(interactionId, aabb, pos, config.responsive()); this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(interactionId); }}); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java index a3e853d17..68be109f9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -33,14 +33,14 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon boolean canBeHitByProjectile, boolean invisible, Vector3f size, - boolean responsive) { + boolean interactive) { super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile); this.size = size; - this.responsive = responsive; + this.responsive = interactive; this.invisible = invisible; InteractionEntityData.Height.addEntityDataIfNotDefaultValue(size.y, cachedValues); InteractionEntityData.Width.addEntityDataIfNotDefaultValue(size.x, cachedValues); - InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(responsive, cachedValues); + InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedValues); if (invisible) { BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, cachedValues); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java index 9a6c39d43..e4ef37a2a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java @@ -58,7 +58,7 @@ public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { entityIds[1], UUID.randomUUID(), x + offset.x, processedY, z - offset.z, 0, yaw, MEntityTypes.SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0 )); - packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(config.cachedShulkerValues()))); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], config.cachedShulkerValues())); packets.add(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityIds[0], entityIds[1])); // fix some special occasions diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java index 004b73d26..4ca6e465a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -66,6 +66,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedShulkerValues); // Invisible List cachedInteractionValues = new ArrayList<>(); + InteractionEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, cachedInteractionValues); float shulkerHeight = (getPhysicalPeek(peek * 0.01F) + 1) * scale; if (direction == Direction.UP) { InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues); @@ -81,7 +82,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); if (canUseItemOn) { Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d)); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d, interactive)); } } }; @@ -101,7 +102,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); if (canUseItemOn) { Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d)); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d, interactive)); } } }; @@ -132,8 +133,8 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< if (canUseItemOn) { Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z); Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d1, scale, scale), vec3d1)); - aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.fromInteraction(vec3d2, scale, scale), vec3d2)); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d1, scale, scale), vec3d1, interactive)); + aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.fromInteraction(vec3d2, scale, scale), vec3d2, interactive)); } } }; @@ -159,31 +160,31 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< } public float scale() { - return scale; + return this.scale; } public byte peek() { - return peek; + return this.peek; } public boolean interactive() { - return interactive; + return this.interactive; } public boolean interactionEntity() { - return interactionEntity; + return this.interactionEntity; } public Direction direction() { - return direction; + return this.direction; } public DirectionalShulkerSpawner spawner() { - return spawner; + return this.spawner; } public List cachedShulkerValues() { - return cachedShulkerValues; + return this.cachedShulkerValues; } @Override @@ -219,7 +220,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< AABB ceAABB = createAABB(direction, offset, x, y, z); Object level = world.serverWorld(); Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ); - aabb.accept(new FurnitureHitboxPart(entityId, ceAABB, new Vec3d(x, y, z))); + aabb.accept(new FurnitureHitboxPart(entityId, ceAABB, new Vec3d(x, y, z), false)); return new BukkitCollider(level, nmsAABB, x, y, z, this.canBeHitByProjectile(), true, this.blocksBuilding()); } 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 578925913..afa959aec 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 @@ -3826,6 +3826,9 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes for (Seat seat : hitBox.seats()) { if (!seat.isOccupied()) { if (seat.spawnSeat(serverPlayer, furniture.position())) { + if (!part.interactive()) { + serverPlayer.swingHand(InteractionHand.MAIN_HAND); + } break; } } 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 index 9ea08846d..354d39baf 100644 --- 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 @@ -29,6 +29,7 @@ items: - position: 0,0,0 type: interaction blocks-building: true + invisible: true width: 0.7 height: 1.2 interactive: true diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java index e69a747f5..acf13acd3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitboxPart.java @@ -3,5 +3,5 @@ package net.momirealms.craftengine.core.entity.furniture.hitbox; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.collision.AABB; -public record FurnitureHitboxPart(int entityId, AABB aabb, Vec3d pos) { +public record FurnitureHitboxPart(int entityId, AABB aabb, Vec3d pos, boolean interactive) { } From 462cea8d0a99ffab30c4ffe4e0a11600ee59304e Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 3 Dec 2025 17:27:17 +0800 Subject: [PATCH 074/135] Update BukkitServerPlayer.java --- .../craftengine/bukkit/plugin/user/BukkitServerPlayer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a67b52a51..d887a07d7 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 @@ -546,7 +546,7 @@ public class BukkitServerPlayer extends Player { Object mountPos = FastNMS.INSTANCE.method$Entity$getPassengerRidingPosition(FastNMS.INSTANCE.method$CraftEntity$getHandle(vehicle), serverPlayer); unsureEyeLocation.set(FastNMS.INSTANCE.field$Vec3$x(mountPos), FastNMS.INSTANCE.field$Vec3$y(mountPos) + bukkitPlayer.getEyeHeight(), FastNMS.INSTANCE.field$Vec3$z(mountPos)); } - if (Config.predictBreaking() && !this.isDestroyingCustomBlock && !unsureEyeLocation.equals(this.eyeLocation)) { + if (Config.predictBreaking() && this.eyeLocation != null && !this.isDestroyingCustomBlock && !unsureEyeLocation.equals(this.eyeLocation)) { // if it's not destroying blocks, we do predict if ((gameTicks() + entityID()) % Config.predictBreakingInterval() == 0) { this.predictNextBlockToMine(); From 1d00df89c51839ec27c1c34a82a7f648a6eb6928 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 3 Dec 2025 21:25:54 +0800 Subject: [PATCH 075/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0custom=E7=A2=B0?= =?UTF-8?q?=E6=92=9E=E7=AE=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hitbox/BukkitFurnitureHitboxTypes.java | 2 +- .../hitbox/CustomFurnitureHitbox.java | 83 +++++++++++++ .../hitbox/CustomFurnitureHitboxConfig.java | 114 ++++++++++++++++++ .../hitbox/HappyGhastFurnitureHitbox.java | 2 +- .../HappyGhastFurnitureHitboxConfig.java | 2 +- .../hitbox/InteractionFurnitureHitbox.java | 2 +- .../InteractionFurnitureHitboxConfig.java | 2 +- .../hitbox/ShulkerFurnitureHitboxConfig.java | 8 +- .../reflection/minecraft/CoreReflections.java | 23 ++++ .../configuration/furniture/wooden_chair.yml | 9 +- .../core/world/collision/AABB.java | 2 +- 11 files changed, 234 insertions(+), 15 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java index 10c1a33d2..715cb7678 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java @@ -10,7 +10,7 @@ public class BukkitFurnitureHitboxTypes extends FurnitureHitBoxTypes { static { register(INTERACTION, InteractionFurnitureHitboxConfig.FACTORY); register(SHULKER, ShulkerFurnitureHitboxConfig.FACTORY); - // register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); + register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); if (VersionHelper.isOrAbove1_21_6()) { register(HAPPY_GHAST, HappyGhastFurnitureHitboxConfig.FACTORY); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java new file mode 100644 index 000000000..461f4ea07 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java @@ -0,0 +1,83 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +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.NetworkReflections; +import net.momirealms.craftengine.core.entity.furniture.Collider; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +public class CustomFurnitureHitbox extends AbstractFurnitureHitBox { + private final CustomFurnitureHitboxConfig config; + private final Collider collider; + private final Object spawnPacket; + private final Object despawnPacket; + private final FurnitureHitboxPart part; + + public CustomFurnitureHitbox(Furniture furniture, CustomFurnitureHitboxConfig config) { + super(furniture, config); + this.config = config; + WorldPosition position = furniture.position(); + Vec3d pos = Furniture.getRelativePosition(position, config.position()); + AABB aabb = AABB.makeBoundingBox(pos, config.width(), config.height()); + this.collider = createCollider(furniture.world(), pos, aabb, false, config.blocksBuilding(), config.canBeHitByProjectile()); + int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + List packets = new ArrayList<>(3); + packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, + config.entityType(), 0, CoreReflections.instance$Vec3$Zero, 0 + )); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, config.cachedValues())); + if (VersionHelper.isOrAbove1_20_5()) { + try { + Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); + CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale()); + packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityId, Collections.singletonList(attributeInstance))); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to apply scale attribute", e); + } + } + this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); + this.part = new FurnitureHitboxPart(entityId, aabb, pos, false); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(entityId); }}); + } + + @Override + public List colliders() { + return List.of(this.collider); + } + + @Override + public List parts() { + return List.of(this.part); + } + + @Override + public void show(Player player) { + player.sendPacket(this.spawnPacket, false); + } + + @Override + public void hide(Player player) { + player.sendPacket(this.despawnPacket, false); + } + + @Override + public CustomFurnitureHitboxConfig config() { + return this.config; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java new file mode 100644 index 000000000..d7147708c --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java @@ -0,0 +1,114 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; + +import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; +import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; +import net.momirealms.craftengine.core.entity.seat.SeatConfig; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class CustomFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig { + public static final Factory FACTORY = new Factory(); + private final float scale; + private final Object entityType; + private final List cachedValues = new ArrayList<>(); + private final float width; + private final float height; + + public CustomFurnitureHitboxConfig(SeatConfig[] seats, + Vector3f position, + boolean canUseItemOn, + boolean blocksBuilding, + boolean canBeHitByProjectile, + float width, + float height, + boolean fixed, + float scale, + Object type) { + super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile); + this.scale = scale; + this.entityType = type; + this.width = fixed ? width : width * scale; + this.height = fixed ? height : height * scale; + BaseEntityData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedValues); + BaseEntityData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedValues); + BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues); + } + + public float scale() { + return this.scale; + } + + public Object entityType() { + return this.entityType; + } + + public List cachedValues() { + return this.cachedValues; + } + + public float width() { + return width; + } + + public float height() { + return height; + } + + @Override + public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { + if (this.blocksBuilding) { + Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); + aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, this.width, this.height)); + } + } + + @Override + public CustomFurnitureHitbox create(Furniture furniture) { + return new CustomFurnitureHitbox(furniture, this); + } + + public static class Factory implements FurnitureHitBoxConfigFactory { + + @Override + public CustomFurnitureHitboxConfig create(Map arguments) { + 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"); + Object nmsEntityType = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ENTITY_TYPE, KeyUtils.toResourceLocation(Key.of(type))); + if (nmsEntityType == null) { + throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.custom.invalid_entity", new IllegalArgumentException("EntityType not found: " + type), type); + } + float width; + float height; + boolean fixed; + try { + Object dimensions = CoreReflections.field$EntityType$dimensions.get(nmsEntityType); + width = CoreReflections.field$EntityDimensions$width.getFloat(dimensions); + height = CoreReflections.field$EntityDimensions$height.getFloat(dimensions); + fixed = CoreReflections.field$EntityDimensions$fixed.getBoolean(dimensions); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to get dimensions for " + nmsEntityType, e); + } + boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", false), "can-use-item-on"); + boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile"); + boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building"); + return new CustomFurnitureHitboxConfig(SeatConfig.fromObj(arguments.get("seats")), position, canUseItemOn, blocksBuilding, canBeHitByProjectile, width, height, fixed, scale, nmsEntityType); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java index fcf530b95..750ee7c1c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java @@ -37,7 +37,7 @@ public class HappyGhastFurnitureHitbox extends AbstractFurnitureHitBox { WorldPosition position = furniture.position(); this.pos = Furniture.getRelativePosition(position, config.position()); double bbSize = 4 * config.scale(); - AABB aabb = AABB.fromInteraction(this.pos, bbSize, bbSize); + AABB aabb = AABB.makeBoundingBox(this.pos, bbSize, bbSize); this.yaw = position.yRot; this.entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); this.packets = new ArrayList<>(3); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java index 1d2cffb8a..6264b3a5f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java @@ -59,7 +59,7 @@ public class HappyGhastFurnitureHitboxConfig extends AbstractFurnitureHitBoxConf public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { if (this.blocksBuilding) { Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); - aabbConsumer.accept(AABB.fromInteraction(relativePosition, 4 * this.scale, 4 * this.scale)); + aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, 4 * this.scale, 4 * this.scale)); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index c44037500..1c4e45ca6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -27,7 +27,7 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { this.config = config; WorldPosition position = furniture.position(); Vec3d pos = Furniture.getRelativePosition(position, config.position()); - AABB aabb = AABB.fromInteraction(pos, config.size().x, config.size().y); + AABB aabb = AABB.makeBoundingBox(pos, config.size().x, config.size().y); this.collider = createCollider(furniture.world(), pos, aabb, false, config.blocksBuilding(), config.canBeHitByProjectile()); int interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java index 68be109f9..909335b0a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -73,7 +73,7 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { if (this.blocksBuilding) { Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); - aabbConsumer.accept(AABB.fromInteraction(relativePosition, size.x, size.y)); + aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, size.x, size.y)); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java index 4ca6e465a..169acd8f2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -82,7 +82,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); if (canUseItemOn) { Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d, interactive)); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive)); } } }; @@ -102,7 +102,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); if (canUseItemOn) { Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d, interactive)); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive)); } } }; @@ -133,8 +133,8 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< if (canUseItemOn) { Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z); Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d1, scale, scale), vec3d1, interactive)); - aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.fromInteraction(vec3d2, scale, scale), vec3d2, interactive)); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d1, scale, scale), vec3d1, interactive)); + aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.makeBoundingBox(vec3d2, scale, scale), vec3d2, interactive)); } } }; 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 f92d2c2cd..146162837 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 @@ -4594,4 +4594,27 @@ public final class CoreReflections { throw new ReflectionInitException("Failed to init EmptyBlockGetter$INSTANCE", e); } } + + public static final Class clazz$EntityDimensions = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.entity.EntitySize", + "world.entity.EntityDimensions" + ) + ); + + public static final Field field$EntityType$dimensions = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$EntityType, clazz$EntityDimensions, 0) + ); + + public static final Field field$EntityDimensions$width = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$EntityDimensions, float.class, 0) + ); + + public static final Field field$EntityDimensions$height = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$EntityDimensions, float.class, 1) + ); + + public static final Field field$EntityDimensions$fixed = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$EntityDimensions, boolean.class, 0) + ); } 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 index 354d39baf..fddace996 100644 --- 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 @@ -27,12 +27,11 @@ items: translation: 0,0.5,0 hitboxes: - position: 0,0,0 - type: interaction - blocks-building: true + type: custom + entity-type: slime invisible: true - width: 0.7 - height: 1.2 - interactive: true + can-use-item-on: true + blocks-building: true seats: - 0,0,-0.1 0 loot: 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 c5a725f59..dc328fc4d 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 @@ -57,7 +57,7 @@ public class AABB { return x * x + y * y + z * z; } - public static AABB fromInteraction(Vec3d pos, double width, double height) { + public static AABB makeBoundingBox(Vec3d pos, double width, double height) { return new AABB( pos.x - width / 2, pos.y, From 1c95eb75bdbbac2afc0bf71118ba7a09959d46a3 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 3 Dec 2025 22:25:24 +0800 Subject: [PATCH 076/135] =?UTF-8?q?=E9=99=90=E5=88=B6=E6=91=86=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hitbox/BukkitFurnitureHitboxTypes.java | 2 +- .../hitbox/InteractionFurnitureHitbox.java | 2 +- .../item/behavior/FurnitureItemBehavior.java | 107 ++++++++++++++---- .../default/configuration/furniture/bench.yml | 8 +- .../configuration/furniture/flower_basket.yml | 32 +++--- .../configuration/furniture/wooden_chair.yml | 9 +- .../core/entity/furniture/AnchorType.java | 15 ++- .../core/util/ResourceConfigUtils.java | 2 +- 8 files changed, 126 insertions(+), 51 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java index 715cb7678..f7fa5e2e1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitFurnitureHitboxTypes.java @@ -10,7 +10,7 @@ public class BukkitFurnitureHitboxTypes extends FurnitureHitBoxTypes { static { register(INTERACTION, InteractionFurnitureHitboxConfig.FACTORY); register(SHULKER, ShulkerFurnitureHitboxConfig.FACTORY); - register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); + register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY); if (VersionHelper.isOrAbove1_21_6()) { register(HAPPY_GHAST, HappyGhastFurnitureHitboxConfig.FACTORY); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index 1c4e45ca6..65b0ffd9e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -32,7 +32,7 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { int interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - interactionId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot, + interactionId, UUID.randomUUID(), pos.x, pos.y, pos.z, 0, position.yRot, MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 ), FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, config.cachedValues()) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index 19a9aea16..b74926aa9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -8,9 +8,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.EventUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; -import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; -import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; +import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.entity.player.Player; @@ -26,10 +24,8 @@ 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.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.Cancellable; -import net.momirealms.craftengine.core.util.ItemUtils; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.plugin.logger.Debugger; +import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; @@ -38,17 +34,17 @@ import org.bukkit.Location; import org.bukkit.World; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; public class FurnitureItemBehavior extends ItemBehavior { public static final Factory FACTORY = new Factory(); + private static final Set ALLOWED_ANCHOR_TYPES = Set.of("wall", "ceiling", "ground"); private final Key id; + private final Map rules; - public FurnitureItemBehavior(Key id) { + public FurnitureItemBehavior(Key id, Map rules) { this.id = id; + this.rules = rules; } public Key furnitureId() { @@ -67,12 +63,24 @@ public class FurnitureItemBehavior extends ItemBehavior { return InteractionResult.FAIL; } + Direction clickedFace = context.getClickedFace(); + AnchorType anchorType = switch (clickedFace) { + case EAST, WEST, NORTH, SOUTH -> AnchorType.WALL; + case UP -> AnchorType.GROUND; + case DOWN -> AnchorType.CEILING; + }; + FurnitureConfig customFurniture = optionalCustomFurniture.get(); - FurnitureVariant variant = customFurniture.anyVariant(); + FurnitureVariant variant = customFurniture.getVariant(anchorType.variantName()); if (variant == null) { return InteractionResult.FAIL; } + Rule rule = this.rules.get(anchorType); + if (rule == null) { + rule = Rule.DEFAULT; + } + Player player = context.getPlayer(); if (player != null && player.isAdventureMode()) { return InteractionResult.FAIL; @@ -80,14 +88,27 @@ public class FurnitureItemBehavior extends ItemBehavior { Vec3d clickedPosition = context.getClickLocation(); + // get position and rotation for placement + Vec3d finalPlacePosition; + double furnitureYaw; + if (anchorType == AnchorType.WALL) { + furnitureYaw = Direction.getYaw(clickedFace); + if (clickedFace == Direction.EAST || clickedFace == Direction.WEST) { + Pair xz = rule.alignmentRule().apply(Pair.of(clickedPosition.y(), clickedPosition.z())); + finalPlacePosition = new Vec3d(clickedPosition.x(), xz.left(), xz.right()); + } else { + Pair xz = rule.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.y())); + finalPlacePosition = new Vec3d(xz.left(), xz.right(), clickedPosition.z()); + } + } else { + furnitureYaw = rule.rotationRule().apply(180 + (player != null ? player.yRot() : 0)); + Pair xz = rule.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.z())); + finalPlacePosition = new Vec3d(xz.left(), clickedPosition.y(), xz.right()); + } + // trigger event org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null; World world = (World) context.getLevel().platformWorld(); - - // get position and rotation for placement - Vec3d finalPlacePosition = clickedPosition; - double furnitureYaw = 180 + (player != null ? player.yRot() : 0); - Location furnitureLocation = new Location(world, finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, 0); WorldPosition furniturePos = LocationUtils.toWorldPosition(furnitureLocation); List aabbs = new ArrayList<>(); @@ -162,17 +183,59 @@ public class FurnitureItemBehavior extends ItemBehavior { if (id == null) { throw new LocalizedResourceConfigException("warning.config.item.behavior.furniture.missing_furniture", new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior")); } + Map rulesMap = ResourceConfigUtils.getAsMapOrNull(arguments.get("rules"), "rules"); + Key furnitureId; if (id instanceof Map map) { + Map furnitureSection; if (map.containsKey(key.toString())) { // 防呆 - BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, MiscUtils.castToMap(map.get(key.toString()), false))); + furnitureSection = MiscUtils.castToMap(map.get(key.toString()), false); + BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, furnitureSection)); } else { - BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, MiscUtils.castToMap(map, false))); + furnitureSection = MiscUtils.castToMap(map, false); + BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, furnitureSection)); + } + furnitureId = key; + // 兼容老版本 + if (rulesMap == null) { + Map placementSection = ResourceConfigUtils.getAsMapOrNull(furnitureSection.get("placement"), "placement"); + if (placementSection != null) { + rulesMap = new HashMap<>(); + for (Map.Entry entry : placementSection.entrySet()) { + if (entry.getValue() instanceof Map innerMap) { + if (innerMap.containsKey("rules")) { + Map rules = ResourceConfigUtils.getAsMap(innerMap.get("rules"), "rules"); + if (ALLOWED_ANCHOR_TYPES.contains(entry.getKey())) { + rulesMap.put(entry.getKey(), rules); + } + } + } + } + } } - return new FurnitureItemBehavior(key); } else { - return new FurnitureItemBehavior(Key.of(id.toString())); + furnitureId = Key.of(id.toString()); } + Map rules = new EnumMap<>(AnchorType.class); + if (rulesMap != null) { + for (Map.Entry entry : rulesMap.entrySet()) { + try { + AnchorType type = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ROOT)); + Map ruleSection = MiscUtils.castToMap(entry.getValue(), true); + rules.put(type, new Rule( + ResourceConfigUtils.getAsEnum(ruleSection.get("alignment"), AlignmentRule.class, AlignmentRule.ANY), + ResourceConfigUtils.getAsEnum(ruleSection.get("rotation"), RotationRule.class, RotationRule.ANY) + )); + } catch (IllegalArgumentException ignored) { + Debugger.FURNITURE.debug(() -> "Invalid anchor type: " + entry.getKey()); + } + } + } + return new FurnitureItemBehavior(furnitureId, rules); } } + + public record Rule(AlignmentRule alignmentRule, RotationRule rotationRule) { + public static final Rule DEFAULT = new Rule(AlignmentRule.ANY, RotationRule.ANY); + } } 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 index 06523d269..66e36f644 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml @@ -6,6 +6,10 @@ items: model: minecraft:item/custom/bench behavior: type: furniture_item + rules: + ground: + rotation: four + alignment: center furniture: settings: item: default:bench @@ -17,8 +21,8 @@ items: loot-spawn-offset: 0.5,0.5,0 elements: - item: default:bench - display-transform: NONE - billboard: FIXED + display-transform: none + billboard: fixed position: 0.5,0,0 translation: 0,0.5,0 shadow-radius: 1 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 index 15194da75..acfcedb15 100644 --- 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 @@ -7,6 +7,16 @@ items: behavior: type: furniture_item furniture: default:flower_basket + rules: + ground: + rotation: any + alignment: any + wall: + rotation: any + alignment: any + ceiling: + rotation: any + alignment: any default:flower_basket_ground: material: nether_brick model: minecraft:item/custom/flower_basket_ground @@ -27,15 +37,12 @@ furniture: template: default:loot_table/furniture arguments: item: default:flower_basket - placement: + variants: ground: - rules: - rotation: ANY - alignment: ANY elements: - item: default:flower_basket_ground - display-transform: NONE - billboard: FIXED + display-transform: none + billboard: fixed position: 0,0,0 translation: 0,0.5,0 shadow-radius: 0.5 @@ -50,12 +57,10 @@ furniture: height: 0.5 interactive: true wall: - rules: - alignment: ANY elements: - item: default:flower_basket_wall - display-transform: NONE - billboard: FIXED + display-transform: none + billboard: fixed position: 0,0,0.2 translation: 0,0,0 hitboxes: @@ -76,13 +81,10 @@ furniture: height: 0.75 interactive: true ceiling: - rules: - rotation: ANY - alignment: ANY elements: - item: default:flower_basket_ceiling - display-transform: NONE - billboard: FIXED + display-transform: none + billboard: fixed position: 0,-0.46,0 translation: 0,0,0 hitboxes: 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 index fddace996..7ad0017fe 100644 --- 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 @@ -6,18 +6,19 @@ items: model: minecraft:item/custom/wooden_chair behavior: type: furniture_item + rules: + ground: + rotation: any + alignment: any furniture: settings: item: default:wooden_chair sounds: break: minecraft:block.bamboo_wood.break place: minecraft:block.bamboo_wood.place - placement: + variants: ground: loot-spawn-offset: 0,0.4,0 - rules: - rotation: ANY - alignment: ANY elements: - item: default:wooden_chair display-transform: NONE diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java index b389f52f7..8ec7b6749 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AnchorType.java @@ -1,21 +1,26 @@ package net.momirealms.craftengine.core.entity.furniture; -@Deprecated(since = "0.0.66") public enum AnchorType { - GROUND(0), - WALL(1), - CEILING(2); + GROUND(0, "ground"), + WALL(1, "wall"), + CEILING(2, "ceiling"); private final int id; + private final String variantName; - AnchorType(int id) { + AnchorType(int id, String variantName) { this.id = id; + this.variantName = variantName; } public int getId() { return id; } + public String variantName() { + return variantName; + } + public static AnchorType byId(int id) { return values()[id]; } 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 43a35d7dc..e3be821e6 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 @@ -36,7 +36,7 @@ public final class ResourceConfigUtils { return defaultValue; } try { - return Enum.valueOf(clazz, o.toString().toUpperCase(Locale.ENGLISH)); + return Enum.valueOf(clazz, o.toString().toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { return defaultValue; } From 1d74e2764f363a53c8433e2a9b4605ea86f75ba6 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 00:28:45 +0800 Subject: [PATCH 077/135] =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=96=B0=E5=AE=B6?= =?UTF-8?q?=E5=85=B7API=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../skript/condition/CondIsFurniture.java | 2 +- .../skript/effect/EffRemoveFurniture.java | 2 +- .../expression/ExprEntityFurnitureID.java | 2 +- .../bukkit/api/CraftEngineFurniture.java | 167 ++++++++++++++++-- .../plugin/command/feature/TestCommand.java | 8 + .../configuration/furniture/flower_basket.yml | 3 + .../furniture/FurnitureDataAccessor.java | 6 + 7 files changed, 169 insertions(+), 21 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/condition/CondIsFurniture.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/condition/CondIsFurniture.java index 96b57c2d5..5be023307 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/condition/CondIsFurniture.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/condition/CondIsFurniture.java @@ -25,7 +25,7 @@ public class CondIsFurniture extends Condition { @Override public boolean check(Event event) { return entities.check(event, entity -> { - BukkitFurniture baseEntity = CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity); + BukkitFurniture baseEntity = CraftEngineFurniture.getLoadedFurnitureByMetaEntity(entity); return baseEntity != null; }, isNegated()); } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/effect/EffRemoveFurniture.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/effect/EffRemoveFurniture.java index 995f3364a..b65a9bc1b 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/effect/EffRemoveFurniture.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/effect/EffRemoveFurniture.java @@ -22,7 +22,7 @@ public class EffRemoveFurniture extends Effect { @Override protected void execute(Event e) { for (Entity entity : entities.getArray(e)) { - Furniture bukkitFurniture = CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity); + Furniture bukkitFurniture = CraftEngineFurniture.getLoadedFurnitureByMetaEntity(entity); if (bukkitFurniture != null) { bukkitFurniture.destroy(); } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/expression/ExprEntityFurnitureID.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/expression/ExprEntityFurnitureID.java index 0e8826602..fdf2674be 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/expression/ExprEntityFurnitureID.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/skript/expression/ExprEntityFurnitureID.java @@ -16,7 +16,7 @@ public class ExprEntityFurnitureID extends SimplePropertyExpression it.id().toString()) .orElse(null); } 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 7f2a1cabe..c8ab62b47 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 @@ -6,10 +6,12 @@ import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeatManager; import net.momirealms.craftengine.bukkit.nms.CollisionEntity; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.entity.furniture.AnchorType; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; +import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.LootTable; @@ -19,11 +21,13 @@ import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.sparrow.nbt.CompoundTag; +import org.bukkit.FluidCollisionMode; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; +import org.bukkit.util.RayTraceResult; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -59,6 +63,45 @@ public final class CraftEngineFurniture { return BukkitFurnitureManager.instance().furnitureById(id).orElse(null); } + /** + * Performs ray tracing to find the furniture entity that the player is currently targeting + * + * @param player The player performing the ray trace + * @param maxDistance Maximum ray trace distance (in blocks) + * @return The furniture being targeted by the player, or null if no furniture is found + */ + @Nullable + public static BukkitFurniture rayTrace(Player player, double maxDistance) { + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); + Location eyeLocation = serverPlayer.getEyeLocation(); + RayTraceResult result = player.getWorld().rayTrace(eyeLocation, eyeLocation.getDirection(), maxDistance, FluidCollisionMode.NEVER, true, 0d, CraftEngineFurniture::isCollisionEntity); + if (result == null) + return null; + Entity hitEntity = result.getHitEntity(); + if (hitEntity == null) + return null; + return getLoadedFurnitureByCollider(hitEntity); + } + + /** + * Performs ray tracing to find the furniture entity that the player is currently targeting + * + * @param player The player performing the ray trace + * @return The furniture being targeted by the player, or null if no furniture is found + */ + @Nullable + public static BukkitFurniture rayTrace(Player player) { + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); + Location eyeLocation = serverPlayer.getEyeLocation(); + RayTraceResult result = player.getWorld().rayTrace(eyeLocation, eyeLocation.getDirection(), serverPlayer.getCachedInteractionRange(), FluidCollisionMode.NEVER, true, 0d, CraftEngineFurniture::isCollisionEntity); + if (result == null) + return null; + Entity hitEntity = result.getHitEntity(); + if (hitEntity == null) + return null; + return getLoadedFurnitureByCollider(hitEntity); + } + /** * Places furniture at certain location * @@ -70,9 +113,7 @@ public final class CraftEngineFurniture { public static BukkitFurniture place(Location location, Key furnitureId) { FurnitureConfig furniture = byId(furnitureId); if (furniture == null) return null; - // fixme API -// return place(location, furnitureId, furniture.getAnyAnchorType()); - return null; + return place(location, furniture, furniture.anyVariantName(), false); } /** @@ -86,11 +127,22 @@ public final class CraftEngineFurniture { @Nullable @Deprecated(since = "0.0.66", forRemoval = true) public static BukkitFurniture place(Location location, Key furnitureId, AnchorType anchorType) { + return place(location, furnitureId, anchorType.variantName()); + } + + /** + * Places furniture at certain location + * + * @param location location + * @param furnitureId furniture to place + * @param variant variant type + * @return the loaded furniture + */ + @Nullable + public static BukkitFurniture place(Location location, Key furnitureId, String variant) { FurnitureConfig furniture = byId(furnitureId); if (furniture == null) return null; - // fixme API -// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), true); - return null; + return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.ofVariant(variant), true); } /** @@ -104,9 +156,7 @@ public final class CraftEngineFurniture { @NotNull @Deprecated(since = "0.0.66", forRemoval = true) public static BukkitFurniture place(Location location, FurnitureConfig furniture, AnchorType anchorType) { - // fixme API -// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), true); - return null; + return place(location, furniture, anchorType.variantName(), true); } /** @@ -123,9 +173,23 @@ public final class CraftEngineFurniture { public static BukkitFurniture place(Location location, Key furnitureId, AnchorType anchorType, boolean playSound) { FurnitureConfig furniture = byId(furnitureId); if (furniture == null) return null; - // fixme API -// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), playSound); - return null; + return place(location, furniture, anchorType.variantName(), playSound); + } + + /** + * Places furniture at certain location + * + * @param location location + * @param furnitureId furniture to place + * @param variant variant + * @param playSound whether to play place sounds + * @return the loaded furniture + */ + @Nullable + public static BukkitFurniture place(Location location, Key furnitureId, String variant, boolean playSound) { + FurnitureConfig furniture = byId(furnitureId); + if (furniture == null) return null; + return place(location, furniture, variant, playSound); } /** @@ -140,9 +204,49 @@ public final class CraftEngineFurniture { @NotNull @Deprecated(since = "0.0.66", forRemoval = true) public static BukkitFurniture place(Location location, FurnitureConfig furniture, AnchorType anchorType, boolean playSound) { - // fixme API -// return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.builder().anchorType(anchorType).build(), playSound); - return null; + return place(location, furniture, anchorType.variantName(), playSound); + } + + /** + * Places furniture at certain location + * + * @param location location + * @param furniture furniture to place + * @param variant variant + * @param playSound whether to play place sounds + * @return the loaded furniture + */ + @NotNull + public static BukkitFurniture place(Location location, FurnitureConfig furniture, String variant, boolean playSound) { + return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.ofVariant(variant), playSound); + } + + /** + * Places furniture at certain location + * + * @param location location + * @param furniture furniture to place + * @param data furniture data + * @param playSound whether to play place sounds + * @return the loaded furniture + */ + @NotNull + public static BukkitFurniture place(Location location, FurnitureConfig furniture, CompoundTag data, boolean playSound) { + return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.of(data), playSound); + } + + /** + * Places furniture at certain location + * + * @param location location + * @param furniture furniture to place + * @param dataAccessor furniture data accessor + * @param playSound whether to play place sounds + * @return the loaded furniture + */ + @NotNull + public static BukkitFurniture place(Location location, FurnitureConfig furniture, FurnitureDataAccessor dataAccessor, boolean playSound) { + return BukkitFurnitureManager.instance().place(location, furniture, dataAccessor, playSound); } /** @@ -178,18 +282,30 @@ public final class CraftEngineFurniture { } /** - * Gets the base furniture by the base entity + * Gets the furniture by the meta entity * * @param baseEntity base entity * @return the loaded furniture */ @Nullable - public static BukkitFurniture getLoadedFurnitureByBaseEntity(@NotNull Entity baseEntity) { + public static BukkitFurniture getLoadedFurnitureByMetaEntity(@NotNull Entity baseEntity) { return BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(baseEntity.getEntityId()); } /** - * Gets the base furniture by the seat entity + * Gets the furniture by the meta entity + * + * @param baseEntity base entity + * @return the loaded furniture + */ + @Nullable + @Deprecated(since = "0.0.66") + public static BukkitFurniture getLoadedFurnitureByBaseEntity(@NotNull Entity baseEntity) { + return getLoadedFurnitureByMetaEntity(baseEntity); + } + + /** + * Gets the furniture by the seat entity * * @param seat seat entity * @return the loaded furniture @@ -204,6 +320,21 @@ public final class CraftEngineFurniture { return null; } + /** + * Gets the furniture by the collider entity + * + * @param collider collider entity + * @return the loaded furniture + */ + @Nullable + public static BukkitFurniture getLoadedFurnitureByCollider(@NotNull Entity collider) { + Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(collider); + if (nmsEntity instanceof CollisionEntity collisionEntity) { + return BukkitFurnitureManager.instance().loadedFurnitureByColliderEntityId(collisionEntity.getId()); + } + return null; + } + /** * Removes furniture * 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 437855e70..cc9bdb782 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,9 +1,12 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; +import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import org.incendo.cloud.Command; public class TestCommand extends BukkitCommandFeature { @@ -15,9 +18,14 @@ public class TestCommand extends BukkitCommandFeature { @Override public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder + .senderType(Player.class) .handler(context -> { // DO NOT PUSH ANY CODE FOR TEST COMMAND // 禁止推送含有实现的Test指令 + BukkitFurniture furniture = CraftEngineFurniture.rayTrace(context.sender(), 4); + if (furniture != null) { + System.out.println(furniture.id()); + } }); } 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 index acfcedb15..416b43aad 100644 --- 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 @@ -20,12 +20,15 @@ items: default:flower_basket_ground: material: nether_brick model: minecraft:item/custom/flower_basket_ground + item-name: default:flower_basket_wall: material: nether_brick model: minecraft:item/custom/flower_basket_wall + item-name: default:flower_basket_ceiling: material: nether_brick model: minecraft:item/custom/flower_basket_ceiling + item-name: furniture: default:flower_basket: settings: diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java index c6bc0793a..fb4785432 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java @@ -31,6 +31,12 @@ public class FurnitureDataAccessor { return new FurnitureDataAccessor(data); } + public static FurnitureDataAccessor ofVariant(String variant) { + FurnitureDataAccessor accessor = new FurnitureDataAccessor(new CompoundTag()); + accessor.setVariant(variant); + return accessor; + } + public CompoundTag copyTag() { return this.data.copy(); } From 7d2496b06d663ddc435259ed4d4e263c727c985a Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 01:12:48 +0800 Subject: [PATCH 078/135] Update DebugSpawnFurnitureCommand.java --- .../feature/DebugSpawnFurnitureCommand.java | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java index 7b32aeba1..832a1c40e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java @@ -1,10 +1,16 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; +import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; -import net.momirealms.craftengine.core.entity.furniture.AnchorType; +import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.FlagKeys; +import net.momirealms.craftengine.core.util.Key; +import org.bukkit.Location; +import org.bukkit.NamespacedKey; import org.bukkit.command.CommandSender; import org.checkerframework.checker.nullness.qual.NonNull; import org.incendo.cloud.Command; @@ -12,10 +18,12 @@ import org.incendo.cloud.bukkit.parser.NamespacedKeyParser; import org.incendo.cloud.bukkit.parser.location.LocationParser; import org.incendo.cloud.context.CommandContext; import org.incendo.cloud.context.CommandInput; -import org.incendo.cloud.parser.standard.EnumParser; +import org.incendo.cloud.parser.standard.StringParser; import org.incendo.cloud.suggestion.Suggestion; import org.incendo.cloud.suggestion.SuggestionProvider; +import java.util.List; +import java.util.Optional; import java.util.concurrent.CompletableFuture; public class DebugSpawnFurnitureCommand extends BukkitCommandFeature { @@ -24,7 +32,6 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder @@ -35,22 +42,30 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + NamespacedKey namespacedKey = context.get("id"); + Key id = KeyUtils.namespacedKey2Key(namespacedKey); + BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance(); + Optional optionalCustomFurniture = furnitureManager.furnitureById(id); + return optionalCustomFurniture.>>map(config -> CompletableFuture.completedFuture(config.variants().keySet().stream().map(Suggestion::suggestion).toList())).orElseGet(() -> CompletableFuture.completedFuture(List.of())); + } + })) .flag(FlagKeys.SILENT_FLAG) .handler(context -> { - // fixme 指令 -// NamespacedKey namespacedKey = context.get("id"); -// Key id = KeyUtils.namespacedKey2Key(namespacedKey); -// BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance(); -// Optional optionalCustomFurniture = furnitureManager.furnitureById(id); -// if (optionalCustomFurniture.isEmpty()) { -// return; -// } -// Location location = context.get("location"); -// FurnitureConfig customFurniture = optionalCustomFurniture.get(); -// AnchorType anchorType = (AnchorType) context.optional("anchor-type").orElse(customFurniture.getAnyAnchorType()); -// boolean playSound = context.flags().hasFlag("silent"); -// CraftEngineFurniture.place(location, customFurniture, anchorType, playSound); + NamespacedKey namespacedKey = context.get("id"); + Key id = KeyUtils.namespacedKey2Key(namespacedKey); + BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance(); + Optional optionalCustomFurniture = furnitureManager.furnitureById(id); + if (optionalCustomFurniture.isEmpty()) { + return; + } + Location location = context.get("location"); + FurnitureConfig customFurniture = optionalCustomFurniture.get(); + String variant = (String) context.optional("variant").orElse(customFurniture.anyVariantName()); + boolean playSound = context.flags().hasFlag("silent"); + CraftEngineFurniture.place(location, customFurniture, variant, playSound); }); } From 1610ec97362b4f594c23950855e588953797be11 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 01:13:57 +0800 Subject: [PATCH 079/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dreplacement=E7=A9=BA?= =?UTF-8?q?=E6=8C=87=E9=92=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/item/listener/ItemEventListener.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 88f5173de..4d7e6e374 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 @@ -457,9 +457,11 @@ public class ItemEventListener implements Listener { } } else { // fixme 如何取消堆叠数量>1的物品的默认replacement - Item replacementItem = this.plugin.itemManager().createWrappedItem(replacement, serverPlayer); - if (replacementItem != null) { - PlayerUtils.giveItem(serverPlayer, 1, replacementItem); + if (replacement != null) { + Item replacementItem = this.plugin.itemManager().createWrappedItem(replacement, serverPlayer); + if (replacementItem != null) { + PlayerUtils.giveItem(serverPlayer, 1, replacementItem); + } } } } From a1569423443de371e6a872baae437d873769f0f7 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 01:49:27 +0800 Subject: [PATCH 080/135] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E5=AE=B6=E5=85=B7=E6=9C=80=E5=A4=A7AABB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hitbox/CustomFurnitureHitboxConfig.java | 5 ++++ .../HappyGhastFurnitureHitboxConfig.java | 5 ++++ .../InteractionFurnitureHitboxConfig.java | 5 ++++ .../hitbox/ShulkerFurnitureHitboxConfig.java | 5 ++++ .../plugin/command/feature/TestCommand.java | 8 ------- .../furniture/AbstractFurnitureManager.java | 24 +++++++++++++++---- .../hitbox/FurnitureHitBoxConfig.java | 2 ++ .../function/ReplaceFurnitureFunction.java | 15 +++++------- .../function/SpawnFurnitureFunction.java | 15 +++++------- .../core/world/collision/AABB.java | 12 ++++++++++ 10 files changed, 66 insertions(+), 30 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java index d7147708c..58e2ed92f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java @@ -78,6 +78,11 @@ public class CustomFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig aabbConsumer) { + aabbConsumer.accept(AABB.makeBoundingBox(this.position, this.width, this.height)); + } + @Override public CustomFurnitureHitbox create(Furniture furniture) { return new CustomFurnitureHitbox(furniture, this); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java index 6264b3a5f..df7458e19 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java @@ -63,6 +63,11 @@ public class HappyGhastFurnitureHitboxConfig extends AbstractFurnitureHitBoxConf } } + @Override + public void collectBoundingBox(Consumer aabbConsumer) { + aabbConsumer.accept(AABB.makeBoundingBox(this.position, 4 * this.scale, 4 * this.scale)); + } + public static class Factory implements FurnitureHitBoxConfigFactory { @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java index 909335b0a..3f823705a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -77,6 +77,11 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon } } + @Override + public void collectBoundingBox(Consumer aabbConsumer) { + aabbConsumer.accept(AABB.makeBoundingBox(this.position, size.x, size.y)); + } + @Override public InteractionFurnitureHitbox create(Furniture furniture) { return new InteractionFurnitureHitbox(furniture, this); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java index 169acd8f2..ebc750c2a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -159,6 +159,11 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< } } + @Override + public void collectBoundingBox(Consumer aabbConsumer) { + aabbConsumer.accept(this.aabbCreator.create(position.x, position.y, position.z, 180, new Vector3f(0))); + } + public float scale() { return this.scale; } 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 cc9bdb782..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,12 +1,9 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; -import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; import org.incendo.cloud.Command; public class TestCommand extends BukkitCommandFeature { @@ -18,14 +15,9 @@ public class TestCommand extends BukkitCommandFeature { @Override public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder - .senderType(Player.class) .handler(context -> { // DO NOT PUSH ANY CODE FOR TEST COMMAND // 禁止推送含有实现的Test指令 - BukkitFurniture furniture = CraftEngineFurniture.rayTrace(context.sender(), 4); - if (furniture != null) { - System.out.println(furniture.id()); - } }); } 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 c0569fd87..e056dfc73 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 @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxTypes; import net.momirealms.craftengine.core.loot.LootTable; @@ -141,9 +142,25 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { hitboxes = List.of(defaultHitBox()); } - // fixme 动态计算aabb,因为家具具有朝向 - AABB maxAABB = new AABB(0,0,0,1,1,1); - + List aabbs = new ArrayList<>(); + for (FurnitureHitBoxConfig hitBox : hitboxes) { + hitBox.collectBoundingBox(aabbs::add); + } + double minX = 0; + double minY = 0; + double minZ = 0; + double maxX = 0; + double maxY = 0; + double maxZ = 0; + for (AABB aabb : aabbs) { + minX = Math.min(minX, aabb.minX); + minY = Math.min(minY, aabb.minY); + minZ = Math.min(minZ, aabb.minZ); + maxX = Math.max(maxX, aabb.maxX); + maxY = Math.max(maxY, aabb.maxY); + maxZ = Math.max(maxZ, aabb.maxZ); + } + AABB maxAABB = new AABB(minX, minY, minZ, maxX, maxY, maxZ); variants.put(variantName, new FurnitureVariant( variantName, parseCullingData(section.get("entity-culling"), maxAABB), @@ -160,7 +177,6 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { .variants(variants) .events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"))) .lootTable(LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), true))) - .build(); AbstractFurnitureManager.this.byId.put(id, furniture); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java index 689cb130c..f2361c098 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java @@ -23,4 +23,6 @@ public interface FurnitureHitBoxConfig { boolean canUseItemOn(); void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer); + + void collectBoundingBox(Consumer aabbConsumer); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java index 61eec5c6c..2c4577909 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.core.plugin.context.function; -import net.momirealms.craftengine.core.entity.furniture.AnchorType; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; @@ -15,7 +14,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -@SuppressWarnings("deprecation") public class ReplaceFurnitureFunction extends AbstractConditionalFunction { private final Key newFurnitureId; private final NumberProvider x; @@ -23,7 +21,7 @@ public class ReplaceFurnitureFunction extends AbstractCondi private final NumberProvider z; private final NumberProvider pitch; private final NumberProvider yaw; - private final AnchorType anchorType; + private final String variant; private final boolean dropLoot; private final boolean playSound; @@ -34,7 +32,7 @@ public class ReplaceFurnitureFunction extends AbstractCondi NumberProvider z, NumberProvider pitch, NumberProvider yaw, - AnchorType anchorType, + String variant, boolean dropLoot, boolean playSound, List> predicates @@ -46,7 +44,7 @@ public class ReplaceFurnitureFunction extends AbstractCondi this.z = z; this.pitch = pitch; this.yaw = yaw; - this.anchorType = anchorType; + this.variant = variant; this.dropLoot = dropLoot; this.playSound = playSound; } @@ -72,8 +70,7 @@ public class ReplaceFurnitureFunction extends AbstractCondi RemoveFurnitureFunction.removeFurniture(ctx, oldFurniture, dropLoot, playSound); // Place the new furniture - // fixme function -// SpawnFurnitureFunction.spawnFurniture(this.newFurnitureId, newPosition, this.anchorType, this.playSound); + SpawnFurnitureFunction.spawnFurniture(this.newFurnitureId, newPosition, this.variant, this.playSound); } } @@ -96,10 +93,10 @@ public class ReplaceFurnitureFunction extends AbstractCondi NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); 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); + String variant = ResourceConfigUtils.getAsStringOrNull(ResourceConfigUtils.get(arguments, "variant", "anchor-type")); boolean dropLoot = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("drop-loot", true), "drop-loot"); boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound"); - return new ReplaceFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, anchorType, dropLoot, playSound, getPredicates(arguments)); + return new ReplaceFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, variant, dropLoot, playSound, getPredicates(arguments)); } } } 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 d29b7758e..e297cf2f8 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 @@ -1,5 +1,7 @@ package net.momirealms.craftengine.core.plugin.context.function; +import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; +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; @@ -55,18 +57,13 @@ public class SpawnFurnitureFunction extends AbstractConditi float pitchValue = this.pitch.getFloat(ctx); float yawValue = this.yaw.getFloat(ctx); WorldPosition position = new WorldPosition(world, xPos, yPos, zPos, pitchValue, yawValue); - // fixme api -// spawnFurniture(this.furnitureId, position, this.anchorType, this.playSound); + spawnFurniture(this.furnitureId, position, this.variant, this.playSound); }); } -// public static void spawnFurniture(Key furnitureId, WorldPosition position, AnchorType anchorType, boolean playSound) { -// CraftEngine.instance().furnitureManager().furnitureById(furnitureId).ifPresent(furniture -> { -// AnchorType anchor = Optional.ofNullable(anchorType).orElse(furniture.getAnyAnchorType()); -// FurnitureDataAccessor extraData = FurnitureDataAccessor.builder().anchorType(anchor).build(); -// CraftEngine.instance().furnitureManager().place(position, furniture, extraData, playSound); -// }); -// } + public static void spawnFurniture(Key furnitureId, WorldPosition position, String variant, boolean playSound) { + CraftEngine.instance().furnitureManager().furnitureById(furnitureId).ifPresent(furniture -> CraftEngine.instance().furnitureManager().place(position, furniture, FurnitureDataAccessor.ofVariant(variant), playSound)); + } @Override public Key type() { 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 dc328fc4d..94c6f52e2 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 @@ -5,6 +5,7 @@ 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; +import org.joml.Vector3f; import java.util.Optional; @@ -68,6 +69,17 @@ public class AABB { ); } + public static AABB makeBoundingBox(Vector3f pos, double width, double height) { + return new AABB( + pos.x - width / 2, + pos.y, + pos.z - width / 2, + pos.x + width / 2, + pos.y + height, + pos.z + width / 2 + ); + } + public Optional clip(Vec3d min, Vec3d max) { double[] traceDistance = {1.0}; double deltaX = max.x - min.x; From 14b0c711472eb75e1e84567d007af30b860d7bb6 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 04:22:34 +0800 Subject: [PATCH 081/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84id=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/api/CraftEngineFurniture.java | 2 +- .../entity/furniture/BukkitCollider.java | 2 +- .../entity/furniture/BukkitFurniture.java | 4 +- .../furniture/BukkitFurnitureManager.java | 43 ++++++++++++++----- .../hitbox/CustomFurnitureHitbox.java | 7 +++ .../hitbox/HappyGhastFurnitureHitbox.java | 5 +++ .../hitbox/InteractionFurnitureHitbox.java | 8 ++++ .../hitbox/ShulkerFurnitureHitbox.java | 15 ++++--- .../hitbox/ShulkerFurnitureHitboxConfig.java | 22 ++++------ .../plugin/network/BukkitNetworkManager.java | 10 +++-- common-files/src/main/resources/config.yml | 2 +- .../configuration/furniture/wooden_chair.yml | 27 ++++++++---- .../core/entity/furniture/Furniture.java | 23 +++++++++- .../furniture/hitbox/FurnitureHitBox.java | 3 ++ gradle.properties | 2 +- 15 files changed, 128 insertions(+), 47 deletions(-) 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 c8ab62b47..096f42b67 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 @@ -330,7 +330,7 @@ public final class CraftEngineFurniture { public static BukkitFurniture getLoadedFurnitureByCollider(@NotNull Entity collider) { Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(collider); if (nmsEntity instanceof CollisionEntity collisionEntity) { - return BukkitFurnitureManager.instance().loadedFurnitureByColliderEntityId(collisionEntity.getId()); + return BukkitFurnitureManager.instance().loadedFurnitureByColliderEntityId(collisionEntity.getEntityId()); } return null; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCollider.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCollider.java index 69b8c9775..7b330e60a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCollider.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitCollider.java @@ -21,7 +21,7 @@ public class BukkitCollider implements Collider { @Override public int entityId() { - return this.collisionEntity.getId(); + return this.collisionEntity.getEntityId(); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index 4455884dc..d7bbe6f9d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -31,10 +31,12 @@ public class BukkitFurniture extends Furniture { public void addCollidersToWorld() { Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(this.location.getWorld()); for (Collider entity : super.colliders) { - FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(world, entity.handle()); Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity.handle()); bukkitEntity.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_COLLISION, PersistentDataType.BYTE, (byte) 1); bukkitEntity.setPersistent(false); + if (!bukkitEntity.isValid()) { + FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(world, entity.handle()); + } } } 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 21693ed18..0451bcf32 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 @@ -1,15 +1,19 @@ package net.momirealms.craftengine.bukkit.entity.furniture; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionFurnitureHitboxConfig; import net.momirealms.craftengine.bukkit.nms.CollisionEntity; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.util.Key; @@ -29,6 +33,7 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; +import java.util.function.Consumer; public class BukkitFurnitureManager extends AbstractFurnitureManager { public static final NamespacedKey FURNITURE_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_KEY); @@ -73,7 +78,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } catch (IOException e) { this.plugin.logger().warn("Failed to set furniture PDC for " + furniture.id().toString(), e); } - handleMetaEntityAfterChunkLoad(display); + handleMetaEntityDuringChunkLoad(display); }); if (playSound) { SoundData sound = furniture.settings().sounds().placeSound(); @@ -228,6 +233,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { createFurnitureInstance(entity, customFurniture); } + @SuppressWarnings("deprecation") protected void handleMetaEntityAfterChunkLoad(ItemDisplay entity) { // 实体可能不是持久的 if (!entity.isPersistent()) { @@ -239,7 +245,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { if (id == null) return; // 这个区块还处于加载实体中,这个时候不处理 - if (!isEntitiesLoaded(entity.getLocation())) { + Location location = entity.getLocation(); + if (!isEntitiesLoaded(location)) { return; } @@ -254,13 +261,16 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { if (previous != null) return; createFurnitureInstance(entity, customFurniture); + // 补发一次包 + for (Player player : entity.getTrackedPlayers()) { + BukkitAdaptors.adapt(player).sendPacket(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entity.getEntityId(), entity.getUniqueId(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), + MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 + ), false); + } } protected void handleCollisionEntityAfterChunkLoad(Entity entity) { - // 实体可能不是持久的 - if (!entity.isPersistent()) { - return; - } // 如果是碰撞实体,那么就忽略 if (FastNMS.INSTANCE.method$CraftEntity$getHandle(entity) instanceof CollisionEntity) { return; @@ -271,11 +281,13 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { return; } // 实体未加载 - if (!isEntitiesLoaded(entity.getLocation())) { + Location location = entity.getLocation(); + if (!isEntitiesLoaded(location)) { return; } + // 移除被WorldEdit错误复制的碰撞实体 - entity.remove(); + runSafeEntityOperation(location, entity::remove); } public void handleCollisionEntityDuringChunkLoad(Entity collisionEntity) { @@ -306,7 +318,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } // 创建家具实例,并初始化碰撞实体 - private void createFurnitureInstance(ItemDisplay display, FurnitureConfig furniture) { + private BukkitFurniture createFurnitureInstance(ItemDisplay display, FurnitureConfig furniture) { BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, getFurnitureDataAccessor(display)); this.byMetaEntityId.put(display.getEntityId(), bukkitFurniture); for (int entityId : bukkitFurniture.virtualEntityIds()) { @@ -315,7 +327,18 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { for (Collider collisionEntity : bukkitFurniture.colliders()) { this.byColliderEntityId.put(collisionEntity.entityId(), bukkitFurniture); } - bukkitFurniture.addCollidersToWorld(); + Location location = display.getLocation(); + runSafeEntityOperation(location, bukkitFurniture::addCollidersToWorld); + return bukkitFurniture; + } + + private void runSafeEntityOperation(Location location, Runnable action) { + boolean preventChange = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); + if (preventChange) { + this.plugin.scheduler().sync().runLater(action, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); + } else { + action.run(); + } } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java index 461f4ea07..900909794 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java @@ -27,6 +27,7 @@ public class CustomFurnitureHitbox extends AbstractFurnitureHitBox { private final Object spawnPacket; private final Object despawnPacket; private final FurnitureHitboxPart part; + private final int entityId; public CustomFurnitureHitbox(Furniture furniture, CustomFurnitureHitboxConfig config) { super(furniture, config); @@ -54,6 +55,7 @@ public class CustomFurnitureHitbox extends AbstractFurnitureHitBox { this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); this.part = new FurnitureHitboxPart(entityId, aabb, pos, false); this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(entityId); }}); + this.entityId = entityId; } @Override @@ -76,6 +78,11 @@ public class CustomFurnitureHitbox extends AbstractFurnitureHitBox { player.sendPacket(this.despawnPacket, false); } + @Override + public void collectVirtualEntityId(Consumer collector) { + collector.accept(this.entityId); + } + @Override public CustomFurnitureHitboxConfig config() { return this.config; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java index 750ee7c1c..e5306701a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java @@ -83,6 +83,11 @@ public class HappyGhastFurnitureHitbox extends AbstractFurnitureHitBox { player.sendPacket(this.despawnPacket, false); } + @Override + public void collectVirtualEntityId(Consumer collector) { + collector.accept(this.entityId); + } + @Override public HappyGhastFurnitureHitboxConfig config() { return this.config; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index 65b0ffd9e..4b4a34110 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.world.collision.AABB; import java.util.List; import java.util.UUID; +import java.util.function.Consumer; public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { private final InteractionFurnitureHitboxConfig config; @@ -21,6 +22,7 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { private final Object spawnPacket; private final Object despawnPacket; private final FurnitureHitboxPart part; + private final int entityId; public InteractionFurnitureHitbox(Furniture furniture, InteractionFurnitureHitboxConfig config) { super(furniture, config); @@ -39,6 +41,7 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { )); this.part = new FurnitureHitboxPart(interactionId, aabb, pos, config.responsive()); this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(interactionId); }}); + this.entityId = interactionId; } @Override @@ -56,6 +59,11 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { return this.config; } + @Override + public void collectVirtualEntityId(Consumer collector) { + collector.accept(this.entityId); + } + @Override public void show(Player player) { player.sendPacket(this.spawnPacket, false); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java index e4ef37a2a..db6c0b865 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java @@ -17,10 +17,7 @@ import net.momirealms.craftengine.core.world.WorldPosition; import org.joml.Quaternionf; import org.joml.Vector3f; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.function.Consumer; import java.util.function.Supplier; @@ -30,11 +27,12 @@ public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { private final List colliders; private final Object spawnPacket; private final Object despawnPacket; + private final int[] entityIds; public ShulkerFurnitureHitbox(Furniture furniture, ShulkerFurnitureHitboxConfig config) { super(furniture, config); this.config = config; - int[] entityIds = acquireEntityIds(CoreReflections.instance$Entity$ENTITY_COUNTER::incrementAndGet); + this.entityIds = acquireEntityIds(CoreReflections.instance$Entity$ENTITY_COUNTER::incrementAndGet); WorldPosition position = furniture.position(); Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - position.yRot()), 0f).conjugate(); Vector3f offset = conjugated.transform(new Vector3f(config.position())); @@ -99,6 +97,13 @@ public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { return this.parts; } + @Override + public void collectVirtualEntityId(Consumer collector) { + for (int entityId : entityIds) { + collector.accept(entityId); + } + } + @Override public void show(Player player) { player.sendPacket(this.spawnPacket, false); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java index ebc750c2a..c418fd36b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -80,10 +80,8 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 )); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); - if (canUseItemOn) { - Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive)); - } + Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive)); } }; this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.UP, offset, x, y, z); @@ -100,10 +98,8 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 )); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues))); - if (canUseItemOn) { - Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive)); - } + Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive)); } }; this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.DOWN, offset, x, y, z); @@ -130,12 +126,10 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 )); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues))); - if (canUseItemOn) { - Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z); - Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance); - aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d1, scale, scale), vec3d1, interactive)); - aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.makeBoundingBox(vec3d2, scale, scale), vec3d2, interactive)); - } + Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z); + Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance); + aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d1, scale, scale), vec3d1, interactive)); + aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.makeBoundingBox(vec3d2, scale, scale), vec3d2, interactive)); } }; this.aabbCreator = (x, y, z, yaw, offset) -> { 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 afa959aec..e6c351aaa 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 @@ -3954,14 +3954,16 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(id); if (furniture != null) { - serverPlayer.entityPacketHandlers().put(id, new FurniturePacketHandler(id, furniture.virtualEntityIds())); + EntityPacketHandler previous = serverPlayer.entityPacketHandlers().put(id, new FurniturePacketHandler(id, furniture.virtualEntityIds())); if (Config.enableEntityCulling()) { serverPlayer.addTrackedFurniture(id, furniture); } else { - furniture.show(serverPlayer); + // 修复addEntityToWorld,包比事件先发的问题 (WE) + if (previous == null || previous instanceof ItemDisplayPacketHandler) { + furniture.show(serverPlayer); + } } - // fixme 外部模型 - if (Config.hideBaseEntity() && true) { + if (Config.hideBaseEntity() && !furniture.hasExternalModel()) { event.setCancelled(true); } } else { diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 95815b4c5..5e33a6b41 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -558,7 +558,7 @@ chunk-system: client-optimization: # Requires a restart to fully apply. entity-culling: - enable: true + enable: false # Using server-side ray tracing algorithms to hide block entities/furniture and reduce client-side rendering pressure. ray-tracing: true # Cull entities based on distance 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 index 7ad0017fe..a399a75d3 100644 --- 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 @@ -27,14 +27,25 @@ items: billboard: FIXED translation: 0,0.5,0 hitboxes: - - position: 0,0,0 - type: custom - entity-type: slime - invisible: true - can-use-item-on: true - blocks-building: true - seats: - - 0,0,-0.1 0 + $$>=1.20.5: + - position: 0,0,0 + type: custom + entity-type: slime + invisible: true + can-use-item-on: true + blocks-building: true + seats: + - 0,0,-0.1 0 + $$fallback: + - position: 0,0,0 + type: interaction + blocks-building: true + invisible: true + width: 0.7 + height: 1.2 + interactive: true + seats: + - 0,0,-0.1 0 loot: template: default:loot_table/furniture arguments: diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index a69ccd26f..5099472cf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.momirealms.craftengine.core.entity.AbstractEntity; import net.momirealms.craftengine.core.entity.Entity; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; @@ -13,6 +14,7 @@ import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxCo import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.seat.Seat; +import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.QuaternionUtils; @@ -25,6 +27,7 @@ import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; +import java.util.Optional; import java.util.UUID; public abstract class Furniture implements Cullable { @@ -41,6 +44,8 @@ public abstract class Furniture implements Cullable { protected int[] virtualEntityIds; protected int[] colliderEntityIds; + private boolean hasExternalModel; + protected Furniture(Entity metaDataEntity, FurnitureDataAccessor data, FurnitureConfig config) { this.config = config; this.dataAccessor = data; @@ -77,8 +82,8 @@ public abstract class Furniture implements Cullable { this.hitboxes[i] = hitbox; for (FurnitureHitboxPart part : hitbox.parts()) { this.hitboxMap.put(part.entityId(), hitbox); - virtualEntityIds.add(part.entityId()); } + hitbox.collectVirtualEntityId(virtualEntityIds::addLast); colliders.addAll(hitbox.colliders()); } // 虚拟碰撞箱的实体id @@ -86,6 +91,18 @@ public abstract class Furniture implements Cullable { this.colliders = colliders.toArray(new Collider[0]); this.colliderEntityIds = colliders.stream().mapToInt(Collider::entityId).toArray(); this.cullingData = createCullingData(variant.cullingData()); + // 外部模型 + Optional externalModel = variant.externalModel(); + if (externalModel.isPresent()) { + this.hasExternalModel = true; + try { + externalModel.get().bindModel((AbstractEntity) this.metaDataEntity); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to load external model for furniture " + id(), e); + } + } else { + this.hasExternalModel = false; + } } private CullingData createCullingData(CullingData parent) { @@ -181,6 +198,10 @@ public abstract class Furniture implements Cullable { return this.metaDataEntity.entityID(); } + public boolean hasExternalModel() { + return hasExternalModel; + } + public Vec3d getRelativePosition(Vector3f position) { return getRelativePosition(this.position(), position); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java index 0a249acd4..95f7b652a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBox.java @@ -9,6 +9,7 @@ import net.momirealms.craftengine.core.world.Vec3d; import java.util.List; import java.util.Optional; +import java.util.function.Consumer; public interface FurnitureHitBox extends SeatOwner { @@ -24,6 +25,8 @@ public interface FurnitureHitBox extends SeatOwner { FurnitureHitBoxConfig config(); + void collectVirtualEntityId(Consumer collector); + default Optional clip(Vec3d min, Vec3d max) { for (FurnitureHitboxPart value : parts()) { Optional clip = value.aabb().clip(min, max); diff --git a/gradle.properties b/gradle.properties index 46db05f12..15594bad8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -48,7 +48,7 @@ byte_buddy_version=1.18.1 ahocorasick_version=0.6.3 snake_yaml_version=2.5 anti_grief_version=1.0.5 -nms_helper_version=1.0.138 +nms_helper_version=1.0.140 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.38.7 From 26862498dc9df36c1b825cab2025c1f82bcc36cd Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 04:50:09 +0800 Subject: [PATCH 082/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E7=9A=84=E6=91=84=E5=83=8F=E6=9C=BA=E4=BD=8D=E7=BD=AE=E4=B8=BA?= =?UTF-8?q?=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/bukkit/plugin/user/BukkitServerPlayer.java | 3 +++ 1 file changed, 3 insertions(+) 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 d887a07d7..98fd209ee 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 @@ -602,6 +602,9 @@ public class BukkitServerPlayer extends Player { @Override public void entityCullingTick() { this.culling.restoreTokenOnTick(); + if (this.firstPersonCameraVec3 == null || this.thirdPersonCameraVec3 == null) { + return; + } boolean useRayTracing = Config.entityCullingRayTracing(); if (this.enableEntityCulling) { for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) { From 6cf114b4f0f8dc50fdabdd6853fcee9fcfa7424d Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 05:45:14 +0800 Subject: [PATCH 083/135] Update BukkitFurnitureManager.java --- .../entity/furniture/BukkitFurnitureManager.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) 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 0451bcf32..ff12e6dcc 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 @@ -224,6 +224,11 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { Optional optionalFurniture = furnitureById(key); if (optionalFurniture.isEmpty()) return; + // 只对1.20.2及以上生效,1.20.1比较特殊 + if (!VersionHelper.isOrAbove1_20_2()) { + return; + } + // 已经在其他事件里加载过了 FurnitureConfig customFurniture = optionalFurniture.get(); BukkitFurniture previous = this.byMetaEntityId.get(entity.getEntityId()); @@ -244,9 +249,9 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { String id = entity.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING); if (id == null) return; - // 这个区块还处于加载实体中,这个时候不处理 + // 这个区块还处于加载实体中,这个时候不处理(1.20.1需要特殊处理) Location location = entity.getLocation(); - if (!isEntitiesLoaded(location)) { + if (VersionHelper.isOrAbove1_20_2() && !isEntitiesLoaded(location)) { return; } @@ -261,11 +266,12 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { if (previous != null) return; createFurnitureInstance(entity, customFurniture); - // 补发一次包 + + // 补发一次包,修复 for (Player player : entity.getTrackedPlayers()) { BukkitAdaptors.adapt(player).sendPacket(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entity.getEntityId(), entity.getUniqueId(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), - MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 + entity.getEntityId(), entity.getUniqueId(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), + MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 ), false); } } From fa6f837852b57c33547ccbdebb4d1785cef834bd Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 05:57:38 +0800 Subject: [PATCH 084/135] Update gradle.properties --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e816ca6c7..c1b0ab52a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings -project_version=0.0.65.15.1 +project_version=0.0.65.16 config_version=60 lang_version=41 project_group=net.momirealms From cac852eb8102ec3d6307fcfc4ba4326013ef65a8 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 17:32:51 +0800 Subject: [PATCH 085/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=91=94=E8=90=BD?= =?UTF-8?q?=E9=9F=B3=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bukkit/compatibility/build.gradle.kts | 6 +-- .../bukkit/block/BlockEventListener.java | 37 +++++++++++++++++-- .../craftengine/bukkit/util/EntityUtils.java | 10 ++--- .../plugin/context/event/EventTrigger.java | 3 +- gradle.properties | 2 +- 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/bukkit/compatibility/build.gradle.kts b/bukkit/compatibility/build.gradle.kts index 949b79fe4..13d93e5cd 100644 --- a/bukkit/compatibility/build.gradle.kts +++ b/bukkit/compatibility/build.gradle.kts @@ -41,8 +41,8 @@ dependencies { compileOnly("io.github.toxicity188:bettermodel:1.14.0") compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}") // MMOItems - compileOnly("net.Indyuce:MMOItems-API:6.10-SNAPSHOT") - compileOnly("io.lumine:MythicLib-dist:1.6.2-SNAPSHOT") + compileOnly("net.Indyuce:MMOItems-API:6.10.1-SNAPSHOT") + compileOnly("io.lumine:MythicLib-dist:1.7.1-SNAPSHOT") // Nexo compileOnly("com.nexomc:nexo:1.13.0") // LuckPerms @@ -62,7 +62,7 @@ dependencies { // McMMO compileOnly("com.gmail.nossr50.mcMMO:mcMMO:2.2.038") // MMOCore - compileOnly("net.Indyuce:MMOCore-API:1.12.1-SNAPSHOT") + compileOnly("net.Indyuce:MMOCore-API:1.13.1-SNAPSHOT") // JobsReborn compileOnly("com.github.Zrips:Jobs:v5.2.2.3") // CustomFishing 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 8da86d668..49ffc0057 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java @@ -21,6 +21,7 @@ import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.sound.SoundSource; import net.momirealms.craftengine.core.util.Cancellable; import net.momirealms.craftengine.core.util.ItemUtils; @@ -38,6 +39,7 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPhysicsEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.world.GenericGameEvent; import org.bukkit.inventory.ItemStack; @@ -233,12 +235,14 @@ public final class BlockEventListener implements Listener { @EventHandler(priority = EventPriority.LOW) public void onStep(GenericGameEvent event) { - if (event.getEvent() != GameEvent.STEP) return; + GameEvent gameEvent = event.getEvent(); + // 只处理落地和走路 + if (gameEvent != GameEvent.STEP) return; Entity entity = event.getEntity(); if (!(entity instanceof Player player)) return; BlockPos pos = EntityUtils.getOnPos(player); Block block = player.getWorld().getBlockAt(pos.x(), pos.y(), pos.z()); - Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(block.getWorld()), LocationUtils.toBlockPos(block.getX(), block.getY(), block.getZ())); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()), LocationUtils.toBlockPos(pos)); Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState); if (optionalCustomState.isPresent()) { Location location = player.getLocation(); @@ -253,7 +257,8 @@ public final class BlockEventListener implements Listener { if (cancellable.isCancelled() && !Config.processCancelledStep()) { return; } - player.playSound(location, state.settings().sounds().stepSound().id().toString(), SoundCategory.BLOCKS, state.settings().sounds().stepSound().volume().get(), state.settings().sounds().stepSound().pitch().get()); + SoundData soundData = state.settings().sounds().stepSound(); + player.playSound(location, soundData.id().toString(), SoundCategory.BLOCKS, soundData.volume().get(), soundData.pitch().get()); } else if (Config.enableSoundSystem()) { if (event.isCancelled() && !Config.processCancelledStep()) { return; @@ -267,6 +272,32 @@ public final class BlockEventListener implements Listener { } } + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) + public void onFall(EntityDamageEvent event) { + if (event.getCause() != EntityDamageEvent.DamageCause.FALL) + return; + if (!(event.getEntity() instanceof Player player)) return; + BlockPos pos = EntityUtils.getOnPos(player); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()), LocationUtils.toBlockPos(pos)); + Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState); + if (optionalCustomState.isPresent()) { + Location location = player.getLocation(); + ImmutableBlockState state = optionalCustomState.get(); + SoundData soundData = state.settings().sounds().fallSound(); + player.playSound(location, soundData.id().toString(), SoundCategory.BLOCKS, soundData.volume().get(), soundData.pitch().get()); + } else if (Config.enableSoundSystem()) { + if (event.isCancelled() && !Config.processCancelledStep()) { + return; + } + Object soundType = FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(blockState); + Object soundEvent = FastNMS.INSTANCE.field$SoundType$fallSound(soundType); + Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent); + if (this.manager.isStepSoundMissing(soundId)) { + player.playSound(player.getLocation(), soundId.toString(), SoundCategory.BLOCKS, 0.15f, 1f); + } + } + } + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) public void onBlockPhysics(BlockPhysicsEvent event) { // for vanilla blocks diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java index f9cc52975..81396c16e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java @@ -26,13 +26,9 @@ public final class EntityUtils { } public static BlockPos getOnPos(Player player) { - try { - Object serverPlayer = FastNMS.INSTANCE.method$CraftPlayer$getHandle(player); - Object blockPos = CoreReflections.method$Entity$getOnPos.invoke(serverPlayer, 1.0E-5F); - return LocationUtils.fromBlockPos(blockPos); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } + Object serverPlayer = FastNMS.INSTANCE.method$CraftPlayer$getHandle(player); + Object blockPos = FastNMS.INSTANCE.method$Entity$getOnPos(serverPlayer); + return LocationUtils.fromBlockPos(blockPos); } public static Entity spawnEntity(World world, Location loc, EntityType type, Consumer function) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventTrigger.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventTrigger.java index 2a29e7fd9..0faee9842 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventTrigger.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventTrigger.java @@ -11,7 +11,8 @@ public enum EventTrigger { BREAK("break", "dig"), PLACE("place", "build"), PICK_UP("pick_up", "pick"), - STEP("step"),; + STEP("step"), + FALL("fall"),; public static final Map BY_NAME = new HashMap<>(); private final String[] names; diff --git a/gradle.properties b/gradle.properties index c1b0ab52a..5e2c657b8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -48,7 +48,7 @@ byte_buddy_version=1.18.1 ahocorasick_version=0.6.3 snake_yaml_version=2.5 anti_grief_version=1.0.5 -nms_helper_version=1.0.140 +nms_helper_version=1.0.141 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.38.7 From cba5de8f9860caafff512c92a71050aae6d5611e Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 19:05:02 +0800 Subject: [PATCH 086/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=A4=9A=E8=AF=AD?= =?UTF-8?q?=E8=A8=80=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BukkitCompatibilityManager.java | 7 ++-- .../advancement/BukkitAdvancementManager.java | 5 +++ .../furniture/BukkitFurnitureManager.java | 3 -- .../hitbox/ShulkerFurnitureHitbox.java | 5 ++- .../bukkit/loot/BukkitVanillaLootManager.java | 12 ++++++ .../bukkit/pack/BukkitPackManager.java | 4 +- .../src/main/resources/translations/en.yml | 14 +++++++ .../src/main/resources/translations/zh_cn.yml | 14 +++++++ .../core/block/AbstractBlockManager.java | 17 ++++++++ .../furniture/AbstractFurnitureManager.java | 6 ++- .../core/font/AbstractFontManager.java | 12 +++++- .../core/item/AbstractItemManager.java | 10 +++++ .../item/recipe/AbstractRecipeManager.java | 5 +++ .../recipe/CustomSmithingTransformRecipe.java | 5 ++- .../core/pack/AbstractPackManager.java | 39 +++++++++++-------- .../core/pack/host/impl/AlistHost.java | 3 +- .../core/pack/host/impl/DropboxHost.java | 3 +- .../core/pack/host/impl/GitLabHost.java | 3 +- .../core/pack/host/impl/LobFileHost.java | 3 +- .../core/pack/host/impl/OneDriveHost.java | 3 +- .../pack/host/impl/SelfHostHttpServer.java | 3 +- .../core/plugin/config/ConfigParser.java | 8 ++++ .../config/template/TemplateManagerImpl.java | 5 +++ .../plugin/context/GlobalVariableManager.java | 5 +++ .../gui/category/ItemBrowserManagerImpl.java | 5 +++ .../plugin/locale/TranslationManager.java | 16 ++++++-- .../plugin/locale/TranslationManagerImpl.java | 24 ++++++++++++ .../core/sound/AbstractSoundManager.java | 10 +++++ gradle.properties | 2 +- 29 files changed, 213 insertions(+), 38 deletions(-) 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 325159bdc..28d614af0 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 @@ -37,6 +37,7 @@ import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.plugin.context.condition.AlwaysFalseCondition; import net.momirealms.craftengine.core.plugin.context.event.EventConditions; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.text.minimessage.FormattedLine; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; @@ -181,7 +182,7 @@ public class BukkitCompatibilityManager implements CompatibilityManager { } private void logHook(String plugin) { - this.plugin.logger().info("[Compatibility] " + plugin + " hooked"); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.compatibility", plugin)); } @Override @@ -252,8 +253,8 @@ public class BukkitCompatibilityManager implements CompatibilityManager { if (VersionHelper.isOrAbove1_20_3()) { this.plugin.logger().severe(""); if (Locale.getDefault() == Locale.SIMPLIFIED_CHINESE) { - this.plugin.logger().severe("[Compatibility] 插件需要更新 FastAsyncWorldEdit 到 2.13.0 或更高版本,以获得更好的兼容性。(当前版本: " + version + ")"); - this.plugin.logger().severe("[Compatibility] 请前往 https://ci.athion.net/job/FastAsyncWorldEdit/ 下载最新版本"); + this.plugin.logger().severe("[兼容性] 插件需要更新 FastAsyncWorldEdit 到 2.13.0 或更高版本,以获得更好的兼容性。(当前版本: " + version + ")"); + this.plugin.logger().severe("[兼容性] 请前往 https://ci.athion.net/job/FastAsyncWorldEdit/ 下载最新版本"); } else { this.plugin.logger().severe("[Compatibility] Update FastAsyncWorldEdit to v2.13.0+ for better compatibility (Current: " + version + ")"); this.plugin.logger().severe("[Compatibility] Download latest version: https://ci.athion.net/job/FastAsyncWorldEdit/"); 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 9a5f8acc3..5d98d2130 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 @@ -119,6 +119,11 @@ public final class BukkitAdvancementManager extends AbstractAdvancementManager { return LoadingSequence.ADVANCEMENT; } + @Override + public int count() { + return 0; + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (advancements.containsKey(id)) { 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 ff12e6dcc..5b0f5cacc 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 @@ -7,13 +7,11 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; -import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.util.Key; @@ -33,7 +31,6 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; -import java.util.function.Consumer; public class BukkitFurnitureManager extends AbstractFurnitureManager { public static final NamespacedKey FURNITURE_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_KEY); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java index db6c0b865..ac88d288d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java @@ -17,7 +17,10 @@ import net.momirealms.craftengine.core.world.WorldPosition; import org.joml.Quaternionf; import org.joml.Vector3f; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; import java.util.function.Consumer; import java.util.function.Supplier; 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 d38e58a24..7836a0812 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 @@ -93,6 +93,7 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme public class VanillaLootParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"vanilla-loots", "vanilla-loot"}; + private int count; @Override public int loadingSequence() { @@ -104,6 +105,16 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme return CONFIG_SECTION_NAME; } + @Override + public int count() { + return this.count; + } + + @Override + public void preProcess() { + this.count = 0; + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("type"), "warning.config.vanilla_loot.missing_type"); @@ -147,6 +158,7 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme } } } + this.count++; } } } 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 4b7aa72cc..fc961ac8d 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 @@ -14,6 +14,8 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData; import net.momirealms.craftengine.core.pack.obfuscation.ObfA; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.plugin.locale.TranslationManagerImpl; import net.momirealms.craftengine.core.util.Base64Utils; import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Bukkit; @@ -94,7 +96,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener { return; } if (!Config.sendPackOnUpload()) return; - CraftEngine.instance().logger().info("Completed uploading resource pack"); + CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.resource_pack.upload")); for (BukkitServerPlayer player : this.plugin.networkManager().onlineUsers()) { sendResourcePack(player); } diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 2377b162b..9e9be8c07 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -38,6 +38,20 @@ argument.parse.failure.aggregate.missing: "Missing component ''Invalid component '': " argument.parse.failure.either: "Could not resolve or from ''" argument.parse.failure.namedtextcolor: "'' is not a named text color" +info.pack.load: "Loaded pack: . Default namespace: " +info.resource.load: "Loaded in ms ()" +info.resource_pack.start: "Generating resource pack..." +info.resource_pack.generate: "Generated resource pack in ms" +info.resource_pack.validate: "Validated resource pack in ms" +info.resource_pack.optimize: "Optimized resource pack in ms" +info.resource_pack.optimize.json: "> Optimizing json files..." +info.resource_pack.optimize.texture: "> Optimizing textures..." +info.resource_pack.optimize.result: "□ Before/After/Ratio: KB/ KB/%" +info.resource_pack.create: "Created resource pack zip in ms" +info.resource_pack.upload: "Completed uploading resource pack" +info.host.self.netty_server: "Netty HTTP server started on port: " +info.host.cache.load: "[] Loaded cached resource pack metadata" +info.compatibility: "[Compatibility] hooked" command.reload.config.success: "Configs reloaded in ms. (Async: ms | Sync: ms)" command.reload.config.failure: "Config reload failed. Check console logs." command.reload.pack.success: "Resource pack reloaded in ms." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 06d51777c..54850c5f5 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -38,6 +38,20 @@ argument.parse.failure.aggregate.missing: "缺少组件 ''" argument.parse.failure.aggregate.failure: "无效的组件 '': " argument.parse.failure.either: "无法从 '' 解析 " argument.parse.failure.namedtextcolor: "'' 不是颜色代码" +info.pack.load: "已加载包: . 默认命名空间: " +info.resource.load: "加载 耗时 ms ()" +info.resource_pack.start: "正在开始生成资源包..." +info.resource_pack.generate: "生成资源包耗时 ms" +info.resource_pack.validate: "验证资源包耗时 ms" +info.resource_pack.optimize: "优化资源包耗时 ms" +info.resource_pack.optimize.json: "> 正在优化json文件..." +info.resource_pack.optimize.texture: "> 正在优化贴图文件..." +info.resource_pack.optimize.result: "□ 优化前/优化后/比例: KB/ KB/%" +info.resource_pack.create: "创建资源包文件耗时 ms" +info.resource_pack.upload: "资源包上传完成" +info.host.self.netty_server: "Netty HTTP 服务已在端口 开启" +info.host.cache.load: "[] 已加载缓存的资源包元数据" +info.compatibility: "[兼容性] 已挂钩 " command.reload.config.success: "重新加载配置完成. 耗时 毫秒 (异步: ms | 同步: ms)" command.reload.config.failure: "重新加载配置失败, 请检查控制台日志" command.reload.pack.success: "资源包重新加载完成. 耗时 毫秒" 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 5707d7b2f..9f620cd82 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 @@ -261,6 +261,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem public class BlockStateMappingParser extends SectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[]{"block-state-mappings", "block-state-mapping"}; + private int count; @Override public String[] sectionId() { @@ -272,6 +273,16 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem return LoadingSequence.BLOCK_STATE_MAPPING; } + @Override + public int count() { + return this.count; + } + + @Override + public void preProcess() { + this.count = 0; + } + @Override public void parseSection(Pack pack, Path path, Map section) throws LocalizedException { ExceptionCollector exceptionCollector = new ExceptionCollector<>(); @@ -302,6 +313,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem List blockStateWrappers = AbstractBlockManager.this.blockStateArranger.computeIfAbsent(blockOwnerId, k -> new ArrayList<>()); blockStateWrappers.add(beforeState); AbstractBlockManager.this.autoVisualBlockStateCandidates[beforeState.registryId()] = createVisualBlockCandidate(beforeState); + this.count++; } exceptionCollector.throwIfPresent(); } @@ -331,6 +343,11 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem this.visualBlockStateAllocator = new VisualBlockStateAllocator(AbstractBlockManager.this.plugin.dataFolderPath().resolve("cache").resolve("visual-block-states.json"), candidates, AbstractBlockManager.this::createVanillaBlockState); } + @Override + public int count() { + return AbstractBlockManager.this.byId.size(); + } + public void addPendingConfigSection(PendingConfigSection section) { this.pendingConfigSections.add(section); } 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 e056dfc73..98a5fb890 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 @@ -2,7 +2,6 @@ package net.momirealms.craftengine.core.entity.furniture; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs; -import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxTypes; import net.momirealms.craftengine.core.loot.LootTable; @@ -109,6 +108,11 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { return LoadingSequence.FURNITURE; } + @Override + public int count() { + return AbstractFurnitureManager.this.byId.size(); + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (AbstractFurnitureManager.this.byId.containsKey(id)) { 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 df9e4fdff..cfa52da0b 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 @@ -442,9 +442,14 @@ public abstract class AbstractFontManager implements FontManager { return LoadingSequence.EMOJI; } + @Override + public int count() { + return AbstractFontManager.this.emojis.size(); + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { - if (emojis.containsKey(id)) { + if (AbstractFontManager.this.emojis.containsKey(id)) { throw new LocalizedResourceConfigException("warning.config.emoji.duplicate"); } String permission = (String) section.get("permission"); @@ -510,6 +515,11 @@ public abstract class AbstractFontManager implements FontManager { return LoadingSequence.IMAGE; } + @Override + public int count() { + return AbstractFontManager.this.images.size(); + } + @Override public void postProcess() { for (Map.Entry entry : this.idAllocators.entrySet()) { 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 d6defc9b4..3104b4e53 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 @@ -351,6 +351,11 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl .toList(); registerArmorTrimPattern(trims); } + + @Override + public int count() { + return AbstractItemManager.this.equipments.size(); + } } public void addOrMergeEquipment(ComponentBasedEquipment equipment) { @@ -368,6 +373,11 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl public static final String[] CONFIG_SECTION_NAME = new String[] {"items", "item"}; private final Map idAllocators = new HashMap<>(); + @Override + public int count() { + return AbstractItemManager.this.customItemsById.size(); + } + private boolean isModernFormatRequired() { return Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_4); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java index 93d1ea157..657906f95 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java @@ -134,6 +134,11 @@ public abstract class AbstractRecipeManager implements RecipeManager { return CONFIG_SECTION_NAME; } + @Override + public int count() { + return Math.max(0, AbstractRecipeManager.this.byId.size() - AbstractRecipeManager.this.dataPackRecipes.size()); + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (!Config.enableRecipeSystem()) return; 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 87730aa0e..965772071 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 @@ -19,7 +19,10 @@ import net.momirealms.craftengine.core.util.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; 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 1aa740527..ab9ffd4e5 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 @@ -31,10 +31,7 @@ import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.config.SectionConfigParser; import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor; -import net.momirealms.craftengine.core.plugin.locale.LangData; -import net.momirealms.craftengine.core.plugin.locale.LocalizedException; -import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.plugin.locale.*; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.sound.AbstractSoundManager; import net.momirealms.craftengine.core.sound.SoundEvent; @@ -407,7 +404,7 @@ public abstract class AbstractPackManager implements PackManager { } Pack pack = new Pack(path, new PackMeta(author, description, version, namespace), enable); this.loadedPacks.put(path.getFileName().toString(), pack); - this.plugin.logger().info("Loaded pack: " + pack.folder().getFileName() + ". Default namespace: " + namespace); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.pack.load", pack.folder().getFileName().toString(), namespace)); } } } catch (IOException e) { @@ -695,7 +692,12 @@ public abstract class AbstractPackManager implements PackManager { parser.loadAll(); parser.postProcess(); long t2 = System.nanoTime(); - this.plugin.logger().info("Loaded " + parser.sectionId()[0] + " in " + String.format("%.2f", ((t2 - t1) / 1_000_000.0)) + " ms"); + int count = parser.count(); + if (parser.silentIfNotExists() && count == 0) { + continue; + } + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource.load", + parser.sectionId()[0], String.format("%.2f", ((t2 - t1) / 1_000_000.0)), String.valueOf(count))); } } @@ -720,7 +722,7 @@ public abstract class AbstractPackManager implements PackManager { @Override public void generateResourcePack() throws IOException { - this.plugin.logger().info("Generating resource pack..."); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.start")); long time1 = System.currentTimeMillis(); // Create cache data @@ -768,17 +770,17 @@ public abstract class AbstractPackManager implements PackManager { this.removeAllShaders(generatedPackPath); } long time2 = System.currentTimeMillis(); - this.plugin.logger().info("Generated resource pack in " + (time2 - time1) + "ms"); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.generate", String.valueOf(time2 - time1))); if (Config.validateResourcePack()) { this.validateResourcePack(generatedPackPath); } long time3 = System.currentTimeMillis(); - this.plugin.logger().info("Validated resource pack in " + (time3 - time2) + "ms"); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.validate", String.valueOf(time3 - time2))); if (Config.optimizeResourcePack()) { this.optimizeResourcePack(generatedPackPath); } long time4 = System.currentTimeMillis(); - this.plugin.logger().info("Optimized resource pack in " + (time4 - time3) + "ms"); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.optimize", String.valueOf(time4 - time3))); Path finalPath = resourcePackPath(); Files.createDirectories(finalPath.getParent()); try { @@ -787,7 +789,7 @@ public abstract class AbstractPackManager implements PackManager { this.plugin.logger().severe("Error zipping resource pack", e); } long time5 = System.currentTimeMillis(); - this.plugin.logger().info("Created resource pack zip file in " + (time5 - time4) + "ms"); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.create", String.valueOf(time5 - time4))); this.generationEventDispatcher.accept(generatedPackPath, finalPath); } } @@ -1042,7 +1044,7 @@ public abstract class AbstractPackManager implements PackManager { } if (Config.optimizeJson()) { - this.plugin.logger().info("> Optimizing json files..."); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.optimize.json")); AtomicLong previousBytes = new AtomicLong(0L); AtomicLong afterBytes = new AtomicLong(0L); List> futures = new ArrayList<>(); @@ -1109,11 +1111,11 @@ public abstract class AbstractPackManager implements PackManager { long originalSize = previousBytes.get(); long optimizedSize = afterBytes.get(); double compressionRatio = ((double) optimizedSize / originalSize) * 100; - this.plugin.logger().info("□ Before/After/Ratio: " + formatSize(originalSize) + "/" + formatSize(optimizedSize) + "/" + String.format("%.2f%%", compressionRatio)); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.optimize.result", formatSize(originalSize), formatSize(optimizedSize), String.format("%.2f%%", compressionRatio))); } if (Config.optimizeTexture()) { - this.plugin.logger().info("> Optimizing textures..."); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.optimize.texture")); AtomicLong previousBytes = new AtomicLong(0L); AtomicLong afterBytes = new AtomicLong(0L); List> futures = new ArrayList<>(); @@ -1155,7 +1157,7 @@ public abstract class AbstractPackManager implements PackManager { long originalSize = previousBytes.get(); long optimizedSize = afterBytes.get(); double compressionRatio = ((double) optimizedSize / originalSize) * 100; - this.plugin.logger().info("□ Before/After/Ratio: " + formatSize(originalSize) + "/" + formatSize(optimizedSize) + "/" + String.format("%.2f%%", compressionRatio)); + this.plugin.logger().info(TranslationManager.instance().translateLog("info.resource_pack.optimize.result", formatSize(originalSize), formatSize(optimizedSize), String.format("%.2f%%", compressionRatio))); } } @@ -1170,7 +1172,7 @@ public abstract class AbstractPackManager implements PackManager { " ".repeat(Math.max(0, emptyLength)) + "]"; return String.format( - "%s %d/%d (%.1f%%) | Time: %ss", + "%s %d/%d (%.1f%%) | %ss", progressBar, current, total, @@ -2854,6 +2856,11 @@ public abstract class AbstractPackManager implements PackManager { this.excludeJson.clear(); } + @Override + public int count() { + return this.excludeJson.size() + this.excludeTexture.size(); + } + public Set excludeTexture() { return excludeTexture; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java index 674fbf908..8b36730a6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java @@ -9,6 +9,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory; import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.*; import org.jetbrains.annotations.Nullable; @@ -85,7 +86,7 @@ public class AlistHost implements ResourcePackHost { new TypeToken>(){}.getType() ); this.cachedSha1 = cache.get("sha1"); - CraftEngine.instance().logger().info("[Alist] Loaded cached resource pack metadata"); + CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.host.cache.load", "Alist")); } catch (Exception e) { CraftEngine.instance().logger().warn("[Alist] Failed to load cache " + cachePath, e); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java index beab2350a..98b65751b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory; import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.*; import java.io.IOException; @@ -58,7 +59,7 @@ public class DropboxHost implements ResourcePackHost { this.refreshToken = getString(cache, "refresh_token"); this.accessToken = getString(cache, "access_token"); this.expiresAt = getLong(cache, "expires_at"); - CraftEngine.instance().logger().info("[Dropbox] Loaded cached resource pack info"); + CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.host.cache.load", "Dropbox")); } catch (Exception e) { CraftEngine.instance().logger().warn("[Dropbox] Failed to load cache " + cachePath, e); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java index b94adf1f1..d2e4fd58b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java @@ -7,6 +7,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory; import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.*; import java.io.IOException; @@ -58,7 +59,7 @@ public class GitLabHost implements ResourcePackHost { if (uuidString != null && !uuidString.isEmpty()) { this.uuid = UUID.fromString(uuidString); } - CraftEngine.instance().logger().info("[GitLab] Loaded cached resource pack info"); + CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.host.cache.load", "GitLab")); } catch (Exception e) { CraftEngine.instance().logger().warn( "[GitLab] Failed to read cache file: " + cachePath, e); 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 22a0dfc37..89db7002d 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 @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory; import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; @@ -70,7 +71,7 @@ public class LobFileHost implements ResourcePackHost { if (uuidString != null && !uuidString.isEmpty()) { this.uuid = UUID.fromString(uuidString); } - CraftEngine.instance().logger().info("[LobFile] Loaded cached resource pack info"); + CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.host.cache.load", "LobFile")); } catch (Exception e) { CraftEngine.instance().logger().warn( "[LobFile] Failed to read cache file: " + e.getMessage()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java index a15fa632f..3fb95aa92 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory; import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.*; import java.io.FileNotFoundException; @@ -76,7 +77,7 @@ public class OneDriveHost implements ResourcePackHost { this.sha1 = cache.get("sha1"); this.fileId = cache.get("file-id"); - CraftEngine.instance().logger().info("[OneDrive] Loaded cached resource pack info"); + CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.host.cache.load", "OneDrive")); } catch (Exception e) { CraftEngine.instance().logger().warn( "[OneDrive] Failed to load cache" + cachePath, e); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java index 39961f3aa..29563b49c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java @@ -21,6 +21,7 @@ import io.netty.util.CharsetUtil; import io.netty.util.concurrent.GlobalEventExecutor; import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import org.jetbrains.annotations.Nullable; import java.io.ByteArrayInputStream; @@ -166,7 +167,7 @@ public class SelfHostHttpServer { }); try { serverChannel = b.bind(port).sync().channel(); - CraftEngine.instance().logger().info("Netty HTTP server started on port: " + port); + CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.host.self.netty_server", String.valueOf(port))); } catch (InterruptedException e) { CraftEngine.instance().logger().warn("Failed to start Netty server", e); Thread.currentThread().interrupt(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java index 5fc06c91e..915fb7cc7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/ConfigParser.java @@ -25,4 +25,12 @@ public interface ConfigParser extends Comparable { void loadAll(); void clear(); + + default int count() { + return -1; + } + + default boolean silentIfNotExists() { + return true; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java index 3b0502db5..cfe45b6ca 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java @@ -51,6 +51,11 @@ public class TemplateManagerImpl implements TemplateManager { return LoadingSequence.TEMPLATE; } + @Override + public int count() { + return TemplateManagerImpl.this.templates.size(); + } + @Override public void parseObject(Pack pack, Path path, String node, Key id, Object obj) { if (TemplateManagerImpl.this.templates.containsKey(id)) { 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 078a8c600..42af0daa5 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 @@ -49,6 +49,11 @@ public class GlobalVariableManager implements Manageable { return CONFIG_SECTION_NAME; } + @Override + public int count() { + return GlobalVariableManager.this.globalVariables.size(); + } + @Override public void parseObject(Pack pack, Path path, String node, Key id, Object object) throws LocalizedException { if (object != null) { 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 b4d6d30f4..27baae5e7 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 @@ -109,6 +109,11 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { return LoadingSequence.CATEGORY; } + @Override + public int count() { + return ItemBrowserManagerImpl.this.byId.size(); + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { String name = section.getOrDefault("name", id).toString(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java index 89fa4edea..8f9909250 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java @@ -4,13 +4,12 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.translation.Translator; import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.ConfigParser; +import net.momirealms.craftengine.core.plugin.text.minimessage.IndexedArgumentTag; +import net.momirealms.craftengine.core.util.AdventureHelper; import org.incendo.cloud.suggestion.Suggestion; import org.jetbrains.annotations.Nullable; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; public interface TranslationManager extends Manageable { @@ -74,6 +73,15 @@ public interface TranslationManager extends Manageable { } } + default String translateLog(String id, String... arguments) { + String translation = miniMessageTranslation(id); + if (translation == null) { + return id; + } + Component deserialize = AdventureHelper.customMiniMessage().deserialize(translation, new IndexedArgumentTag(Arrays.stream(arguments).map(Component::text).toList())); + return AdventureHelper.plainTextContent(deserialize); + } + Set translationKeys(); void log(String id, String... args); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java index 44cd01d8c..e193d837b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java @@ -277,6 +277,7 @@ public class TranslationManagerImpl implements TranslationManager { public class TranslationParser extends IdSectionConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"translations", "translation", "l10n", "localization", "i18n", "internationalization"}; + private int count; @Override public int loadingSequence() { @@ -288,6 +289,16 @@ public class TranslationManagerImpl implements TranslationManager { return CONFIG_SECTION_NAME; } + @Override + public int count() { + return this.count; + } + + @Override + public void preProcess() { + this.count = 0; + } + @Override public void parseSection(Pack pack, Path path, String node, net.momirealms.craftengine.core.util.Key id, Map section) { Locale locale = TranslationManager.parseLocale(id.value()); @@ -300,6 +311,7 @@ public class TranslationManagerImpl implements TranslationManager { String key = entry.getKey(); bundle.put(key, entry.getValue().toString()); TranslationManagerImpl.this.translationKeys.add(key); + this.count++; } TranslationManagerImpl.this.registry.registerAll(locale, bundle); @@ -313,6 +325,7 @@ public class TranslationManagerImpl implements TranslationManager { Component deserialize = AdventureHelper.miniMessage().deserialize(AdventureHelper.legacyToMiniMessage(s), ShiftTag.INSTANCE, ImageTag.INSTANCE); return AdventureHelper.getLegacy().serialize(deserialize); }; + private int count; @Override public int loadingSequence() { @@ -324,6 +337,16 @@ public class TranslationManagerImpl implements TranslationManager { return CONFIG_SECTION_NAME; } + @Override + public int count() { + return this.count; + } + + @Override + public void preProcess() { + this.count = 0; + } + @Override public void parseSection(Pack pack, Path path, String node, net.momirealms.craftengine.core.util.Key id, Map section) { String langId = id.value().toLowerCase(Locale.ENGLISH); @@ -333,6 +356,7 @@ public class TranslationManagerImpl implements TranslationManager { entry -> this.langProcessor.apply(String.valueOf(entry.getValue())) )); TranslationManagerImpl.this.addClientTranslation(langId, sectionData); + this.count += sectionData.size(); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java b/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java index 0972a6eed..1a31d0140 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/sound/AbstractSoundManager.java @@ -97,6 +97,11 @@ public abstract class AbstractSoundManager implements SoundManager { return CONFIG_SECTION_NAME; } + @Override + public int count() { + return AbstractSoundManager.this.songs.size(); + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (AbstractSoundManager.this.songs.containsKey(id)) { @@ -124,6 +129,11 @@ public abstract class AbstractSoundManager implements SoundManager { return CONFIG_SECTION_NAME; } + @Override + public int count() { + return AbstractSoundManager.this.byId.size(); + } + @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { if (AbstractSoundManager.this.byId.containsKey(id)) { diff --git a/gradle.properties b/gradle.properties index 5e2c657b8..255046cbe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings project_version=0.0.65.16 config_version=60 -lang_version=41 +lang_version=43 project_group=net.momirealms latest_supported_version=1.21.10 From 64a510d89d440de2df926b260bda5541118986ee Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 19:18:25 +0800 Subject: [PATCH 087/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8E=89=E8=90=BD?= =?UTF-8?q?=E7=BB=8F=E9=AA=8C=E6=96=B9=E5=9D=97=E5=88=AB=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java | 2 ++ .../bukkit/block/behavior/DropExperienceBlockBehavior.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) 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 090512f4c..aae33ecc1 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 @@ -48,6 +48,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key SNOWY_BLOCK = Key.from("craftengine:snowy_block"); public static final Key HANGABLE_BLOCK = Key.from("craftengine:hangable_block"); public static final Key DROP_EXPERIENCE_BLOCK = Key.from("craftengine:drop_experience_block"); + public static final Key DROP_EXP_BLOCK = Key.from("craftengine:drop_exp_block"); public static void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); @@ -94,5 +95,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(SNOWY_BLOCK, SnowyBlockBehavior.FACTORY); register(HANGABLE_BLOCK, HangableBlockBehavior.FACTORY); register(DROP_EXPERIENCE_BLOCK, DropExperienceBlockBehavior.FACTORY); + register(DROP_EXP_BLOCK, DropExperienceBlockBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java index ef629c558..78de11707 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java @@ -90,7 +90,7 @@ public class DropExperienceBlockBehavior extends BukkitBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { - NumberProvider amount = NumberProviders.fromObject(arguments.getOrDefault("amount", 0)); + NumberProvider amount = NumberProviders.fromObject(ResourceConfigUtils.get(arguments, "amount", "count")); Condition conditions = null; List> conditionList = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(arguments, "conditions", "condition"), EventConditions::fromMap); if (conditionList.size() == 1) { From be84b0292a835b96ab6c604c94ae36347053bdd7 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 20:48:13 +0800 Subject: [PATCH 088/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=87=8D=E8=BD=BD?= =?UTF-8?q?=E5=90=8E=E5=AE=B9=E5=99=A8=E9=97=A8=E5=85=B3=E4=B8=8D=E4=B8=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/entity/SimpleStorageBlockEntity.java | 2 +- .../bukkit/plugin/user/BukkitServerPlayer.java | 17 ++++++++++++++++- .../craftengine/core/entity/player/Player.java | 4 ++++ 3 files changed, 21 insertions(+), 2 deletions(-) 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 index 5649bccc4..706ebf20d 100644 --- 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 @@ -172,7 +172,7 @@ public class SimpleStorageBlockEntity extends BlockEntity { public void updateOpenBlockState(boolean open) { ImmutableBlockState state = super.world.getBlockStateAtIfLoaded(this.pos); - if (state == null || state.behavior() != this.behavior) return; + if (state == null) return; SimpleStorageBlockBehavior behavior = state.behavior().getAs(SimpleStorageBlockBehavior.class).orElse(null); if (behavior == null) return; Property property = behavior.openProperty(); 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 98fd209ee..adcf7bcc5 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 @@ -76,6 +76,7 @@ public class BukkitServerPlayer extends Player { public static final Key SELECTED_LOCALE_KEY = Key.of("craftengine:locale"); public static final Key ENTITY_CULLING_VIEW_DISTANCE_SCALE = Key.of("craftengine:entity_culling_view_distance_scale"); public static final Key ENABLE_ENTITY_CULLING = Key.of("craftengine:enable_entity_culling"); + public static final Key ENABLE_FURNITURE_DEBUG = Key.of("craftengine:enable_furniture_debug"); private final BukkitCraftEngine plugin; // connection state @@ -153,6 +154,8 @@ public class BukkitServerPlayer extends Player { private boolean enableEntityCulling; // 玩家眼睛所在位置 private Location eyeLocation; + // 是否启用家具调试 + private boolean enableFurnitureDebug; public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) { this.channel = channel; @@ -180,6 +183,7 @@ public class BukkitServerPlayer extends Player { String locale = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(SELECTED_LOCALE_KEY), PersistentDataType.STRING); Double scale = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENTITY_CULLING_VIEW_DISTANCE_SCALE), PersistentDataType.DOUBLE); this.enableEntityCulling = Optional.ofNullable(player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENABLE_ENTITY_CULLING), PersistentDataType.BOOLEAN)).orElse(true); + this.enableFurnitureDebug = Optional.ofNullable(player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENABLE_FURNITURE_DEBUG), PersistentDataType.BOOLEAN)).orElse(false); this.culling.setDistanceScale(Optional.ofNullable(scale).orElse(1.0)); this.selectedLocale = TranslationManager.parseLocale(locale); this.trackedChunks = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(512, 0.5f); @@ -1374,7 +1378,18 @@ public class BukkitServerPlayer extends Player { @Override public boolean enableEntityCulling() { - return enableEntityCulling; + return this.enableEntityCulling; + } + + @Override + public void setEnableFurnitureDebug(boolean enable) { + this.enableFurnitureDebug = enable; + platformPlayer().getPersistentDataContainer().set(KeyUtils.toNamespacedKey(ENABLE_FURNITURE_DEBUG), PersistentDataType.BOOLEAN, enable); + } + + @Override + public boolean enableFurnitureDebug() { + return enableFurnitureDebug; } @Override 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 2f89bf90b..19e240b33 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 @@ -198,6 +198,10 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract boolean enableEntityCulling(); + public abstract boolean enableFurnitureDebug(); + + public abstract void setEnableFurnitureDebug(boolean enableFurnitureDebug); + public abstract void giveExperiencePoints(int xpPoints); public abstract void giveExperienceLevels(int levels); From af592e4287fd1c995c39393215c8eb08fca9be11 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 22:16:12 +0800 Subject: [PATCH 089/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=89=94=E9=99=A4?= =?UTF-8?q?=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../item/behavior/FurnitureItemBehavior.java | 4 + .../SetEntityViewDistanceScaleCommand.java | 4 +- .../feature/ToggleEntityCullingCommand.java | 2 +- .../plugin/network/BukkitNetworkManager.java | 1 + .../plugin/user/BukkitServerPlayer.java | 8 ++ .../furniture/AbstractFurnitureManager.java | 31 ++------ .../core/entity/furniture/Furniture.java | 62 +++++++++++++++- .../core/entity/player/Player.java | 2 + .../plugin/entityculling/EntityCulling.java | 56 ++++++++------ .../core/world/collision/AABB.java | 74 +++++++++++++++++++ 10 files changed, 191 insertions(+), 53 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index b74926aa9..83f516cff 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -31,6 +31,7 @@ import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import net.momirealms.sparrow.nbt.CompoundTag; import org.bukkit.Location; +import org.bukkit.Particle; import org.bukkit.World; import java.nio.file.Path; @@ -119,6 +120,9 @@ public class FurnitureItemBehavior extends ItemBehavior { // 检查方块、实体阻挡 if (!aabbs.isEmpty()) { if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { + if (player != null && player.enableFurnitureDebug() && VersionHelper.isPaper()) { +// bukkitPlayer.getWorld().spawnParticle(Particle.FLAME, , List.of(bukkitPlayer), ); + } return InteractionResult.FAIL; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java index df4afd16e..d8a9d0d9a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java @@ -30,11 +30,11 @@ public class SetEntityViewDistanceScaleCommand extends BukkitCommandFeature { if (!Config.enableEntityCulling()) { - context.sender().sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); + plugin().senderFactory().wrap(context.sender()).sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); return; } if (Config.entityCullingViewDistance() <= 0) { - context.sender().sendMessage(Component.text("View distance is not enabled on this server").color(NamedTextColor.RED)); + plugin().senderFactory().wrap(context.sender()).sendMessage(Component.text("View distance is not enabled on this server").color(NamedTextColor.RED)); return; } Player player = context.get("player"); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java index 29971f70a..1d8786ec9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java @@ -32,7 +32,7 @@ public class ToggleEntityCullingCommand extends BukkitCommandFeature { if (!Config.enableEntityCulling()) { - context.sender().sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); + plugin().senderFactory().wrap(context.sender()).sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); return; } Player player = context.get("player"); 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 7728e1641..19c66e96d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.plugin.network; +import com.destroystokyo.paper.ParticleBuilder; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.gson.JsonElement; 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 adcf7bcc5..1ddab9cd6 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 @@ -1480,6 +1480,14 @@ public class BukkitServerPlayer extends Player { return LocationUtils.toWorldPosition(this.getEyeLocation()); } + @Override + public void playParticle(Key particleId, double x, double y, double z) { + Particle particle = Registry.PARTICLE_TYPE.get(KeyUtils.toNamespacedKey(particleId)); + if (particle != null) { + platformPlayer().getWorld().spawnParticle(particle, List.of(platformPlayer()), null, x, y, z, 1, 0, 0,0, 0, null, false); + } + } + public Location getEyeLocation() { org.bukkit.entity.Player player = platformPlayer(); Location eyeLocation = player.getEyeLocation(); 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 98a5fb890..2e3a308a6 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 @@ -146,28 +146,9 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { hitboxes = List.of(defaultHitBox()); } - List aabbs = new ArrayList<>(); - for (FurnitureHitBoxConfig hitBox : hitboxes) { - hitBox.collectBoundingBox(aabbs::add); - } - double minX = 0; - double minY = 0; - double minZ = 0; - double maxX = 0; - double maxY = 0; - double maxZ = 0; - for (AABB aabb : aabbs) { - minX = Math.min(minX, aabb.minX); - minY = Math.min(minY, aabb.minY); - minZ = Math.min(minZ, aabb.minZ); - maxX = Math.max(maxX, aabb.maxX); - maxY = Math.max(maxY, aabb.maxY); - maxZ = Math.max(maxZ, aabb.maxZ); - } - AABB maxAABB = new AABB(minX, minY, minZ, maxX, maxY, maxZ); variants.put(variantName, new FurnitureVariant( variantName, - parseCullingData(section.get("entity-culling"), maxAABB), + parseCullingData(section.get("entity-culling")), elements.toArray(new FurnitureElementConfig[0]), hitboxes.toArray(new FurnitureHitBoxConfig[0]), externalModel, @@ -185,16 +166,18 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { AbstractFurnitureManager.this.byId.put(id, furniture); } - private CullingData parseCullingData(Object arguments, AABB maxHitbox) { + + + private CullingData parseCullingData(Object arguments) { if (arguments instanceof Boolean b && !b) return null; if (!(arguments instanceof Map)) - return new CullingData(maxHitbox, Config.entityCullingViewDistance(), 0.5, true); + return new CullingData(null, Config.entityCullingViewDistance(), 0.25, true); Map argumentsMap = ResourceConfigUtils.getAsMap(arguments, "entity-culling"); return new CullingData( - ResourceConfigUtils.getAsAABB(argumentsMap.getOrDefault("aabb", maxHitbox), "aabb"), + ResourceConfigUtils.getOrDefault(argumentsMap.get("aabb"), it -> ResourceConfigUtils.getAsAABB(it, "aabb"), null), ResourceConfigUtils.getAsInt(argumentsMap.getOrDefault("view-distance", Config.entityCullingViewDistance()), "view-distance"), - ResourceConfigUtils.getAsDouble(argumentsMap.getOrDefault("aabb-expansion", 0.5), "aabb-expansion"), + ResourceConfigUtils.getAsDouble(argumentsMap.getOrDefault("aabb-expansion", 0.25), "aabb-expansion"), ResourceConfigUtils.getAsBoolean(argumentsMap.getOrDefault("ray-tracing", true), "ray-tracing") ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index 5099472cf..9210c649e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -23,10 +23,13 @@ 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.collision.AABB; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -109,9 +112,62 @@ public abstract class Furniture implements Cullable { if (parent == null) return null; AABB aabb = parent.aabb; WorldPosition position = position(); - Vec3d pos1 = getRelativePosition(position, new Vector3f((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ)); - Vec3d pos2 = getRelativePosition(position, new Vector3f((float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ)); - return new CullingData(new AABB(pos1.x, pos1.y, pos1.z, pos2.x, pos2.y, pos2.z), parent.maxDistance, parent.aabbExpansion, parent.rayTracing); + if (aabb == null) { + List aabbs = new ArrayList<>(); + for (FurnitureHitBoxConfig hitBoxConfig : this.currentVariant.hitBoxConfigs()) { + hitBoxConfig.prepareForPlacement(position, aabbs::add); + } + return new CullingData(getMaxAABB(aabbs), parent.maxDistance, parent.aabbExpansion, parent.rayTracing); + } else { + Vector3f[] vertices = new Vector3f[] { + // 底面两个对角点 + new Vector3f((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ), + new Vector3f((float) aabb.maxX, (float) aabb.minY, (float) aabb.maxZ), + // 顶面两个对角点 + new Vector3f((float) aabb.minX, (float) aabb.maxY, (float) aabb.minZ), + new Vector3f((float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ) + }; + double minX = Double.MAX_VALUE, minY = aabb.minY; // Y方向不变 + double maxX = -Double.MAX_VALUE, maxY = aabb.maxY; // Y方向不变 + double minZ = Double.MAX_VALUE, maxZ = -Double.MAX_VALUE; + for (Vector3f vertex : vertices) { + Vec3d rotatedPos = getRelativePosition(position, vertex); + minX = Math.min(minX, rotatedPos.x); + minZ = Math.min(minZ, rotatedPos.z); + maxX = Math.max(maxX, rotatedPos.x); + maxZ = Math.max(maxZ, rotatedPos.z); + } + return new CullingData(new AABB(minX, minY, minZ, maxX, maxY, maxZ), + parent.maxDistance, parent.aabbExpansion, parent.rayTracing); + } + } + + private static @NotNull AABB getMaxAABB(List aabbs) { + double minX = 0; + double minY = 0; + double minZ = 0; + double maxX = 0; + double maxY = 0; + double maxZ = 0; + for (int i = 0; i < aabbs.size(); i++) { + AABB aabb = aabbs.get(i); + if (i == 0) { + minX = aabb.minX; + minY = aabb.minY; + minZ = aabb.minZ; + maxX = aabb.maxX; + maxY = aabb.maxY; + maxZ = aabb.maxZ; + } else { + minX = Math.min(minX, aabb.minX); + minY = Math.min(minY, aabb.minY); + minZ = Math.min(minZ, aabb.minZ); + maxX = Math.max(maxX, aabb.maxX); + maxY = Math.max(maxY, aabb.maxY); + maxZ = Math.max(maxZ, aabb.maxZ); + } + } + return new AABB(minX, minY, minZ, maxX, maxY, maxZ); } @Nullable 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 19e240b33..6fd240dbb 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 @@ -230,6 +230,8 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public void remove() { } + public abstract void playParticle(Key particleId, double x, double y, double z); + public abstract void removeTrackedFurniture(int entityId); public abstract void clearTrackedFurniture(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java index 5ced80c59..df5d26b97 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.plugin.entityculling; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.MutableVec3d; @@ -59,13 +60,12 @@ public final class EntityCulling { AABB aabb = cullable.aabb; double aabbExpansion = cullable.aabbExpansion; - // 根据AABB获取能包裹此AABB的最小长方体 - int minX = MiscUtils.floor(aabb.minX - aabbExpansion); - int minY = MiscUtils.floor(aabb.minY - aabbExpansion); - int minZ = MiscUtils.floor(aabb.minZ - aabbExpansion); - int maxX = MiscUtils.ceil(aabb.maxX + aabbExpansion); - int maxY = MiscUtils.ceil(aabb.maxY + aabbExpansion); - int maxZ = MiscUtils.ceil(aabb.maxZ + aabbExpansion); + double minX = aabb.minX - aabbExpansion; + double minY = aabb.minY - aabbExpansion; + double minZ = aabb.minZ - aabbExpansion; + double maxX = aabb.maxX + aabbExpansion; + double maxY = aabb.maxY + aabbExpansion; + double maxZ = aabb.maxZ + aabbExpansion; double cameraX = cameraPos.x; double cameraY = cameraPos.y; @@ -95,6 +95,10 @@ public final class EntityCulling { if (distanceSq > maxDistanceSq) { return false; } + // 太近了,不剔除 + else if (distanceSq < 1) { + return true; + } } if (!rayTracing || !cullable.rayTracing) { @@ -120,25 +124,31 @@ public final class EntityCulling { } int size = 0; - if (this.dotSelectors[0]) targetPoints[size++].set(minX + 0.05, minY + 0.05, minZ + 0.05); - if (this.dotSelectors[1]) targetPoints[size++].set(maxX - 0.05, minY + 0.05, minZ + 0.05); - if (this.dotSelectors[2]) targetPoints[size++].set(minX + 0.05, minY + 0.05, maxZ - 0.05); - if (this.dotSelectors[3]) targetPoints[size++].set(maxX - 0.05, minY + 0.05, maxZ - 0.05); - if (this.dotSelectors[4]) targetPoints[size++].set(minX + 0.05, maxY - 0.05, minZ + 0.05); - if (this.dotSelectors[5]) targetPoints[size++].set(maxX - 0.05, maxY - 0.05, minZ + 0.05); - if (this.dotSelectors[6]) targetPoints[size++].set(minX + 0.05, maxY - 0.05, maxZ - 0.05); - if (this.dotSelectors[7]) targetPoints[size++].set(maxX - 0.05, maxY - 0.05, maxZ - 0.05); + if (this.dotSelectors[0]) targetPoints[size++].set(minX, minY, minZ); + if (this.dotSelectors[1]) targetPoints[size++].set(maxX, minY, minZ); + if (this.dotSelectors[2]) targetPoints[size++].set(minX, minY, maxZ); + if (this.dotSelectors[3]) targetPoints[size++].set(maxX, minY, maxZ); + if (this.dotSelectors[4]) targetPoints[size++].set(minX, maxY, minZ); + if (this.dotSelectors[5]) targetPoints[size++].set(maxX, maxY, minZ); + if (this.dotSelectors[6]) targetPoints[size++].set(minX, maxY, maxZ); + if (this.dotSelectors[7]) targetPoints[size++].set(maxX, maxY, maxZ); // 面中心点 double averageX = (minX + maxX) / 2.0; double averageY = (minY + maxY) / 2.0; double averageZ = (minZ + maxZ) / 2.0; - if (this.dotSelectors[8]) targetPoints[size++].set(averageX, averageY, minZ + 0.05); - if (this.dotSelectors[9]) targetPoints[size++].set(averageX, averageY, maxZ - 0.05); - if (this.dotSelectors[10]) targetPoints[size++].set(minX + 0.05, averageY, averageZ); - if (this.dotSelectors[11]) targetPoints[size++].set(maxX - 0.05, averageY, averageZ); - if (this.dotSelectors[12]) targetPoints[size++].set(averageX, minY + 0.05, averageZ); - if (this.dotSelectors[13]) targetPoints[size].set(averageX, maxY - 0.05, averageZ); + if (this.dotSelectors[8]) targetPoints[size++].set(averageX, averageY, minZ); + if (this.dotSelectors[9]) targetPoints[size++].set(averageX, averageY, maxZ); + if (this.dotSelectors[10]) targetPoints[size++].set(minX, averageY, averageZ); + if (this.dotSelectors[11]) targetPoints[size++].set(maxX, averageY, averageZ); + if (this.dotSelectors[12]) targetPoints[size++].set(averageX, minY, averageZ); + if (this.dotSelectors[13]) targetPoints[size++].set(averageX, maxY, averageZ); +// if (Config.debugEntityCulling()) { +// for (int i = 0; i < size; i++) { +// MutableVec3d targetPoint = this.targetPoints[i]; +// this.player.playParticle(Key.of("flame"), targetPoint.x, targetPoint.y, targetPoint.z); +// } +// } return isVisible(cameraPos, this.targetPoints, size); } @@ -356,7 +366,7 @@ public final class EntityCulling { return deltaX + 32 * deltaY + 32 * 32 * deltaZ; } - private double distanceSq(int min, int max, double camera, Relative rel) { + private double distanceSq(double min, double max, double camera, Relative rel) { if (rel == Relative.NEGATIVE) { double dx = camera - max; return dx * dx; @@ -394,7 +404,7 @@ public final class EntityCulling { private enum Relative { INSIDE, POSITIVE, NEGATIVE; - public static Relative from(int min, int max, double pos) { + public static Relative from(double min, double max, double pos) { if (min > pos) return POSITIVE; else if (max < pos) return NEGATIVE; return INSIDE; 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 94c6f52e2..1f759dada 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 @@ -7,6 +7,8 @@ import net.momirealms.craftengine.core.world.Vec3d; import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; public class AABB { @@ -159,6 +161,78 @@ public class AABB { return (value >= min - EPSILON) && (value <= max + EPSILON); } + public List getEdgePoints(double interval) { + List points = new ArrayList<>(); + + // AABB的8个顶点 + Vec3d[] vertices = { + new Vec3d(minX, minY, minZ), // 0 + new Vec3d(maxX, minY, minZ), // 1 + new Vec3d(minX, maxY, minZ), // 2 + new Vec3d(maxX, maxY, minZ), // 3 + new Vec3d(minX, minY, maxZ), // 4 + new Vec3d(maxX, minY, maxZ), // 5 + new Vec3d(minX, maxY, maxZ), // 6 + new Vec3d(maxX, maxY, maxZ) // 7 + }; + + // 12条边的定义(连接哪两个顶点) + int[][] edges = { + {0, 1}, // 底部X边(前) + {1, 3}, // 底部Y边(右) + {3, 2}, // 底部X边(后) + {2, 0}, // 底部Y边(左) + + {4, 5}, // 顶部X边(前) + {5, 7}, // 顶部Y边(右) + {7, 6}, // 顶部X边(后) + {6, 4}, // 顶部Y边(左) + + {0, 4}, // Z边(左下前) + {1, 5}, // Z边(右下前) + {2, 6}, // Z边(左后上) + {3, 7} // Z边(右后上) + }; + + for (int[] edge : edges) { + Vec3d start = vertices[edge[0]]; + Vec3d end = vertices[edge[1]]; + points.addAll(sampleLine(start, end, interval)); + } + + return points; + } + + private List sampleLine(Vec3d start, Vec3d end, double interval) { + List points = new ArrayList<>(); + + // 计算线段长度 + double dx = end.x - start.x; + double dy = end.y - start.y; + double dz = end.z - start.z; + double length = Math.sqrt(dx * dx + dy * dy + dz * dz); + + // 计算采样点数(去掉终点避免重复) + int numPoints = (int) Math.floor(length / interval); + + // 如果线段太短,至少返回起点 + if (numPoints <= 0) { + points.add(start); + return points; + } + + // 按间隔采样 + for (int i = 0; i <= numPoints; i++) { + double t = (double) i / numPoints; + double x = start.x + dx * t; + double y = start.y + dy * t; + double z = start.z + dz * t; + points.add(new Vec3d(x, y, z)); + } + + return points; + } + @Override public String toString() { return "AABB{" + From 568d1c2b7b96f3f8ab1546e5f58fa71afd486822 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 22:56:00 +0800 Subject: [PATCH 090/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AE=B6=E5=85=B7deb?= =?UTF-8?q?ug=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hitbox/CustomFurnitureHitboxConfig.java | 5 --- .../HappyGhastFurnitureHitboxConfig.java | 5 --- .../InteractionFurnitureHitboxConfig.java | 5 --- .../hitbox/ShulkerFurnitureHitboxConfig.java | 5 --- .../item/behavior/FurnitureItemBehavior.java | 10 ++++-- .../bukkit/pack/BukkitPackManager.java | 1 - .../plugin/command/BukkitCommandManager.java | 1 + .../feature/DebugFurnitureCommand.java | 36 +++++++++++++++++++ .../plugin/network/BukkitNetworkManager.java | 1 - .../plugin/user/BukkitServerPlayer.java | 35 ++++++++++++++++++ common-files/src/main/resources/commands.yml | 7 ++++ .../furniture/AbstractFurnitureManager.java | 3 -- .../hitbox/FurnitureHitBoxConfig.java | 1 - .../core/pack/AbstractPackManager.java | 5 ++- .../plugin/entityculling/EntityCulling.java | 5 --- 15 files changed, 91 insertions(+), 34 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugFurnitureCommand.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java index 58e2ed92f..d7147708c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java @@ -78,11 +78,6 @@ public class CustomFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig aabbConsumer) { - aabbConsumer.accept(AABB.makeBoundingBox(this.position, this.width, this.height)); - } - @Override public CustomFurnitureHitbox create(Furniture furniture) { return new CustomFurnitureHitbox(furniture, this); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java index df7458e19..6264b3a5f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java @@ -63,11 +63,6 @@ public class HappyGhastFurnitureHitboxConfig extends AbstractFurnitureHitBoxConf } } - @Override - public void collectBoundingBox(Consumer aabbConsumer) { - aabbConsumer.accept(AABB.makeBoundingBox(this.position, 4 * this.scale, 4 * this.scale)); - } - public static class Factory implements FurnitureHitBoxConfigFactory { @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java index 3f823705a..909335b0a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -77,11 +77,6 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon } } - @Override - public void collectBoundingBox(Consumer aabbConsumer) { - aabbConsumer.accept(AABB.makeBoundingBox(this.position, size.x, size.y)); - } - @Override public InteractionFurnitureHitbox create(Furniture furniture) { return new InteractionFurnitureHitbox(furniture, this); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java index c418fd36b..2b309b806 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -153,11 +153,6 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< } } - @Override - public void collectBoundingBox(Consumer aabbConsumer) { - aabbConsumer.accept(this.aabbCreator.create(position.x, position.y, position.z, 180, new Vector3f(0))); - } - public float scale() { return this.scale; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index 83f516cff..3c3d34027 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -31,7 +31,6 @@ import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import net.momirealms.sparrow.nbt.CompoundTag; import org.bukkit.Location; -import org.bukkit.Particle; import org.bukkit.World; import java.nio.file.Path; @@ -121,7 +120,14 @@ public class FurnitureItemBehavior extends ItemBehavior { if (!aabbs.isEmpty()) { if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { if (player != null && player.enableFurnitureDebug() && VersionHelper.isPaper()) { -// bukkitPlayer.getWorld().spawnParticle(Particle.FLAME, , List.of(bukkitPlayer), ); + player.playSound(Key.of("minecraft:entity.villager.no")); + Key flame = Key.of("flame"); + for (AABB aabb : aabbs) { + List edgePoints = aabb.getEdgePoints(0.125); + for (Vec3d edgePoint : edgePoints) { + player.playParticle(flame, edgePoint.x(), edgePoint.y(), edgePoint.z()); + } + } } return InteractionResult.FAIL; } 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 fc961ac8d..e074577b1 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 @@ -15,7 +15,6 @@ import net.momirealms.craftengine.core.pack.obfuscation.ObfA; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; -import net.momirealms.craftengine.core.plugin.locale.TranslationManagerImpl; import net.momirealms.craftengine.core.util.Base64Utils; import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Bukkit; 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 fb43eac52..533032261 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 @@ -50,6 +50,7 @@ public class BukkitCommandManager extends AbstractCommandManager new DebugAppearanceStateUsageCommand(this, plugin), new DebugClearCooldownCommand(this, plugin), new DebugEntityIdCommand(this, plugin), + new DebugFurnitureCommand(this, plugin), new DebugRealStateUsageCommand(this, plugin), new DebugItemDataCommand(this, plugin), new DebugSetBlockCommand(this, plugin), diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugFurnitureCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugFurnitureCommand.java new file mode 100644 index 000000000..ee2ac39d5 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugFurnitureCommand.java @@ -0,0 +1,36 @@ +package net.momirealms.craftengine.bukkit.plugin.command.feature; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; + +public class DebugFurnitureCommand extends BukkitCommandFeature { + + public DebugFurnitureCommand(CraftEngineCommandManager commandManager, CraftEngine plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .handler(context -> { + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(context.sender()); + boolean b = !serverPlayer.enableFurnitureDebug(); + serverPlayer.setEnableFurnitureDebug(b); + serverPlayer.sendMessage(Component.text("Furniture Debug Mode: ").append(Component.text(b ? "ON" : "OFF").color(b ? NamedTextColor.GREEN : NamedTextColor.RED)), false); + }); + } + + @Override + public String getFeatureID() { + return "debug_furniture"; + } +} 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 19c66e96d..7728e1641 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.bukkit.plugin.network; -import com.destroystokyo.paper.ParticleBuilder; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.gson.JsonElement; 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 1ddab9cd6..f9e38db29 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 @@ -7,7 +7,9 @@ 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.api.CraftEngineFurniture; import net.momirealms.craftengine.bukkit.block.entity.BlockEntityHolder; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; @@ -26,6 +28,8 @@ import net.momirealms.craftengine.core.block.entity.BlockEntity; import net.momirealms.craftengine.core.block.entity.render.ConstantBlockEntityRenderer; import net.momirealms.craftengine.core.entity.data.EntityData; import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.player.GameMode; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.Player; @@ -156,6 +160,10 @@ public class BukkitServerPlayer extends Player { private Location eyeLocation; // 是否启用家具调试 private boolean enableFurnitureDebug; + // 上一次对准的家具 + private BukkitFurniture lastHitFurniture; + // 缓存的tick + private int lastHitFurnitureTick; public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) { this.channel = channel; @@ -542,6 +550,33 @@ public class BukkitServerPlayer extends Player { this.updateGUI(); } + if (this.enableFurnitureDebug) { + BukkitFurniture furniture = CraftEngineFurniture.rayTrace(platformPlayer()); + boolean forceShow = furniture != this.lastHitFurniture; + if (forceShow) { + this.lastHitFurnitureTick = 0; + } else { + this.lastHitFurnitureTick++; + if (this.lastHitFurnitureTick % 30 == 0) { + forceShow = true; + } + } + this.lastHitFurniture = furniture; + if (furniture != null && forceShow) { + FurnitureVariant currentVariant = furniture.getCurrentVariant(); + List aabbs = new ArrayList<>(); + for (FurnitureHitBoxConfig config : currentVariant.hitBoxConfigs()) { + config.prepareForPlacement(furniture.position(), aabbs::add); + } + Key endRod = Key.of("end_rod"); + for (AABB aabb : aabbs) { + for (Vec3d point : aabb.getEdgePoints(0.125)) { + this.playParticle(endRod, point.x, point.y, point.z); + } + } + } + } + // 更新眼睛位置 { Location unsureEyeLocation = bukkitPlayer.getEyeLocation(); diff --git a/common-files/src/main/resources/commands.yml b/common-files/src/main/resources/commands.yml index 41fbb153b..24e0bcccf 100644 --- a/common-files/src/main/resources/commands.yml +++ b/common-files/src/main/resources/commands.yml @@ -270,6 +270,13 @@ debug_generate_internal_assets: - /craftengine debug generate-internal-assets - /ce debug generate-internal-assets +debug_furniture: + enable: true + permission: ce.command.debug.furniture + usage: + - /craftengine debug furniture + - /ce debug furniture + debug_test: enable: true permission: ce.command.debug.test 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 2e3a308a6..23b3227ba 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 @@ -18,7 +18,6 @@ import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.world.collision.AABB; import org.incendo.cloud.suggestion.Suggestion; import org.joml.Vector3f; @@ -166,8 +165,6 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { AbstractFurnitureManager.this.byId.put(id, furniture); } - - private CullingData parseCullingData(Object arguments) { if (arguments instanceof Boolean b && !b) return null; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java index f2361c098..7c8325e00 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java @@ -24,5 +24,4 @@ public interface FurnitureHitBoxConfig { void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer); - void collectBoundingBox(Consumer aabbConsumer); } 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 ab9ffd4e5..8c66bbff7 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 @@ -31,7 +31,10 @@ import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.config.SectionConfigParser; import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor; -import net.momirealms.craftengine.core.plugin.locale.*; +import net.momirealms.craftengine.core.plugin.locale.LangData; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.sound.AbstractSoundManager; import net.momirealms.craftengine.core.sound.SoundEvent; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java index df5d26b97..79dac8655 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java @@ -2,7 +2,6 @@ package net.momirealms.craftengine.core.plugin.entityculling; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.MutableVec3d; @@ -95,10 +94,6 @@ public final class EntityCulling { if (distanceSq > maxDistanceSq) { return false; } - // 太近了,不剔除 - else if (distanceSq < 1) { - return true; - } } if (!rayTracing || !cullable.rayTracing) { From e3e3489198b19c80394e8ec718e5b5dedab2a9d2 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 4 Dec 2025 23:03:41 +0800 Subject: [PATCH 091/135] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=B2=92=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/bukkit/plugin/user/BukkitServerPlayer.java | 2 +- .../default/configuration/furniture/flower_basket.yml | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) 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 f9e38db29..2bc3a8635 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 @@ -568,7 +568,7 @@ public class BukkitServerPlayer extends Player { for (FurnitureHitBoxConfig config : currentVariant.hitBoxConfigs()) { config.prepareForPlacement(furniture.position(), aabbs::add); } - Key endRod = Key.of("end_rod"); + Key endRod = Key.of("soul_fire_flame"); for (AABB aabb : aabbs) { for (Vec3d point : aabb.getEdgePoints(0.125)) { this.playParticle(endRod, point.x, point.y, point.z); 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 index 416b43aad..4fdd08219 100644 --- 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 @@ -59,6 +59,7 @@ furniture: width: 0.7 height: 0.5 interactive: true + invisible: true wall: elements: - item: default:flower_basket_wall @@ -75,6 +76,7 @@ furniture: width: 0.46 height: 0.75 interactive: true + invisible: true - type: interaction can-use-item-on: true can-be-hit-by-projectile: true @@ -83,6 +85,7 @@ furniture: width: 0.46 height: 0.75 interactive: true + invisible: true ceiling: elements: - item: default:flower_basket_ceiling @@ -98,4 +101,5 @@ furniture: position: 0,-0.7,0 width: 0.7 height: 0.7 - interactive: true \ No newline at end of file + interactive: true + invisible: true \ No newline at end of file From df24a4047dd48a62048feb4d99237720e63598a1 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 01:19:59 +0800 Subject: [PATCH 092/135] =?UTF-8?q?=E4=B8=BA=E6=B2=99=E5=8F=91=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=97=8B=E8=BD=AC=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/behavior/SeatBlockBehavior.java | 2 +- .../bukkit/block/entity/SeatBlockEntity.java | 12 ++- .../entity/furniture/BukkitFurniture.java | 91 ++++++++++++++++++- .../furniture/BukkitFurnitureManager.java | 28 ++++-- .../furniture/FurnitureEventListener.java | 59 ++++++++++++ .../item/listener/DebugStickListener.java | 13 ++- .../plugin/network/BukkitNetworkManager.java | 6 ++ .../default/configuration/blocks/sofa.yml | 2 + .../configuration/templates/events.yml | 31 +++++++ .../furniture/AbstractFurnitureManager.java | 2 +- .../core/entity/furniture/Furniture.java | 35 +++++-- .../entity/furniture/FurnitureConfigImpl.java | 3 +- .../craftengine/core/item/ItemKeys.java | 1 + .../core/pack/AbstractPackManager.java | 1 + .../function/CycleBlockPropertyFunction.java | 3 +- 15 files changed, 257 insertions(+), 32 deletions(-) create mode 100644 common-files/src/main/resources/resources/default/configuration/templates/events.yml diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SeatBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SeatBlockBehavior.java index e1c68747b..c54f6eb66 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SeatBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SeatBlockBehavior.java @@ -33,7 +33,7 @@ public class SeatBlockBehavior extends BukkitBlockBehavior implements EntityBloc @Override public BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state) { - return new SeatBlockEntity(pos, state, this.seats, this.directionProperty); + return new SeatBlockEntity(pos, state, this.seats); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SeatBlockEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SeatBlockEntity.java index 5ff7a8812..00352c68c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SeatBlockEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/SeatBlockEntity.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.block.entity; +import net.momirealms.craftengine.bukkit.block.behavior.SeatBlockBehavior; import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.entity.BlockEntity; @@ -13,14 +14,14 @@ import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.sparrow.nbt.CompoundTag; +import java.util.Optional; + public class SeatBlockEntity extends BlockEntity implements SeatOwner { private final Seat[] seats; - private final Property facing; @SuppressWarnings("unchecked") - public SeatBlockEntity(BlockPos pos, ImmutableBlockState blockState, SeatConfig[] seats, Property directionProperty) { + public SeatBlockEntity(BlockPos pos, ImmutableBlockState blockState, SeatConfig[] seats) { super(BukkitBlockEntityTypes.SEAT, pos, blockState); - this.facing = directionProperty; this.seats = new Seat[seats.length]; for (int i = 0; i < seats.length; i++) { this.seats[i] = new BukkitSeat<>(this, seats[i]); @@ -41,7 +42,10 @@ public class SeatBlockEntity extends BlockEntity implements SeatOwner { public boolean spawnSeat(Player player) { int yRot = 0; - if (this.facing != null) { + Optional behavior = super.blockState.behavior().getAs(SeatBlockBehavior.class); + if (behavior.isEmpty()) return false; + Property facing = behavior.get().directionProperty(); + if (facing != null) { HorizontalDirection direction = super.blockState.get(facing); yRot = switch (direction) { case NORTH -> 0; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index d7bbe6f9d..e7fe83ce7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -1,21 +1,34 @@ package net.momirealms.craftengine.bukkit.entity.furniture; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.entity.BukkitEntity; import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.core.entity.furniture.Collider; -import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; -import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.entity.furniture.*; +import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.QuaternionUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; +import net.momirealms.craftengine.core.world.collision.AABB; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.Player; import org.bukkit.persistence.PersistentDataType; import org.joml.Quaternionf; import org.joml.Vector3f; import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; +import java.util.concurrent.CompletableFuture; public class BukkitFurniture extends Furniture { private final WeakReference metaEntity; @@ -40,6 +53,76 @@ public class BukkitFurniture extends Furniture { } } + @Override + public boolean setVariant(String variantName) { + FurnitureVariant variant = this.config.getVariant(variantName); + if (variant == null) return false; + if (this.currentVariant == variant) return false; + BukkitFurnitureManager.instance().invalidateFurniture(this); + super.clearColliders(); + super.setVariantInternal(variant); + BukkitFurnitureManager.instance().initFurniture(this); + this.addCollidersToWorld(); + this.refresh(); + return true; + } + + @SuppressWarnings("deprecation") + @Override + public CompletableFuture moveTo(WorldPosition position) { + ItemDisplay itemDisplay = this.metaEntity.get(); + if (itemDisplay == null) return CompletableFuture.completedFuture(false); + // 检查新位置是否可用 + List aabbs = new ArrayList<>(); + for (FurnitureHitBoxConfig hitBoxConfig : getCurrentVariant().hitBoxConfigs()) { + hitBoxConfig.prepareForPlacement(position, aabbs::add); + } + if (!aabbs.isEmpty()) { + if (!FastNMS.INSTANCE.checkEntityCollision(position.world.serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { + return CompletableFuture.completedFuture(false); + } + } + // 准备传送 + CompletableFuture future = new CompletableFuture<>(); + BukkitFurnitureManager.instance().invalidateFurniture(this); + super.clearColliders(); + this.location = LocationUtils.toLocation(position); + Object removePacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(itemDisplay.getEntityId()); }}); + for (Player player : itemDisplay.getTrackedPlayers()) { + BukkitAdaptors.adapt(player).sendPacket(removePacket, false); + } + itemDisplay.teleportAsync(this.location).thenAccept(result -> { + if (result) { + super.setVariantInternal(getCurrentVariant()); + BukkitFurnitureManager.instance().initFurniture(this); + this.addCollidersToWorld(); + Object addPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(itemDisplay.getEntityId(), itemDisplay.getUniqueId(), + itemDisplay.getX(), itemDisplay.getY(), itemDisplay.getZ(), itemDisplay.getPitch(), itemDisplay.getYaw(), MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0); + for (Player player : itemDisplay.getTrackedPlayers()) { + BukkitAdaptors.adapt(player).sendPacket(addPacket, false); + } + future.complete(true); + } else { + future.complete(false); + } + }); + return future; + } + + @SuppressWarnings("deprecation") + @Override + protected void refresh() { + ItemDisplay itemDisplay = this.metaEntity.get(); + if (itemDisplay == null) return; + Object removePacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(itemDisplay.getEntityId()); }}); + Object addPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(itemDisplay.getEntityId(), itemDisplay.getUniqueId(), + itemDisplay.getX(), itemDisplay.getY(), itemDisplay.getZ(), itemDisplay.getPitch(), itemDisplay.getYaw(), MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0); + for (Player player : itemDisplay.getTrackedPlayers()) { + BukkitAdaptors.adapt(player).sendPacket(removePacket, false); + BukkitAdaptors.adapt(player).sendPacket(addPacket, false); + } + } + @Override public void destroy() { Optional.ofNullable(this.metaEntity.get()).ifPresent(Entity::remove); 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 5b0f5cacc..c1cb3f717 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 @@ -323,18 +323,32 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { // 创建家具实例,并初始化碰撞实体 private BukkitFurniture createFurnitureInstance(ItemDisplay display, FurnitureConfig furniture) { BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, getFurnitureDataAccessor(display)); - this.byMetaEntityId.put(display.getEntityId(), bukkitFurniture); - for (int entityId : bukkitFurniture.virtualEntityIds()) { - this.byVirtualEntityId.put(entityId, bukkitFurniture); - } - for (Collider collisionEntity : bukkitFurniture.colliders()) { - this.byColliderEntityId.put(collisionEntity.entityId(), bukkitFurniture); - } + initFurniture(bukkitFurniture); Location location = display.getLocation(); runSafeEntityOperation(location, bukkitFurniture::addCollidersToWorld); return bukkitFurniture; } + protected void initFurniture(BukkitFurniture furniture) { + this.byMetaEntityId.put(furniture.entityId(), furniture); + for (int entityId : furniture.virtualEntityIds()) { + this.byVirtualEntityId.put(entityId, furniture); + } + for (Collider collisionEntity : furniture.colliders()) { + this.byColliderEntityId.put(collisionEntity.entityId(), furniture); + } + } + + protected void invalidateFurniture(BukkitFurniture furniture) { + this.byMetaEntityId.remove(furniture.entityId()); + for (int entityId : furniture.virtualEntityIds()) { + this.byVirtualEntityId.remove(entityId); + } + for (Collider collisionEntity : furniture.colliders()) { + this.byColliderEntityId.remove(collisionEntity.entityId()); + } + } + private void runSafeEntityOperation(Location location, Runnable action) { boolean preventChange = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4); if (preventChange) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java index 534a3b841..c71a480de 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java @@ -2,11 +2,26 @@ package net.momirealms.craftengine.bukkit.entity.furniture; import com.destroystokyo.paper.event.entity.EntityAddToWorldEvent; import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent; +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; +import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.RandomUtils; import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.chunk.CEChunk; import org.bukkit.entity.Entity; import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -14,8 +29,12 @@ import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.EntitiesLoadEvent; import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldUnloadEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; import java.util.List; +import java.util.Map; public class FurnitureEventListener implements Listener { private final BukkitFurnitureManager manager; @@ -104,4 +123,44 @@ public class FurnitureEventListener implements Listener { this.manager.handleCollisionEntityUnload(entity); } } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onInteractFurniture(FurnitureInteractEvent event) { + Player bukkitPlayer = event.getPlayer(); + BukkitServerPlayer player = BukkitAdaptors.adapt(bukkitPlayer); + if (!(player.canInstabuild() && player.hasPermission("minecraft.debugstick")) && !player.hasPermission("minecraft.debugstick.always")) { + return; + } + Item itemInHand = player.getItemInHand(InteractionHand.MAIN_HAND); + if (!itemInHand.vanillaId().equals(ItemKeys.DEBUG_STICK)) return; + BukkitFurniture furniture = event.furniture(); + List variants = new ArrayList<>(furniture.config.variants().keySet()); + if (variants.size() == 1) { + try { + Object systemChatPacket = NetworkReflections.constructor$ClientboundSystemChatPacket.newInstance( + ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.empty").arguments(Component.text(furniture.id().asString()))), true); + player.sendPacket(systemChatPacket, false); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Could not create system chat packet", e); + } + } else { + String variantName = furniture.getCurrentVariant().name(); + int index = variants.indexOf(variantName) + 1; + if (index >= variants.size()) { + index = 0; + } + furniture.setVariant(variants.get(index)); + try { + Object systemChatPacket = NetworkReflections.constructor$ClientboundSystemChatPacket.newInstance( + ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.update") + .arguments( + Component.text("variant"), + Component.text(variants.get(index)) + )), true); + player.sendPacket(systemChatPacket, false); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Could not create system chat packet", e); + } + } + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java index b3f763ce1..88d7d106c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java @@ -16,7 +16,9 @@ import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.UpdateOption; import net.momirealms.craftengine.core.block.properties.Property; +import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.util.MiscUtils; import org.bukkit.Material; import org.bukkit.block.Block; @@ -44,12 +46,10 @@ public class DebugStickListener implements Listener { public void onUseDebugStick(PlayerInteractEvent event) { Block clickedBlock = event.getClickedBlock(); if (clickedBlock == null) return; - ItemStack itemInHand = event.getItem(); - if (ItemStackUtils.isEmpty(itemInHand)) return; - Material material = itemInHand.getType(); - if (material != Material.DEBUG_STICK) return; Player bukkitPlayer = event.getPlayer(); BukkitServerPlayer player = BukkitAdaptors.adapt(bukkitPlayer); + Item itemInHand = player.getItemInHand(event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND); + if (!itemInHand.vanillaId().equals(ItemKeys.DEBUG_STICK)) return; if (!(player.canInstabuild() && player.hasPermission("minecraft.debugstick")) && !player.hasPermission("minecraft.debugstick.always")) { return; } @@ -73,8 +73,7 @@ public class DebugStickListener implements Listener { ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.empty").arguments(Component.text(blockId))), true); player.sendPacket(systemChatPacket, false); } else { - Item wrapped = BukkitItemManager.instance().wrap(itemInHand); - Object storedData = wrapped.getJavaTag("craftengine:debug_stick_state"); + Object storedData = itemInHand.getJavaTag("craftengine:debug_stick_state"); if (storedData == null) storedData = new HashMap<>(); if (storedData instanceof Map map) { Map data = new HashMap<>(MiscUtils.castToMap(map, false)); @@ -96,7 +95,7 @@ public class DebugStickListener implements Listener { } else { currentProperty = getRelative(properties, currentProperty, player.isSecondaryUseActive()); data.put(blockId, currentProperty.name()); - wrapped.setTag(data, "craftengine:debug_stick_state"); + itemInHand.setTag(data, "craftengine:debug_stick_state"); Object systemChatPacket = NetworkReflections.constructor$ClientboundSystemChatPacket.newInstance( ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.select") .arguments( 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 7728e1641..18cc1033c 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 @@ -67,6 +67,7 @@ import net.momirealms.craftengine.core.font.FontManager; import net.momirealms.craftengine.core.font.IllegalCharacterProcessResult; import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.item.recipe.network.legacy.LegacyRecipeHolder; @@ -3804,6 +3805,11 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes return; } + // 不处理调试棒 + if (itemInHand.vanillaId().equals(ItemKeys.DEBUG_STICK)) { + return; + } + // 必须从网络包层面处理,否则无法获取交互的具体实体 if (serverPlayer.isSecondaryUseActive() && !itemInHand.isEmpty() && hitBox.config().canUseItemOn()) { Optional> optionalCustomItem = itemInHand.getCustomItem(); 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 index 120c02811..bdd8f8131 100644 --- a/common-files/src/main/resources/resources/default/configuration/blocks/sofa.yml +++ b/common-files/src/main/resources/resources/default/configuration/blocks/sofa.yml @@ -53,6 +53,8 @@ items: behavior: type: block_item block: + events: + - template: default:rotatable_block loot: template: default:loot_table/self settings: diff --git a/common-files/src/main/resources/resources/default/configuration/templates/events.yml b/common-files/src/main/resources/resources/default/configuration/templates/events.yml new file mode 100644 index 000000000..d0727291f --- /dev/null +++ b/common-files/src/main/resources/resources/default/configuration/templates/events.yml @@ -0,0 +1,31 @@ +templates: + default:rotatable_block: + on: right_click + conditions: + - type: expression + expression: + functions: + - type: update_interaction_tick + - type: play_sound + sound: ${rotate_sound:-'minecraft:block.bamboo.place'} + - type: swing_hand + - type: cycle_block_property + property: facing + rules: + north: east + east: south + south: west + west: north + default:rotatable_furniture: + on: right_click + conditions: + - type: expression + expression: + functions: + - type: rotate_furniture + degree: 90 + on-success: + - type: swing_hand + - type: play_sound + sound: ${rotate_sound:-'minecraft:block.bamboo.place'} + on-failure: [] \ No newline at end of file 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 23b3227ba..c3c0d50a8 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 @@ -123,7 +123,7 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { throw new LocalizedResourceConfigException("warning.config.furniture.missing_variants"); } - Map variants = new HashMap<>(); + Map variants = new LinkedHashMap<>(); for (Map.Entry e0 : variantsMap.entrySet()) { String variantName = e0.getKey(); Map variantArguments = ResourceConfigUtils.getAsMap(e0.getValue(), variantName); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index 9210c649e..d5d51596e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CompletableFuture; public abstract class Furniture implements Cullable { public final FurnitureConfig config; @@ -53,7 +54,7 @@ public abstract class Furniture implements Cullable { this.config = config; this.dataAccessor = data; this.metaDataEntity = metaDataEntity; - this.setVariant(config.getVariant(data)); + this.setVariantInternal(config.getVariant(data)); } public Entity metaDataEntity() { @@ -64,7 +65,21 @@ public abstract class Furniture implements Cullable { return this.currentVariant; } - public void setVariant(FurnitureVariant variant) { + public abstract boolean setVariant(String variantName); + + public abstract CompletableFuture moveTo(WorldPosition position); + + protected abstract void refresh(); + + protected void clearColliders() { + if (this.colliders != null) { + for (Collider collider : this.colliders) { + collider.destroy(); + } + } + } + + protected void setVariantInternal(FurnitureVariant variant) { this.currentVariant = variant; this.hitboxMap = new Int2ObjectOpenHashMap<>(); // 初始化家具元素 @@ -201,20 +216,28 @@ public abstract class Furniture implements Cullable { @Override public void show(Player player) { for (FurnitureElement element : this.elements) { - element.show(player); + if (element != null) { + element.show(player); + } } for (FurnitureHitBox hitbox : this.hitboxes) { - hitbox.show(player); + if (hitbox != null) { + hitbox.show(player); + } } } @Override public void hide(Player player) { for (FurnitureElement element : this.elements) { - element.hide(player); + if (element != null) { + element.hide(player); + } } for (FurnitureHitBox hitbox : this.hitboxes) { - hitbox.hide(player); + if (hitbox != null) { + hitbox.hide(player); + } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java index a68613fef..2833306c8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.entity.furniture; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSortedMap; import net.momirealms.craftengine.core.entity.furniture.behavior.EmptyFurnitureBehavior; import net.momirealms.craftengine.core.entity.furniture.behavior.FurnitureBehavior; import net.momirealms.craftengine.core.loot.LootTable; @@ -33,7 +34,7 @@ class FurnitureConfigImpl implements FurnitureConfig { @Nullable LootTable lootTable) { this.id = id; this.settings = settings; - this.variants = ImmutableMap.copyOf(variants); + this.variants = ImmutableSortedMap.copyOf(variants); this.lootTable = lootTable; this.behavior = behavior; this.events = events; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java index 4242b8d6a..161ea63a7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemKeys.java @@ -65,6 +65,7 @@ public final class ItemKeys { public static final Key PURPLE_DYE = Key.of("minecraft:purple_dye"); public static final Key MAGENTA_DYE = Key.of("minecraft:magenta_dye"); public static final Key PINK_DYE = Key.of("minecraft:pink_dye"); + public static final Key DEBUG_STICK = Key.of("minecraft:debug_stick"); public static final Key CARROT = Key.of("minecraft:carrot"); public static final Key POTATO = Key.of("minecraft:potato"); 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 8c66bbff7..8ec309b93 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 @@ -471,6 +471,7 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/configuration/templates/loot_tables.yml"); plugin.saveResource("resources/default/configuration/templates/recipes.yml"); plugin.saveResource("resources/default/configuration/templates/tool_levels.yml"); + plugin.saveResource("resources/default/configuration/templates/events.yml"); plugin.saveResource("resources/default/configuration/categories.yml"); plugin.saveResource("resources/default/configuration/emoji.yml"); plugin.saveResource("resources/default/configuration/translations.yml"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java index b99ef0e72..393468694 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CycleBlockPropertyFunction.java @@ -16,6 +16,7 @@ import net.momirealms.craftengine.core.world.WorldPosition; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; @@ -71,7 +72,7 @@ public class CycleBlockPropertyFunction extends AbstractCon if (value == null) { return wrapper.cycleProperty(this.property, inverse); } - String mapValue = this.rules.get(value.toString()); + String mapValue = this.rules.get(value.toString().toLowerCase(Locale.ROOT)); if (mapValue == null) { return wrapper.cycleProperty(this.property, inverse); } From 2eca8041ba8121f21a92118e41e830ad02bb86db Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Fri, 5 Dec 2025 01:34:04 +0800 Subject: [PATCH 093/135] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=AF=AD=E8=A8=80?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/translations/zh_cn.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 69455c7eb..db23b186b 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -88,7 +88,8 @@ command.send_resource_pack.success.multiple: "发送资源包给 command.locale.set.failure: "区域设置格式无效: " command.locale.set.success: "已为 更新选定区域设置为 " command.locale.unset.success: "已清除 的选定区域设置" -command.entity_view_distance_scale.set.success: "已为 的实体可见距离百分比更新为 " +command.entity_view_distance_scale.set.success: "已为 的实体可见距离百分比设置为 " +command.entity_culling.toggle.success: "已为 的实体剔除状态设置为 " warning.network.resource_pack.unverified_uuid: "玩家 使用未经服务器验证的 UUID () 尝试请求获取资源包" warning.config.pack.duplicated_files: "发现重复文件 请通过 config.yml 的 'resource-pack.duplicated-files-handler' 部分解决" warning.config.yaml.duplicated_key: "在文件 发现问题 - 在第行发现重复的键 '', 这可能会导致一些意料之外的问题" @@ -102,6 +103,7 @@ warning.config.type.quaternionf: "在文件 发现问题 - 无 warning.config.type.vector3f: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为三维向量类型 (选项 '')" warning.config.type.vec3d: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为双精度浮点数三维向量类型 (选项 '')" warning.config.type.map: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为映射类型 (选项 '')" +warning.config.type.aabb: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为轴对齐包围盒类型 (选项 '')" warning.config.type.snbt.invalid_syntax: "在文件 发现问题 - 无法加载 '': 无效的 SNBT 语法 ''" warning.config.type.snbt.invalid_syntax.parse_error: ",位于第个字符:" warning.config.type.snbt.invalid_syntax.here: "<--[此处]" @@ -230,6 +232,7 @@ warning.config.jukebox_song.duplicate: "在文件 发现问题 - warning.config.jukebox_song.missing_sound: "在文件 发现问题 - 唱片机歌曲 '' 缺少必需的 'sound' 参数" warning.config.furniture.duplicate: "在文件 发现问题 - 重复的家具 '' 请检查其他文件中是否存在相同配置" warning.config.furniture.missing_variants: "在文件 发现问题 - 家具 '' 缺少必需的 'variants' 参数" +warning.config.furniture.element.invalid_type: "在文件 发现问题 - 家具 '' 使用了无效的元素类型 ''" warning.config.furniture.element.item_display.missing_item: "在文件 发现问题 - 家具 '' 的 'item_display' 元素缺少必需的 'item' 参数" warning.config.furniture.settings.unknown: "在文件 发现问题 - 家具 '' 使用了未知的设置类型 ''" warning.config.furniture.hitbox.invalid_type: "在文件 发现问题 - 家具 '' 使用了无效的碰撞箱类型 ''" From 6537a4cea9661058b50f02c2d74e84e9ab51583d Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 01:48:50 +0800 Subject: [PATCH 094/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=85=89=E7=BA=BF?= =?UTF-8?q?=E8=BF=BD=E8=B8=AA=E9=83=A8=E5=88=86=E5=AE=B6=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/entity/furniture/BukkitFurniture.java | 6 +----- .../furniture/hitbox/CustomFurnitureHitboxConfig.java | 4 ++-- .../furniture/hitbox/HappyGhastFurnitureHitboxConfig.java | 4 ++-- .../furniture/hitbox/InteractionFurnitureHitboxConfig.java | 4 ++-- .../furniture/hitbox/ShulkerFurnitureHitboxConfig.java | 4 ++-- .../bukkit/item/behavior/FurnitureItemBehavior.java | 2 +- .../craftengine/bukkit/plugin/user/BukkitServerPlayer.java | 2 +- .../craftengine/core/entity/furniture/Furniture.java | 2 +- .../core/entity/furniture/hitbox/FurnitureHitBoxConfig.java | 2 +- .../core/plugin/entityculling/EntityCulling.java | 1 + 10 files changed, 14 insertions(+), 17 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index e7fe83ce7..c8030e0d8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -9,11 +9,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityType import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; -import net.momirealms.craftengine.core.entity.player.InteractionResult; -import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.QuaternionUtils; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import org.bukkit.Location; @@ -75,7 +71,7 @@ public class BukkitFurniture extends Furniture { // 检查新位置是否可用 List aabbs = new ArrayList<>(); for (FurnitureHitBoxConfig hitBoxConfig : getCurrentVariant().hitBoxConfigs()) { - hitBoxConfig.prepareForPlacement(position, aabbs::add); + hitBoxConfig.prepareBoundingBox(position, aabbs::add, false); } if (!aabbs.isEmpty()) { if (!FastNMS.INSTANCE.checkEntityCollision(position.world.serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java index d7147708c..bd53f0015 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitboxConfig.java @@ -71,8 +71,8 @@ public class CustomFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig aabbConsumer) { - if (this.blocksBuilding) { + public void prepareBoundingBox(WorldPosition targetPos, Consumer aabbConsumer, boolean ignoreBlocksBuilding) { + if (this.blocksBuilding || ignoreBlocksBuilding) { Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, this.width, this.height)); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java index 6264b3a5f..3ee687d59 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitboxConfig.java @@ -56,8 +56,8 @@ public class HappyGhastFurnitureHitboxConfig extends AbstractFurnitureHitBoxConf } @Override - public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { - if (this.blocksBuilding) { + public void prepareBoundingBox(WorldPosition targetPos, Consumer aabbConsumer, boolean ignoreBlocksBuilding) { + if (this.blocksBuilding || ignoreBlocksBuilding) { Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, 4 * this.scale, 4 * this.scale)); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java index 909335b0a..40caaf1ee 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitboxConfig.java @@ -70,8 +70,8 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon } @Override - public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { - if (this.blocksBuilding) { + public void prepareBoundingBox(WorldPosition targetPos, Consumer aabbConsumer, boolean ignoreBlocksBuilding) { + if (this.blocksBuilding || ignoreBlocksBuilding) { Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position); aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, size.x, size.y)); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java index 2b309b806..6756948bb 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitboxConfig.java @@ -145,8 +145,8 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig< } @Override - public void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer) { - if (this.blocksBuilding) { + public void prepareBoundingBox(WorldPosition targetPos, Consumer aabbConsumer, boolean ignoreBlocksBuilding) { + if (this.blocksBuilding || ignoreBlocksBuilding) { Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - targetPos.yRot()), 0f).conjugate(); Vector3f offset = conjugated.transform(new Vector3f(position())); aabbConsumer.accept(this.aabbCreator.create(targetPos.x, targetPos.y, targetPos.z, targetPos.yRot, offset)); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index 3c3d34027..28493cd5f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -114,7 +114,7 @@ public class FurnitureItemBehavior extends ItemBehavior { List aabbs = new ArrayList<>(); // 收集阻挡的碰撞箱 for (FurnitureHitBoxConfig hitBoxConfig : variant.hitBoxConfigs()) { - hitBoxConfig.prepareForPlacement(furniturePos, aabbs::add); + hitBoxConfig.prepareBoundingBox(furniturePos, aabbs::add, false); } // 检查方块、实体阻挡 if (!aabbs.isEmpty()) { 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 2bc3a8635..06653a9f4 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 @@ -566,7 +566,7 @@ public class BukkitServerPlayer extends Player { FurnitureVariant currentVariant = furniture.getCurrentVariant(); List aabbs = new ArrayList<>(); for (FurnitureHitBoxConfig config : currentVariant.hitBoxConfigs()) { - config.prepareForPlacement(furniture.position(), aabbs::add); + config.prepareBoundingBox(furniture.position(), aabbs::add, true); } Key endRod = Key.of("soul_fire_flame"); for (AABB aabb : aabbs) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index d5d51596e..ec865cdff 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -130,7 +130,7 @@ public abstract class Furniture implements Cullable { if (aabb == null) { List aabbs = new ArrayList<>(); for (FurnitureHitBoxConfig hitBoxConfig : this.currentVariant.hitBoxConfigs()) { - hitBoxConfig.prepareForPlacement(position, aabbs::add); + hitBoxConfig.prepareBoundingBox(position, aabbs::add, true); } return new CullingData(getMaxAABB(aabbs), parent.maxDistance, parent.aabbExpansion, parent.rayTracing); } else { diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java index 7c8325e00..319a0f6a1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/hitbox/FurnitureHitBoxConfig.java @@ -22,6 +22,6 @@ public interface FurnitureHitBoxConfig { boolean canUseItemOn(); - void prepareForPlacement(WorldPosition targetPos, Consumer aabbConsumer); + void prepareBoundingBox(WorldPosition targetPos, Consumer aabbConsumer, boolean ignoreBlocksBuilding); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java index 79dac8655..183558688 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.plugin.entityculling; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.MutableVec3d; From 94bc0e84824b10fbf0d94d7cdf81eba2c21cf684 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 01:51:43 +0800 Subject: [PATCH 095/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=85=B3=E9=97=AD?= =?UTF-8?q?=E7=89=A9=E5=93=81=E6=A0=8F=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/context/event/EventFunctions.java | 1 + .../function/CloseInventoryFunction.java | 50 +++++++++++++++++++ .../context/function/CommonFunctions.java | 1 + 3 files changed, 52 insertions(+) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CloseInventoryFunction.java 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 fbc8a4e69..4ddd76b8e 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 @@ -58,6 +58,7 @@ public class EventFunctions { register(CommonFunctions.SET_EXP, new SetExpFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.SET_LEVEL, new SetLevelFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.PLAY_TOTEM_ANIMATION, new PlayTotemAnimationFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.CLOSE_INVENTORY, new CloseInventoryFunction.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/CloseInventoryFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CloseInventoryFunction.java new file mode 100644 index 000000000..2b052af0f --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CloseInventoryFunction.java @@ -0,0 +1,50 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.entity.player.Player; +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.context.selector.PlayerSelector; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; +import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +public class CloseInventoryFunction extends AbstractConditionalFunction { + private final PlayerSelector selector; + + public CloseInventoryFunction(List> predicates, @Nullable PlayerSelector selector) { + super(predicates); + this.selector = selector; + } + + @Override + public void runInternal(CTX ctx) { + if (this.selector == null) { + ctx.getOptionalParameter(DirectContextParameters.PLAYER).ifPresent(Player::closeInventory); + } else { + for (Player viewer : this.selector.get(ctx)) { + viewer.closeInventory(); + } + } + } + + @Override + public Key type() { + return CommonFunctions.TITLE; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + return new CloseInventoryFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory())); + } + } +} 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 937534105..128799962 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 @@ -49,4 +49,5 @@ public final class CommonFunctions { public static final Key SET_EXP = Key.of("craftengine:set_exp"); public static final Key SET_LEVEL = Key.of("craftengine:set_level"); public static final Key PLAY_TOTEM_ANIMATION = Key.of("craftengine:play_totem_animation"); + public static final Key CLOSE_INVENTORY = Key.of("craftengine:close_inventory"); } From 77bc8f8ae40f821c2a2e7b590de9e52af02e90c3 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 01:53:32 +0800 Subject: [PATCH 096/135] Update CloseInventoryFunction.java --- .../core/plugin/context/function/CloseInventoryFunction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CloseInventoryFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CloseInventoryFunction.java index 2b052af0f..4b99be7dd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CloseInventoryFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CloseInventoryFunction.java @@ -33,7 +33,7 @@ public class CloseInventoryFunction extends AbstractConditi @Override public Key type() { - return CommonFunctions.TITLE; + return CommonFunctions.CLOSE_INVENTORY; } public static class FactoryImpl extends AbstractFactory { From f85aa51f9d18c88b2250a7c8005cafba12621b0b Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 02:31:46 +0800 Subject: [PATCH 097/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=93=AD=E7=89=8C?= =?UTF-8?q?=E5=B8=BD=E5=AD=90=E9=AB=98=E5=BA=A6=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BukkitCompatibilityManager.java | 2 + .../tag/CustomNameplateHatSettings.java | 84 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/tag/CustomNameplateHatSettings.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 28d614af0..c56a998aa 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 @@ -19,6 +19,7 @@ import net.momirealms.craftengine.bukkit.compatibility.quickshop.QuickShopItemEx 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.tag.CustomNameplateHatSettings; import net.momirealms.craftengine.bukkit.compatibility.tag.CustomNameplateProviders; import net.momirealms.craftengine.bukkit.compatibility.viaversion.ViaVersionUtils; import net.momirealms.craftengine.bukkit.compatibility.worldedit.WorldEditBlockRegister; @@ -97,6 +98,7 @@ public class BukkitCompatibilityManager implements CompatibilityManager { registerTagResolverProvider(new CustomNameplateProviders.Background()); registerTagResolverProvider(new CustomNameplateProviders.Nameplate()); registerTagResolverProvider(new CustomNameplateProviders.Bubble()); + new CustomNameplateHatSettings().register(); logHook("CustomNameplates"); } Key worldGuardRegion = Key.of("worldguard:region"); diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/tag/CustomNameplateHatSettings.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/tag/CustomNameplateHatSettings.java new file mode 100644 index 000000000..232161645 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/tag/CustomNameplateHatSettings.java @@ -0,0 +1,84 @@ +package net.momirealms.craftengine.bukkit.compatibility.tag; + +import io.papermc.paper.event.entity.EntityEquipmentChangedEvent; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.core.item.CustomItem; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemSettings; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.CustomDataType; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.CustomNameplatesAPI; +import net.momirealms.customnameplates.api.feature.tag.TagRenderer; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +public final class CustomNameplateHatSettings implements Listener { + public static final CustomDataType HAT_HEIGHT = new CustomDataType<>(); + + public void register() { + ItemSettings.Modifiers.registerFactory("hat-height", height -> { + double heightD = ResourceConfigUtils.getAsDouble(height, "hat-height"); + return settings -> settings.addCustomData(HAT_HEIGHT, heightD); + }); + Bukkit.getPluginManager().registerEvents(this, BukkitCraftEngine.instance().javaPlugin()); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onEquipmentChange(EntityEquipmentChangedEvent event) { + if (!(event.getEntity() instanceof Player player)) return; + Map equipmentChanges = event.getEquipmentChanges(); + EntityEquipmentChangedEvent.EquipmentChange equipmentChange = equipmentChanges.get(EquipmentSlot.HEAD); + if (equipmentChange == null) return; + ItemStack newItem = equipmentChange.newItem(); + updateHatHeight(player, newItem); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + // 稍微延迟一下,可以等待背包同步插件的处理 + if (VersionHelper.isFolia()) { + player.getScheduler().runDelayed(BukkitCraftEngine.instance().javaPlugin(), t1 -> { + if (player.isOnline()) { + updateHatHeight(player, player.getInventory().getItem(EquipmentSlot.HEAD)); + } + }, null, 10); + } else { + CraftEngine.instance().scheduler().sync().runLater(() -> { + if (player.isOnline()) { + updateHatHeight(player, player.getInventory().getItem(EquipmentSlot.HEAD)); + } + }, 10); + } + } + + public void updateHatHeight(Player player, ItemStack newItem) { + CNPlayer cnPlayer = CustomNameplatesAPI.getInstance().getPlayer(player.getUniqueId()); + if (cnPlayer == null) return; + TagRenderer tagRender = CustomNameplates.getInstance().getUnlimitedTagManager().getTagRender(cnPlayer); + if (tagRender == null) return; + Item wrapped = BukkitItemManager.instance().wrap(newItem); + Optional> optionalCustomItem = wrapped.getCustomItem(); + if (optionalCustomItem.isEmpty()) { + tagRender.hatOffset(0d); + return; + } + Double customHeight = optionalCustomItem.get().settings().getCustomData(HAT_HEIGHT); + tagRender.hatOffset(Objects.requireNonNullElse(customHeight, 0d)); + } +} From 5902d3c95544c689dc3d93655b59abeab23bbe9d Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Fri, 5 Dec 2025 02:46:10 +0800 Subject: [PATCH 098/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B8=85=E7=90=86?= =?UTF-8?q?=E7=89=A9=E5=93=81=E5=87=BD=E6=95=B0=E5=92=8C=E7=89=A9=E5=93=81?= =?UTF-8?q?=E6=A0=8F=E5=AD=98=E5=9C=A8=E7=89=A9=E5=93=81=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../papi/CheckItemExpansion.java | 7 +-- .../plugin/user/BukkitServerPlayer.java | 10 ++++ .../core/entity/player/Player.java | 2 + .../context/condition/CommonConditions.java | 1 + .../condition/InventoryHasItemCondition.java | 51 ++++++++++++++++++ .../plugin/context/event/EventConditions.java | 1 + .../plugin/context/event/EventFunctions.java | 1 + .../context/function/ClearItemFunction.java | 54 +++++++++++++++++++ .../context/function/CommonFunctions.java | 1 + 9 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/InventoryHasItemCondition.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ClearItemFunction.java diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/CheckItemExpansion.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/CheckItemExpansion.java index a208e2901..f86fc0dbd 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/CheckItemExpansion.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/CheckItemExpansion.java @@ -108,11 +108,6 @@ public class CheckItemExpansion extends PlaceholderExpansion { } private int getItemCount(BukkitServerPlayer player, String[] param) { - Key itemId = Key.of(param[0], param[1]); - Predicate predicate = nmsStack -> this.plugin.itemManager().wrap(ItemStackUtils.asCraftMirror(nmsStack)).id().equals(itemId); - Object inventory = FastNMS.INSTANCE.method$Player$getInventory(player.serverPlayer()); - Object inventoryMenu = FastNMS.INSTANCE.field$Player$inventoryMenu(player.serverPlayer()); - Object craftSlots = FastNMS.INSTANCE.method$InventoryMenu$getCraftSlots(inventoryMenu); - return FastNMS.INSTANCE.method$Inventory$clearOrCountMatchingItems(inventory, predicate, 0, craftSlots); + return player.clearOrCountMatchingInventoryItems(Key.of(param[0], param[1]), 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 2bc3a8635..0358696d1 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 @@ -75,6 +75,7 @@ import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; public class BukkitServerPlayer extends Player { public static final Key SELECTED_LOCALE_KEY = Key.of("craftengine:locale"); @@ -1492,6 +1493,15 @@ public class BukkitServerPlayer extends Player { this.trackedBlockEntityRenderers.clear(); } + @Override + public int clearOrCountMatchingInventoryItems(Key itemId, int count) { + Predicate predicate = nmsStack -> this.plugin.itemManager().wrap(ItemStackUtils.asCraftMirror(nmsStack)).id().equals(itemId); + Object inventory = FastNMS.INSTANCE.method$Player$getInventory(serverPlayer()); + Object inventoryMenu = FastNMS.INSTANCE.field$Player$inventoryMenu(serverPlayer()); + Object craftSlots = FastNMS.INSTANCE.method$InventoryMenu$getCraftSlots(inventoryMenu); + return FastNMS.INSTANCE.method$Inventory$clearOrCountMatchingItems(inventory, predicate, count, craftSlots); + } + @Override public void addTrackedFurniture(int entityId, Furniture furniture) { this.trackedFurniture.put(entityId, new VirtualCullableObject(furniture)); 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 6fd240dbb..b8792bde0 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 @@ -226,6 +226,8 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void clearTrackedBlockEntities(); + public abstract int clearOrCountMatchingInventoryItems(Key itemId, int count); + @Override public void remove() { } 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 991ea9e0a..16c0c5d22 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 @@ -31,4 +31,5 @@ public final class CommonConditions { public static final Key IS_NULL = Key.from("craftengine:is_null"); public static final Key HAND = Key.from("craftengine:hand"); public static final Key HAS_PLAYER = Key.from("craftengine:has_player"); + public static final Key INVENTORY_HAS_ITEM = Key.from("craftengine:inventory_has_item"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/InventoryHasItemCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/InventoryHasItemCondition.java new file mode 100644 index 000000000..3498ccd7f --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/InventoryHasItemCondition.java @@ -0,0 +1,51 @@ +package net.momirealms.craftengine.core.plugin.context.condition; + +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.Item; +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.ItemUtils; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Optional; + +public class InventoryHasItemCondition implements Condition { + private final Key itemId; + private final NumberProvider count; + + public InventoryHasItemCondition(Key itemId, NumberProvider count) { + this.itemId = itemId; + this.count = count; + } + + @Override + public Key type() { + return CommonConditions.INVENTORY_HAS_ITEM; + } + + @Override + public boolean test(CTX ctx) { + Optional optionalPlayer = ctx.getOptionalParameter(DirectContextParameters.PLAYER); + if (optionalPlayer.isEmpty()) { + return false; + } + Player player = optionalPlayer.get(); + return player.clearOrCountMatchingInventoryItems(this.itemId, 0) >= this.count.getInt(ctx); + } + + public static class FactoryImpl implements ConditionFactory { + + @Override + public Condition create(Map arguments) { + Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(ResourceConfigUtils.get(arguments, "id", "item"), "warning.config.condition.inventory_has_item.missing_item_id")); + NumberProvider count = NumberProviders.fromObject(arguments.getOrDefault("count", 1)); + return new InventoryHasItemCondition<>(itemId, count); + } + } +} 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 b17921ed4..6d2aa4996 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 @@ -40,6 +40,7 @@ public class EventConditions { register(CommonConditions.IS_NULL, new IsNullCondition.FactoryImpl<>()); register(CommonConditions.HAND, new HandCondition.FactoryImpl<>()); register(CommonConditions.ON_COOLDOWN, new OnCooldownCondition.FactoryImpl<>()); + register(CommonConditions.INVENTORY_HAS_ITEM, new InventoryHasItemCondition.FactoryImpl<>()); } public static void register(Key key, ConditionFactory factory) { 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 fbc8a4e69..19bb76b41 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 @@ -58,6 +58,7 @@ public class EventFunctions { register(CommonFunctions.SET_EXP, new SetExpFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.SET_LEVEL, new SetLevelFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.PLAY_TOTEM_ANIMATION, new PlayTotemAnimationFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.CLEAR_ITEM, new ClearItemFunction.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/ClearItemFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ClearItemFunction.java new file mode 100644 index 000000000..0be7887b5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ClearItemFunction.java @@ -0,0 +1,54 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.entity.player.Player; +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.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class ClearItemFunction extends AbstractConditionalFunction { + private final Key itemId; + private final NumberProvider count; + + public ClearItemFunction(List> predicates, Key itemId, NumberProvider count) { + super(predicates); + this.itemId = itemId; + this.count = count; + } + + @Override + protected void runInternal(CTX ctx) { + Optional optionalPlayer = ctx.getOptionalParameter(DirectContextParameters.PLAYER); + if (optionalPlayer.isEmpty()) { + return; + } + Player player = optionalPlayer.get(); + player.clearOrCountMatchingInventoryItems(itemId, count.getInt(ctx)); + } + + @Override + public Key type() { + return CommonFunctions.CLEAR_ITEM; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(ResourceConfigUtils.get(arguments, "id", "item"), "warning.config.function.clear_item.missing_item_id")); + NumberProvider count = NumberProviders.fromObject(arguments.getOrDefault("count", 1)); + return new ClearItemFunction<>(getPredicates(arguments), itemId, count); + } + } +} 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 937534105..ccb288010 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 @@ -49,4 +49,5 @@ public final class CommonFunctions { public static final Key SET_EXP = Key.of("craftengine:set_exp"); public static final Key SET_LEVEL = Key.of("craftengine:set_level"); public static final Key PLAY_TOTEM_ANIMATION = Key.of("craftengine:play_totem_animation"); + public static final Key CLEAR_ITEM = Key.of("craftengine:clear_item"); } From e04d5c5572836a714b83f52992385039cc48c61a Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Fri, 5 Dec 2025 02:52:05 +0800 Subject: [PATCH 099/135] =?UTF-8?q?=E8=AF=AD=E8=A8=80=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/translations/en.yml | 2 ++ common-files/src/main/resources/translations/zh_cn.yml | 2 ++ .../plugin/context/condition/InventoryHasItemCondition.java | 2 +- .../core/plugin/context/function/ClearItemFunction.java | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index b4b992ed4..5089cdfb1 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -170,6 +170,7 @@ warning.config.condition.is_null.missing_argument: "Issue found in file warning.config.condition.hand.missing_hand: "Issue found in file - The config '' is missing the required 'hand' argument for 'hand' condition." warning.config.condition.hand.invalid_hand: "Issue found in file - The config '' is using an invalid 'hand' argument '' for 'hand' condition. Allowed hand types: []" warning.config.condition.on_cooldown.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'on_cooldown' condition." +warning.config.condition.inventory_has_item.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'inventory_has_item' condition." warning.config.structure.not_section: "Issue found in file - The config '' is expected to be a config section while it's actually a(n) ''." warning.config.image.duplicate: "Issue found in file - Duplicated image ''. Please check if there is the same configuration in other files." warning.config.image.missing_height: "Issue found in file - The image '' is missing the required 'height' argument." @@ -543,6 +544,7 @@ warning.config.function.transform_block.missing_block: "Issue found in f warning.config.function.cycle_block_property.missing_property: "Issue found in file - The config '' is missing the required 'property' argument for 'cycle_block_property' function." warning.config.function.set_exp.missing_count: "Issue found in file - The config '' is missing the required 'count' argument for 'set_exp' function." warning.config.function.set_level.missing_count: "Issue found in file - The config '' is missing the required 'count' argument for 'set_level' function." +warning.config.function.clear_item.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'clear_item' function." 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 ''." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 71fbf6f5e..bf82355f0 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -167,6 +167,7 @@ warning.config.condition.is_null.missing_argument: "在文件 warning.config.condition.hand.missing_hand: "在文件 发现问题 - 配置项 '' 缺少 'hand' 条件必需的 'hand' 参数" warning.config.condition.hand.invalid_hand: "在文件 发现问题 - 配置项 '' 使用了无效的 'hand' 参数 '' ('hand' 条件). 允许的手部类型: []" warning.config.condition.on_cooldown.missing_id: "在文件 发现问题 - 配置项 '' 缺少 'on_cooldown' 条件必需的 'id' 参数" +warning.config.condition.inventory_has_item.missing_id: "在文件 发现问题 - 配置项 '' 缺少 'inventory_has_item' 条件必需的 'id' 参数" warning.config.structure.not_section: "在文件 发现问题 - 配置项 '' 应为配置段落 但实际类型为 ''" warning.config.image.duplicate: "在文件 发现问题 - 重复的图片配置 '' 请检查其他文件中是否存在相同配置" warning.config.image.missing_height: "在文件 发现问题 - 图片 '' 缺少必需的 'height' 参数" @@ -535,6 +536,7 @@ warning.config.function.transform_block.missing_block: "在文件 在文件 发现问题 - 配置项 '' 缺少 'cycle_block_property' 函数所需的 'property' 参数" warning.config.function.set_exp.missing_count: "在文件 发现问题 - 配置项 '' 缺少 'set_exp' 函数所需的 'count' 参数" warning.config.function.set_level.missing_count: "在文件 发现问题 - 配置项 '' 缺少 'set_level' 函数所需的 'count' 参数" +warning.config.function.clear_item.missing_id: "在文件 发现问题 - 配置项 '' 缺少 'clear_item' 函数所需的 'id' 参数" warning.config.selector.missing_type: "在文件 发现问题 - 配置项 '' 缺少选择器必需的 'type' 参数" warning.config.selector.invalid_type: "在文件 发现问题 - 配置项 '' 使用了无效的选择器类型 ''" warning.config.selector.invalid_target: "在文件 发现问题 - 配置项 '' 使用了无效的选择器目标 ''" diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/InventoryHasItemCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/InventoryHasItemCondition.java index 3498ccd7f..4b849ab41 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/InventoryHasItemCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/InventoryHasItemCondition.java @@ -43,7 +43,7 @@ public class InventoryHasItemCondition implements Condition @Override public Condition create(Map arguments) { - Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(ResourceConfigUtils.get(arguments, "id", "item"), "warning.config.condition.inventory_has_item.missing_item_id")); + Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(ResourceConfigUtils.get(arguments, "id", "item"), "warning.config.condition.inventory_has_item.missing_id")); NumberProvider count = NumberProviders.fromObject(arguments.getOrDefault("count", 1)); return new InventoryHasItemCondition<>(itemId, count); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ClearItemFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ClearItemFunction.java index 0be7887b5..578266f53 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ClearItemFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ClearItemFunction.java @@ -46,7 +46,7 @@ public class ClearItemFunction extends AbstractConditionalF @Override public Function create(Map arguments) { - Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(ResourceConfigUtils.get(arguments, "id", "item"), "warning.config.function.clear_item.missing_item_id")); + Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(ResourceConfigUtils.get(arguments, "id", "item"), "warning.config.function.clear_item.missing_id")); NumberProvider count = NumberProviders.fromObject(arguments.getOrDefault("count", 1)); return new ClearItemFunction<>(getPredicates(arguments), itemId, count); } From b310559c0dbd9c2616f8c3a3aed6640a47616e53 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 02:56:19 +0800 Subject: [PATCH 100/135] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=9E=84=E9=80=A0?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E5=8F=82=E6=95=B0=E9=A1=BA=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/context/event/EventFunctions.java | 1 + .../context/function/BreakBlockFunction.java | 4 +- .../context/function/CommonFunctions.java | 1 + .../context/function/DamageFunction.java | 4 +- .../context/function/DamageItemFunction.java | 4 +- .../context/function/LevelerExpFunction.java | 4 +- .../function/MythicMobsSkillFunction.java | 4 +- .../context/function/ParticleFunction.java | 4 +- .../context/function/PlaceBlockFunction.java | 9 +- .../context/function/PlaySoundFunction.java | 12 +-- .../function/PotionEffectFunction.java | 4 +- .../function/RemoveCooldownFunction.java | 6 +- .../function/RemoveFurnitureFunction.java | 4 +- .../function/RemovePotionEffectFunction.java | 6 +- .../function/ReplaceFurnitureFunction.java | 13 +-- .../function/RotateFurnitureFunction.java | 84 +++++++++++++++++++ .../plugin/context/function/RunFunction.java | 4 +- .../context/function/SetCooldownFunction.java | 4 +- .../context/function/SetCountFunction.java | 4 +- .../context/function/SetFoodFunction.java | 4 +- .../function/SetSaturationFunction.java | 4 +- .../function/SpawnFurnitureFunction.java | 12 +-- .../context/function/SwingHandFunction.java | 4 +- .../function/TransformBlockFunction.java | 11 +-- .../function/UpdateBlockPropertyFunction.java | 10 +-- gradle.properties | 12 +-- 26 files changed, 140 insertions(+), 93 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RotateFurnitureFunction.java 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 0feab6450..5cbe2e8db 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 @@ -43,6 +43,7 @@ public class EventFunctions { register(CommonFunctions.SPAWN_FURNITURE, new SpawnFurnitureFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.REMOVE_FURNITURE, new RemoveFurnitureFunction.FactoryImpl<>(EventConditions::fromMap)); register(CommonFunctions.REPLACE_FURNITURE, new ReplaceFurnitureFunction.FactoryImpl<>(EventConditions::fromMap)); + register(CommonFunctions.ROTATE_FURNITURE, new RotateFurnitureFunction.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)); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/BreakBlockFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/BreakBlockFunction.java index d0b63f852..ad9543d18 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/BreakBlockFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/BreakBlockFunction.java @@ -18,7 +18,7 @@ public class BreakBlockFunction extends AbstractConditional private final NumberProvider y; private final NumberProvider z; - public BreakBlockFunction(NumberProvider x, NumberProvider y, NumberProvider z, List> predicates) { + public BreakBlockFunction(List> predicates, NumberProvider y, NumberProvider z, NumberProvider x) { super(predicates); this.x = x; this.y = y; @@ -47,7 +47,7 @@ public class BreakBlockFunction extends AbstractConditional NumberProvider x = NumberProviders.fromObject(arguments.getOrDefault("x", "")); NumberProvider y = NumberProviders.fromObject(arguments.getOrDefault("y", "")); NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "")); - return new BreakBlockFunction<>(x, y, z, getPredicates(arguments)); + return new BreakBlockFunction<>(getPredicates(arguments), y, z, x); } } } 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 b77e6fc06..3083f3cf6 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 @@ -32,6 +32,7 @@ public final class CommonFunctions { public static final Key SPAWN_FURNITURE = Key.of("craftengine:spawn_furniture"); 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 ROTATE_FURNITURE = Key.of("craftengine:rotate_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"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java index 97e197b55..6fd52692b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageFunction.java @@ -17,7 +17,7 @@ public class DamageFunction extends AbstractConditionalFunc private final Key damageType; private final NumberProvider amount; - public DamageFunction(PlayerSelector selector, Key damageType, NumberProvider amount, List> predicates) { + public DamageFunction(List> predicates, Key damageType, NumberProvider amount, PlayerSelector selector) { super(predicates); this.selector = selector; this.damageType = damageType; @@ -45,7 +45,7 @@ public class DamageFunction extends AbstractConditionalFunc PlayerSelector selector = PlayerSelectors.fromObject(arguments.getOrDefault("target", "self"), conditionFactory()); Key damageType = Key.of(ResourceConfigUtils.getAsStringOrNull(arguments.getOrDefault("damage-type", "generic"))); NumberProvider amount = NumberProviders.fromObject(arguments.getOrDefault("amount", 1f)); - return new DamageFunction<>(selector, damageType, amount, getPredicates(arguments)); + return new DamageFunction<>(getPredicates(arguments), damageType, amount, selector); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageItemFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageItemFunction.java index e5cbedec6..d1580f33b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageItemFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageItemFunction.java @@ -17,7 +17,7 @@ import java.util.Map; public class DamageItemFunction extends AbstractConditionalFunction { private final NumberProvider amount; - public DamageItemFunction(NumberProvider amount, List> predicates) { + public DamageItemFunction(List> predicates, NumberProvider amount) { super(predicates); this.amount = amount; } @@ -51,7 +51,7 @@ public class DamageItemFunction extends AbstractConditional @Override public Function create(Map arguments) { NumberProvider amount = NumberProviders.fromObject(arguments.getOrDefault("amount", 1)); - return new DamageItemFunction<>(amount, getPredicates(arguments)); + return new DamageItemFunction<>(getPredicates(arguments), amount); } } } 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 51316f239..a6ddff9b4 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 @@ -20,7 +20,7 @@ public class LevelerExpFunction extends AbstractConditional private final String leveler; private final String plugin; - public LevelerExpFunction(NumberProvider count, String leveler, String plugin, PlayerSelector selector, List> predicates) { + public LevelerExpFunction(List> predicates, String leveler, String plugin, PlayerSelector selector, NumberProvider count) { super(predicates); this.count = count; this.leveler = leveler; @@ -58,7 +58,7 @@ public class LevelerExpFunction extends AbstractConditional Object count = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("count"), "warning.config.function.leveler_exp.missing_count"); String leveler = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("leveler"), "warning.config.function.leveler_exp.missing_leveler"); String plugin = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("plugin"), "warning.config.function.leveler_exp.missing_plugin"); - return new LevelerExpFunction<>(NumberProviders.fromObject(count), leveler, plugin, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), getPredicates(arguments)); + return new LevelerExpFunction<>(getPredicates(arguments), leveler, plugin, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), NumberProviders.fromObject(count)); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MythicMobsSkillFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MythicMobsSkillFunction.java index 031ed08da..9d97eb14c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MythicMobsSkillFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/MythicMobsSkillFunction.java @@ -14,7 +14,7 @@ public class MythicMobsSkillFunction extends AbstractCondit private final String skill; private final float power; - public MythicMobsSkillFunction(String skill, float power, List> predicates) { + public MythicMobsSkillFunction(List> predicates, float power, String skill) { super(predicates); this.skill = skill; this.power = power; @@ -42,7 +42,7 @@ public class MythicMobsSkillFunction extends AbstractCondit public Function create(Map args) { String skill = ResourceConfigUtils.requireNonEmptyStringOrThrow(args.get("skill"), "warning.config.function.mythic_mobs_skill.missing_skill"); float power = ResourceConfigUtils.getAsFloat(args.getOrDefault("power", 1.0), "power"); - return new MythicMobsSkillFunction<>(skill, power, getPredicates(args)); + return new MythicMobsSkillFunction<>(getPredicates(args), power, skill); } } } 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 4893d19f8..dd4f29458 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 @@ -17,7 +17,7 @@ import java.util.Optional; public class ParticleFunction extends AbstractConditionalFunction { private final ParticleConfig config; - public ParticleFunction(ParticleConfig config, List> predicates) { + public ParticleFunction(List> predicates, ParticleConfig config) { super(predicates); this.config = config; } @@ -45,7 +45,7 @@ public class ParticleFunction extends AbstractConditionalFu @Override public Function create(Map arguments) { - return new ParticleFunction<>(ParticleConfig.fromMap$function(arguments), getPredicates(arguments)); + return new ParticleFunction<>(getPredicates(arguments), ParticleConfig.fromMap$function(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 fedd7639a..f71b2ae08 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 @@ -26,7 +26,7 @@ public class PlaceBlockFunction extends AbstractConditional private final NumberProvider z; private final NumberProvider updateFlags; - public PlaceBlockFunction(LazyReference lazyBlockState, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider updateFlags, List> predicates) { + public PlaceBlockFunction(List> predicates, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider updateFlags, LazyReference lazyBlockState) { super(predicates); this.lazyBlockState = lazyBlockState; this.x = x; @@ -58,12 +58,7 @@ public class PlaceBlockFunction extends AbstractConditional @Override public Function create(Map arguments) { String state = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("block-state"), "warning.config.function.place_block.missing_block_state"); - return new PlaceBlockFunction<>(LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createBlockState(state)), - NumberProviders.fromObject(arguments.getOrDefault("x", "")), - NumberProviders.fromObject(arguments.getOrDefault("y", "")), - NumberProviders.fromObject(arguments.getOrDefault("z", "")), - Optional.ofNullable(arguments.get("update-flags")).map(NumberProviders::fromObject).orElse(NumberProviders.direct(UpdateOption.UPDATE_ALL.flags())), - getPredicates(arguments)); + return new PlaceBlockFunction<>(getPredicates(arguments), NumberProviders.fromObject(arguments.getOrDefault("x", "")), NumberProviders.fromObject(arguments.getOrDefault("y", "")), NumberProviders.fromObject(arguments.getOrDefault("z", "")), Optional.ofNullable(arguments.get("update-flags")).map(NumberProviders::fromObject).orElse(NumberProviders.direct(UpdateOption.UPDATE_ALL.flags())), LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createBlockState(state))); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlaySoundFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlaySoundFunction.java index 00004bbb8..cc71ff1ba 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlaySoundFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/PlaySoundFunction.java @@ -31,15 +31,7 @@ public class PlaySoundFunction extends AbstractConditionalF private final PlayerSelector selector; public PlaySoundFunction( - Key soundEvent, - NumberProvider x, - NumberProvider y, - NumberProvider z, - NumberProvider volume, - NumberProvider pitch, - SoundSource source, - PlayerSelector selector, - List> predicates + List> predicates, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider volume, NumberProvider pitch, SoundSource source, PlayerSelector selector, Key soundEvent ) { super(predicates); this.soundEvent = soundEvent; @@ -89,7 +81,7 @@ public class PlaySoundFunction extends AbstractConditionalF NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", 1)); SoundSource source = Optional.ofNullable(arguments.get("source")).map(String::valueOf).map(it -> SoundSource.valueOf(it.toUpperCase(Locale.ENGLISH))).orElse(SoundSource.MASTER); PlayerSelector selector = PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()); - return new PlaySoundFunction<>(soundEvent, x, y, z, volume, pitch, source, selector, getPredicates(arguments)); + return new PlaySoundFunction<>(getPredicates(arguments), x, y, z, volume, pitch, source, selector, soundEvent); } } } 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 fa25af75f..cf87e324c 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 @@ -21,7 +21,7 @@ public class PotionEffectFunction extends AbstractCondition private final boolean ambient; private final boolean particles; - public PotionEffectFunction(Key potionEffectType, NumberProvider duration, NumberProvider amplifier, boolean ambient, boolean particles, PlayerSelector selector, List> predicates) { + public PotionEffectFunction(List> predicates, NumberProvider duration, NumberProvider amplifier, boolean ambient, boolean particles, PlayerSelector selector, Key potionEffectType) { super(predicates); this.potionEffectType = potionEffectType; this.duration = duration; @@ -63,7 +63,7 @@ public class PotionEffectFunction extends AbstractCondition NumberProvider amplifier = NumberProviders.fromObject(arguments.getOrDefault("amplifier", 0)); boolean ambient = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("ambient", false), "ambient"); boolean particles = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("particles", true), "particles"); - return new PotionEffectFunction<>(effectType, duration, amplifier, ambient, particles, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), getPredicates(arguments)); + return new PotionEffectFunction<>(getPredicates(arguments), duration, amplifier, ambient, particles, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), effectType); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveCooldownFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveCooldownFunction.java index 84982ec78..5beae2853 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveCooldownFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveCooldownFunction.java @@ -19,7 +19,7 @@ public class RemoveCooldownFunction extends AbstractConditi private final String id; private final boolean all; - public RemoveCooldownFunction(String id, boolean all, PlayerSelector selector, List> predicates) { + public RemoveCooldownFunction(List> predicates, boolean all, PlayerSelector selector, String id) { super(predicates); this.selector = selector; this.id = id; @@ -59,10 +59,10 @@ public class RemoveCooldownFunction extends AbstractConditi public Function create(Map arguments) { boolean all = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("all", false), "all"); if (all) { - return new RemoveCooldownFunction<>(null, true, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), getPredicates(arguments)); + return new RemoveCooldownFunction<>(getPredicates(arguments), true, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), null); } else { String id = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("id"), "warning.config.function.remove_cooldown.missing_id"); - return new RemoveCooldownFunction<>(id, false, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), getPredicates(arguments)); + return new RemoveCooldownFunction<>(getPredicates(arguments), false, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), id); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java index 518b061cd..3fb802c80 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemoveFurnitureFunction.java @@ -24,7 +24,7 @@ public class RemoveFurnitureFunction extends AbstractCondit private final boolean dropLoot; private final boolean playSound; - public RemoveFurnitureFunction(boolean dropLoot, boolean playSound, List> predicates) { + public RemoveFurnitureFunction(List> predicates, boolean playSound, boolean dropLoot) { super(predicates); this.dropLoot = dropLoot; this.playSound = playSound; @@ -80,7 +80,7 @@ public class RemoveFurnitureFunction extends AbstractCondit public Function create(Map arguments) { boolean dropLoot = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("drop-loot", true), "drop-loot"); boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound"); - return new RemoveFurnitureFunction<>(dropLoot, playSound, getPredicates(arguments)); + return new RemoveFurnitureFunction<>(getPredicates(arguments), playSound, dropLoot); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemovePotionEffectFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemovePotionEffectFunction.java index 55322a946..70af392c1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemovePotionEffectFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RemovePotionEffectFunction.java @@ -17,7 +17,7 @@ public class RemovePotionEffectFunction extends AbstractCon private final Key potionEffectType; private final boolean all; - public RemovePotionEffectFunction(Key potionEffectType, boolean all, PlayerSelector selector, List> predicates) { + public RemovePotionEffectFunction(List> predicates, boolean all, PlayerSelector selector, Key potionEffectType) { super(predicates); this.potionEffectType = potionEffectType; this.selector = selector; @@ -54,10 +54,10 @@ public class RemovePotionEffectFunction extends AbstractCon public Function create(Map arguments) { boolean all = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("all", false), "all"); if (all) { - return new RemovePotionEffectFunction<>(null, true, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), getPredicates(arguments)); + return new RemovePotionEffectFunction<>(getPredicates(arguments), true, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), null); } else { Key effectType = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("potion-effect"), "warning.config.function.remove_potion_effect.missing_potion_effect")); - return new RemovePotionEffectFunction<>(effectType, false, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), getPredicates(arguments)); + return new RemovePotionEffectFunction<>(getPredicates(arguments), false, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), effectType); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java index 2c4577909..c2bfed095 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ReplaceFurnitureFunction.java @@ -26,16 +26,7 @@ public class ReplaceFurnitureFunction extends AbstractCondi private final boolean playSound; public ReplaceFurnitureFunction( - Key newFurnitureId, - NumberProvider x, - NumberProvider y, - NumberProvider z, - NumberProvider pitch, - NumberProvider yaw, - String variant, - boolean dropLoot, - boolean playSound, - List> predicates + List> predicates, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider pitch, NumberProvider yaw, String variant, boolean dropLoot, boolean playSound, Key newFurnitureId ) { super(predicates); this.newFurnitureId = newFurnitureId; @@ -96,7 +87,7 @@ public class ReplaceFurnitureFunction extends AbstractCondi String variant = ResourceConfigUtils.getAsStringOrNull(ResourceConfigUtils.get(arguments, "variant", "anchor-type")); boolean dropLoot = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("drop-loot", true), "drop-loot"); boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound"); - return new ReplaceFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, variant, dropLoot, playSound, getPredicates(arguments)); + return new ReplaceFurnitureFunction<>(getPredicates(arguments), x, y, z, pitch, yaw, variant, dropLoot, playSound, furnitureId); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RotateFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RotateFurnitureFunction.java new file mode 100644 index 000000000..1256826a8 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RotateFurnitureFunction.java @@ -0,0 +1,84 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.loot.LootTable; +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.event.EventFunctions; +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.sound.SoundData; +import net.momirealms.craftengine.core.sound.SoundSource; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldPosition; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class RotateFurnitureFunction extends AbstractConditionalFunction { + private final NumberProvider degree; + private final List> successFunctions; + private final List> failureFunctions; + + public RotateFurnitureFunction(List> predicates, NumberProvider degree, List> successFunctions, List> failureFunctions) { + super(predicates); + this.degree = degree; + this.successFunctions = successFunctions; + this.failureFunctions = failureFunctions; + } + + @Override + public void runInternal(CTX ctx) { + ctx.getOptionalParameter(DirectContextParameters.FURNITURE).ifPresent(furniture -> rotateFurniture(ctx, furniture)); + } + + public void rotateFurniture(CTX ctx, Furniture furniture) { + if (!furniture.isValid()) return; + WorldPosition position = furniture.position(); + WorldPosition newPos = new WorldPosition(position.world, position.x, position.y, position.z, position.xRot, position.yRot + this.degree.getFloat(ctx)); + furniture.moveTo(newPos).thenAccept(success -> { + if (success) { + for (Function successFunction : this.successFunctions) { + successFunction.run(ctx); + } + } else { + for (Function failureFunction : this.failureFunctions) { + failureFunction.run(ctx); + } + } + }); + } + + @Override + public Key type() { + return CommonFunctions.ROTATE_FURNITURE; + } + + public NumberProvider degree() { + return degree; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + NumberProvider degree = NumberProviders.fromObject(arguments.getOrDefault("degree", 90)); + List> onSuccess = ResourceConfigUtils.parseConfigAsList(arguments.get("on-success"), EventFunctions::fromMap); + List> onFailure = ResourceConfigUtils.parseConfigAsList(arguments.get("on-failure"), EventFunctions::fromMap); + return new RotateFurnitureFunction<>(getPredicates(arguments), degree, onSuccess, onFailure); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RunFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RunFunction.java index 15f5b0586..154bbc919 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RunFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RunFunction.java @@ -21,7 +21,7 @@ public class RunFunction extends AbstractConditionalFunctio private final List> functions; private final NumberProvider delay; - public RunFunction(List> functions, NumberProvider delay, List> predicates) { + public RunFunction(List> predicates, NumberProvider delay, List> functions) { super(predicates); this.functions = functions; this.delay = delay; @@ -75,7 +75,7 @@ public class RunFunction extends AbstractConditionalFunctio for (Map function : functions) { fun.add(this.functionFactory.apply(function)); } - return new RunFunction<>(fun, delay, getPredicates(arguments)); + return new RunFunction<>(getPredicates(arguments), delay, fun); } } } 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 5634d5f19..a773bdcb1 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 @@ -21,7 +21,7 @@ public class SetCooldownFunction extends AbstractConditiona private final String id; private final boolean add; - public SetCooldownFunction(TextProvider time, String id, boolean add, PlayerSelector selector, List> predicates) { + public SetCooldownFunction(List> predicates, String id, boolean add, PlayerSelector selector, TextProvider time) { super(predicates); this.time = time; this.add = add; @@ -66,7 +66,7 @@ public class SetCooldownFunction extends AbstractConditiona String id = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("id"), "warning.config.function.set_cooldown.missing_id"); String time = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("time"), "warning.config.function.set_cooldown.missing_time"); boolean add = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("add", false), "add"); - return new SetCooldownFunction<>(TextProviders.fromString(time), id, add, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), getPredicates(arguments)); + return new SetCooldownFunction<>(getPredicates(arguments), id, add, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), TextProviders.fromString(time)); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetCountFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetCountFunction.java index aa005230c..b6cd3f22d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetCountFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SetCountFunction.java @@ -17,7 +17,7 @@ public class SetCountFunction extends AbstractConditionalFu private final NumberProvider count; private final boolean add; - public SetCountFunction(NumberProvider count, boolean add, List> predicates) { + public SetCountFunction(List> predicates, boolean add, NumberProvider count) { super(predicates); this.count = count; this.add = add; @@ -51,7 +51,7 @@ public class SetCountFunction extends AbstractConditionalFu public Function create(Map arguments) { Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("count"), "warning.config.function.set_count.missing_count"); boolean add = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("add", false), "add"); - return new SetCountFunction<>(NumberProviders.fromObject(value), add, getPredicates(arguments)); + return new SetCountFunction<>(getPredicates(arguments), add, NumberProviders.fromObject(value)); } } } 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 544a2a147..1c77bab4e 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 @@ -19,7 +19,7 @@ public class SetFoodFunction extends AbstractConditionalFun private final NumberProvider count; private final boolean add; - public SetFoodFunction(NumberProvider count, boolean add, PlayerSelector selector, List> predicates) { + public SetFoodFunction(List> predicates, boolean add, PlayerSelector selector, NumberProvider count) { super(predicates); this.count = count; this.add = add; @@ -54,7 +54,7 @@ public class SetFoodFunction extends AbstractConditionalFun public Function create(Map arguments) { Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("food"), "warning.config.function.set_food.missing_food"); boolean add = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("add", false), "add"); - return new SetFoodFunction<>(NumberProviders.fromObject(value), add, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), getPredicates(arguments)); + return new SetFoodFunction<>(getPredicates(arguments), add, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), NumberProviders.fromObject(value)); } } } 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 9c6d24e98..56fbef9d2 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 @@ -19,7 +19,7 @@ public class SetSaturationFunction extends AbstractConditio private final NumberProvider count; private final boolean add; - public SetSaturationFunction(NumberProvider count, boolean add, PlayerSelector selector, List> predicates) { + public SetSaturationFunction(List> predicates, boolean add, PlayerSelector selector, NumberProvider count) { super(predicates); this.count = count; this.add = add; @@ -54,7 +54,7 @@ public class SetSaturationFunction extends AbstractConditio public Function create(Map arguments) { Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("saturation"), "warning.config.function.set_saturation.missing_saturation"); boolean add = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("add", false), "add"); - return new SetSaturationFunction<>(NumberProviders.fromObject(value), add, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), getPredicates(arguments)); + return new SetSaturationFunction<>(getPredicates(arguments), add, PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), NumberProviders.fromObject(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 e297cf2f8..e7e91d5e2 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 @@ -26,15 +26,7 @@ public class SpawnFurnitureFunction extends AbstractConditi private final boolean playSound; public SpawnFurnitureFunction( - Key furnitureId, - NumberProvider x, - NumberProvider y, - NumberProvider z, - NumberProvider pitch, - NumberProvider yaw, - String variant, - boolean playSound, - List> predicates + List> predicates, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider pitch, NumberProvider yaw, String variant, boolean playSound, Key furnitureId ) { super(predicates); this.furnitureId = furnitureId; @@ -86,7 +78,7 @@ public class SpawnFurnitureFunction extends AbstractConditi NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", "")); String variant = ResourceConfigUtils.getAsStringOrNull(ResourceConfigUtils.get(arguments, "variant", "anchor-type")); boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound"); - return new SpawnFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, variant, playSound, getPredicates(arguments)); + return new SpawnFurnitureFunction<>(getPredicates(arguments), x, y, z, pitch, yaw, variant, playSound, furnitureId); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SwingHandFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SwingHandFunction.java index 935fa210e..3f79089c6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SwingHandFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/SwingHandFunction.java @@ -15,7 +15,7 @@ import java.util.Optional; public class SwingHandFunction extends AbstractConditionalFunction { private final Optional hand; - public SwingHandFunction(Optional hand, List> predicates) { + public SwingHandFunction(List> predicates, Optional hand) { super(predicates); this.hand = hand; } @@ -46,7 +46,7 @@ public class SwingHandFunction extends AbstractConditionalF @Override public Function create(Map arguments) { Optional optionalHand = Optional.ofNullable(arguments.get("hand")).map(it -> InteractionHand.valueOf(it.toString().toUpperCase(Locale.ENGLISH))); - return new SwingHandFunction<>(optionalHand, getPredicates(arguments)); + return new SwingHandFunction<>(getPredicates(arguments), optionalHand); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TransformBlockFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TransformBlockFunction.java index b27f438c4..2cd56d21e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TransformBlockFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/TransformBlockFunction.java @@ -30,7 +30,7 @@ public class TransformBlockFunction extends AbstractConditi private final NumberProvider z; private final NumberProvider updateFlags; - public TransformBlockFunction(LazyReference lazyBlockState, CompoundTag properties, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider updateFlags, List> predicates) { + public TransformBlockFunction(List> predicates, CompoundTag properties, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider updateFlags, LazyReference lazyBlockState) { super(predicates); this.properties = properties; this.x = x; @@ -84,13 +84,8 @@ public class TransformBlockFunction extends AbstractConditi } } return new TransformBlockFunction<>( - LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createBlockState(block)), - properties, - NumberProviders.fromObject(arguments.getOrDefault("x", "")), - NumberProviders.fromObject(arguments.getOrDefault("y", "")), - NumberProviders.fromObject(arguments.getOrDefault("z", "")), - Optional.ofNullable(arguments.get("update-flags")).map(NumberProviders::fromObject).orElse(NumberProviders.direct(UpdateOption.UPDATE_ALL.flags())), - getPredicates(arguments)); + getPredicates(arguments), properties, NumberProviders.fromObject(arguments.getOrDefault("x", "")), NumberProviders.fromObject(arguments.getOrDefault("y", "")), NumberProviders.fromObject(arguments.getOrDefault("z", "")), Optional.ofNullable(arguments.get("update-flags")).map(NumberProviders::fromObject).orElse(NumberProviders.direct(UpdateOption.UPDATE_ALL.flags())), LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createBlockState(block)) + ); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/UpdateBlockPropertyFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/UpdateBlockPropertyFunction.java index b59202282..dd079a78c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/UpdateBlockPropertyFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/UpdateBlockPropertyFunction.java @@ -26,7 +26,7 @@ public class UpdateBlockPropertyFunction extends AbstractCo private final NumberProvider z; private final NumberProvider updateFlags; - public UpdateBlockPropertyFunction(CompoundTag properties, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider updateFlags, List> predicates) { + public UpdateBlockPropertyFunction(List> predicates, NumberProvider x, NumberProvider y, NumberProvider z, NumberProvider updateFlags, CompoundTag properties) { super(predicates); this.properties = properties; this.x = x; @@ -67,12 +67,8 @@ public class UpdateBlockPropertyFunction extends AbstractCo for (Map.Entry entry : state.entrySet()) { properties.putString(entry.getKey(), String.valueOf(entry.getValue())); } - return new UpdateBlockPropertyFunction<>(properties, - NumberProviders.fromObject(arguments.getOrDefault("x", "")), - NumberProviders.fromObject(arguments.getOrDefault("y", "")), - NumberProviders.fromObject(arguments.getOrDefault("z", "")), - Optional.ofNullable(arguments.get("update-flags")).map(NumberProviders::fromObject).orElse(NumberProviders.direct(UpdateOption.UPDATE_ALL.flags())), - getPredicates(arguments)); + return new UpdateBlockPropertyFunction<>(getPredicates(arguments), NumberProviders.fromObject(arguments.getOrDefault("x", "")), NumberProviders.fromObject(arguments.getOrDefault("y", "")), NumberProviders.fromObject(arguments.getOrDefault("z", "")), Optional.ofNullable(arguments.get("update-flags")).map(NumberProviders::fromObject).orElse(NumberProviders.direct(UpdateOption.UPDATE_ALL.flags())), properties + ); } } } diff --git a/gradle.properties b/gradle.properties index 255046cbe..48efe6d60 100644 --- a/gradle.properties +++ b/gradle.properties @@ -59,9 +59,9 @@ concurrent_util_version=0.0.3 bucket4j_version=8.15.0 # Proxy settings -#systemProp.socks.proxyHost=127.0.0.1 -#systemProp.socks.proxyPort=7890 -#systemProp.http.proxyHost=127.0.0.1 -#systemProp.http.proxyPort=7890 -#systemProp.https.proxyHost=127.0.0.1 -#systemProp.https.proxyPort=7890 \ No newline at end of file +systemProp.socks.proxyHost=127.0.0.1 +systemProp.socks.proxyPort=7890 +systemProp.http.proxyHost=127.0.0.1 +systemProp.http.proxyPort=7890 +systemProp.https.proxyHost=127.0.0.1 +systemProp.https.proxyPort=7890 \ No newline at end of file From 44ec90275399f083ebc2356c2285f745a3fc9186 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Fri, 5 Dec 2025 03:03:14 +0800 Subject: [PATCH 101/135] =?UTF-8?q?=E8=AF=AD=E8=A8=80=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/translations/en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 111302e69..bb9da733c 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -544,8 +544,8 @@ warning.config.function.transform_block.missing_block: "Issue found in f warning.config.function.cycle_block_property.missing_property: "Issue found in file - The config '' is missing the required 'property' argument for 'cycle_block_property' function." warning.config.function.set_exp.missing_count: "Issue found in file - The config '' is missing the required 'count' argument for 'set_exp' function." warning.config.function.set_level.missing_count: "Issue found in file - The config '' is missing the required 'count' argument for 'set_level' function." -warning.config.function.clear_item.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'clear_item' function." warning.config.function.play_totem_animation.missing_item: "Issue found in file - The config '' is missing the required 'item' argument for 'play_totem_animation' function." +warning.config.function.clear_item.missing_id: "Issue found in file - The config '' is missing the required 'id' argument for 'clear_item' function." 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 ''." From 37eaf97dc47d00ee1026e43c82c88cb4de884e56 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 04:09:45 +0800 Subject: [PATCH 102/135] =?UTF-8?q?=E6=97=8B=E8=BD=AC=E5=AE=B6=E5=85=B7?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../papi/CheckItemExpansion.java | 4 -- .../entity/furniture/BukkitFurniture.java | 14 +++++- .../furniture/FurnitureEventListener.java | 6 --- .../item/behavior/FurnitureItemBehavior.java | 34 ++++++++++++-- .../item/listener/DebugStickListener.java | 3 -- .../bukkit/plugin/BukkitPlatform.java | 6 --- .../craftengine/bukkit/util/EntityUtils.java | 2 +- .../default/configuration/furniture/bench.yml | 2 + .../configuration/furniture/flower_basket.yml | 5 +++ .../configuration/furniture/wooden_chair.yml | 30 +++++-------- .../configuration/templates/events.yml | 45 ++++++++++++++++++- .../entity/furniture/FurnitureConfigImpl.java | 1 - .../craftengine/core/loot/LootConditions.java | 3 ++ .../core/pack/CachedConfigSection.java | 9 ++-- .../plugin/config/IdSectionConfigParser.java | 12 ++++- .../context/condition/CommonConditions.java | 1 + .../condition/InventoryHasItemCondition.java | 3 -- .../MatchFurnitureVariantCondition.java | 39 ++++++++++++++++ .../plugin/context/event/EventConditions.java | 1 + .../function/RotateFurnitureFunction.java | 12 +---- .../plugin/entityculling/EntityCulling.java | 1 - .../core/util/ResourceConfigUtils.java | 5 ++- .../core/util/snbt/SnbtOperations.java | 1 - .../LocalizedDynamicCommandExceptionType.java | 1 - gradle.properties | 2 +- 25 files changed, 171 insertions(+), 71 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchFurnitureVariantCondition.java diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/CheckItemExpansion.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/CheckItemExpansion.java index f86fc0dbd..2a163b32d 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/CheckItemExpansion.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/CheckItemExpansion.java @@ -1,10 +1,8 @@ package net.momirealms.craftengine.bukkit.compatibility.papi; import me.clip.placeholderapi.expansion.PlaceholderExpansion; -import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; -import net.momirealms.craftengine.bukkit.util.ItemStackUtils; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.CraftEngine; @@ -13,8 +11,6 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.function.Predicate; - public class CheckItemExpansion extends PlaceholderExpansion { private final CraftEngine plugin; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index c8030e0d8..08bb220f8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -54,6 +54,8 @@ public class BukkitFurniture extends Furniture { FurnitureVariant variant = this.config.getVariant(variantName); if (variant == null) return false; if (this.currentVariant == variant) return false; + // 删除椅子 + super.destroySeats(); BukkitFurnitureManager.instance().invalidateFurniture(this); super.clearColliders(); super.setVariantInternal(variant); @@ -74,10 +76,20 @@ public class BukkitFurniture extends Furniture { hitBoxConfig.prepareBoundingBox(position, aabbs::add, false); } if (!aabbs.isEmpty()) { - if (!FastNMS.INSTANCE.checkEntityCollision(position.world.serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { + if (!FastNMS.INSTANCE.checkEntityCollision(position.world.serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList(), + o -> { + for (Collider collider : super.colliders) { + if (o == collider.handle()) { + return false; + } + } + return true; + })) { return CompletableFuture.completedFuture(false); } } + // 删除椅子 + super.destroySeats(); // 准备传送 CompletableFuture future = new CompletableFuture<>(); BukkitFurnitureManager.instance().invalidateFurniture(this); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java index c71a480de..74d3784b1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java @@ -5,19 +5,15 @@ import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent; -import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; -import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.RandomUtils; import net.momirealms.craftengine.core.world.CEWorld; -import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.chunk.CEChunk; import org.bukkit.entity.Entity; import org.bukkit.entity.ItemDisplay; @@ -29,12 +25,10 @@ import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.EntitiesLoadEvent; import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldUnloadEvent; -import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import java.util.ArrayList; import java.util.List; -import java.util.Map; public class FurnitureEventListener implements Listener { private final BukkitFurnitureManager manager; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index 28493cd5f..30863c737 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -35,22 +35,39 @@ import org.bukkit.World; import java.nio.file.Path; import java.util.*; +import java.util.function.Predicate; public class FurnitureItemBehavior extends ItemBehavior { public static final Factory FACTORY = new Factory(); private static final Set ALLOWED_ANCHOR_TYPES = Set.of("wall", "ceiling", "ground"); private final Key id; private final Map rules; + private final boolean ignorePlacer; + private final boolean ignoreEntities; - public FurnitureItemBehavior(Key id, Map rules) { + public FurnitureItemBehavior(Key id, Map rules, boolean ignorePlacer, boolean ignoreEntities) { this.id = id; this.rules = rules; + this.ignorePlacer = ignorePlacer; + this.ignoreEntities = ignoreEntities; } public Key furnitureId() { return this.id; } + public Map rules() { + return this.rules; + } + + public boolean ignorePlacer() { + return this.ignorePlacer; + } + + public boolean ignoreEntities() { + return this.ignoreEntities; + } + @Override public InteractionResult useOnBlock(UseOnContext context) { return this.place(context); @@ -118,7 +135,15 @@ public class FurnitureItemBehavior extends ItemBehavior { } // 检查方块、实体阻挡 if (!aabbs.isEmpty()) { - if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { + Predicate entityPredicate; + if (this.ignoreEntities) { + entityPredicate = (o) -> false; + } else if (this.ignorePlacer) { + entityPredicate = player != null ? (o) -> o != player.serverPlayer() : (o) -> true; + } else { + entityPredicate = (o) -> true; + } + if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList(), entityPredicate)) { if (player != null && player.enableFurnitureDebug() && VersionHelper.isPaper()) { player.playSound(Key.of("minecraft:entity.villager.no")); Key flame = Key.of("flame"); @@ -241,7 +266,10 @@ public class FurnitureItemBehavior extends ItemBehavior { } } } - return new FurnitureItemBehavior(furnitureId, rules); + return new FurnitureItemBehavior(furnitureId, rules, + ResourceConfigUtils.getAsBoolean(arguments.get("ignore-placer"), "ignore-placer"), + ResourceConfigUtils.getAsBoolean(arguments.get("ignore-entities"), "ignore-entities") + ); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java index 88d7d106c..0a21878f0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/listener/DebugStickListener.java @@ -3,14 +3,12 @@ package net.momirealms.craftengine.bukkit.item.listener; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; -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.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.ComponentUtils; -import net.momirealms.craftengine.bukkit.util.ItemStackUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; @@ -20,7 +18,6 @@ import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.util.MiscUtils; -import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; 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 21bd8791a..08c658e0b 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 @@ -1,24 +1,18 @@ 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; - public class BukkitPlatform implements Platform { private final BukkitCraftEngine plugin; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java index 81396c16e..8ff14ab33 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java @@ -73,7 +73,7 @@ public final class EntityUtils { if (!canDismount) { continue; } - if (!FastNMS.INSTANCE.checkEntityCollision(serverLevel, List.of(newAABB))) { + if (!FastNMS.INSTANCE.checkEntityCollision(serverLevel, List.of(newAABB), o -> true)) { continue; } if (VersionHelper.isFolia()) { 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 index 66e36f644..595ed0f67 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture/bench.yml @@ -11,6 +11,8 @@ items: rotation: four alignment: center furniture: + events: + - template: default:rotatable_furniture_4 settings: item: default:bench sounds: 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 index 4fdd08219..9f4d079ba 100644 --- 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 @@ -31,6 +31,11 @@ items: item-name: furniture: default:flower_basket: + events: + - template: default:rotatable_furniture_8 + arguments: + blacklist_variants: + - wall settings: item: default:flower_basket sounds: 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 index a399a75d3..8fce322a6 100644 --- 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 @@ -11,6 +11,8 @@ items: rotation: any alignment: any furniture: + events: + - template: default:rotatable_furniture_8 settings: item: default:wooden_chair sounds: @@ -27,25 +29,15 @@ items: billboard: FIXED translation: 0,0.5,0 hitboxes: - $$>=1.20.5: - - position: 0,0,0 - type: custom - entity-type: slime - invisible: true - can-use-item-on: true - blocks-building: true - seats: - - 0,0,-0.1 0 - $$fallback: - - position: 0,0,0 - type: interaction - blocks-building: true - invisible: true - width: 0.7 - height: 1.2 - interactive: true - seats: - - 0,0,-0.1 0 + - position: 0,0,0 + type: interaction + blocks-building: true + invisible: true + width: 0.7 + height: 1.2 + interactive: true + seats: + - 0,0,-0.1 0 loot: template: default:loot_table/furniture arguments: diff --git a/common-files/src/main/resources/resources/default/configuration/templates/events.yml b/common-files/src/main/resources/resources/default/configuration/templates/events.yml index d0727291f..fd93462b9 100644 --- a/common-files/src/main/resources/resources/default/configuration/templates/events.yml +++ b/common-files/src/main/resources/resources/default/configuration/templates/events.yml @@ -16,7 +16,7 @@ templates: east: south south: west west: north - default:rotatable_furniture: + default:rotatable_furniture_4: on: right_click conditions: - type: expression @@ -28,4 +28,45 @@ templates: - type: swing_hand - type: play_sound sound: ${rotate_sound:-'minecraft:block.bamboo.place'} - on-failure: [] \ No newline at end of file + on-failure: + - type: rotate_furniture + degree: 180 + on-success: + - type: swing_hand + - type: play_sound + sound: ${rotate_sound:-'minecraft:block.bamboo.place'} + on-failure: + - type: rotate_furniture + degree: 270 + on-success: + - type: swing_hand + - type: play_sound + sound: ${rotate_sound:-'minecraft:block.bamboo.place'} + default:rotatable_furniture_8: + on: right_click + conditions: + - type: expression + expression: + - type: "!match_furniture_variant" + variants: ${blacklist_variants:-null} + functions: + - type: rotate_furniture + degree: 45 + on-success: + - type: swing_hand + - type: play_sound + sound: ${rotate_sound:-'minecraft:block.bamboo.place'} + on-failure: + - type: rotate_furniture + degree: 90 + on-success: + - type: swing_hand + - type: play_sound + sound: ${rotate_sound:-'minecraft:block.bamboo.place'} + on-failure: + - type: rotate_furniture + degree: 135 + on-success: + - type: swing_hand + - type: play_sound + sound: ${rotate_sound:-'minecraft:block.bamboo.place'} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java index 2833306c8..facc01f27 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.core.entity.furniture; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedMap; import net.momirealms.craftengine.core.entity.furniture.behavior.EmptyFurnitureBehavior; import net.momirealms.craftengine.core.entity.furniture.behavior.FurnitureBehavior; diff --git a/core/src/main/java/net/momirealms/craftengine/core/loot/LootConditions.java b/core/src/main/java/net/momirealms/craftengine/core/loot/LootConditions.java index eea0f0f4b..e7badf2ff 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/loot/LootConditions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/loot/LootConditions.java @@ -39,6 +39,9 @@ public class LootConditions { register(CommonConditions.EXPRESSION, new ExpressionCondition.FactoryImpl<>()); register(CommonConditions.IS_NULL, new IsNullCondition.FactoryImpl<>()); register(CommonConditions.HAND, new HandCondition.FactoryImpl<>()); + register(CommonConditions.ON_COOLDOWN, new OnCooldownCondition.FactoryImpl<>()); + register(CommonConditions.INVENTORY_HAS_ITEM, new InventoryHasItemCondition.FactoryImpl<>()); + register(CommonConditions.MATCH_FURNITURE_VARIANT, new MatchFurnitureVariantCondition.FactoryImpl<>()); } public static void register(Key key, ConditionFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigSection.java b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigSection.java index e8016ce2a..66165e422 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigSection.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/CachedConfigSection.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.pack; import java.nio.file.Path; import java.util.Map; +import java.util.Objects; public class CachedConfigSection { private final Pack pack; @@ -10,10 +11,10 @@ public class CachedConfigSection { private final Map config; public CachedConfigSection(String prefix, Map config, Path filePath, Pack pack) { - this.config = config; - this.filePath = filePath; - this.pack = pack; - this.prefix = prefix; + this.config = Objects.requireNonNull(config); + this.filePath = Objects.requireNonNull(filePath); + this.pack = Objects.requireNonNull(pack); + this.prefix = Objects.requireNonNull(prefix); } public Map config() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/IdSectionConfigParser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/IdSectionConfigParser.java index a15b91c71..1acba1e29 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/IdSectionConfigParser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/IdSectionConfigParser.java @@ -29,13 +29,21 @@ public abstract class IdSectionConfigParser extends AbstractConfigParser { continue; } Map config = castToMap(section, false); + String node = cached.prefix() + "." + key; if ((boolean) config.getOrDefault("debug", false)) { - CraftEngine.instance().logger().info(GsonHelper.get().toJson(CraftEngine.instance().templateManager().applyTemplates(id, config))); + if (!ResourceConfigUtils.runCatching( + cached.filePath(), + node, + () -> CraftEngine.instance().logger().info(GsonHelper.get().toJson(CraftEngine.instance().templateManager().applyTemplates(id, config))), + () -> GsonHelper.get().toJson(section) + )) { + // 发生异常 + continue; + } } if (!(boolean) config.getOrDefault("enable", true)) { continue; } - String node = cached.prefix() + "." + key; ResourceConfigUtils.runCatching( cached.filePath(), node, 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 16c0c5d22..256ff4e3b 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 @@ -15,6 +15,7 @@ public final class CommonConditions { 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 MATCH_FURNITURE_VARIANT = Key.from("craftengine:match_furniture_variant"); public static final Key TABLE_BONUS = Key.from("craftengine:table_bonus"); public static final Key SURVIVES_EXPLOSION = Key.from("craftengine:survives_explosion"); public static final Key RANDOM = Key.from("craftengine:random"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/InventoryHasItemCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/InventoryHasItemCondition.java index 4b849ab41..432074e5d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/InventoryHasItemCondition.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/InventoryHasItemCondition.java @@ -1,16 +1,13 @@ package net.momirealms.craftengine.core.plugin.context.condition; import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.item.Item; 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.ItemUtils; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import org.jetbrains.annotations.Nullable; import java.util.Map; import java.util.Optional; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchFurnitureVariantCondition.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchFurnitureVariantCondition.java new file mode 100644 index 000000000..31ded2c60 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/condition/MatchFurnitureVariantCondition.java @@ -0,0 +1,39 @@ +package net.momirealms.craftengine.core.plugin.context.condition; + +import net.momirealms.craftengine.core.entity.furniture.Furniture; +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.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.*; + +public class MatchFurnitureVariantCondition implements Condition { + private final Set variants; + + public MatchFurnitureVariantCondition(Collection variants) { + this.variants = new HashSet<>(variants); + } + + @Override + public Key type() { + return CommonConditions.MATCH_FURNITURE_VARIANT; + } + + @Override + public boolean test(CTX ctx) { + Optional furniture = ctx.getOptionalParameter(DirectContextParameters.FURNITURE); + return furniture.filter(value -> this.variants.contains(value.getCurrentVariant().name())).isPresent(); + } + + public static class FactoryImpl implements ConditionFactory { + + @Override + public Condition create(Map arguments) { + List variants = MiscUtils.getAsStringList(ResourceConfigUtils.get(arguments, "variant", "variants")); + return new MatchFurnitureVariantCondition<>(variants); + } + } +} 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 6d2aa4996..6c9466d0a 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 @@ -41,6 +41,7 @@ public class EventConditions { register(CommonConditions.HAND, new HandCondition.FactoryImpl<>()); register(CommonConditions.ON_COOLDOWN, new OnCooldownCondition.FactoryImpl<>()); register(CommonConditions.INVENTORY_HAS_ITEM, new InventoryHasItemCondition.FactoryImpl<>()); + register(CommonConditions.MATCH_FURNITURE_VARIANT, new MatchFurnitureVariantCondition.FactoryImpl<>()); } public static void register(Key key, ConditionFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RotateFurnitureFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RotateFurnitureFunction.java index 1256826a8..eb16cedd0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RotateFurnitureFunction.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/RotateFurnitureFunction.java @@ -1,28 +1,18 @@ package net.momirealms.craftengine.core.plugin.context.function; import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.player.InteractionHand; -import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.loot.LootTable; 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.event.EventFunctions; 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.sound.SoundData; -import net.momirealms.craftengine.core.sound.SoundSource; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; -import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Optional; public class RotateFurnitureFunction extends AbstractConditionalFunction { private final NumberProvider degree; @@ -64,7 +54,7 @@ public class RotateFurnitureFunction extends AbstractCondit } public NumberProvider degree() { - return degree; + return this.degree; } public static class FactoryImpl extends AbstractFactory { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java index 183558688..79dac8655 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java @@ -2,7 +2,6 @@ package net.momirealms.craftengine.core.plugin.entityculling; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.MutableVec3d; 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 e3be821e6..f445270dc 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 @@ -324,17 +324,20 @@ public final class ResourceConfigUtils { } } - public static void runCatching(Path configPath, String node, Runnable runnable, Supplier config) { + public static boolean runCatching(Path configPath, String node, Runnable runnable, Supplier config) { try { runnable.run(); + return true; } catch (LocalizedException e) { printWarningRecursively(e, configPath, node); + return false; } catch (Exception e) { String message = "Unexpected error loading file " + configPath + " - '" + node + "'."; if (config != null) { message += " Configuration details: " + config.get(); } CraftEngine.instance().logger().warn(message, e); + return false; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java index 53a6fa7f0..c715eba12 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java @@ -2,7 +2,6 @@ package net.momirealms.craftengine.core.util.snbt; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.serialization.DynamicOps; import net.momirealms.craftengine.core.util.snbt.parse.*; import net.momirealms.sparrow.nbt.util.UUIDUtil; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java index ee80a3bee..99dde080a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java @@ -4,7 +4,6 @@ import com.mojang.brigadier.ImmutableStringReader; import com.mojang.brigadier.Message; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; -import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import java.util.function.Function; diff --git a/gradle.properties b/gradle.properties index 48efe6d60..2ba672eaf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -48,7 +48,7 @@ byte_buddy_version=1.18.1 ahocorasick_version=0.6.3 snake_yaml_version=2.5 anti_grief_version=1.0.5 -nms_helper_version=1.0.141 +nms_helper_version=1.0.142 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.38.7 From eea83e16fb865c1bfe631e43b6d13582254e1432 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 04:24:14 +0800 Subject: [PATCH 103/135] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=81=9A=E5=90=88?= =?UTF-8?q?=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../behavior/DropExperienceBlockBehavior.java | 19 +++++++------------ .../plugin/network/BukkitNetworkManager.java | 2 -- .../core/plugin/gui/category/Category.java | 11 +++++------ .../gui/category/ItemBrowserManagerImpl.java | 10 +++------- 4 files changed, 15 insertions(+), 27 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java index 78de11707..d6b6262fb 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DropExperienceBlockBehavior.java @@ -15,11 +15,11 @@ import net.momirealms.craftengine.core.loot.LootContext; 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.condition.AllOfCondition; import net.momirealms.craftengine.core.plugin.context.event.EventConditions; 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.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; @@ -30,16 +30,17 @@ import org.bukkit.inventory.ItemStack; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; +import java.util.function.Predicate; public class DropExperienceBlockBehavior extends BukkitBlockBehavior { public static final Factory FACTORY = new Factory(); private final NumberProvider amount; - private final Condition conditions; + private final Predicate condition; - public DropExperienceBlockBehavior(CustomBlock customBlock, NumberProvider amount, Condition conditions) { + public DropExperienceBlockBehavior(CustomBlock customBlock, NumberProvider amount, Predicate condition) { super(customBlock); this.amount = amount; - this.conditions = conditions; + this.condition = condition; } @Override @@ -76,7 +77,7 @@ public class DropExperienceBlockBehavior extends BukkitBlockBehavior { .withParameter(DirectContextParameters.ITEM_IN_HAND, item) .build(); LootContext context = new LootContext(world, null, 1.0f, holder); - if (this.conditions != null && !this.conditions.test(context)) { + if (!this.condition.test(context)) { return; } int finalAmount = this.amount.getInt(context); @@ -91,14 +92,8 @@ public class DropExperienceBlockBehavior extends BukkitBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { NumberProvider amount = NumberProviders.fromObject(ResourceConfigUtils.get(arguments, "amount", "count")); - Condition conditions = null; List> conditionList = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(arguments, "conditions", "condition"), EventConditions::fromMap); - if (conditionList.size() == 1) { - conditions = conditionList.getFirst(); - } else if (!conditionList.isEmpty()) { - conditions = new AllOfCondition<>(conditionList); - } - return new DropExperienceBlockBehavior(block, amount, conditions); + return new DropExperienceBlockBehavior(block, amount, MiscUtils.allOf(conditionList)); } } } 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 f2631ddea..24a96b9f9 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 @@ -14,7 +14,6 @@ import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; -import io.papermc.paper.event.player.AsyncPlayerSpawnLocationEvent; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -130,7 +129,6 @@ import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.BiFunction; import java.util.function.Predicate; 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 639ccbbfc..903e9264e 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 @@ -1,13 +1,12 @@ package net.momirealms.craftengine.core.plugin.gui.category; -import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.Context; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; public class Category implements Comparable { private final Key id; @@ -17,9 +16,9 @@ public class Category implements Comparable { private final List members; private final int priority; private final boolean hidden; - private final Condition condition; + private final Predicate condition; - public Category(Key id, String displayName, List displayLore, Key icon, List members, int priority, boolean hidden, Condition condition) { + public Category(Key id, String displayName, List displayLore, Key icon, List members, int priority, boolean hidden, Predicate condition) { this.id = id; this.displayName = displayName; this.members = new ArrayList<>(members); @@ -50,8 +49,8 @@ public class Category implements Comparable { return hidden; } - @Nullable - public Condition condition() { + @NotNull + public Predicate condition() { return condition; } 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 c4b664d83..43f881ac4 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 @@ -17,7 +17,6 @@ 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.PlayerOptionalContext; -import net.momirealms.craftengine.core.plugin.context.condition.AllOfCondition; import net.momirealms.craftengine.core.plugin.context.event.EventConditions; import net.momirealms.craftengine.core.plugin.gui.*; import net.momirealms.craftengine.core.plugin.gui.Ingredient; @@ -127,8 +126,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { List lore = MiscUtils.getAsStringList(section.getOrDefault("lore", List.of())); boolean hidden = ResourceConfigUtils.getAsBoolean(section.getOrDefault("hidden", false), "hidden"); List> conditionList = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(section, "conditions", "condition"), EventConditions::fromMap); - Condition conditions = conditionList.isEmpty() ? null : conditionList.size() == 1 ? conditionList.getFirst() : new AllOfCondition<>(conditionList); - Category category = new Category(id, name, lore, icon, new ArrayList<>(members), priority, hidden, conditions); + Category category = new Category(id, name, lore, icon, new ArrayList<>(members), priority, hidden, MiscUtils.allOf(conditionList)); if (ItemBrowserManagerImpl.this.byId.containsKey(id)) { ItemBrowserManagerImpl.this.byId.get(id).merge(category); } else { @@ -169,8 +167,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { ); List iconList = this.categoryOnMainPage.stream().map(it -> { - Condition condition = it.condition(); - if (condition != null && !condition.test(PlayerOptionalContext.of(player))) { + if (!it.condition().test(PlayerOptionalContext.of(player))) { return null; } Item item = this.plugin.itemManager().createWrappedItem(it.icon(), player); @@ -262,8 +259,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { 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 { - Condition condition = subCategory.condition(); - if (condition != null && !condition.test(PlayerOptionalContext.of(player))) { + if (!subCategory.condition().test(PlayerOptionalContext.of(player))) { return null; } item = this.plugin.itemManager().createWrappedItem(subCategory.icon(), player); From ab5a92b18ae332de72ff8e25b13095ee357b6d9a Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 04:39:00 +0800 Subject: [PATCH 104/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=85=A8=E9=83=A8?= =?UTF-8?q?=E7=89=A9=E5=93=81=E5=88=86=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../default/configuration/blocks/palm_tree.yml | 9 ++++++--- .../default/configuration/furniture/flower_basket.yml | 6 +++--- .../resources/resources/internal/configuration/gui.yml | 9 +++++++++ .../resources/internal/configuration/translations.yml | 2 ++ .../craftengine/core/item/AbstractItemManager.java | 2 +- .../plugin/gui/category/ItemBrowserManagerImpl.java | 10 +++++++++- 6 files changed, 30 insertions(+), 8 deletions(-) diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/palm_tree.yml b/common-files/src/main/resources/resources/default/configuration/blocks/palm_tree.yml index 7fdbbe403..656002f56 100644 --- a/common-files/src/main/resources/resources/default/configuration/blocks/palm_tree.yml +++ b/common-files/src/main/resources/resources/default/configuration/blocks/palm_tree.yml @@ -509,11 +509,10 @@ items: textures: texture: minecraft:block/custom/palm_planks -items#pfence: +items#fence: default:palm_fence: material: nether_brick - data: - item-name: + data::item-name: model: type: minecraft:model path: minecraft:item/custom/palm_fence_inventory @@ -526,6 +525,7 @@ items#pfence: block: default:palm_fence default:palm_fence_post: material: nether_brick + data::item-name: model: type: minecraft:model path: minecraft:block/custom/palm_fence_post @@ -535,6 +535,7 @@ items#pfence: texture: minecraft:block/custom/palm_planks default:palm_fence_side: material: nether_brick + data::item-name: model: type: minecraft:model path: minecraft:block/custom/palm_fence_side @@ -593,6 +594,7 @@ items#button: block: default:palm_button default:palm_button_pressed: material: nether_brick + data::item-name: model: type: minecraft:model path: minecraft:block/custom/palm_button_pressed @@ -602,6 +604,7 @@ items#button: texture: minecraft:block/custom/palm_planks default:palm_button_not_pressed: material: nether_brick + data::item-name: model: type: minecraft:model path: minecraft:block/custom/palm_button_not_pressed 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 index 9f4d079ba..010629735 100644 --- 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 @@ -20,15 +20,15 @@ items: default:flower_basket_ground: material: nether_brick model: minecraft:item/custom/flower_basket_ground - item-name: + data::item-name: default:flower_basket_wall: material: nether_brick model: minecraft:item/custom/flower_basket_wall - item-name: + data::item-name: default:flower_basket_ceiling: material: nether_brick model: minecraft:item/custom/flower_basket_ceiling - item-name: + data::item-name: furniture: default:flower_basket: events: diff --git a/common-files/src/main/resources/resources/internal/configuration/gui.yml b/common-files/src/main/resources/resources/internal/configuration/gui.yml index 87efc97c5..7835a1919 100644 --- a/common-files/src/main/resources/resources/internal/configuration/gui.yml +++ b/common-files/src/main/resources/resources/internal/configuration/gui.yml @@ -1,3 +1,12 @@ +categories: + craftengine:all: + priority: -1000 + name: + icon: minecraft:command_block + conditions: + - type: permission + permission: craftengine.admin + all-items: true images: internal:item_browser: height: 140 diff --git a/common-files/src/main/resources/resources/internal/configuration/translations.yml b/common-files/src/main/resources/resources/internal/configuration/translations.yml index af4c34ee5..6c1c57b0d 100644 --- a/common-files/src/main/resources/resources/internal/configuration/translations.yml +++ b/common-files/src/main/resources/resources/internal/configuration/translations.yml @@ -12,6 +12,7 @@ translations: internal.cooking_info: Recipe Information internal.cooking_info.0: 'Time: ticks' internal.cooking_info.1: 'Experience: ' + category.all.name: 'All Items' zh_cn: internal.next_page: 下一页 internal.previous_page: 上一页 @@ -25,6 +26,7 @@ translations: internal.cooking_info: 配方信息 internal.cooking_info.0: '时间: 刻' internal.cooking_info.1: '经验: ' + category.all.name: '全部物品' de: internal.next_page: Nächste Seite internal.previous_page: Vorherige Seite 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 3104b4e53..1af81c706 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 @@ -51,7 +51,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl private final ItemParser itemParser; private final EquipmentParser equipmentParser; protected final Map> externalItemSources = new HashMap<>(); - protected final Map> customItemsById = new HashMap<>(); + protected final Map> customItemsById = new LinkedHashMap<>(); protected final Map> customItemsByPath = new HashMap<>(); protected final Map> customItemTags = new HashMap<>(); protected final Map modernItemModels1_21_4 = new HashMap<>(); 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 43f881ac4..a8f463235 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 @@ -7,6 +7,7 @@ import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.ItemKeys; +import net.momirealms.craftengine.core.item.ItemManager; import net.momirealms.craftengine.core.item.recipe.*; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; @@ -24,6 +25,7 @@ import net.momirealms.craftengine.core.util.*; import java.nio.file.Path; import java.util.*; +import java.util.stream.Collectors; @SuppressWarnings("DuplicatedCode") public class ItemBrowserManagerImpl implements ItemBrowserManager { @@ -120,7 +122,13 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { @Override public void parseSection(Pack pack, Path path, String node, Key id, Map section) { String name = section.getOrDefault("name", id).toString(); - List members = MiscUtils.getAsStringList(section.getOrDefault("list", List.of())); + List members; + if (ResourceConfigUtils.getAsBoolean(section.get("all-items"), "all-items")) { + ItemManager itemManager = ItemBrowserManagerImpl.this.plugin.itemManager(); + members = itemManager.loadedItems().keySet().stream().filter(it -> !itemManager.isVanillaItem(it)).map(Key::asString).collect(Collectors.toList()); + } else { + 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"); List lore = MiscUtils.getAsStringList(section.getOrDefault("lore", List.of())); From 2b9ba636bd90637719bcde46b820a8031fb61b05 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 04:40:58 +0800 Subject: [PATCH 105/135] Update palm_tree.yml --- .../resources/default/configuration/blocks/palm_tree.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common-files/src/main/resources/resources/default/configuration/blocks/palm_tree.yml b/common-files/src/main/resources/resources/default/configuration/blocks/palm_tree.yml index 656002f56..b10df0501 100644 --- a/common-files/src/main/resources/resources/default/configuration/blocks/palm_tree.yml +++ b/common-files/src/main/resources/resources/default/configuration/blocks/palm_tree.yml @@ -525,7 +525,7 @@ items#fence: block: default:palm_fence default:palm_fence_post: material: nether_brick - data::item-name: + data::item-name: model: type: minecraft:model path: minecraft:block/custom/palm_fence_post @@ -535,7 +535,7 @@ items#fence: texture: minecraft:block/custom/palm_planks default:palm_fence_side: material: nether_brick - data::item-name: + data::item-name: model: type: minecraft:model path: minecraft:block/custom/palm_fence_side From e987bb5996fe70facc17c159c7edb2d5313e5902 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 05:00:53 +0800 Subject: [PATCH 106/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B6=B2=E4=BD=93?= =?UTF-8?q?=E7=A2=B0=E6=92=9E=E5=AE=B6=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../item/behavior/BukkitItemBehaviors.java | 2 + .../item/behavior/FurnitureItemBehavior.java | 2 +- .../LiquidCollisionFurnitureItemBehavior.java | 157 ++++++++++++++++++ 3 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionFurnitureItemBehavior.java 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 23fde9447..f236c8c29 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 @@ -8,6 +8,7 @@ public class BukkitItemBehaviors extends ItemBehaviors { public static final Key BLOCK_ITEM = Key.from("craftengine:block_item"); public static final Key ON_LIQUID_BLOCK_ITEM = Key.from("craftengine:liquid_collision_block_item"); public static final Key FURNITURE_ITEM = Key.from("craftengine:furniture_item"); + public static final Key ON_LIQUID_FURNITURE_ITEM = Key.from("craftengine:liquid_collision_furniture_item"); public static final Key FLINT_AND_STEEL_ITEM = Key.from("craftengine:flint_and_steel_item"); public static final Key COMPOSTABLE_ITEM = Key.from("craftengine:compostable_item"); public static final Key AXE_ITEM = Key.from("craftengine:axe_item"); @@ -21,6 +22,7 @@ public class BukkitItemBehaviors extends ItemBehaviors { register(BLOCK_ITEM, BlockItemBehavior.FACTORY); register(ON_LIQUID_BLOCK_ITEM, LiquidCollisionBlockItemBehavior.FACTORY); register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY); + register(ON_LIQUID_FURNITURE_ITEM, LiquidCollisionFurnitureItemBehavior.FACTORY); register(FLINT_AND_STEEL_ITEM, FlintAndSteelItemBehavior.FACTORY); register(COMPOSTABLE_ITEM, CompostableItemBehavior.FACTORY); register(AXE_ITEM, AxeItemBehavior.FACTORY); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index 30863c737..12e62569e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -39,7 +39,7 @@ import java.util.function.Predicate; public class FurnitureItemBehavior extends ItemBehavior { public static final Factory FACTORY = new Factory(); - private static final Set ALLOWED_ANCHOR_TYPES = Set.of("wall", "ceiling", "ground"); + protected static final Set ALLOWED_ANCHOR_TYPES = Set.of("wall", "ceiling", "ground"); private final Key id; private final Map rules; private final boolean ignorePlacer; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionFurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionFurnitureItemBehavior.java new file mode 100644 index 000000000..74b31a612 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/LiquidCollisionFurnitureItemBehavior.java @@ -0,0 +1,157 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; +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.furniture.AlignmentRule; +import net.momirealms.craftengine.core.entity.furniture.AnchorType; +import net.momirealms.craftengine.core.entity.furniture.RotationRule; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.entity.player.InteractionResult; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.behavior.ItemBehavior; +import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; +import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.pack.PendingConfigSection; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.plugin.logger.Debugger; +import net.momirealms.craftengine.core.util.Direction; +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.BlockHitResult; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.World; +import org.jetbrains.annotations.Nullable; + +import java.nio.file.Path; +import java.util.*; + +public class LiquidCollisionFurnitureItemBehavior extends FurnitureItemBehavior { + public static final Factory FACTORY = new Factory(); + private final List liquidTypes; + private final boolean sourceOnly; + + public LiquidCollisionFurnitureItemBehavior(Key id, Map rules, boolean ignorePlacer, boolean ignoreEntities, boolean sourceOnly, List liquidTypes) { + super(id, rules, ignorePlacer, ignoreEntities); + this.liquidTypes = liquidTypes; + this.sourceOnly = sourceOnly; + } + + @Override + public InteractionResult useOnBlock(UseOnContext context) { + return use(context.getLevel(), context.getPlayer(), context.getHand()); + } + + @Override + public InteractionResult use(World world, @Nullable Player player, InteractionHand hand) { + try { + if (player == null) return InteractionResult.FAIL; + Object blockHitResult = CoreReflections.method$Item$getPlayerPOVHitResult.invoke(null, world.serverWorld(), player.serverPlayer(), CoreReflections.instance$ClipContext$Fluid$ANY); + Object blockPos = FastNMS.INSTANCE.field$BlockHitResult$blockPos(blockHitResult); + BlockPos above = new BlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos), FastNMS.INSTANCE.field$Vec3i$z(blockPos)); + 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.EMPTY) { + return InteractionResult.PASS; + } + String liquid = null; + if (fluidType == MFluids.LAVA) { + liquid = "lava"; + } else if (fluidType == MFluids.WATER) { + liquid = "water"; + } else if (fluidType == MFluids.FLOWING_LAVA) { + if (this.sourceOnly) return InteractionResult.PASS; + liquid = "lava"; + } else if (fluidType == MFluids.FLOWING_WATER) { + if (this.sourceOnly) return InteractionResult.PASS; + liquid = "water"; + } + if (!this.liquidTypes.contains(liquid)) { + return InteractionResult.PASS; + } + if (miss) { + return super.useOnBlock(new UseOnContext(player, hand, BlockHitResult.miss(hitPos, direction, above))); + } else { + boolean inside = CoreReflections.field$BlockHitResult$inside.getBoolean(blockHitResult); + return super.useOnBlock(new UseOnContext(player, hand, new BlockHitResult(hitPos, direction, above, inside))); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Error handling use", e); + return InteractionResult.FAIL; + } + } + + public static class Factory implements ItemBehaviorFactory { + + @Override + public ItemBehavior create(Pack pack, Path path, String node, Key key, Map arguments) { + Object id = arguments.get("furniture"); + if (id == null) { + throw new LocalizedResourceConfigException("warning.config.item.behavior.furniture.missing_furniture", new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior")); + } + Map rulesMap = ResourceConfigUtils.getAsMapOrNull(arguments.get("rules"), "rules"); + Key furnitureId; + if (id instanceof Map map) { + Map furnitureSection; + if (map.containsKey(key.toString())) { + // 防呆 + furnitureSection = MiscUtils.castToMap(map.get(key.toString()), false); + BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, furnitureSection)); + } else { + furnitureSection = MiscUtils.castToMap(map, false); + BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, furnitureSection)); + } + furnitureId = key; + // 兼容老版本 + if (rulesMap == null) { + Map placementSection = ResourceConfigUtils.getAsMapOrNull(furnitureSection.get("placement"), "placement"); + if (placementSection != null) { + rulesMap = new HashMap<>(); + for (Map.Entry entry : placementSection.entrySet()) { + if (entry.getValue() instanceof Map innerMap) { + if (innerMap.containsKey("rules")) { + Map rules = ResourceConfigUtils.getAsMap(innerMap.get("rules"), "rules"); + if (ALLOWED_ANCHOR_TYPES.contains(entry.getKey())) { + rulesMap.put(entry.getKey(), rules); + } + } + } + } + } + } + } else { + furnitureId = Key.of(id.toString()); + } + Map rules = new EnumMap<>(AnchorType.class); + if (rulesMap != null) { + for (Map.Entry entry : rulesMap.entrySet()) { + try { + AnchorType type = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ROOT)); + Map ruleSection = MiscUtils.castToMap(entry.getValue(), true); + rules.put(type, new Rule( + ResourceConfigUtils.getAsEnum(ruleSection.get("alignment"), AlignmentRule.class, AlignmentRule.ANY), + ResourceConfigUtils.getAsEnum(ruleSection.get("rotation"), RotationRule.class, RotationRule.ANY) + )); + } catch (IllegalArgumentException ignored) { + Debugger.FURNITURE.debug(() -> "Invalid anchor type: " + entry.getKey()); + } + } + } + return new LiquidCollisionFurnitureItemBehavior(furnitureId, rules, + ResourceConfigUtils.getAsBoolean(arguments.get("ignore-placer"), "ignore-placer"), + ResourceConfigUtils.getAsBoolean(arguments.get("ignore-entities"), "ignore-entities"), + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("source-only", true), "source-only"), + MiscUtils.getAsStringList(arguments.get("liquid-type")) + ); + } + } +} From 369a9e45b98a7276eacda09851b5a799c24ae638 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 05:23:59 +0800 Subject: [PATCH 107/135] Update gradle.properties --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2ba672eaf..95c1bf9cc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings -project_version=0.0.65.16 +project_version=0.0.65.17 config_version=60 lang_version=43 project_group=net.momirealms From 7a5bda03fbb024f6dc91fcef598a8a22a69c6559 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 17:37:28 +0800 Subject: [PATCH 108/135] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E8=AF=AD=E8=A8=80?= =?UTF-8?q?=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../item/modifier/ComponentsModifier.java | 4 +- .../config/template/ArgumentString.java | 4 +- .../core/util/snbt/SnbtGrammar.java | 1015 ----------------- .../core/util/snbt/SnbtOperations.java | 91 -- .../craftengine/core/util/snbt/TagParser.java | 97 -- .../core/util/snbt/parse/Atom.java | 15 - .../util/snbt/parse/CachedParseState.java | 235 ---- .../core/util/snbt/parse/Control.java | 18 - .../util/snbt/parse/DelayedException.java | 19 - .../core/util/snbt/parse/Dictionary.java | 93 -- .../core/util/snbt/parse/ErrorCollector.java | 97 -- .../core/util/snbt/parse/ErrorEntry.java | 4 - .../LocalizedCommandSyntaxException.java | 84 -- .../LocalizedDynamicCommandExceptionType.java | 27 - .../util/snbt/parse/LocalizedMessage.java | 53 - .../LocalizedSimpleCommandExceptionType.java | 25 - .../core/util/snbt/parse/NamedRule.java | 7 - .../core/util/snbt/parse/ParseState.java | 37 - .../core/util/snbt/parse/Rule.java | 51 - .../core/util/snbt/parse/Scope.java | 315 ----- .../util/snbt/parse/SuggestionSupplier.java | 11 - .../core/util/snbt/parse/Term.java | 235 ---- .../core/util/snbt/parse/grammar/Grammar.java | 51 - .../parse/grammar/GreedyPatternParseRule.java | 33 - .../grammar/GreedyPredicateParseRule.java | 48 - .../parse/grammar/NumberRunParseRule.java | 46 - .../grammar/StringReaderParserState.java | 29 - .../snbt/parse/grammar/StringReaderTerms.java | 64 -- .../grammar/UnquotedStringParseRule.java | 32 - gradle.properties | 2 +- 30 files changed, 5 insertions(+), 2837 deletions(-) delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Atom.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Control.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorEntry.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedMessage.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedSimpleCommandExceptionType.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/NamedRule.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java index e5f551690..d9a56564a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java @@ -9,7 +9,7 @@ import net.momirealms.craftengine.core.util.GsonHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Pair; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.snbt.TagParser; +import net.momirealms.craftengine.core.util.TagParser; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.Tag; @@ -47,7 +47,7 @@ public class ComponentsModifier implements ItemDataModifier { String snbt = string.substring("(snbt) ".length()); try { return TagParser.parseTagFully(snbt); - } catch (CommandSyntaxException e) { + } catch (Exception e) { throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e.getMessage()); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java index 15f821b76..0241d73b2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.plugin.config.template; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.momirealms.craftengine.core.plugin.config.template.argument.TemplateArgument; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.snbt.TagParser; +import net.momirealms.craftengine.core.util.TagParser; import java.util.ArrayList; import java.util.List; @@ -71,7 +71,7 @@ public interface ArgumentString { Object parsed; try { parsed = TagParser.parseObjectFully(defaultValueString); - } catch (CommandSyntaxException e) { + } catch (Exception e) { throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e.getMessage()); } try { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java deleted file mode 100644 index 0ff4982f2..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtGrammar.java +++ /dev/null @@ -1,1015 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap.Builder; -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; -import com.mojang.serialization.DynamicOps; -import com.mojang.serialization.JavaOps; -import it.unimi.dsi.fastutil.bytes.ByteArrayList; -import it.unimi.dsi.fastutil.bytes.ByteList; -import it.unimi.dsi.fastutil.chars.CharList; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.util.snbt.parse.*; -import net.momirealms.craftengine.core.util.snbt.parse.Dictionary; -import net.momirealms.craftengine.core.util.snbt.parse.grammar.*; -import net.momirealms.sparrow.nbt.codec.LegacyJavaOps; - -import javax.annotation.Nullable; -import java.nio.ByteBuffer; -import java.util.*; -import java.util.Map.Entry; -import java.util.regex.Pattern; -import java.util.stream.IntStream; -import java.util.stream.LongStream; - -public class SnbtGrammar { - private static final DynamicCommandExceptionType ERROR_NUMBER_PARSE_FAILURE = new LocalizedDynamicCommandExceptionType( - message -> new LocalizedMessage("warning.config.type.snbt.parser.number_parse_failure", String.valueOf(message)) - ); - static final DynamicCommandExceptionType ERROR_EXPECTED_HEX_ESCAPE = new LocalizedDynamicCommandExceptionType( - length -> new LocalizedMessage("warning.config.type.snbt.parser.expected_hex_escape", String.valueOf(length)) - ); - private static final DynamicCommandExceptionType ERROR_INVALID_CODEPOINT = new LocalizedDynamicCommandExceptionType( - codepoint -> new LocalizedMessage("warning.config.type.snbt.parser.invalid_codepoint", String.valueOf(codepoint)) - ); - private static final DynamicCommandExceptionType ERROR_NO_SUCH_OPERATION = new LocalizedDynamicCommandExceptionType( - operation -> new LocalizedMessage("warning.config.type.snbt.parser.no_such_operation", String.valueOf(operation)) - ); - static final DelayedException ERROR_EXPECTED_INTEGER_TYPE = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_integer_type")) - ); - private static final DelayedException ERROR_EXPECTED_FLOAT_TYPE = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_float_type")) - ); - static final DelayedException ERROR_EXPECTED_NON_NEGATIVE_NUMBER = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_non_negative_number")) - ); - private static final DelayedException ERROR_INVALID_CHARACTER_NAME = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.invalid_character_name")) - ); - static final DelayedException ERROR_INVALID_ARRAY_ELEMENT_TYPE = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.invalid_array_element_type")) - ); - private static final DelayedException ERROR_INVALID_UNQUOTED_START = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.invalid_unquoted_start")) - ); - private static final DelayedException ERROR_EXPECTED_UNQUOTED_STRING = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_unquoted_string")) - ); - private static final DelayedException ERROR_INVALID_STRING_CONTENTS = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.invalid_string_contents")) - ); - private static final DelayedException ERROR_EXPECTED_BINARY_NUMERAL = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_binary_numeral")) - ); - private static final DelayedException ERROR_UNDERSCORE_NOT_ALLOWED = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.underscore_not_allowed")) - ); - private static final DelayedException ERROR_EXPECTED_DECIMAL_NUMERAL = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_decimal_numeral")) - ); - private static final DelayedException ERROR_EXPECTED_HEX_NUMERAL = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_hex_numeral")) - ); - private static final DelayedException ERROR_EMPTY_KEY = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.empty_key")) - ); - private static final DelayedException ERROR_LEADING_ZERO_NOT_ALLOWED = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.leading_zero_not_allowed")) - ); - private static final DelayedException ERROR_INFINITY_NOT_ALLOWED = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.infinity_not_allowed")) - ); - private static final NumberRunParseRule BINARY_NUMERAL = new NumberRunParseRule(ERROR_EXPECTED_BINARY_NUMERAL, ERROR_UNDERSCORE_NOT_ALLOWED) { - @Override - protected boolean isAccepted(char c) { - return switch (c) { - case '0', '1', '_' -> true; - default -> false; - }; - } - }; - private static final NumberRunParseRule DECIMAL_NUMERAL = new NumberRunParseRule(ERROR_EXPECTED_DECIMAL_NUMERAL, ERROR_UNDERSCORE_NOT_ALLOWED) { - @Override - protected boolean isAccepted(char c) { - return switch (c) { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_' -> true; - default -> false; - }; - } - }; - private static final NumberRunParseRule HEX_NUMERAL = new NumberRunParseRule(ERROR_EXPECTED_HEX_NUMERAL, ERROR_UNDERSCORE_NOT_ALLOWED) { - @Override - protected boolean isAccepted(char c) { - return switch (c) { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', '_', 'a', 'b', 'c', 'd', 'e', 'f' -> true; - default -> false; - }; - } - }; - private static final GreedyPredicateParseRule PLAIN_STRING_CHUNK = new GreedyPredicateParseRule(1, ERROR_INVALID_STRING_CONTENTS) { - @Override - protected boolean isAccepted(char c) { - return switch (c) { - case '"', '\'', '\\' -> false; - default -> true; - }; - } - }; - private static final StringReaderTerms.TerminalCharacters NUMBER_LOOKEAHEAD = new StringReaderTerms.TerminalCharacters(CharList.of()) { - @Override - protected boolean isAccepted(char c) { - return canStartNumber(c); - } - }; - private static final Pattern UNICODE_NAME = Pattern.compile("[-a-zA-Z0-9 ]+"); - - static DelayedException createNumberParseError(NumberFormatException ex) { - return DelayedException.create(ERROR_NUMBER_PARSE_FAILURE, ex.getMessage()); - } - - private static boolean isAllowedToStartUnquotedString(char c) { - return !canStartNumber(c); - } - - static boolean canStartNumber(char c) { - return switch (c) { - case '+', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> true; - default -> false; - }; - } - - static boolean needsUnderscoreRemoval(String contents) { - return contents.indexOf(95) != -1; - } - - private static void cleanAndAppend(StringBuilder output, String contents) { - cleanAndAppend(output, contents, needsUnderscoreRemoval(contents)); - } - - static void cleanAndAppend(StringBuilder output, String contents, boolean needsUnderscoreRemoval) { - if (needsUnderscoreRemoval) { - for (char c : contents.toCharArray()) { - if (c != '_') { - output.append(c); - } - } - return; - } - output.append(contents); - } - - static short parseUnsignedShort(String string, int radix) { - int parse = Integer.parseInt(string, radix); - if (parse >> 16 == 0) { - return (short) parse; - } - throw new NumberFormatException("out of range: " + parse); - } - - @Nullable - private static T createFloat( - DynamicOps ops, - Sign sign, - @Nullable String whole, - @Nullable String fraction, - @Nullable Signed exponent, - @Nullable TypeSuffix typeSuffix, - ParseState state - ) { - StringBuilder result = new StringBuilder(); - sign.append(result); - if (whole != null) { - cleanAndAppend(result, whole); - } - - if (fraction != null) { - result.append('.'); - cleanAndAppend(result, fraction); - } - - if (exponent != null) { - result.append('e'); - exponent.sign().append(result); - cleanAndAppend(result, exponent.value); - } - - try { - String string = result.toString(); - - return switch (typeSuffix) { - case null -> convertDouble(ops, state, string); - case FLOAT -> convertFloat(ops, state, string); - case DOUBLE -> convertDouble(ops, state, string); - default -> { - state.errorCollector().store(state.mark(), ERROR_EXPECTED_FLOAT_TYPE); - yield null; - } - }; - } catch (NumberFormatException e) { - state.errorCollector().store(state.mark(), createNumberParseError(e)); - return null; - } - } - - @Nullable - private static T convertFloat(DynamicOps ops, ParseState state, String contents) { - float value = Float.parseFloat(contents); - if (!Float.isFinite(value)) { - state.errorCollector().store(state.mark(), ERROR_INFINITY_NOT_ALLOWED); - return null; - } - return ops.createFloat(value); - } - - @Nullable - private static T convertDouble(DynamicOps ops, ParseState state, String contents) { - double value = Double.parseDouble(contents); - if (!Double.isFinite(value)) { - state.errorCollector().store(state.mark(), ERROR_INFINITY_NOT_ALLOWED); - return null; - } - return ops.createDouble(value); - } - - private static String joinList(List list) { - return switch (list.size()) { - case 0 -> ""; - case 1 -> list.getFirst(); - default -> String.join("", list); - }; - } - - @SuppressWarnings("unchecked") - public static Grammar createParser(DynamicOps ops) { - T trueValue = ops.createBoolean(true); - T falseValue = ops.createBoolean(false); - T emptyMapValue = ops.emptyMap(); - T emptyList = ops.emptyList(); - T nullString = ops.createString(SnbtOperations.BUILTIN_NULL); - boolean isJavaType = SnbtOperations.BUILTIN_NULL.equals(nullString); // 确定是 Java 类型的 - - Dictionary rules = new Dictionary<>(); - - // 符号解析规则 - Atom sign = Atom.of("sign"); - rules.put( - sign, - Term.alternative( - Term.sequence(StringReaderTerms.character('+'), Term.marker(sign, Sign.PLUS)), - Term.sequence(StringReaderTerms.character('-'), Term.marker(sign, Sign.MINUS)) - ), - scope -> scope.getOrThrow(sign) - ); - - // 整数后缀解析规则 - Atom integerSuffix = Atom.of("integer_suffix"); - rules.put( - integerSuffix, - Term.alternative( - Term.sequence( - StringReaderTerms.characters('u', 'U'), - Term.alternative( - Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.BYTE))), - Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.SHORT))), - Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.INT))), - Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.UNSIGNED, TypeSuffix.LONG))) - ) - ), - Term.sequence( - StringReaderTerms.characters('s', 'S'), - Term.alternative( - Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.BYTE))), - Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.SHORT))), - Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.INT))), - Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(SignedPrefix.SIGNED, TypeSuffix.LONG))) - ) - ), - Term.sequence(StringReaderTerms.characters('b', 'B'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.BYTE))), - Term.sequence(StringReaderTerms.characters('s', 'S'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.SHORT))), - Term.sequence(StringReaderTerms.characters('i', 'I'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.INT))), - Term.sequence(StringReaderTerms.characters('l', 'L'), Term.marker(integerSuffix, new IntegerSuffix(null, TypeSuffix.LONG))) - ), - scope -> scope.getOrThrow(integerSuffix) - ); - - // 二进制解析规则 - Atom binaryNumeral = Atom.of("binary_numeral"); - rules.put(binaryNumeral, BINARY_NUMERAL); - - // 十进制解析规则 - Atom decimalNumeral = Atom.of("decimal_numeral"); - rules.put(decimalNumeral, DECIMAL_NUMERAL); - - // 十六进制解析规则 - Atom hexNumeral = Atom.of("hex_numeral"); - rules.put(hexNumeral, HEX_NUMERAL); - - // 整数常量解析规则 - Atom integerLiteral = Atom.of("integer_literal"); - NamedRule integerLiteralRule = rules.put( - integerLiteral, - Term.sequence( - Term.optional(rules.named(sign)), - Term.alternative( - Term.sequence( - StringReaderTerms.character('0'), - Term.cut(), - Term.alternative( - Term.sequence(StringReaderTerms.characters('x', 'X'), Term.cut(), rules.named(hexNumeral)), - Term.sequence(StringReaderTerms.characters('b', 'B'), rules.named(binaryNumeral)), - Term.sequence(rules.named(decimalNumeral), Term.cut(), Term.fail(ERROR_LEADING_ZERO_NOT_ALLOWED)), - Term.marker(decimalNumeral, "0") - ) - ), - rules.named(decimalNumeral) - ), - Term.optional(rules.named(integerSuffix)) - ), - scope -> { - IntegerSuffix suffix = scope.getOrDefault(integerSuffix, IntegerSuffix.EMPTY); - Sign signValue = scope.getOrDefault(sign, Sign.PLUS); - String decimalContents = scope.get(decimalNumeral); - if (decimalContents != null) { - return new IntegerLiteral(signValue, Base.DECIMAL, decimalContents, suffix); - } - String hexContents = scope.get(hexNumeral); - if (hexContents != null) { - return new IntegerLiteral(signValue, Base.HEX, hexContents, suffix); - } - String binaryContents = scope.getOrThrow(binaryNumeral); - return new IntegerLiteral(signValue, Base.BINARY, binaryContents, suffix); - } - ); - - // 浮点型后缀解析规则 - Atom floatTypeSuffix = Atom.of("float_type_suffix"); - rules.put( - floatTypeSuffix, - Term.alternative( - Term.sequence(StringReaderTerms.characters('f', 'F'), Term.marker(floatTypeSuffix, TypeSuffix.FLOAT)), - Term.sequence(StringReaderTerms.characters('d', 'D'), Term.marker(floatTypeSuffix, TypeSuffix.DOUBLE)) - ), - scope -> scope.getOrThrow(floatTypeSuffix) - ); - - // 浮点数指数部分解析规则 - Atom> floatExponentPart = Atom.of("float_exponent_part"); - rules.put( - floatExponentPart, - Term.sequence( - StringReaderTerms.characters('e', 'E'), - Term.optional(rules.named(sign)), - rules.named(decimalNumeral) - ), - scope -> new Signed<>(scope.getOrDefault(sign, Sign.PLUS), scope.getOrThrow(decimalNumeral)) - ); - - // 浮点数常量解析规则 - Atom floatWholePart = Atom.of("float_whole_part"); // 整数部分 - Atom floatFractionPart = Atom.of("float_fraction_part"); // 小数部分 - Atom floatLiteral = Atom.of("float_literal"); - rules.putComplex( - floatLiteral, - Term.sequence( - Term.optional(rules.named(sign)), - Term.alternative( - Term.sequence( - rules.namedWithAlias(decimalNumeral, floatWholePart), - StringReaderTerms.character('.'), - Term.cut(), - Term.optional(rules.namedWithAlias(decimalNumeral, floatFractionPart)), - Term.optional(rules.named(floatExponentPart)), - Term.optional(rules.named(floatTypeSuffix)) - ), - Term.sequence( - StringReaderTerms.character('.'), - Term.cut(), - rules.namedWithAlias(decimalNumeral, floatFractionPart), - Term.optional(rules.named(floatExponentPart)), - Term.optional(rules.named(floatTypeSuffix)) - ), - Term.sequence( - rules.namedWithAlias(decimalNumeral, floatWholePart), - rules.named(floatExponentPart), - Term.cut(), - Term.optional(rules.named(floatTypeSuffix)) - ), - Term.sequence( - rules.namedWithAlias(decimalNumeral, floatWholePart), - Term.optional(rules.named(floatExponentPart)), - rules.named(floatTypeSuffix) - ) - ) - ), - state -> { - Scope scope = state.scope(); - Sign wholeSign = scope.getOrDefault(sign, Sign.PLUS); - String whole = scope.get(floatWholePart); - String fraction = scope.get(floatFractionPart); - Signed exponent = scope.get(floatExponentPart); - TypeSuffix typeSuffix = scope.get(floatTypeSuffix); - return createFloat(ops, wholeSign, whole, fraction, exponent, typeSuffix, state); - } - ); - - // 二位十六进制字符串解析规则 - Atom stringHex2 = Atom.of("string_hex_2"); - rules.put(stringHex2, new SimpleHexLiteralParseRule(2)); - - // 四位十六进制字符串解析规则 - Atom stringHex4 = Atom.of("string_hex_4"); - rules.put(stringHex4, new SimpleHexLiteralParseRule(4)); - - // 八位十六进制字符串解析规则 - Atom stringHex8 = Atom.of("string_hex_8"); - rules.put(stringHex8, new SimpleHexLiteralParseRule(8)); - - // Unicode名称字符串解析规则 - Atom stringUnicodeName = Atom.of("string_unicode_name"); - rules.put(stringUnicodeName, new GreedyPatternParseRule(UNICODE_NAME, ERROR_INVALID_CHARACTER_NAME)); - - // 字符串转义序列解析规则 - Atom stringEscapeSequence = Atom.of("string_escape_sequence"); - rules.putComplex( - stringEscapeSequence, - Term.alternative( - Term.sequence(StringReaderTerms.character('b'), Term.marker(stringEscapeSequence, "\b")), - Term.sequence(StringReaderTerms.character('s'), Term.marker(stringEscapeSequence, " ")), - Term.sequence(StringReaderTerms.character('t'), Term.marker(stringEscapeSequence, "\t")), - Term.sequence(StringReaderTerms.character('n'), Term.marker(stringEscapeSequence, "\n")), - Term.sequence(StringReaderTerms.character('f'), Term.marker(stringEscapeSequence, "\f")), - Term.sequence(StringReaderTerms.character('r'), Term.marker(stringEscapeSequence, "\r")), - Term.sequence(StringReaderTerms.character('\\'), Term.marker(stringEscapeSequence, "\\")), - Term.sequence(StringReaderTerms.character('\''), Term.marker(stringEscapeSequence, "'")), - Term.sequence(StringReaderTerms.character('"'), Term.marker(stringEscapeSequence, "\"")), - Term.sequence(StringReaderTerms.character('x'), rules.named(stringHex2)), - Term.sequence(StringReaderTerms.character('u'), rules.named(stringHex4)), - Term.sequence(StringReaderTerms.character('U'), rules.named(stringHex8)), - Term.sequence( - StringReaderTerms.character('N'), - StringReaderTerms.character('{'), - rules.named(stringUnicodeName), - StringReaderTerms.character('}') - ) - ), - state -> { - Scope scope = state.scope(); - String plainEscape = scope.getAny(stringEscapeSequence); - if (plainEscape != null) { - return plainEscape; - } - String hexEscape = scope.getAny(stringHex2, stringHex4, stringHex8); - if (hexEscape != null) { - int codePoint = HexFormat.fromHexDigits(hexEscape); - if (!Character.isValidCodePoint(codePoint)) { - state.errorCollector().store(state.mark(), DelayedException.create(ERROR_INVALID_CODEPOINT, String.format(Locale.ROOT, "U+%08X", codePoint))); - return null; - } - return Character.toString(codePoint); - } - String character = scope.getOrThrow(stringUnicodeName); - - int codePoint; - try { - codePoint = Character.codePointOf(character); - } catch (IllegalArgumentException var12x) { - state.errorCollector().store(state.mark(), ERROR_INVALID_CHARACTER_NAME); - return null; - } - - return Character.toString(codePoint); - } - ); - - // 纯文本字符串解析规则 - Atom stringPlainContents = Atom.of("string_plain_contents"); - rules.put(stringPlainContents, PLAIN_STRING_CHUNK); - - // 字符串解析规则 - Atom> stringChunks = Atom.of("string_chunks"); // 字符串块 - Atom stringContents = Atom.of("string_contents"); // 字符串内容 - - // 单引号字符串块解析规则 - Atom singleQuotedStringChunk = Atom.of("single_quoted_string_chunk"); - NamedRule singleQuotedStringChunkRule = rules.put( - singleQuotedStringChunk, - Term.alternative( - rules.namedWithAlias(stringPlainContents, stringContents), - Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), - Term.sequence(StringReaderTerms.character('"'), Term.marker(stringContents, "\"")) - ), - scope -> scope.getOrThrow(stringContents) - ); - - // 单引号字符串内容解析规则 - Atom singleQuotedStringContents = Atom.of("single_quoted_string_contents"); - rules.put( - singleQuotedStringContents, - Term.repeated(singleQuotedStringChunkRule, stringChunks), - scope -> joinList(scope.getOrThrow(stringChunks)) - ); - - // 双引号字符串块解析规则 - Atom doubleQuotedStringChunk = Atom.of("double_quoted_string_chunk"); - NamedRule doubleQuotedStringChunkRule = rules.put( - doubleQuotedStringChunk, - Term.alternative( - rules.namedWithAlias(stringPlainContents, stringContents), - Term.sequence(StringReaderTerms.character('\\'), rules.namedWithAlias(stringEscapeSequence, stringContents)), - Term.sequence(StringReaderTerms.character('\''), Term.marker(stringContents, "'")) - ), - scope -> scope.getOrThrow(stringContents) - ); - - // 双引号字符串内容解析规则 - Atom doubleQuotedStringContents = Atom.of("double_quoted_string_contents"); - rules.put( - doubleQuotedStringContents, - Term.repeated(doubleQuotedStringChunkRule, stringChunks), - scope -> joinList(scope.getOrThrow(stringChunks)) - ); - - // 带引号的字符串字面量解析规则 - Atom quotedStringLiteral = Atom.of("quoted_string_literal"); - rules.put( - quotedStringLiteral, - Term.alternative( - Term.sequence( - StringReaderTerms.character('"'), - Term.cut(), - Term.optional(rules.namedWithAlias(doubleQuotedStringContents, stringContents)), - StringReaderTerms.character('"') - ), - Term.sequence( - StringReaderTerms.character('\''), - Term.optional(rules.namedWithAlias(singleQuotedStringContents, stringContents)), - StringReaderTerms.character('\'') - ) - ), - scope -> scope.getOrThrow(stringContents) - ); - - // 不带引号的字符串解析规则 - Atom unquotedString = Atom.of("unquoted_string"); - rules.put( - unquotedString, - new UnquotedStringParseRule(1, ERROR_EXPECTED_UNQUOTED_STRING) - ); - - // 列表解析规则 - Atom literal = Atom.of("literal"); // 字面量 - Atom> argumentList = Atom.of("arguments"); // 参数 - rules.put( - argumentList, - Term.repeatedWithTrailingSeparator( - rules.forward(literal), - argumentList, - StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) - ), - scope -> scope.getOrThrow(argumentList) - ); - - // 不带引号的字符串或内置表达式解析规则 - Atom unquotedStringOrBuiltIn = Atom.of("unquoted_string_or_builtin"); - rules.putComplex( - unquotedStringOrBuiltIn, - Term.sequence( - rules.named(unquotedString), - Term.optional(Term.sequence( - StringReaderTerms.character('('), - rules.named(argumentList), - StringReaderTerms.character(')') - )) - ), - state -> { - Scope scope = state.scope(); - String contents = scope.getOrThrow(unquotedString); - if (!contents.isEmpty() && isAllowedToStartUnquotedString(contents.charAt(0))) { // 非空且是合法开头字符 - List arguments = scope.get(argumentList); - if (arguments != null) { // 带参数尝试解析内置表达式 - SnbtOperations.BuiltinKey key = new SnbtOperations.BuiltinKey(contents, arguments.size()); - SnbtOperations.BuiltinOperation operation = SnbtOperations.BUILTIN_OPERATIONS.get(key); - if (operation != null) { - return operation.run(ops, arguments, state); - } - state.errorCollector().store(state.mark(), DelayedException.create(ERROR_NO_SUCH_OPERATION, key.toString())); - return null; - } else if (contents.equalsIgnoreCase(SnbtOperations.BUILTIN_TRUE)) { // 解析不带引号的 true 为布尔值 - return trueValue; - } else if (contents.equalsIgnoreCase(SnbtOperations.BUILTIN_FALSE)) { // 解析不带引号的 false 为布尔值 - return falseValue; - } else if (contents.equalsIgnoreCase(SnbtOperations.BUILTIN_NULL)) { // 解析不带引号的 null 为空值,该功能并非标准 SNBT 语法 - return Objects.requireNonNullElseGet(ops.empty() /*一般来说这里都不会是null*/, () -> { - if (isJavaType) { - // 如果是 null 一般就是使用 Object 对象的 Java 类型,可以安全的强行转换 - return (T) CachedParseState.JAVA_NULL_VALUE_MARKER; - } - return nullString; // 意外情况保持 SNBT 默认行为 - }); - } - return ops.createString(contents); - } - state.errorCollector().store(state.mark(), SnbtOperations.BUILTIN_IDS, ERROR_INVALID_UNQUOTED_START); - return null; - } - ); - - // 映射键解析规则 - Atom mapKey = Atom.of("map_key"); - rules.put( - mapKey, - Term.alternative( - rules.named(quotedStringLiteral), - rules.named(unquotedString) - ), - scope -> scope.getAnyOrThrow(quotedStringLiteral, unquotedString) - ); - - // 映射条目解析规则 - Atom> mapEntry = Atom.of("map_entry"); - NamedRule> mapEntryRule = rules.putComplex( - mapEntry, - Term.sequence( - rules.named(mapKey), - StringReaderTerms.character(TagParser.NAME_VALUE_SEPARATOR), - rules.named(literal) - ), - state -> { - Scope scope = state.scope(); - String key = scope.getOrThrow(mapKey); - if (key.isEmpty()) { - state.errorCollector().store(state.mark(), ERROR_EMPTY_KEY); - return null; - } - T value = scope.getOrThrow(literal); - return Map.entry(key, value); - } - ); - - // 映射条目集合解析规则 - Atom>> mapEntries = Atom.of("map_entries"); - rules.put( - mapEntries, - Term.repeatedWithTrailingSeparator( - mapEntryRule, - mapEntries, - StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) - ), - scope -> scope.getOrThrow(mapEntries) - ); - - // 映射字面量解析规则 - Atom mapLiteral = Atom.of("map_literal"); - rules.put( - mapLiteral, - Term.sequence( - StringReaderTerms.character('{'), - Scope.increaseDepth(), - rules.named(mapEntries), - Scope.decreaseDepth(), - StringReaderTerms.character('}') - ), - scope -> { - List> entries = scope.getOrThrow(mapEntries); - if (entries.isEmpty()) { - return emptyMapValue; - } - Builder builder = ImmutableMap.builderWithExpectedSize(entries.size()); - for (Entry e : entries) { - builder.put(ops.createString(e.getKey()), e.getValue()); - } - return ops.createMap(builder.buildKeepingLast()); - } - ); - - // 列表条目集合解析规则 - Atom> listEntries = Atom.of("list_entries"); - rules.put( - listEntries, - Term.repeatedWithTrailingSeparator( - rules.forward(literal), - listEntries, - StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) - ), - scope -> scope.getOrThrow(listEntries) - ); - - // 数组前缀解析规则 - Atom arrayPrefix = Atom.of("array_prefix"); - rules.put( - arrayPrefix, - Term.alternative( - Term.sequence(StringReaderTerms.character('B'), Term.marker(arrayPrefix, ArrayPrefix.BYTE)), - Term.sequence(StringReaderTerms.character('L'), Term.marker(arrayPrefix, ArrayPrefix.LONG)), - Term.sequence(StringReaderTerms.character('I'), Term.marker(arrayPrefix, ArrayPrefix.INT)) - ), - scope -> scope.getOrThrow(arrayPrefix) - ); - - // 整数数组条目集合解析规则 - Atom> intArrayEntries = Atom.of("int_array_entries"); - rules.put( - intArrayEntries, - Term.repeatedWithTrailingSeparator( - integerLiteralRule, - intArrayEntries, - StringReaderTerms.character(TagParser.ELEMENT_SEPARATOR) - ), - scope -> scope.getOrThrow(intArrayEntries) - ); - - // 列表字面量解析规则 - Atom listLiteral = Atom.of("list_literal"); - rules.putComplex( - listLiteral, - Term.sequence( - StringReaderTerms.character('['), - Scope.increaseDepth(), - Term.alternative( - Term.sequence( - rules.named(arrayPrefix), - StringReaderTerms.character(';'), - rules.named(intArrayEntries) - ), - rules.named(listEntries) - ), - Scope.decreaseDepth(), - StringReaderTerms.character(']') - ), - state -> { - Scope scope = state.scope(); - ArrayPrefix arrayType = scope.get(arrayPrefix); - if (arrayType != null) { - List entries = scope.getOrThrow(intArrayEntries); - return entries.isEmpty() ? arrayType.create(ops) : arrayType.create(ops, entries, state); - } - List entries = scope.getOrThrow(listEntries); - return entries.isEmpty() ? emptyList : ops.createList(entries.stream()); - } - ); - - // 基本规则解析规则 - NamedRule literalRule = rules.putComplex( - literal, - Term.alternative( - Term.sequence( - Term.positiveLookahead(NUMBER_LOOKEAHEAD), - Term.alternative( - rules.namedWithAlias(floatLiteral, literal), - rules.named(integerLiteral) - ) - ), - Term.sequence( - Term.positiveLookahead(StringReaderTerms.characters('"', '\'')), - Term.cut(), - rules.named(quotedStringLiteral) - ), - Term.sequence( - Term.positiveLookahead(StringReaderTerms.character('{')), - Term.cut(), - rules.namedWithAlias(mapLiteral, literal) - ), - Term.sequence( - Term.positiveLookahead(StringReaderTerms.character('[')), - Term.cut(), - rules.namedWithAlias(listLiteral, literal) - ), - rules.namedWithAlias(unquotedStringOrBuiltIn, literal) - ), - state -> { - Scope scope = state.scope(); - String quotedString = scope.get(quotedStringLiteral); - if (quotedString != null) { - return ops.createString(quotedString); - } - IntegerLiteral integer = scope.get(integerLiteral); - return integer != null ? integer.create(ops, state) : scope.getOrThrow(literal); - } - ); - - return new Grammar<>(rules, literalRule); - } - - enum ArrayPrefix { - BYTE(TypeSuffix.BYTE) { - private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap(new byte[0]); - - @Override - public T create(DynamicOps ops) { - return ops.createByteList(EMPTY_BUFFER); - } - - @Nullable - @Override - public T create(DynamicOps ops, List entries, ParseState state) { - ByteList result = new ByteArrayList(); - for (IntegerLiteral entry : entries) { - Number number = this.buildNumber(entry, state); - if (number == null) { - return null; - } - result.add(number.byteValue()); - } - return ops.createByteList(ByteBuffer.wrap(result.toByteArray())); - } - }, - INT(TypeSuffix.INT, TypeSuffix.BYTE, TypeSuffix.SHORT) { - @Override - public T create(DynamicOps ops) { - return ops.createIntList(IntStream.empty()); - } - - @Nullable - @Override - public T create(DynamicOps ops, List entries, ParseState state) { - IntStream.Builder result = IntStream.builder(); - for (IntegerLiteral entry : entries) { - Number parsedNumber = this.buildNumber(entry, state); - if (parsedNumber == null) { - return null; - } - result.add(parsedNumber.intValue()); - } - return ops.createIntList(result.build()); - } - }, - LONG(TypeSuffix.LONG, TypeSuffix.BYTE, TypeSuffix.SHORT, TypeSuffix.INT) { - @Override - public T create(DynamicOps ops) { - return ops.createLongList(LongStream.empty()); - } - - @Nullable - @Override - public T create(DynamicOps ops, List entries, ParseState state) { - LongStream.Builder result = LongStream.builder(); - for (IntegerLiteral entry : entries) { - Number parsedNumber = this.buildNumber(entry, state); - if (parsedNumber == null) { - return null; - } - result.add(parsedNumber.longValue()); - } - return ops.createLongList(result.build()); - } - }; - - private final TypeSuffix defaultType; - private final Set additionalTypes; - - ArrayPrefix(final TypeSuffix defaultType, final TypeSuffix... additionalTypes) { - this.additionalTypes = Set.of(additionalTypes); - this.defaultType = defaultType; - } - - public boolean isAllowed(TypeSuffix type) { - return type == this.defaultType || this.additionalTypes.contains(type); - } - public abstract T create(DynamicOps ops); - - @Nullable - public abstract T create(DynamicOps ops, List entries, ParseState state); - - @Nullable - protected Number buildNumber(IntegerLiteral entry, ParseState state) { - TypeSuffix actualType = this.computeType(entry.suffix); - if (actualType == null) { - state.errorCollector().store(state.mark(), ERROR_INVALID_ARRAY_ELEMENT_TYPE); - return null; - } - return (Number) entry.create(VersionHelper.isOrAbove1_20_5() ? JavaOps.INSTANCE : LegacyJavaOps.INSTANCE, actualType, state); - } - - @Nullable - private TypeSuffix computeType(IntegerSuffix value) { - TypeSuffix type = value.type(); - if (type == null) { - return this.defaultType; - } - return !this.isAllowed(type) ? null : type; - } - } - enum Base { - BINARY, - DECIMAL, - HEX - } - - record IntegerLiteral(Sign sign, Base base, String digits, IntegerSuffix suffix) { - private SignedPrefix signedOrDefault() { - return Objects.requireNonNullElseGet(this.suffix.signed, () -> switch (this.base) { - case BINARY, HEX -> SignedPrefix.UNSIGNED; - case DECIMAL -> SignedPrefix.SIGNED; - }); - } - - private String cleanupDigits(Sign sign) { - boolean needsUnderscoreRemoval = needsUnderscoreRemoval(this.digits); - if (sign != Sign.MINUS && !needsUnderscoreRemoval) { - return this.digits; - } - StringBuilder result = new StringBuilder(); - sign.append(result); - cleanAndAppend(result, this.digits, needsUnderscoreRemoval); - return result.toString(); - } - - @Nullable - public T create(DynamicOps ops, ParseState state) { - return this.create(ops, Objects.requireNonNullElse(this.suffix.type, TypeSuffix.INT), state); - } - - @Nullable - public T create(DynamicOps ops, TypeSuffix type, ParseState state) { - boolean isSigned = this.signedOrDefault() == SignedPrefix.SIGNED; - if (!isSigned && this.sign == Sign.MINUS) { - state.errorCollector().store(state.mark(), ERROR_EXPECTED_NON_NEGATIVE_NUMBER); - return null; - } - String fixedDigits = this.cleanupDigits(this.sign); - - int radix = switch (this.base) { - case BINARY -> 2; - case DECIMAL -> 10; - case HEX -> 16; - }; - - try { - if (isSigned) { - return switch (type) { - case BYTE -> ops.createByte(Byte.parseByte(fixedDigits, radix)); - case SHORT -> ops.createShort(Short.parseShort(fixedDigits, radix)); - case INT -> ops.createInt(Integer.parseInt(fixedDigits, radix)); - case LONG -> ops.createLong(Long.parseLong(fixedDigits, radix)); - default -> { - state.errorCollector().store(state.mark(), ERROR_EXPECTED_INTEGER_TYPE); - yield null; - } - }; - } - return switch (type) { - case BYTE -> ops.createByte(com.google.common.primitives.UnsignedBytes.parseUnsignedByte(fixedDigits, radix)); - case SHORT -> ops.createShort(parseUnsignedShort(fixedDigits, radix)); - case INT -> ops.createInt(Integer.parseUnsignedInt(fixedDigits, radix)); - case LONG -> ops.createLong(Long.parseUnsignedLong(fixedDigits, radix)); - default -> { - state.errorCollector().store(state.mark(), ERROR_EXPECTED_INTEGER_TYPE); - yield null; - } - }; - } catch (NumberFormatException var8) { - state.errorCollector().store(state.mark(), createNumberParseError(var8)); - return null; - } - } - } - - record IntegerSuffix(@Nullable SignedPrefix signed, @Nullable TypeSuffix type) { - public static final IntegerSuffix EMPTY = new IntegerSuffix(null, null); - } - - enum Sign { - PLUS, - MINUS; - - public void append(StringBuilder output) { - if (this == MINUS) { - output.append("-"); - } - } - } - - record Signed(Sign sign, T value) { - } - - enum SignedPrefix { - SIGNED, - UNSIGNED - } - - static class SimpleHexLiteralParseRule extends GreedyPredicateParseRule { - public SimpleHexLiteralParseRule(int size) { - super(size, size, DelayedException.create(ERROR_EXPECTED_HEX_ESCAPE, String.valueOf(size))); - } - - @Override - protected boolean isAccepted(char c) { - return switch (c) { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f' -> true; - default -> false; - }; - } - } - - enum TypeSuffix { - FLOAT, - DOUBLE, - BYTE, - SHORT, - INT, - LONG - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java deleted file mode 100644 index c715eba12..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/SnbtOperations.java +++ /dev/null @@ -1,91 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.serialization.DynamicOps; -import net.momirealms.craftengine.core.util.snbt.parse.*; -import net.momirealms.sparrow.nbt.util.UUIDUtil; -import org.jetbrains.annotations.NotNull; - -import javax.annotation.Nullable; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -public class SnbtOperations { - static final DelayedException ERROR_EXPECTED_STRING_UUID = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_string_uuid")) - ); - static final DelayedException ERROR_EXPECTED_NUMBER_OR_BOOLEAN = DelayedException.create( - new LocalizedSimpleCommandExceptionType(new LocalizedMessage("warning.config.type.snbt.parser.expected_number_or_boolean")) - ); - public static final String BUILTIN_TRUE = "true"; - public static final String BUILTIN_FALSE = "false"; - public static final String BUILTIN_NULL = "null"; - public static final Map BUILTIN_OPERATIONS = Map.of( - new BuiltinKey("bool", 1), new BuiltinOperation() { - @Override - public T run(DynamicOps ops, List arguments, ParseState state) { - Boolean result = convert(ops, arguments.getFirst()); - if (result == null) { - state.errorCollector().store(state.mark(), SnbtOperations.ERROR_EXPECTED_NUMBER_OR_BOOLEAN); - return null; - } - return ops.createBoolean(result); - } - - @Nullable - private static Boolean convert(DynamicOps ops, T arg) { - Optional asBoolean = ops.getBooleanValue(arg).result(); - if (asBoolean.isPresent()) { - return asBoolean.get(); - } else { - Optional asNumber = ops.getNumberValue(arg).result(); - return asNumber.isPresent() ? asNumber.get().doubleValue() != 0.0 : null; - } - } - }, new BuiltinKey("uuid", 1), new BuiltinOperation() { - @Override - public T run(DynamicOps ops, List arguments, ParseState state) { - Optional arg = ops.getStringValue(arguments.getFirst()).result(); - if (arg.isEmpty()) { - state.errorCollector().store(state.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID); - return null; - } - UUID uuid; - try { - uuid = UUID.fromString(arg.get()); - } catch (IllegalArgumentException var7) { - state.errorCollector().store(state.mark(), SnbtOperations.ERROR_EXPECTED_STRING_UUID); - return null; - } - - return ops.createIntList(IntStream.of(UUIDUtil.uuidToIntArray(uuid))); - } - } - ); - public static final SuggestionSupplier BUILTIN_IDS = new SuggestionSupplier<>() { - private final Set keys = Stream.concat( - Stream.of(BUILTIN_FALSE, BUILTIN_TRUE, BUILTIN_NULL), SnbtOperations.BUILTIN_OPERATIONS.keySet().stream().map(BuiltinKey::id) - ) - .collect(Collectors.toSet()); - - @Override - public Stream possibleValues(ParseState state) { - return this.keys.stream(); - } - }; - - public record BuiltinKey(String id, int argCount) { - @Override - public @NotNull String toString() { - return this.id + "/" + this.argCount; - } - } - - public interface BuiltinOperation { - @Nullable - T run(DynamicOps ops, List arguments, ParseState state); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java deleted file mode 100644 index f963c635a..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/TagParser.java +++ /dev/null @@ -1,97 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; -import com.mojang.serialization.DynamicOps; -import com.mojang.serialization.JavaOps; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.util.snbt.parse.LocalizedMessage; -import net.momirealms.craftengine.core.util.snbt.parse.LocalizedSimpleCommandExceptionType; -import net.momirealms.craftengine.core.util.snbt.parse.grammar.Grammar; -import net.momirealms.sparrow.nbt.CompoundTag; -import net.momirealms.sparrow.nbt.Tag; -import net.momirealms.sparrow.nbt.codec.LegacyJavaOps; -import net.momirealms.sparrow.nbt.codec.LegacyNBTOps; -import net.momirealms.sparrow.nbt.codec.NBTOps; - -@SuppressWarnings("unused") -public class TagParser { - public static final SimpleCommandExceptionType ERROR_TRAILING_DATA = new LocalizedSimpleCommandExceptionType( - new LocalizedMessage("warning.config.type.snbt.parser.trailing") - ); - public static final SimpleCommandExceptionType ERROR_EXPECTED_COMPOUND = new LocalizedSimpleCommandExceptionType( - new LocalizedMessage("warning.config.type.snbt.parser.expected.compound") - ); - public static final char ELEMENT_SEPARATOR = ','; - public static final char NAME_VALUE_SEPARATOR = ':'; - public static final TagParser NBT_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? NBTOps.INSTANCE : LegacyNBTOps.INSTANCE); - public static final TagParser JAVA_OPS_PARSER = create(VersionHelper.isOrAbove1_20_5() ? JavaOps.INSTANCE : LegacyJavaOps.INSTANCE); - private final DynamicOps ops; - private final Grammar grammar; - - private TagParser(DynamicOps ops, Grammar grammar) { - this.ops = ops; - this.grammar = grammar; - } - - public DynamicOps ops() { - return this.ops; - } - - public static TagParser create(DynamicOps ops) { - return new TagParser<>(ops, SnbtGrammar.createParser(ops)); - } - - private static CompoundTag castToCompoundOrThrow(StringReader reader, Tag result) throws CommandSyntaxException { - if (result instanceof CompoundTag compoundTag) { - return compoundTag; - } - throw ERROR_EXPECTED_COMPOUND.createWithContext(reader); - } - public static CompoundTag parseCompoundFully(String input) throws CommandSyntaxException { - StringReader reader = new StringReader(input); - Tag result = NBT_OPS_PARSER.parseFully(reader); - return castToCompoundOrThrow(reader, result); - } - - public static Tag parseTagFully(String input) throws CommandSyntaxException { - StringReader reader = new StringReader(input); - return NBT_OPS_PARSER.parseFully(reader); - } - - public static Object parseObjectFully(String input) throws CommandSyntaxException { - StringReader reader = new StringReader(input); - return JAVA_OPS_PARSER.parseFully(reader); - } - - public T parseFully(String input) throws CommandSyntaxException { - return this.parseFully(new StringReader(input)); - } - - public T parseFully(StringReader reader) throws CommandSyntaxException { - T result = this.grammar.parse(reader); - reader.skipWhitespace(); - if (reader.canRead()) { - throw ERROR_TRAILING_DATA.createWithContext(reader); - } - return result; - } - - public T parseAsArgument(StringReader reader) throws CommandSyntaxException { - return this.grammar.parse(reader); - } - - public static CompoundTag parseCompoundAsArgument(StringReader reader) throws CommandSyntaxException { - Tag result = parseTagAsArgument(reader); - return castToCompoundOrThrow(reader, result); - } - - public static Tag parseTagAsArgument(StringReader reader) throws CommandSyntaxException { - return NBT_OPS_PARSER.parseAsArgument(reader); - } - - public static Object parseObjectAsArgument(StringReader reader) throws CommandSyntaxException { - return JAVA_OPS_PARSER.parseAsArgument(reader); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Atom.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Atom.java deleted file mode 100644 index 1601e947b..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Atom.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -import org.jetbrains.annotations.NotNull; - -@SuppressWarnings("unused") -public record Atom(String name) { - @Override - public @NotNull String toString() { - return "<" + this.name + ">"; - } - - public static Atom of(String name) { - return new Atom<>(name); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java deleted file mode 100644 index b71d66c24..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/CachedParseState.java +++ /dev/null @@ -1,235 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -import net.momirealms.craftengine.core.util.MiscUtils; - -import javax.annotation.Nullable; - -@SuppressWarnings("unchecked") -public abstract class CachedParseState implements ParseState { - private PositionCache[] positionCache = new PositionCache[256]; - private final ErrorCollector errorCollector; - private final Scope scope = new Scope(); - private SimpleControl[] controlCache = new SimpleControl[16]; - private int nextControlToReturn; - private final Silent silent = new Silent(); - public static final Object JAVA_NULL_VALUE_MARKER = new Object() { - @Override - public String toString() { - return "null"; - } - }; - - protected CachedParseState(ErrorCollector errorCollector) { - this.errorCollector = errorCollector; - } - - @Override - public Scope scope() { - return this.scope; - } - - @Override - public ErrorCollector errorCollector() { - return this.errorCollector; - } - - @Nullable - @Override - public T parse(NamedRule rule) { - int markBeforeParse = this.mark(); - PositionCache positionCache = this.getCacheForPosition(markBeforeParse); - int entryIndex = positionCache.findKeyIndex(rule.name()); - if (entryIndex != -1) { - CacheEntry value = positionCache.getValue(entryIndex); - if (value != null) { - if (value == CachedParseState.CacheEntry.NEGATIVE) { - return null; - } - this.restore(value.markAfterParse); - return value.value; - } - } else { - entryIndex = positionCache.allocateNewEntry(rule.name()); - } - - T result = rule.value().parse(this); - CacheEntry entry; - if (result == null) { - entry = CacheEntry.negativeEntry(); - } else { - int markAfterParse = this.mark(); - entry = new CacheEntry<>(result, markAfterParse); - } - - positionCache.setValue(entryIndex, entry); - return result; - } - - private PositionCache getCacheForPosition(int index) { - int currentSize = this.positionCache.length; - if (index >= currentSize) { - int newSize = MiscUtils.growByHalf(currentSize, index + 1); - PositionCache[] newCache = new PositionCache[newSize]; - System.arraycopy(this.positionCache, 0, newCache, 0, currentSize); - this.positionCache = newCache; - } - - PositionCache result = this.positionCache[index]; - if (result == null) { - result = new PositionCache(); - this.positionCache[index] = result; - } - - return result; - } - - @Override - public Control acquireControl() { - int currentSize = this.controlCache.length; - if (this.nextControlToReturn >= currentSize) { - int newSize = MiscUtils.growByHalf(currentSize, this.nextControlToReturn + 1); - SimpleControl[] newControlCache = new SimpleControl[newSize]; - System.arraycopy(this.controlCache, 0, newControlCache, 0, currentSize); - this.controlCache = newControlCache; - } - - int controlIndex = this.nextControlToReturn++; - SimpleControl entry = this.controlCache[controlIndex]; - if (entry == null) { - entry = new SimpleControl(); - this.controlCache[controlIndex] = entry; - } else { - entry.reset(); - } - - return entry; - } - - @Override - public void releaseControl() { - this.nextControlToReturn--; - } - - @Override - public ParseState silent() { - return this.silent; - } - - record CacheEntry(@Nullable T value, int markAfterParse) { - public static final CacheEntry NEGATIVE = new CacheEntry<>(null, -1); - - public static CacheEntry negativeEntry() { - return (CacheEntry) NEGATIVE; - } - } - - static class PositionCache { - public static final int ENTRY_STRIDE = 2; - private static final int NOT_FOUND = -1; - private Object[] atomCache = new Object[16]; - private int nextKey; - - public int findKeyIndex(Atom key) { - for (int i = 0; i < this.nextKey; i += ENTRY_STRIDE) { - if (this.atomCache[i] == key) { - return i; - } - } - - return NOT_FOUND; - } - - public int allocateNewEntry(Atom key) { - int newKeyIndex = this.nextKey; - this.nextKey += ENTRY_STRIDE; - int newValueIndex = newKeyIndex + 1; - int currentSize = this.atomCache.length; - if (newValueIndex >= currentSize) { - int newSize = MiscUtils.growByHalf(currentSize, newValueIndex + 1); - Object[] newCache = new Object[newSize]; - System.arraycopy(this.atomCache, 0, newCache, 0, currentSize); - this.atomCache = newCache; - } - - this.atomCache[newKeyIndex] = key; - return newKeyIndex; - } - - @Nullable - public CacheEntry getValue(int keyIndex) { - return (CacheEntry) this.atomCache[keyIndex + 1]; - } - - public void setValue(int keyIndex, CacheEntry entry) { - this.atomCache[keyIndex + 1] = entry; - } - } - - class Silent implements ParseState { - private final ErrorCollector silentCollector = new ErrorCollector.Nop<>(); - - @Override - public ErrorCollector errorCollector() { - return this.silentCollector; - } - - @Override - public Scope scope() { - return CachedParseState.this.scope(); - } - - @Nullable - @Override - public T parse(NamedRule rule) { - return CachedParseState.this.parse(rule); - } - - @Override - public S input() { - return CachedParseState.this.input(); - } - - @Override - public int mark() { - return CachedParseState.this.mark(); - } - - @Override - public void restore(int mark) { - CachedParseState.this.restore(mark); - } - - @Override - public Control acquireControl() { - return CachedParseState.this.acquireControl(); - } - - @Override - public void releaseControl() { - CachedParseState.this.releaseControl(); - } - - @Override - public ParseState silent() { - return this; - } - } - - static class SimpleControl implements Control { - private boolean hasCut; - - @Override - public void cut() { - this.hasCut = true; - } - - @Override - public boolean hasCut() { - return this.hasCut; - } - - public void reset() { - this.hasCut = false; - } - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Control.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Control.java deleted file mode 100644 index b17ce13ec..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Control.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -public interface Control { - Control UNBOUND = new Control() { - @Override - public void cut() { - } - - @Override - public boolean hasCut() { - return false; - } - }; - - void cut(); - - boolean hasCut(); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java deleted file mode 100644 index 173b85840..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/DelayedException.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; -import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; -import net.momirealms.craftengine.core.util.snbt.parse.grammar.StringReaderTerms; - -@FunctionalInterface -public interface DelayedException { - T create(String contents, int position); - - static DelayedException create(SimpleCommandExceptionType type) { - return (contents, position) -> type.createWithContext(StringReaderTerms.createReader(contents, position)); - } - - static DelayedException create(DynamicCommandExceptionType type, String argument) { - return (contents, position) -> type.createWithContext(StringReaderTerms.createReader(contents, position), argument); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java deleted file mode 100644 index 66ea4e088..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Dictionary.java +++ /dev/null @@ -1,93 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -import javax.annotation.Nullable; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.Supplier; - -@SuppressWarnings("unchecked") -public class Dictionary { - private final Map, Entry> terms = new IdentityHashMap<>(); - - public NamedRule put(Atom name, Rule entry) { - Entry holder = (Entry)this.terms.computeIfAbsent(name, Entry::new); - if (holder.value != null) { - throw new IllegalArgumentException("Trying to override rule: " + name); - } - holder.value = entry; - return holder; - } - - public NamedRule putComplex(Atom name, Term term, Rule.RuleAction action) { - return this.put(name, Rule.fromTerm(term, action)); - } - - public NamedRule put(Atom name, Term term, Rule.SimpleRuleAction action) { - return this.put(name, Rule.fromTerm(term, action)); - } - - public void checkAllBound() { - List> unboundNames = this.terms.entrySet().stream() - .filter(entry -> entry.getValue() == null) - .map(Map.Entry::getKey) - .toList(); - if (!unboundNames.isEmpty()) { - throw new IllegalStateException("Unbound names: " + unboundNames); - } - } - - public NamedRule forward(Atom name) { - return this.getOrCreateEntry(name); - } - - private Entry getOrCreateEntry(Atom name) { - return (Entry)this.terms.computeIfAbsent(name, Entry::new); - } - - public Term named(Atom name) { - return new Reference<>(this.getOrCreateEntry(name), name); - } - - public Term namedWithAlias(Atom nameToParse, Atom nameToStore) { - return new Reference<>(this.getOrCreateEntry(nameToParse), nameToStore); - } - - static class Entry implements NamedRule, Supplier { - private final Atom name; - @Nullable - Rule value; - - private Entry(Atom name) { - this.name = name; - } - - @Override - public Atom name() { - return this.name; - } - - @Override - public Rule value() { - return Objects.requireNonNull(this.value, this); - } - - @Override - public String get() { - return "Unbound rule " + this.name; - } - } - - record Reference(Entry ruleToParse, Atom nameToStore) implements Term { - @Override - public boolean parse(ParseState state, Scope scope, Control control) { - T result = state.parse(this.ruleToParse); - if (result == null) { - return false; - } - scope.put(this.nameToStore, result); - return true; - } - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java deleted file mode 100644 index 1bb43a0ae..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorCollector.java +++ /dev/null @@ -1,97 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -import net.momirealms.craftengine.core.util.MiscUtils; - -import java.util.ArrayList; -import java.util.List; - -@SuppressWarnings("unchecked") -public interface ErrorCollector { - void store(int cursor, SuggestionSupplier suggestions, Object reason); - - default void store(int cursor, Object reason) { - this.store(cursor, SuggestionSupplier.empty(), reason); - } - - void finish(int finalCursor); - - class LongestOnly implements ErrorCollector { - private MutableErrorEntry[] entries = new MutableErrorEntry[16]; - private int nextErrorEntry; - private int lastCursor = -1; - - private void discardErrorsFromShorterParse(int cursor) { - if (cursor > this.lastCursor) { - this.lastCursor = cursor; - this.nextErrorEntry = 0; - } - } - - @Override - public void finish(int finalCursor) { - this.discardErrorsFromShorterParse(finalCursor); - } - - @Override - public void store(int cursor, SuggestionSupplier suggestions, Object reason) { - this.discardErrorsFromShorterParse(cursor); - if (cursor == this.lastCursor) { - this.addErrorEntry(suggestions, reason); - } - } - - private void addErrorEntry(SuggestionSupplier suggestions, Object reason) { - int currentSize = this.entries.length; - if (this.nextErrorEntry >= currentSize) { - int newSize = MiscUtils.growByHalf(currentSize, this.nextErrorEntry + 1); - MutableErrorEntry[] newEntries = new MutableErrorEntry[newSize]; - System.arraycopy(this.entries, 0, newEntries, 0, currentSize); - this.entries = newEntries; - } - - int entryIndex = this.nextErrorEntry++; - MutableErrorEntry entry = this.entries[entryIndex]; - if (entry == null) { - entry = new MutableErrorEntry<>(); - this.entries[entryIndex] = entry; - } - - entry.suggestions = suggestions; - entry.reason = reason; - } - - public List> entries() { - int errorCount = this.nextErrorEntry; - if (errorCount == 0) { - return List.of(); - } - List> result = new ArrayList<>(errorCount); - - for (int i = 0; i < errorCount; i++) { - MutableErrorEntry mutableErrorEntry = this.entries[i]; - result.add(new ErrorEntry<>(this.lastCursor, mutableErrorEntry.suggestions, mutableErrorEntry.reason)); - } - - return result; - } - - public int cursor() { - return this.lastCursor; - } - - static class MutableErrorEntry { - SuggestionSupplier suggestions = SuggestionSupplier.empty(); - Object reason = "empty"; - } - } - - class Nop implements ErrorCollector { - @Override - public void store(int cursor, SuggestionSupplier suggestions, Object reason) { - } - - @Override - public void finish(int finalCursor) { - } - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorEntry.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorEntry.java deleted file mode 100644 index dff487beb..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ErrorEntry.java +++ /dev/null @@ -1,4 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -public record ErrorEntry(int cursor, SuggestionSupplier suggestions, Object reason) { -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java deleted file mode 100644 index 21204ec40..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedCommandSyntaxException.java +++ /dev/null @@ -1,84 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -import com.mojang.brigadier.Message; -import com.mojang.brigadier.exceptions.CommandExceptionType; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.momirealms.craftengine.core.plugin.locale.TranslationManager; -import net.momirealms.craftengine.core.util.AdventureHelper; - -import java.util.Optional; -import java.util.function.Supplier; - -public class LocalizedCommandSyntaxException extends CommandSyntaxException { - public static final int CONTEXT_AMOUNT = 50; - public static final String PARSE_ERROR_NODE = "warning.config.type.snbt.invalid_syntax.parse_error"; - public static final String HERE_NODE = "warning.config.type.snbt.invalid_syntax.here"; - private final Message message; - private final String input; - private final int cursor; - - public LocalizedCommandSyntaxException(CommandExceptionType type, Message message) { - super(type, message); - this.message = message; - this.input = null; - this.cursor = -1; - } - - public LocalizedCommandSyntaxException(CommandExceptionType type, Message message, String input, int cursor) { - super(type, message, input, cursor); - this.message = message; - this.input = input; - this.cursor = cursor; - } - - @Override - public String getMessage() { - String message = this.message.getString(); - final String context = getContext(); - if (context == null) { - return message; - } - return generateLocalizedMessage( - PARSE_ERROR_NODE, - () -> message + " at position " + this.cursor + ": " + context, - message, String.valueOf(this.cursor), context - ); - } - - @Override - public String getContext() { - if (this.input == null || this.cursor < 0) { - return null; - } - final StringBuilder builder = new StringBuilder(); - final int cursor = Math.min(this.input.length(), this.cursor); - - if (cursor > CONTEXT_AMOUNT) { - builder.append("..."); - } - - builder.append(this.input, Math.max(0, cursor - CONTEXT_AMOUNT), cursor); - builder.append(generateLocalizedMessage(HERE_NODE, () -> "<--[HERE]")); - - return builder.toString(); - } - - - private String generateLocalizedMessage(String node, Supplier fallback, String... arguments) { - try { - String rawMessage = Optional.ofNullable(TranslationManager.instance() - .miniMessageTranslation(node)).orElse(fallback.get()); - String cleanMessage = AdventureHelper.miniMessage() - .stripTags(rawMessage); - for (int i = 0; i < arguments.length; i++) { - cleanMessage = cleanMessage.replace( - "", - arguments[i] != null ? arguments[i] : "null" - ); - } - return cleanMessage; - } catch (Exception e) { - return fallback.get(); - } - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java deleted file mode 100644 index 99dde080a..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedDynamicCommandExceptionType.java +++ /dev/null @@ -1,27 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -import com.mojang.brigadier.ImmutableStringReader; -import com.mojang.brigadier.Message; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; - -import java.util.function.Function; - -public class LocalizedDynamicCommandExceptionType extends DynamicCommandExceptionType { - private final Function function; - - public LocalizedDynamicCommandExceptionType(Function function) { - super(function); - this.function = function; - } - - @Override - public CommandSyntaxException create(final Object arg) { - return new LocalizedCommandSyntaxException(this, function.apply(arg)); - } - - @Override - public CommandSyntaxException createWithContext(final ImmutableStringReader reader, final Object arg) { - return new LocalizedCommandSyntaxException(this, function.apply(arg), reader.getString(), reader.getCursor()); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedMessage.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedMessage.java deleted file mode 100644 index c8da1cab7..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedMessage.java +++ /dev/null @@ -1,53 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -import com.mojang.brigadier.Message; -import net.momirealms.craftengine.core.plugin.locale.TranslationManager; -import net.momirealms.craftengine.core.util.AdventureHelper; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Arrays; -import java.util.Optional; - -public class LocalizedMessage implements Message { - private final String node; - private final String[] arguments; - - public LocalizedMessage( - @NotNull String node, - @Nullable String... arguments - ) { - this.node = node; - this.arguments = arguments != null - ? Arrays.copyOf(arguments, arguments.length) - : new String[0]; - } - - @Override - public String getString() { - return generateLocalizedMessage(); - } - - private String generateLocalizedMessage() { - try { - String rawMessage = Optional.ofNullable(TranslationManager.instance() - .miniMessageTranslation(this.node)).orElse(this.node); - String cleanMessage = AdventureHelper.miniMessage() - .stripTags(rawMessage); - for (int i = 0; i < arguments.length; i++) { - cleanMessage = cleanMessage.replace( - "", - arguments[i] != null ? arguments[i] : "null" - ); - } - return cleanMessage; - } catch (Exception e) { - return String.format( - "Failed to translate. Node: %s, Arguments: %s. Cause: %s", - node, - Arrays.toString(arguments), - e.getMessage() - ); - } - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedSimpleCommandExceptionType.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedSimpleCommandExceptionType.java deleted file mode 100644 index b046fab0c..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/LocalizedSimpleCommandExceptionType.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -import com.mojang.brigadier.ImmutableStringReader; -import com.mojang.brigadier.Message; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; - -public class LocalizedSimpleCommandExceptionType extends SimpleCommandExceptionType { - private final Message message; - - public LocalizedSimpleCommandExceptionType(Message message) { - super(message); - this.message = message; - } - - @Override - public CommandSyntaxException create() { - return new LocalizedCommandSyntaxException(this, message); - } - - @Override - public CommandSyntaxException createWithContext(final ImmutableStringReader reader) { - return new LocalizedCommandSyntaxException(this, message, reader.getString(), reader.getCursor()); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/NamedRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/NamedRule.java deleted file mode 100644 index d4f634e1d..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/NamedRule.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -public interface NamedRule { - Atom name(); - - Rule value(); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java deleted file mode 100644 index 2ab971364..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/ParseState.java +++ /dev/null @@ -1,37 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -import javax.annotation.Nullable; -import java.util.Optional; - -public interface ParseState { - Scope scope(); - - ErrorCollector errorCollector(); - - default Optional parseTopRule(NamedRule rule) { - T result = this.parse(rule); - if (result != null) { - this.errorCollector().finish(this.mark()); - } - - if (!this.scope().hasOnlySingleFrame()) { - throw new IllegalStateException("Malformed scope: " + this.scope()); - } - return Optional.ofNullable(result); - } - - @Nullable - T parse(NamedRule rule); - - S input(); - - int mark(); - - void restore(int mark); - - Control acquireControl(); - - void releaseControl(); - - ParseState silent(); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java deleted file mode 100644 index cfb7403c4..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Rule.java +++ /dev/null @@ -1,51 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -import javax.annotation.Nullable; - -public interface Rule { - @Nullable - T parse(ParseState state); - - static Rule fromTerm(Term child, RuleAction action) { - return new WrappedTerm<>(action, child); - } - - static Rule fromTerm(Term child, SimpleRuleAction action) { - return new WrappedTerm<>(action, child); - } - - @FunctionalInterface - interface RuleAction { - @Nullable - T run(ParseState state); - } - - @FunctionalInterface - interface SimpleRuleAction extends RuleAction { - T run(Scope ruleScope); - - @Override - default T run(ParseState state) { - return this.run(state.scope()); - } - } - - record WrappedTerm(RuleAction action, Term child) implements Rule { - @Nullable - @Override - public T parse(ParseState state) { - Scope scope = state.scope(); - scope.pushFrame(); - - try { - if (!this.child.parse(state, scope, Control.UNBOUND)) { - return null; - } - - return this.action.run(state); - } finally { - scope.popFrame(); - } - } - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java deleted file mode 100644 index d152ecc89..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Scope.java +++ /dev/null @@ -1,315 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -import com.google.common.annotations.VisibleForTesting; -import net.momirealms.craftengine.core.util.MiscUtils; - -import javax.annotation.Nullable; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -@SuppressWarnings("unchecked") -public final class Scope { - private static final int NOT_FOUND = -1; - private static final Object FRAME_START_MARKER = new Object() { - @Override - public String toString() { - return "frame"; - } - }; - private static final int ENTRY_STRIDE = 2; - private Object[] stack = new Object[128]; - private int topEntryKeyIndex = 0; - private int topMarkerKeyIndex = 0; - private int depth; - - public Scope() { - this.stack[0] = FRAME_START_MARKER; - this.stack[1] = null; - } - - private int valueIndex(Atom atom) { - for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { - Object key = this.stack[i]; - - assert key instanceof Atom; - - if (key == atom) { - return i + 1; - } - } - - return NOT_FOUND; - } - - public int valueIndexForAny(Atom... atoms) { - for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { - Object key = this.stack[i]; - - assert key instanceof Atom; - - for (Atom atom : atoms) { - if (atom == key) { - return i + 1; - } - } - } - - return NOT_FOUND; - } - - private void ensureCapacity(int additionalEntryCount) { - int currentSize = this.stack.length; - int currentLastValueIndex = this.topEntryKeyIndex + 1; - int newLastValueIndex = currentLastValueIndex + additionalEntryCount * 2; - if (newLastValueIndex >= currentSize) { - int newSize = MiscUtils.growByHalf(currentSize, newLastValueIndex + 1); - Object[] newStack = new Object[newSize]; - System.arraycopy(this.stack, 0, newStack, 0, currentSize); - this.stack = newStack; - } - - assert this.validateStructure(); - } - - private void setupNewFrame() { - this.topEntryKeyIndex += ENTRY_STRIDE; - this.stack[this.topEntryKeyIndex] = FRAME_START_MARKER; - this.stack[this.topEntryKeyIndex + 1] = this.topMarkerKeyIndex; - this.topMarkerKeyIndex = this.topEntryKeyIndex; - } - - public void pushFrame() { - this.ensureCapacity(1); - this.setupNewFrame(); - - assert this.validateStructure(); - } - - private int getPreviousMarkerIndex(int markerKeyIndex) { - return (Integer) this.stack[markerKeyIndex + 1]; - } - - public void popFrame() { - assert this.topMarkerKeyIndex != 0; - - this.topEntryKeyIndex = this.topMarkerKeyIndex - ENTRY_STRIDE; - this.topMarkerKeyIndex = this.getPreviousMarkerIndex(this.topMarkerKeyIndex); - - assert this.validateStructure(); - } - - public void splitFrame() { - int currentFrameMarkerIndex = this.topMarkerKeyIndex; - int nonMarkerEntriesInFrame = (this.topEntryKeyIndex - this.topMarkerKeyIndex) / ENTRY_STRIDE; - this.ensureCapacity(nonMarkerEntriesInFrame + 1); - this.setupNewFrame(); - int sourceCursor = currentFrameMarkerIndex + ENTRY_STRIDE; - int targetCursor = this.topEntryKeyIndex; - - for (int i = 0; i < nonMarkerEntriesInFrame; i++) { - targetCursor += ENTRY_STRIDE; - Object key = this.stack[sourceCursor]; - - assert key != null; - - this.stack[targetCursor] = key; - this.stack[targetCursor + 1] = null; - sourceCursor += ENTRY_STRIDE; - } - - this.topEntryKeyIndex = targetCursor; - - assert this.validateStructure(); - } - - public void clearFrameValues() { - for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { - assert this.stack[i] instanceof Atom; - - this.stack[i + 1] = null; - } - - assert this.validateStructure(); - } - - public void mergeFrame() { - int previousMarkerIndex = this.getPreviousMarkerIndex(this.topMarkerKeyIndex); - int previousFrameCursor = previousMarkerIndex; - int currentFrameCursor = this.topMarkerKeyIndex; - - while (currentFrameCursor < this.topEntryKeyIndex) { - previousFrameCursor += ENTRY_STRIDE; - currentFrameCursor += ENTRY_STRIDE; - Object newKey = this.stack[currentFrameCursor]; - - assert newKey instanceof Atom; - - Object newValue = this.stack[currentFrameCursor + 1]; - Object oldKey = this.stack[previousFrameCursor]; - if (oldKey != newKey) { - this.stack[previousFrameCursor] = newKey; - this.stack[previousFrameCursor + 1] = newValue; - } else if (newValue != null) { - this.stack[previousFrameCursor + 1] = newValue; - } - } - - this.topEntryKeyIndex = previousFrameCursor; - this.topMarkerKeyIndex = previousMarkerIndex; - - assert this.validateStructure(); - } - - public void put(Atom name, @Nullable T value) { - int valueIndex = this.valueIndex(name); - if (valueIndex != NOT_FOUND) { - this.stack[valueIndex] = value; - } else { - this.ensureCapacity(1); - this.topEntryKeyIndex += ENTRY_STRIDE; - this.stack[this.topEntryKeyIndex] = name; - this.stack[this.topEntryKeyIndex + 1] = value; - } - - assert this.validateStructure(); - } - - @Nullable - public T get(Atom name) { - int valueIndex = this.valueIndex(name); - return (T) (valueIndex != NOT_FOUND ? this.stack[valueIndex] : null); - } - - public T getOrThrow(Atom name) { - int valueIndex = this.valueIndex(name); - if (valueIndex == NOT_FOUND) { - throw new IllegalArgumentException("No value for atom " + name); - } - return (T) this.stack[valueIndex]; - } - - public T getOrDefault(Atom name, T fallback) { - int valueIndex = this.valueIndex(name); - return (T) (valueIndex != NOT_FOUND ? this.stack[valueIndex] : fallback); - } - - @Nullable - @SafeVarargs - public final T getAny(Atom... names) { - int valueIndex = this.valueIndexForAny(names); - return (T) (valueIndex != NOT_FOUND ? this.stack[valueIndex] : null); - } - - @SafeVarargs - public final T getAnyOrThrow(Atom... names) { - int valueIndex = this.valueIndexForAny(names); - if (valueIndex == NOT_FOUND) { - throw new IllegalArgumentException("No value for atoms " + Arrays.toString(names)); - } - return (T) this.stack[valueIndex]; - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - boolean afterFrame = true; - - for (int i = 0; i <= this.topEntryKeyIndex; i += ENTRY_STRIDE) { - Object key = this.stack[i]; - Object value = this.stack[i + 1]; - if (key == FRAME_START_MARKER) { - result.append('|'); - afterFrame = true; - } else { - if (!afterFrame) { - result.append(','); - } - - afterFrame = false; - result.append(key).append(':').append(value); - } - } - - return result.toString(); - } - - @VisibleForTesting - public Map, ?> lastFrame() { - HashMap, Object> result = new HashMap<>(); - - for (int i = this.topEntryKeyIndex; i > this.topMarkerKeyIndex; i -= ENTRY_STRIDE) { - Object key = this.stack[i]; - Object value = this.stack[i + 1]; - result.put((Atom) key, value); - } - - return result; - } - - public boolean hasOnlySingleFrame() { - for (int i = this.topEntryKeyIndex; i > 0; i--) { - if (this.stack[i] == FRAME_START_MARKER) { - return false; - } - } - - if (this.stack[0] != FRAME_START_MARKER) { - throw new IllegalStateException("Corrupted stack"); - } - return true; - } - - private boolean validateStructure() { - assert this.topMarkerKeyIndex >= 0; - - assert this.topEntryKeyIndex >= this.topMarkerKeyIndex; - - for (int i = 0; i <= this.topEntryKeyIndex; i += ENTRY_STRIDE) { - Object object = this.stack[i]; - if (object != FRAME_START_MARKER && !(object instanceof Atom)) { - return false; - } - } - - for (int ix = this.topMarkerKeyIndex; ix != 0; ix = this.getPreviousMarkerIndex(ix)) { - Object object = this.stack[ix]; - if (object != FRAME_START_MARKER) { - return false; - } - } - - return true; - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - public static Term increaseDepth() { - class IncreasingDepthTerm implements Term { - public static final IncreasingDepthTerm INSTANCE = new IncreasingDepthTerm(); - - @Override - public boolean parse(final ParseState state, final Scope scope, final Control control) { - if (++scope.depth > 512) { - state.errorCollector().store(state.mark(), new IllegalStateException("Too deep")); - return false; - } - return true; - } - } - return (Term) IncreasingDepthTerm.INSTANCE; - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - public static Term decreaseDepth() { - class DecreasingDepthTerm implements Term { - public static final DecreasingDepthTerm INSTANCE = new DecreasingDepthTerm(); - - @Override - public boolean parse(final ParseState state, final Scope scope, final Control control) { - scope.depth--; - return true; - } - } - return (Term) DecreasingDepthTerm.INSTANCE; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java deleted file mode 100644 index 30c32ac7c..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/SuggestionSupplier.java +++ /dev/null @@ -1,11 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -import java.util.stream.Stream; - -public interface SuggestionSupplier { - Stream possibleValues(ParseState state); - - static SuggestionSupplier empty() { - return state -> Stream.empty(); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java deleted file mode 100644 index 383602b0e..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/Term.java +++ /dev/null @@ -1,235 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse; - -import java.util.ArrayList; -import java.util.List; - -public interface Term { - boolean parse(ParseState state, Scope scope, Control control); - - static Term marker(Atom name, T value) { - return new Marker<>(name, value); - } - - @SafeVarargs - static Term sequence(Term... terms) { - return new Sequence<>(terms); - } - - @SafeVarargs - static Term alternative(Term... terms) { - return new Alternative<>(terms); - } - - static Term optional(Term term) { - return new Maybe<>(term); - } - - static Term repeated(NamedRule element, Atom> listName) { - return repeated(element, listName, 0); - } - - static Term repeated(NamedRule element, Atom> listName, int minRepetitions) { - return new Repeated<>(element, listName, minRepetitions); - } - - static Term repeatedWithTrailingSeparator(NamedRule element, Atom> listName, Term separator) { - return repeatedWithTrailingSeparator(element, listName, separator, 0); - } - - static Term repeatedWithTrailingSeparator(NamedRule element, Atom> listName, Term seperator, int minRepetitions) { - return new RepeatedWithSeparator<>(element, listName, seperator, minRepetitions, true); - } - - static Term positiveLookahead(Term term) { - return new LookAhead<>(term, true); - } - - static Term cut() { - return new Term<>() { - @Override - public boolean parse(ParseState state, Scope scope, Control control) { - control.cut(); - return true; - } - - @Override - public String toString() { - return "↑"; - } - }; - } - - static Term empty() { - return new Term<>() { - @Override - public boolean parse(ParseState state, Scope scope, Control control) { - return true; - } - - @Override - public String toString() { - return "ε"; - } - }; - } - - static Term fail(final Object message) { - return new Term<>() { - @Override - public boolean parse(ParseState state, Scope scope, Control control) { - state.errorCollector().store(state.mark(), message); - return false; - } - - @Override - public String toString() { - return "fail"; - } - }; - } - - record Alternative(Term[] elements) implements Term { - @Override - public boolean parse(ParseState state, Scope scope, Control control) { - Control controlForThis = state.acquireControl(); - - try { - int mark = state.mark(); - scope.splitFrame(); - - for (Term element : this.elements) { - if (element.parse(state, scope, controlForThis)) { - scope.mergeFrame(); - return true; - } - - scope.clearFrameValues(); - state.restore(mark); - if (controlForThis.hasCut()) { - break; - } - } - - scope.popFrame(); - return false; - } finally { - state.releaseControl(); - } - } - } - - record LookAhead(Term term, boolean positive) implements Term { - @Override - public boolean parse(ParseState state, Scope scope, Control control) { - int mark = state.mark(); - boolean result = this.term.parse(state.silent(), scope, control); - state.restore(mark); - return this.positive == result; - } - } - - record Marker(Atom name, T value) implements Term { - @Override - public boolean parse(ParseState state, Scope scope, Control control) { - scope.put(this.name, this.value); - return true; - } - } - - record Maybe(Term term) implements Term { - @Override - public boolean parse(ParseState state, Scope scope, Control control) { - int mark = state.mark(); - if (!this.term.parse(state, scope, control)) { - state.restore(mark); - } - - return true; - } - } - - record Repeated(NamedRule element, Atom> listName, int minRepetitions) implements Term { - @Override - public boolean parse(ParseState state, Scope scope, Control control) { - int mark = state.mark(); - List elements = new ArrayList<>(this.minRepetitions); - - while (true) { - int entryMark = state.mark(); - T parsedElement = state.parse(this.element); - if (parsedElement == null) { - state.restore(entryMark); - if (elements.size() < this.minRepetitions) { - state.restore(mark); - return false; - } - scope.put(this.listName, elements); - return true; - } - - elements.add(parsedElement); - } - } - } - - record RepeatedWithSeparator( - NamedRule element, Atom> listName, Term separator, int minRepetitions, boolean allowTrailingSeparator - ) implements Term { - @Override - public boolean parse(ParseState state, Scope scope, Control control) { - int listMark = state.mark(); - List elements = new ArrayList<>(this.minRepetitions); - boolean first = true; - - while (true) { - int markBeforeSeparator = state.mark(); - if (!first && !this.separator.parse(state, scope, control)) { - state.restore(markBeforeSeparator); - break; - } - - int markAfterSeparator = state.mark(); - T parsedElement = state.parse(this.element); - if (parsedElement == null) { - if (first) { - state.restore(markAfterSeparator); - } else { - if (!this.allowTrailingSeparator) { - state.restore(listMark); - return false; - } - - state.restore(markAfterSeparator); - } - break; - } - - elements.add(parsedElement); - first = false; - } - - if (elements.size() < this.minRepetitions) { - state.restore(listMark); - return false; - } - scope.put(this.listName, elements); - return true; - } - } - - record Sequence(Term[] elements) implements Term { - @Override - public boolean parse(ParseState state, Scope scope, Control control) { - int mark = state.mark(); - - for (Term element : this.elements) { - if (!element.parse(state, scope, control)) { - state.restore(mark); - return false; - } - } - - return true; - } - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java deleted file mode 100644 index e93117570..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/Grammar.java +++ /dev/null @@ -1,51 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse.grammar; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.momirealms.craftengine.core.util.snbt.parse.*; - -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -public record Grammar(Dictionary rules, NamedRule top) { - public Grammar { - rules.checkAllBound(); - } - - public Optional parse(ParseState state) { - return state.parseTopRule(this.top); - } - - public T parse(StringReader reader) throws CommandSyntaxException { - ErrorCollector.LongestOnly errorCollector = new ErrorCollector.LongestOnly<>(); - StringReaderParserState stringReaderParserState = new StringReaderParserState(errorCollector, reader); - Optional optionalResult = this.parse(stringReaderParserState); - if (optionalResult.isPresent()) { - T result = optionalResult.get(); - if (CachedParseState.JAVA_NULL_VALUE_MARKER.equals(result)) { - result = null; - } - return result; - } - List> errorEntries = errorCollector.entries(); - List exceptions = errorEntries.stream().mapMulti((entry, output) -> { - if (entry.reason() instanceof DelayedException delayedException) { - output.accept(delayedException.create(reader.getString(), entry.cursor())); - } else if (entry.reason() instanceof Exception exception1) { - output.accept(exception1); - } - }).toList(); - - for (Exception exception : exceptions) { - if (exception instanceof CommandSyntaxException cse) { - throw cse; - } - } - - if (exceptions.size() == 1 && exceptions.getFirst() instanceof RuntimeException re) { - throw re; - } - throw new IllegalStateException("Failed to parse: " + errorEntries.stream().map(ErrorEntry::toString).collect(Collectors.joining(", "))); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java deleted file mode 100644 index 58bf9d797..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPatternParseRule.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse.grammar; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.momirealms.craftengine.core.util.snbt.parse.DelayedException; -import net.momirealms.craftengine.core.util.snbt.parse.ParseState; -import net.momirealms.craftengine.core.util.snbt.parse.Rule; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public final class GreedyPatternParseRule implements Rule { - private final Pattern pattern; - private final DelayedException error; - - public GreedyPatternParseRule(Pattern pattern, DelayedException error) { - this.pattern = pattern; - this.error = error; - } - - @Override - public String parse(ParseState state) { - StringReader input = state.input(); - String fullString = input.getString(); - Matcher matcher = this.pattern.matcher(fullString).region(input.getCursor(), fullString.length()); - if (!matcher.lookingAt()) { - state.errorCollector().store(state.mark(), this.error); - return null; - } - input.setCursor(matcher.end()); - return matcher.group(0); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java deleted file mode 100644 index 3144dcc27..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/GreedyPredicateParseRule.java +++ /dev/null @@ -1,48 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse.grammar; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.momirealms.craftengine.core.util.snbt.parse.DelayedException; -import net.momirealms.craftengine.core.util.snbt.parse.ParseState; -import net.momirealms.craftengine.core.util.snbt.parse.Rule; - -import javax.annotation.Nullable; - -public abstract class GreedyPredicateParseRule implements Rule { - private final int minSize; - private final int maxSize; - private final DelayedException error; - - public GreedyPredicateParseRule(int minSize, DelayedException error) { - this(minSize, Integer.MAX_VALUE, error); - } - - public GreedyPredicateParseRule(int minSize, int maxSize, DelayedException error) { - this.minSize = minSize; - this.maxSize = maxSize; - this.error = error; - } - - @Nullable - @Override - public String parse(ParseState state) { - StringReader input = state.input(); - String fullString = input.getString(); - int start = input.getCursor(); - int pos = start; - - while (pos < fullString.length() && this.isAccepted(fullString.charAt(pos)) && pos - start < this.maxSize) { - pos++; - } - - int length = pos - start; - if (length < this.minSize) { - state.errorCollector().store(state.mark(), this.error); - return null; - } - input.setCursor(pos); - return fullString.substring(start, pos); - } - - protected abstract boolean isAccepted(char c); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java deleted file mode 100644 index dd4e6249b..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/NumberRunParseRule.java +++ /dev/null @@ -1,46 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse.grammar; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.momirealms.craftengine.core.util.snbt.parse.DelayedException; -import net.momirealms.craftengine.core.util.snbt.parse.ParseState; -import net.momirealms.craftengine.core.util.snbt.parse.Rule; - -import javax.annotation.Nullable; - -public abstract class NumberRunParseRule implements Rule { - private final DelayedException noValueError; - private final DelayedException underscoreNotAllowedError; - - public NumberRunParseRule(DelayedException noValueError, DelayedException underscoreNotAllowedError) { - this.noValueError = noValueError; - this.underscoreNotAllowedError = underscoreNotAllowedError; - } - - @Nullable - @Override - public String parse(ParseState state) { - StringReader input = state.input(); - input.skipWhitespace(); - String fullString = input.getString(); - int start = input.getCursor(); - int pos = start; - - while (pos < fullString.length() && this.isAccepted(fullString.charAt(pos))) { - pos++; - } - - int length = pos - start; - if (length == 0) { - state.errorCollector().store(state.mark(), this.noValueError); - return null; - } else if (fullString.charAt(start) != '_' && fullString.charAt(pos - 1) != '_') { - input.setCursor(pos); - return fullString.substring(start, pos); - } - state.errorCollector().store(state.mark(), this.underscoreNotAllowedError); - return null; - } - - protected abstract boolean isAccepted(char c); -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java deleted file mode 100644 index d076fafd4..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderParserState.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse.grammar; - -import com.mojang.brigadier.StringReader; -import net.momirealms.craftengine.core.util.snbt.parse.CachedParseState; -import net.momirealms.craftengine.core.util.snbt.parse.ErrorCollector; - -public class StringReaderParserState extends CachedParseState { - private final StringReader input; - - public StringReaderParserState(ErrorCollector errorCollector, StringReader input) { - super(errorCollector); - this.input = input; - } - - @Override - public StringReader input() { - return this.input; - } - - @Override - public int mark() { - return this.input.getCursor(); - } - - @Override - public void restore(int mark) { - this.input.setCursor(mark); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java deleted file mode 100644 index 14fe3f3aa..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/StringReaderTerms.java +++ /dev/null @@ -1,64 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse.grammar; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; -import it.unimi.dsi.fastutil.chars.CharList; -import net.momirealms.craftengine.core.util.snbt.parse.*; - -import java.util.stream.Collectors; - -public interface StringReaderTerms { - DynamicCommandExceptionType LITERAL_INCORRECT = new LocalizedDynamicCommandExceptionType( - expected -> new LocalizedMessage("warning.config.type.snbt.parser.incorrect", String.valueOf(expected)) - ); - - static Term character(final char value) { - return new TerminalCharacters(CharList.of(value)) { - @Override - protected boolean isAccepted(char v) { - return value == v; - } - }; - } - - static Term characters(final char v1, final char v2) { - return new TerminalCharacters(CharList.of(v1, v2)) { - @Override - protected boolean isAccepted(char v) { - return v == v1 || v == v2; - } - }; - } - - static StringReader createReader(String contents, int cursor) { - StringReader reader = new StringReader(contents); - reader.setCursor(cursor); - return reader; - } - - abstract class TerminalCharacters implements Term { - private final DelayedException error; - private final SuggestionSupplier suggestions; - - public TerminalCharacters(CharList values) { - String joinedValues = values.intStream().mapToObj(Character::toString).collect(Collectors.joining("|")); - this.error = DelayedException.create(LITERAL_INCORRECT, joinedValues); - this.suggestions = s -> values.intStream().mapToObj(Character::toString); - } - - @Override - public boolean parse(ParseState state, Scope scope, Control control) { - state.input().skipWhitespace(); - int cursor = state.mark(); - if (state.input().canRead() && this.isAccepted(state.input().read())) { - return true; - } - state.errorCollector().store(cursor, this.suggestions, this.error); - return false; - } - - protected abstract boolean isAccepted(char value); - } - -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java b/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java deleted file mode 100644 index 2d46092b7..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/util/snbt/parse/grammar/UnquotedStringParseRule.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.momirealms.craftengine.core.util.snbt.parse.grammar; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.momirealms.craftengine.core.util.snbt.parse.DelayedException; -import net.momirealms.craftengine.core.util.snbt.parse.ParseState; -import net.momirealms.craftengine.core.util.snbt.parse.Rule; - -import javax.annotation.Nullable; - -public class UnquotedStringParseRule implements Rule { - private final int minSize; - private final DelayedException error; - - public UnquotedStringParseRule(int minSize, DelayedException error) { - this.minSize = minSize; - this.error = error; - } - - @Nullable - @Override - public String parse(ParseState state) { - state.input().skipWhitespace(); - int cursor = state.mark(); - String value = state.input().readUnquotedString(); - if (value.length() < this.minSize) { - state.errorCollector().store(cursor, this.error); - return null; - } - return value; - } -} diff --git a/gradle.properties b/gradle.properties index 95c1bf9cc..1b2eee6a6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -38,7 +38,7 @@ zstd_version=1.5.7-6 commons_io_version=2.21.0 commons_lang3_version=3.20.0 sparrow_nbt_version=0.10.6 -sparrow_util_version=0.66 +sparrow_util_version=0.67 fastutil_version=8.5.18 netty_version=4.1.128.Final joml_version=1.10.8 From 1ef8ebc9df91d151e91d3372cbf47efc5cf61f73 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 17:42:53 +0800 Subject: [PATCH 109/135] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=AD=90=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=88=B0=E6=9C=80=E6=96=B0=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wiki b/wiki index bec8bb7cf..eb2118f0d 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit bec8bb7cf8c4331dd54bdc4bb27edfa664805e66 +Subproject commit eb2118f0dbf8c6faab8f023434d6e3c5c861a59d From 727df39d2c3cf6bd125b3411c9694e165af5878f Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 17:49:47 +0800 Subject: [PATCH 110/135] Update ComponentsModifier.java --- .../craftengine/core/item/modifier/ComponentsModifier.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java index d9a56564a..ade22825d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.item.modifier; import com.google.gson.JsonElement; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.momirealms.craftengine.core.item.*; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; From 15593345eae7463138c6dbe561f317dcdf360c02 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 18:30:36 +0800 Subject: [PATCH 111/135] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/api/CraftEngineFurniture.java | 24 ++++++------- .../api/event/FurnitureAttemptPlaceEvent.java | 8 ++--- .../entity/furniture/BukkitFurniture.java | 2 +- .../furniture/BukkitFurnitureManager.java | 14 ++++---- .../item/behavior/FurnitureItemBehavior.java | 4 +-- .../feature/DebugSpawnFurnitureCommand.java | 8 ++--- .../src/main/resources/translations/en.yml | 2 ++ .../block/behavior/BlockBehaviorFactory.java | 1 + .../furniture/AbstractFurnitureManager.java | 9 +++-- ...nitureConfig.java => CustomFurniture.java} | 6 ++-- ...nfigImpl.java => CustomFurnitureImpl.java} | 8 ++--- .../core/entity/furniture/Furniture.java | 6 ++-- .../furniture/FurnitureDataAccessor.java | 3 +- .../entity/furniture/FurnitureManager.java | 8 ++--- .../entity/furniture/FurnitureSounds.java | 17 +++------- .../behavior/FurnitureBehaviorFactory.java | 8 +++++ .../behavior/FurnitureBehaviorType.java | 6 ++++ .../behavior/FurnitureBehaviorTypes.java | 34 +++++++++++++++++++ .../item/modifier/ComponentsModifier.java | 6 +--- .../remainder/CraftRemainderFactory.java | 4 +-- .../recipe/remainder/CraftRemainders.java | 6 ++-- .../recipe/remainder/FixedCraftRemainder.java | 4 +-- .../remainder/HurtAndBreakRemainder.java | 4 +-- .../remainder/RecipeBasedCraftRemainder.java | 4 +-- .../config/template/ArgumentString.java | 1 - .../core/registry/BuiltInRegistries.java | 4 ++- .../craftengine/core/registry/Registries.java | 4 ++- 27 files changed, 122 insertions(+), 83 deletions(-) rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/{FurnitureConfig.java => CustomFurniture.java} (95%) rename core/src/main/java/net/momirealms/craftengine/core/entity/furniture/{FurnitureConfigImpl.java => CustomFurnitureImpl.java} (94%) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorFactory.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorType.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorTypes.java 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 096f42b67..b0bd2dac4 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 @@ -9,8 +9,8 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.entity.furniture.AnchorType; +import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.item.Item; @@ -49,7 +49,7 @@ public final class CraftEngineFurniture { * @return a non-null map containing all loaded custom furniture */ @NotNull - public static Map loadedFurniture() { + public static Map loadedFurniture() { return BukkitFurnitureManager.instance().loadedFurniture(); } @@ -59,7 +59,7 @@ public final class CraftEngineFurniture { * @param id id * @return the custom furniture */ - public static FurnitureConfig byId(@NotNull Key id) { + public static CustomFurniture byId(@NotNull Key id) { return BukkitFurnitureManager.instance().furnitureById(id).orElse(null); } @@ -111,7 +111,7 @@ public final class CraftEngineFurniture { */ @Nullable public static BukkitFurniture place(Location location, Key furnitureId) { - FurnitureConfig furniture = byId(furnitureId); + CustomFurniture furniture = byId(furnitureId); if (furniture == null) return null; return place(location, furniture, furniture.anyVariantName(), false); } @@ -140,7 +140,7 @@ public final class CraftEngineFurniture { */ @Nullable public static BukkitFurniture place(Location location, Key furnitureId, String variant) { - FurnitureConfig furniture = byId(furnitureId); + CustomFurniture furniture = byId(furnitureId); if (furniture == null) return null; return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.ofVariant(variant), true); } @@ -155,7 +155,7 @@ public final class CraftEngineFurniture { */ @NotNull @Deprecated(since = "0.0.66", forRemoval = true) - public static BukkitFurniture place(Location location, FurnitureConfig furniture, AnchorType anchorType) { + public static BukkitFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType) { return place(location, furniture, anchorType.variantName(), true); } @@ -171,7 +171,7 @@ public final class CraftEngineFurniture { @Nullable @Deprecated(since = "0.0.66", forRemoval = true) public static BukkitFurniture place(Location location, Key furnitureId, AnchorType anchorType, boolean playSound) { - FurnitureConfig furniture = byId(furnitureId); + CustomFurniture furniture = byId(furnitureId); if (furniture == null) return null; return place(location, furniture, anchorType.variantName(), playSound); } @@ -187,7 +187,7 @@ public final class CraftEngineFurniture { */ @Nullable public static BukkitFurniture place(Location location, Key furnitureId, String variant, boolean playSound) { - FurnitureConfig furniture = byId(furnitureId); + CustomFurniture furniture = byId(furnitureId); if (furniture == null) return null; return place(location, furniture, variant, playSound); } @@ -203,7 +203,7 @@ public final class CraftEngineFurniture { */ @NotNull @Deprecated(since = "0.0.66", forRemoval = true) - public static BukkitFurniture place(Location location, FurnitureConfig furniture, AnchorType anchorType, boolean playSound) { + public static BukkitFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType, boolean playSound) { return place(location, furniture, anchorType.variantName(), playSound); } @@ -217,7 +217,7 @@ public final class CraftEngineFurniture { * @return the loaded furniture */ @NotNull - public static BukkitFurniture place(Location location, FurnitureConfig furniture, String variant, boolean playSound) { + public static BukkitFurniture place(Location location, CustomFurniture furniture, String variant, boolean playSound) { return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.ofVariant(variant), playSound); } @@ -231,7 +231,7 @@ public final class CraftEngineFurniture { * @return the loaded furniture */ @NotNull - public static BukkitFurniture place(Location location, FurnitureConfig furniture, CompoundTag data, boolean playSound) { + public static BukkitFurniture place(Location location, CustomFurniture furniture, CompoundTag data, boolean playSound) { return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.of(data), playSound); } @@ -245,7 +245,7 @@ public final class CraftEngineFurniture { * @return the loaded furniture */ @NotNull - public static BukkitFurniture place(Location location, FurnitureConfig furniture, FurnitureDataAccessor dataAccessor, boolean playSound) { + public static BukkitFurniture place(Location location, CustomFurniture furniture, FurnitureDataAccessor dataAccessor, boolean playSound) { return BukkitFurnitureManager.instance().place(location, furniture, dataAccessor, playSound); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java index 7e492239e..566a516da 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/event/FurnitureAttemptPlaceEvent.java @@ -1,6 +1,6 @@ package net.momirealms.craftengine.bukkit.api.event; -import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; +import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant; import net.momirealms.craftengine.core.entity.player.InteractionHand; import org.bukkit.Location; @@ -14,14 +14,14 @@ import org.jetbrains.annotations.NotNull; public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Cancellable { private static final HandlerList HANDLER_LIST = new HandlerList(); private boolean cancelled; - private final FurnitureConfig furniture; + private final CustomFurniture furniture; private final Location location; private final FurnitureVariant variant; private final Block clickedBlock; private final InteractionHand hand; public FurnitureAttemptPlaceEvent(@NotNull Player player, - @NotNull FurnitureConfig furniture, + @NotNull CustomFurniture furniture, @NotNull FurnitureVariant variant, @NotNull Location location, @NotNull InteractionHand hand, @@ -60,7 +60,7 @@ public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Can } @NotNull - public FurnitureConfig furniture() { + public CustomFurniture furniture() { return furniture; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index 08bb220f8..ec5a845ca 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -30,7 +30,7 @@ public class BukkitFurniture extends Furniture { private final WeakReference metaEntity; private Location location; - public BukkitFurniture(ItemDisplay metaEntity, FurnitureConfig config, FurnitureDataAccessor data) { + public BukkitFurniture(ItemDisplay metaEntity, CustomFurniture config, FurnitureDataAccessor data) { super(new BukkitEntity(metaEntity), data, config); this.metaEntity = new WeakReference<>(metaEntity); this.location = metaEntity.getLocation(); 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 c1cb3f717..88e847160 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 @@ -62,11 +62,11 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } @Override - public Furniture place(WorldPosition position, FurnitureConfig furniture, FurnitureDataAccessor dataAccessor, boolean playSound) { + public Furniture place(WorldPosition position, CustomFurniture furniture, FurnitureDataAccessor dataAccessor, boolean playSound) { return this.place(LocationUtils.toLocation(position), furniture, dataAccessor, playSound); } - public BukkitFurniture place(Location location, FurnitureConfig furniture, FurnitureDataAccessor data, boolean playSound) { + public BukkitFurniture place(Location location, CustomFurniture furniture, FurnitureDataAccessor data, boolean playSound) { Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> { ItemDisplay display = (ItemDisplay) entity; display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_KEY, PersistentDataType.STRING, furniture.id().toString()); @@ -218,7 +218,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { // 获取家具配置 Key key = Key.of(id); - Optional optionalFurniture = furnitureById(key); + Optional optionalFurniture = furnitureById(key); if (optionalFurniture.isEmpty()) return; // 只对1.20.2及以上生效,1.20.1比较特殊 @@ -227,7 +227,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } // 已经在其他事件里加载过了 - FurnitureConfig customFurniture = optionalFurniture.get(); + CustomFurniture customFurniture = optionalFurniture.get(); BukkitFurniture previous = this.byMetaEntityId.get(entity.getEntityId()); if (previous != null) return; @@ -254,11 +254,11 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { // 获取家具配置 Key key = Key.of(id); - Optional optionalFurniture = furnitureById(key); + Optional optionalFurniture = furnitureById(key); if (optionalFurniture.isEmpty()) return; // 已经在其他事件里加载过了 - FurnitureConfig customFurniture = optionalFurniture.get(); + CustomFurniture customFurniture = optionalFurniture.get(); BukkitFurniture previous = this.byMetaEntityId.get(entity.getEntityId()); if (previous != null) return; @@ -321,7 +321,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } // 创建家具实例,并初始化碰撞实体 - private BukkitFurniture createFurnitureInstance(ItemDisplay display, FurnitureConfig furniture) { + private BukkitFurniture createFurnitureInstance(ItemDisplay display, CustomFurniture furniture) { BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, getFurnitureDataAccessor(display)); initFurniture(bukkitFurniture); Location location = display.getLocation(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index 12e62569e..a40c5a34e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -74,7 +74,7 @@ public class FurnitureItemBehavior extends ItemBehavior { } public InteractionResult place(UseOnContext context) { - Optional optionalCustomFurniture = BukkitFurnitureManager.instance().furnitureById(this.id); + Optional optionalCustomFurniture = BukkitFurnitureManager.instance().furnitureById(this.id); if (optionalCustomFurniture.isEmpty()) { CraftEngine.instance().logger().warn("Furniture " + this.id + " not found"); return InteractionResult.FAIL; @@ -87,7 +87,7 @@ public class FurnitureItemBehavior extends ItemBehavior { case DOWN -> AnchorType.CEILING; }; - FurnitureConfig customFurniture = optionalCustomFurniture.get(); + CustomFurniture customFurniture = optionalCustomFurniture.get(); FurnitureVariant variant = customFurniture.getVariant(anchorType.variantName()); if (variant == null) { return InteractionResult.FAIL; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java index 832a1c40e..016169435 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java @@ -4,7 +4,7 @@ import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.bukkit.util.KeyUtils; -import net.momirealms.craftengine.core.entity.furniture.FurnitureConfig; +import net.momirealms.craftengine.core.entity.furniture.CustomFurniture; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.FlagKeys; @@ -48,7 +48,7 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature optionalCustomFurniture = furnitureManager.furnitureById(id); + Optional optionalCustomFurniture = furnitureManager.furnitureById(id); return optionalCustomFurniture.>>map(config -> CompletableFuture.completedFuture(config.variants().keySet().stream().map(Suggestion::suggestion).toList())).orElseGet(() -> CompletableFuture.completedFuture(List.of())); } })) @@ -57,12 +57,12 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature optionalCustomFurniture = furnitureManager.furnitureById(id); + Optional optionalCustomFurniture = furnitureManager.furnitureById(id); if (optionalCustomFurniture.isEmpty()) { return; } Location location = context.get("location"); - FurnitureConfig customFurniture = optionalCustomFurniture.get(); + CustomFurniture customFurniture = optionalCustomFurniture.get(); String variant = (String) context.optional("variant").orElse(customFurniture.anyVariantName()); boolean playSound = context.flags().hasFlag("silent"); CraftEngineFurniture.place(location, customFurniture, variant, playSound); diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index bb9da733c..e42ae799d 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -238,6 +238,8 @@ warning.config.furniture.element.item_display.missing_item: "Issue found warning.config.furniture.settings.unknown: "Issue found in file - The furniture '' is using an unknown setting type ''." warning.config.furniture.hitbox.invalid_type: "Issue found in file - The furniture '' is using an invalid hitbox type ''." warning.config.furniture.hitbox.custom.invalid_entity: "Issue found in file - The furniture '' is using a custom hitbox with invalid entity type ''." +warning.config.furniture.behavior.missing_type: "Issue found in file - The furniture '' is missing the required 'type' argument for behavior." +warning.config.furniture.behavior.invalid_type: "Issue found in file - The furniture '' is using an invalid behavior type ''." warning.config.item.duplicate: "Issue found in file - Duplicated item ''. Please check if there is the same configuration in other files." warning.config.item.settings.unknown: "Issue found in file - The item '' is using an unknown setting type ''." warning.config.item.settings.invulnerable.invalid_damage_source: "Issue found in file - The item '' is using an unknown damage source ''. Allowed sources: []." diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/BlockBehaviorFactory.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/BlockBehaviorFactory.java index 287d7ecd4..3e2b97bad 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/BlockBehaviorFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/BlockBehaviorFactory.java @@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.block.CustomBlock; import java.util.Map; +// todo refactor this on 1.0 public interface BlockBehaviorFactory { BlockBehavior create(CustomBlock block, Map arguments); 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 c3c0d50a8..d757898ff 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 @@ -25,7 +25,7 @@ import java.nio.file.Path; import java.util.*; public abstract class AbstractFurnitureManager implements FurnitureManager { - protected final Map byId = new HashMap<>(); + protected final Map byId = new HashMap<>(); private final CraftEngine plugin; private final FurnitureParser furnitureParser; // Cached command suggestions @@ -60,12 +60,12 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { } @Override - public Optional furnitureById(Key id) { + public Optional furnitureById(Key id) { return Optional.ofNullable(this.byId.get(id)); } @Override - public Map loadedFurniture() { + public Map loadedFurniture() { return Collections.unmodifiableMap(this.byId); } @@ -130,7 +130,6 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { Optional optionalLootSpawnOffset = Optional.ofNullable(variantArguments.get("loot-spawn-offset")).map(it -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset")); List> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elements"), FurnitureElementConfigs::fromMap); - // fixme 外部模型不应该在这 Optional externalModel; if (variantArguments.containsKey("model-engine")) { externalModel = Optional.of(plugin.compatibilityManager().createModel("ModelEngine", variantArguments.get("model-engine").toString())); @@ -155,7 +154,7 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { )); } - FurnitureConfig furniture = FurnitureConfig.builder() + CustomFurniture furniture = CustomFurniture.builder() .id(id) .settings(FurnitureSettings.fromMap(MiscUtils.castToMap(section.get("settings"), true))) .variants(variants) diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java similarity index 95% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java index cd5634893..6a4b89499 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfig.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java @@ -14,7 +14,7 @@ import java.util.Locale; import java.util.Map; import java.util.Optional; -public interface FurnitureConfig { +public interface CustomFurniture { void execute(Context context, EventTrigger trigger); @@ -68,7 +68,7 @@ public interface FurnitureConfig { } static Builder builder() { - return new FurnitureConfigImpl.BuilderImpl(); + return new CustomFurnitureImpl.BuilderImpl(); } interface Builder { @@ -85,6 +85,6 @@ public interface FurnitureConfig { Builder behavior(FurnitureBehavior behavior); - FurnitureConfig build(); + CustomFurniture build(); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurnitureImpl.java similarity index 94% rename from core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java rename to core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurnitureImpl.java index facc01f27..ca01c0538 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureConfigImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurnitureImpl.java @@ -16,7 +16,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; -class FurnitureConfigImpl implements FurnitureConfig { +class CustomFurnitureImpl implements CustomFurniture { private final Key id; private final FurnitureSettings settings; private final Map variants; @@ -25,7 +25,7 @@ class FurnitureConfigImpl implements FurnitureConfig { @Nullable private final LootTable lootTable; - private FurnitureConfigImpl(@NotNull Key id, + private CustomFurnitureImpl(@NotNull Key id, @NotNull FurnitureSettings settings, @NotNull Map variants, @NotNull Map>> events, @@ -86,8 +86,8 @@ class FurnitureConfigImpl implements FurnitureConfig { private FurnitureBehavior behavior = EmptyFurnitureBehavior.INSTANCE; @Override - public FurnitureConfig build() { - return new FurnitureConfigImpl(this.id, this.settings, this.variants, this.events, this.behavior, this.lootTable); + public CustomFurniture build() { + return new CustomFurnitureImpl(this.id, this.settings, this.variants, this.events, this.behavior, this.lootTable); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index ec865cdff..98033012d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -35,7 +35,7 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; public abstract class Furniture implements Cullable { - public final FurnitureConfig config; + public final CustomFurniture config; public final FurnitureDataAccessor dataAccessor; public final Entity metaDataEntity; @@ -50,7 +50,7 @@ public abstract class Furniture implements Cullable { private boolean hasExternalModel; - protected Furniture(Entity metaDataEntity, FurnitureDataAccessor data, FurnitureConfig config) { + protected Furniture(Entity metaDataEntity, FurnitureDataAccessor data, CustomFurniture config) { this.config = config; this.dataAccessor = data; this.metaDataEntity = metaDataEntity; @@ -257,7 +257,7 @@ public abstract class Furniture implements Cullable { public abstract void destroy(); - public FurnitureConfig config() { + public CustomFurniture config() { return this.config; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java index fb4785432..84357733d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureDataAccessor.java @@ -112,7 +112,6 @@ public class FurnitureDataAccessor { this.data.putString(VARIANT, variant); } - @SuppressWarnings("deprecation") @ApiStatus.Obsolete public Optional anchorType() { if (this.data.containsKey(ANCHOR_TYPE)) return Optional.of(AnchorType.byId(this.data.getInt(ANCHOR_TYPE))); @@ -120,7 +119,7 @@ public class FurnitureDataAccessor { } @ApiStatus.Obsolete - public FurnitureDataAccessor anchorType(@SuppressWarnings("deprecation") AnchorType type) { + public FurnitureDataAccessor anchorType(AnchorType type) { this.data.putInt(ANCHOR_TYPE, type.getId()); return this; } 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 99d4ee3a0..e36080e4c 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 @@ -16,19 +16,17 @@ public interface FurnitureManager extends Manageable { Key FURNITURE_EXTRA_DATA_KEY = Key.of("craftengine:furniture_extra_data"); Key FURNITURE_COLLISION = Key.of("craftengine:collision"); - String FURNITURE_ADMIN_NODE = "craftengine.furniture.admin"; - ConfigParser parser(); void initSuggestions(); Collection cachedSuggestions(); - Furniture place(WorldPosition position, FurnitureConfig furniture, FurnitureDataAccessor extraData, boolean playSound); + Furniture place(WorldPosition position, CustomFurniture furniture, FurnitureDataAccessor extraData, boolean playSound); - Optional furnitureById(Key id); + Optional furnitureById(Key id); - Map loadedFurniture(); + Map loadedFurniture(); boolean isFurnitureMetaEntity(int entityId); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSounds.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSounds.java index 4434eaed4..55584e83f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSounds.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureSounds.java @@ -7,36 +7,29 @@ import java.util.Map; public class FurnitureSounds { public static final SoundData EMPTY_SOUND = new SoundData(Key.of("minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1); - public static final FurnitureSounds EMPTY = new FurnitureSounds(EMPTY_SOUND, EMPTY_SOUND, EMPTY_SOUND); + public static final FurnitureSounds EMPTY = new FurnitureSounds(EMPTY_SOUND, EMPTY_SOUND); private final SoundData breakSound; private final SoundData placeSound; - private final SoundData rotateSound; - public FurnitureSounds(SoundData breakSound, SoundData placeSound, SoundData rotateSound) { + public FurnitureSounds(SoundData breakSound, SoundData placeSound) { this.breakSound = breakSound; this.placeSound = placeSound; - this.rotateSound = rotateSound; } public static FurnitureSounds fromMap(Map map) { if (map == null) return EMPTY; return new FurnitureSounds( SoundData.create(map.getOrDefault("break", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_0_8), - SoundData.create(map.getOrDefault("place", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_0_8), - SoundData.create(map.getOrDefault("rotate", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_0_8) + SoundData.create(map.getOrDefault("place", "minecraft:intentionally_empty"), SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_0_8) ); } public SoundData breakSound() { - return breakSound; + return this.breakSound; } public SoundData placeSound() { - return placeSound; - } - - public SoundData rotateSound() { - return rotateSound; + return this.placeSound; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorFactory.java new file mode 100644 index 000000000..99e3eaa3c --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorFactory.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.entity.furniture.behavior; + +import java.util.Map; + +public interface FurnitureBehaviorFactory { + + T create(Map properties); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorType.java new file mode 100644 index 000000000..08d006d02 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorType.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.core.entity.furniture.behavior; + +import net.momirealms.craftengine.core.util.Key; + +public record FurnitureBehaviorType(Key id, FurnitureBehaviorFactory factory) { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorTypes.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorTypes.java new file mode 100644 index 000000000..18570cbd8 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorTypes.java @@ -0,0 +1,34 @@ +package net.momirealms.craftengine.core.entity.furniture.behavior; + +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.ResourceConfigUtils; +import net.momirealms.craftengine.core.util.ResourceKey; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +public class FurnitureBehaviorTypes { + public static final Key EMPTY = Key.from("craftengine:empty"); + + public static FurnitureBehavior fromMap(@Nullable Map map) { + if (map == null || map.isEmpty()) return EmptyFurnitureBehavior.INSTANCE; + String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.furniture.behavior.missing_type"); + Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); + FurnitureBehaviorType furnitureBehaviorType = BuiltInRegistries.FURNITURE_BEHAVIOR_TYPE.getValue(key); + if (furnitureBehaviorType == null) { + throw new LocalizedResourceConfigException("warning.config.furniture.behavior.invalid_type", type); + } + return furnitureBehaviorType.factory().create(map); + } + + public static FurnitureBehaviorType register(Key id, FurnitureBehaviorFactory factory) { + FurnitureBehaviorType type = new FurnitureBehaviorType<>(id, factory); + ((WritableRegistry>) BuiltInRegistries.FURNITURE_BEHAVIOR_TYPE) + .register(ResourceKey.create(Registries.BLOCK_BEHAVIOR_FACTORY.location(), id), type); + return type; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java index ade22825d..bb4213a32 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java @@ -4,11 +4,7 @@ import com.google.gson.JsonElement; import net.momirealms.craftengine.core.item.*; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.GsonHelper; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.Pair; -import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.util.TagParser; +import net.momirealms.craftengine.core.util.*; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.Tag; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/CraftRemainderFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/CraftRemainderFactory.java index a52960288..e01ecae98 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/CraftRemainderFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/CraftRemainderFactory.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.item.recipe.remainder; import java.util.Map; -public interface CraftRemainderFactory { +public interface CraftRemainderFactory { - CraftRemainder create(Map args); + T create(Map args); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/CraftRemainders.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/CraftRemainders.java index ed1551cac..8bb4a868f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/CraftRemainders.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/CraftRemainders.java @@ -23,15 +23,15 @@ public class CraftRemainders { register(HURT_AND_BREAK, HurtAndBreakRemainder.FACTORY); } - public static void register(Key key, CraftRemainderFactory factory) { - ((WritableRegistry) BuiltInRegistries.CRAFT_REMAINDER_FACTORY) + public static void register(Key key, CraftRemainderFactory factory) { + ((WritableRegistry>) BuiltInRegistries.CRAFT_REMAINDER_FACTORY) .register(ResourceKey.create(Registries.CRAFT_REMAINDER_FACTORY.location(), key), factory); } public static CraftRemainder fromMap(Map map) { String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.settings.craft_remainder.missing_type"); Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE); - CraftRemainderFactory factory = BuiltInRegistries.CRAFT_REMAINDER_FACTORY.getValue(key); + CraftRemainderFactory factory = BuiltInRegistries.CRAFT_REMAINDER_FACTORY.getValue(key); if (factory == null) { throw new LocalizedResourceConfigException("warning.config.item.settings.craft_remainder.invalid_type", type); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/FixedCraftRemainder.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/FixedCraftRemainder.java index 61302aa50..1275e7339 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/FixedCraftRemainder.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/FixedCraftRemainder.java @@ -21,10 +21,10 @@ public class FixedCraftRemainder implements CraftRemainder { return (Item) CraftEngine.instance().itemManager().createWrappedItem(this.item, null); } - public static class Factory implements CraftRemainderFactory { + public static class Factory implements CraftRemainderFactory { @Override - public CraftRemainder create(Map args) { + public FixedCraftRemainder create(Map args) { Key item = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(args.get("item"), "warning.config.item.settings.craft_remainder.fixed.missing_item")); return new FixedCraftRemainder(item); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/HurtAndBreakRemainder.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/HurtAndBreakRemainder.java index 8d304a4f4..8ca7bc1b4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/HurtAndBreakRemainder.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/HurtAndBreakRemainder.java @@ -28,10 +28,10 @@ public class HurtAndBreakRemainder implements CraftRemainder { } } - public static class Factory implements CraftRemainderFactory { + public static class Factory implements CraftRemainderFactory { @Override - public CraftRemainder create(Map args) { + public HurtAndBreakRemainder create(Map args) { int damage = ResourceConfigUtils.getAsInt(args.getOrDefault("damage", 1), "damage"); return new HurtAndBreakRemainder(damage); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/RecipeBasedCraftRemainder.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/RecipeBasedCraftRemainder.java index afe0b8e17..5cf66566e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/RecipeBasedCraftRemainder.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/remainder/RecipeBasedCraftRemainder.java @@ -30,10 +30,10 @@ public class RecipeBasedCraftRemainder implements CraftRemainder { return this.fallback != null ? this.fallback.remainder(recipeId, item) : null; } - public static class Factory implements CraftRemainderFactory { + public static class Factory implements CraftRemainderFactory { @Override - public CraftRemainder create(Map args) { + public RecipeBasedCraftRemainder create(Map args) { Map remainders = new HashMap<>(); List remainderList = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.requireNonNullOrThrow(args.get("terms"), "warning.config.item.settings.craft_remainder.recipe_based.missing_terms"), map -> { List recipes = MiscUtils.getAsStringList(map.get("recipes")).stream().map(Key::of).toList(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java index 0241d73b2..3394bed15 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.core.plugin.config.template; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.momirealms.craftengine.core.plugin.config.template.argument.TemplateArgument; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.TagParser; 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 287ca2752..85a3de7bd 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 @@ -5,6 +5,7 @@ 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.behavior.FurnitureBehaviorType; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; @@ -90,9 +91,10 @@ public class BuiltInRegistries { public static final Registry> MOD_PACKET = createConstantBoundRegistry(Registries.MOD_PACKET, 16); public static final Registry> BLOCK_ENTITY_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_TYPE, 64); public static final Registry> BLOCK_ENTITY_ELEMENT_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_ELEMENT_TYPE, 16); - public static final Registry CRAFT_REMAINDER_FACTORY = createConstantBoundRegistry(Registries.CRAFT_REMAINDER_FACTORY, 16); + public static final Registry> CRAFT_REMAINDER_FACTORY = createConstantBoundRegistry(Registries.CRAFT_REMAINDER_FACTORY, 16); public static final Registry> FURNITURE_ELEMENT_TYPE = createConstantBoundRegistry(Registries.FURNITURE_ELEMENT_TYPE, 16); public static final Registry> FURNITURE_HITBOX_TYPE = createConstantBoundRegistry(Registries.FURNITURE_HITBOX_TYPE, 16); + public static final Registry> FURNITURE_BEHAVIOR_TYPE = createConstantBoundRegistry(Registries.FURNITURE_BEHAVIOR_TYPE, 32); private static Registry createConstantBoundRegistry(ResourceKey> key, int expectedSize) { return new ConstantBoundRegistry<>(key, expectedSize); 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 4afecdcd7..102dec975 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 @@ -5,6 +5,7 @@ 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.behavior.FurnitureBehaviorType; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; @@ -92,7 +93,8 @@ public class Registries { 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")); - public static final ResourceKey> CRAFT_REMAINDER_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("craft_remainder_factory")); + public static final ResourceKey>> CRAFT_REMAINDER_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("craft_remainder_factory")); public static final ResourceKey>> FURNITURE_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_element_type")); public static final ResourceKey>> FURNITURE_HITBOX_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_hitbox_type")); + public static final ResourceKey>> FURNITURE_BEHAVIOR_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_behavior_type")); } From 456dfb00bc0399fdc5d9af769c7b206920a413de Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 20:29:44 +0800 Subject: [PATCH 112/135] =?UTF-8?q?=E5=AE=B6=E5=85=B7=E8=A1=8C=E4=B8=BAAPI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity/furniture/BukkitFurniture.java | 20 ++++ .../furniture/BukkitFurnitureManager.java | 37 +++++-- .../furniture/AbstractFurnitureManager.java | 99 ++++++++++++++++++- .../behavior/EmptyFurnitureBehavior.java | 3 + .../furniture/behavior/FurnitureBehavior.java | 2 + .../behavior/FurnitureBehaviorFactory.java | 3 + .../behavior/FurnitureBehaviorType.java | 2 + .../behavior/FurnitureBehaviorTypes.java | 3 +- .../furniture/tick/FurnitureTicker.java | 2 + .../furniture/tick/TickingFurniture.java | 13 +++ .../furniture/tick/TickingFurnitureImpl.java | 30 ++++++ .../item/recipe/AbstractRecipeSerializer.java | 7 +- .../item/recipe/CustomShapelessRecipe.java | 5 +- .../core/item/recipe/PlacementInfo.java | 2 +- ...ntityTickersList.java => TickersList.java} | 15 +-- .../craftengine/core/world/CEWorld.java | 50 +++++----- 16 files changed, 237 insertions(+), 56 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/TickingFurniture.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/TickingFurnitureImpl.java rename core/src/main/java/net/momirealms/craftengine/core/util/{BlockEntityTickersList.java => TickersList.java} (87%) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index ec5a845ca..ab51b86ff 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; +@SuppressWarnings("DuplicatedCode") public class BukkitFurniture extends Furniture { private final WeakReference metaEntity; private Location location; @@ -54,6 +55,25 @@ public class BukkitFurniture extends Furniture { FurnitureVariant variant = this.config.getVariant(variantName); if (variant == null) return false; if (this.currentVariant == variant) return false; + // 检查新位置是否可用 + List aabbs = new ArrayList<>(); + WorldPosition position = position(); + for (FurnitureHitBoxConfig hitBoxConfig : variant.hitBoxConfigs()) { + hitBoxConfig.prepareBoundingBox(position, aabbs::add, false); + } + if (!aabbs.isEmpty()) { + if (!FastNMS.INSTANCE.checkEntityCollision(position.world.serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList(), + o -> { + for (Collider collider : super.colliders) { + if (o == collider.handle()) { + return false; + } + } + return true; + })) { + return false; + } + } // 删除椅子 super.destroySeats(); BukkitFurnitureManager.instance().invalidateFurniture(this); 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 88e847160..6a441f42f 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 @@ -12,6 +12,9 @@ import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.entity.furniture.tick.FurnitureTicker; +import net.momirealms.craftengine.core.entity.furniture.tick.TickingFurniture; +import net.momirealms.craftengine.core.entity.furniture.tick.TickingFurnitureImpl; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.util.Key; @@ -86,6 +89,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { @Override public void delayedInit() { + super.delayedInit(); + // 确定碰撞箱实体类型 COLLISION_ENTITY_TYPE = Config.colliderType(); COLLISION_ENTITY_CLASS = Config.colliderType() == ColliderType.INTERACTION ? Interaction.class : Boat.class; @@ -127,6 +132,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { @Override public void disable() { + super.disable(); HandlerList.unregisterAll(this.furnitureEventListener); unload(); } @@ -330,19 +336,38 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } protected void initFurniture(BukkitFurniture furniture) { - this.byMetaEntityId.put(furniture.entityId(), furniture); - for (int entityId : furniture.virtualEntityIds()) { - this.byVirtualEntityId.put(entityId, furniture); + int entityId = furniture.entityId(); + this.byMetaEntityId.put(entityId, furniture); + for (int id : furniture.virtualEntityIds()) { + this.byVirtualEntityId.put(id, furniture); } for (Collider collisionEntity : furniture.colliders()) { this.byColliderEntityId.put(collisionEntity.entityId(), furniture); } + if (!this.syncTickers.containsKey(entityId)) { + FurnitureTicker ticker = furniture.config.behavior().createSyncFurnitureTicker(furniture); + if (ticker != null) { + TickingFurnitureImpl tickingFurniture = new TickingFurnitureImpl<>(furniture, ticker); + this.syncTickers.put(entityId, tickingFurniture); + this.addSyncFurnitureTicker(tickingFurniture); + } + } + if (!this.asyncTickers.containsKey(entityId)) { + FurnitureTicker ticker = furniture.config.behavior().createAsyncBlockEntityTicker(furniture); + if (ticker != null) { + TickingFurnitureImpl tickingFurniture = new TickingFurnitureImpl<>(furniture, ticker); + this.asyncTickers.put(entityId, tickingFurniture); + this.addAsyncFurnitureTicker(tickingFurniture); + } + } } protected void invalidateFurniture(BukkitFurniture furniture) { - this.byMetaEntityId.remove(furniture.entityId()); - for (int entityId : furniture.virtualEntityIds()) { - this.byVirtualEntityId.remove(entityId); + int entityId = furniture.entityId(); + // 移除entity id映射 + this.byMetaEntityId.remove(entityId); + for (int id : furniture.virtualEntityIds()) { + this.byVirtualEntityId.remove(id); } for (Collider collisionEntity : furniture.colliders()) { this.byColliderEntityId.remove(collisionEntity.entityId()); 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 d757898ff..038b240e2 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 @@ -1,9 +1,13 @@ package net.momirealms.craftengine.core.entity.furniture; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.momirealms.craftengine.core.block.entity.tick.TickingBlockEntity; +import net.momirealms.craftengine.core.entity.furniture.behavior.FurnitureBehaviorTypes; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxTypes; +import net.momirealms.craftengine.core.entity.furniture.tick.TickingFurniture; import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; @@ -14,10 +18,8 @@ import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser; import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.util.GsonHelper; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; -import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; +import net.momirealms.craftengine.core.util.*; import org.incendo.cloud.suggestion.Suggestion; import org.joml.Vector3f; @@ -31,6 +33,18 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { // Cached command suggestions private final List cachedSuggestions = new ArrayList<>(); + protected final Int2ObjectOpenHashMap syncTickers = new Int2ObjectOpenHashMap<>(256, 0.5f); + protected final Int2ObjectOpenHashMap asyncTickers = new Int2ObjectOpenHashMap<>(256, 0.5f); + protected final TickersList syncTickingFurniture = new TickersList<>(); + protected final List pendingSyncTickingFurniture = new ArrayList<>(); + protected final TickersList asyncTickingFurniture = new TickersList<>(); + protected final List pendingAsyncTickingFurniture = new ArrayList<>(); + private boolean isTickingSyncFurniture = false; + private boolean isTickingAsyncFurniture = false; + + protected SchedulerTask syncTickTask; + protected SchedulerTask asyncTickTask; + public AbstractFurnitureManager(CraftEngine plugin) { this.plugin = plugin; this.furnitureParser = new FurnitureParser(); @@ -69,6 +83,82 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { return Collections.unmodifiableMap(this.byId); } + private void syncTick() { + this.isTickingSyncFurniture = true; + if (!this.pendingSyncTickingFurniture.isEmpty()) { + this.syncTickingFurniture.addAll(this.pendingSyncTickingFurniture); + this.pendingSyncTickingFurniture.clear(); + } + if (!this.syncTickingFurniture.isEmpty()) { + Object[] entities = this.syncTickingFurniture.elements(); + for (int i = 0, size = this.syncTickingFurniture.size(); i < size; i++) { + TickingFurniture entity = (TickingFurniture) entities[i]; + if (entity.isValid()) { + entity.tick(); + } else { + this.syncTickingFurniture.markAsRemoved(i); + this.syncTickers.remove(entity.entityId()); + } + } + this.syncTickingFurniture.removeMarkedEntries(); + } + this.isTickingSyncFurniture = false; + } + + private void asyncTick() { + this.isTickingAsyncFurniture = true; + if (!this.pendingAsyncTickingFurniture.isEmpty()) { + this.asyncTickingFurniture.addAll(this.pendingAsyncTickingFurniture); + this.pendingAsyncTickingFurniture.clear(); + } + if (!this.asyncTickingFurniture.isEmpty()) { + Object[] entities = this.asyncTickingFurniture.elements(); + for (int i = 0, size = this.asyncTickingFurniture.size(); i < size; i++) { + TickingFurniture entity = (TickingFurniture) entities[i]; + if (entity.isValid()) { + entity.tick(); + } else { + this.asyncTickingFurniture.markAsRemoved(i); + this.asyncTickers.remove(entity.entityId()); + } + } + this.asyncTickingFurniture.removeMarkedEntries(); + } + this.isTickingAsyncFurniture = false; + } + + public synchronized void addSyncFurnitureTicker(TickingFurniture ticker) { + if (this.isTickingSyncFurniture) { + this.pendingSyncTickingFurniture.add(ticker); + } else { + this.syncTickingFurniture.add(ticker); + } + } + + public synchronized void addAsyncFurnitureTicker(TickingFurniture ticker) { + if (this.isTickingAsyncFurniture) { + this.pendingAsyncTickingFurniture.add(ticker); + } else { + this.asyncTickingFurniture.add(ticker); + } + } + + @Override + public void delayedInit() { + 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); + } + + @Override + public void disable() { + if (this.syncTickTask != null && !this.syncTickTask.cancelled()) + this.syncTickTask.cancel(); + if (this.asyncTickTask != null && !this.asyncTickTask.cancelled()) + this.asyncTickTask.cancel(); + } + @Override public void unload() { this.byId.clear(); @@ -160,6 +250,7 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { .variants(variants) .events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"))) .lootTable(LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), true))) + .behavior(FurnitureBehaviorTypes.fromMap(ResourceConfigUtils.getAsMapOrNull(ResourceConfigUtils.get(section, "behaviors", "behavior"), "behavior"))) .build(); AbstractFurnitureManager.this.byId.put(id, furniture); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/EmptyFurnitureBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/EmptyFurnitureBehavior.java index 439348957..c646e93b4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/EmptyFurnitureBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/EmptyFurnitureBehavior.java @@ -1,5 +1,8 @@ package net.momirealms.craftengine.core.entity.furniture.behavior; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental public final class EmptyFurnitureBehavior implements FurnitureBehavior { private EmptyFurnitureBehavior() {} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehavior.java index 655168a5a..aa76567e5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehavior.java @@ -2,7 +2,9 @@ package net.momirealms.craftengine.core.entity.furniture.behavior; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.tick.FurnitureTicker; +import org.jetbrains.annotations.ApiStatus; +@ApiStatus.Experimental public interface FurnitureBehavior { default FurnitureTicker createSyncFurnitureTicker(T furniture) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorFactory.java index 99e3eaa3c..1585fc9a9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorFactory.java @@ -1,7 +1,10 @@ package net.momirealms.craftengine.core.entity.furniture.behavior; +import org.jetbrains.annotations.ApiStatus; + import java.util.Map; +@ApiStatus.Experimental public interface FurnitureBehaviorFactory { T create(Map properties); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorType.java index 08d006d02..0bf1ebc1d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorType.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorType.java @@ -1,6 +1,8 @@ package net.momirealms.craftengine.core.entity.furniture.behavior; import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.ApiStatus; +@ApiStatus.Experimental public record FurnitureBehaviorType(Key id, FurnitureBehaviorFactory factory) { } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorTypes.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorTypes.java index 18570cbd8..9647f33fb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/behavior/FurnitureBehaviorTypes.java @@ -7,12 +7,13 @@ import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceKey; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import java.util.Map; +@ApiStatus.Experimental public class FurnitureBehaviorTypes { - public static final Key EMPTY = Key.from("craftengine:empty"); public static FurnitureBehavior fromMap(@Nullable Map map) { if (map == null || map.isEmpty()) return EmptyFurnitureBehavior.INSTANCE; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/FurnitureTicker.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/FurnitureTicker.java index 1f034cbd9..8fe21e145 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/FurnitureTicker.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/FurnitureTicker.java @@ -1,7 +1,9 @@ package net.momirealms.craftengine.core.entity.furniture.tick; import net.momirealms.craftengine.core.entity.furniture.Furniture; +import org.jetbrains.annotations.ApiStatus; +@ApiStatus.Experimental public interface FurnitureTicker { void tick(T furniture); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/TickingFurniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/TickingFurniture.java new file mode 100644 index 000000000..b085e360d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/TickingFurniture.java @@ -0,0 +1,13 @@ +package net.momirealms.craftengine.core.entity.furniture.tick; + +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public interface TickingFurniture { + + void tick(); + + boolean isValid(); + + int entityId(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/TickingFurnitureImpl.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/TickingFurnitureImpl.java new file mode 100644 index 000000000..1388666e3 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/tick/TickingFurnitureImpl.java @@ -0,0 +1,30 @@ +package net.momirealms.craftengine.core.entity.furniture.tick; + +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public class TickingFurnitureImpl implements TickingFurniture { + private final T furniture; + private final FurnitureTicker ticker; + + public TickingFurnitureImpl(T furniture, FurnitureTicker ticker) { + this.furniture = furniture; + this.ticker = ticker; + } + + @Override + public void tick() { + this.ticker.tick(this.furniture); + } + + @Override + public boolean isValid() { + return this.furniture.isValid(); + } + + @Override + public int entityId() { + return this.furniture.entityId(); + } +} 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 b98b86b27..2e81fb837 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 @@ -133,12 +133,12 @@ public abstract class AbstractRecipeSerializer> implement return new CustomRecipeResult<>(CloneableConstantItem.of(result), recipeResult.count(), null); } - @Nullable + @NotNull protected Ingredient toIngredient(String item) { return toIngredient(List.of(item)); } - @Nullable + @NotNull protected Ingredient toIngredient(List items) { Set itemIds = new HashSet<>(); Set minecraftItemIds = new HashSet<>(); @@ -149,6 +149,9 @@ public abstract class AbstractRecipeSerializer> implement Key tag = Key.of(item.substring(1)); elements.add(new IngredientElement.Tag(tag)); List uniqueKeys = itemManager.itemIdsByTag(tag); + if (uniqueKeys.isEmpty()) { + throw new LocalizedResourceConfigException("warning.config.recipe.invalid_ingredient", item); + } itemIds.addAll(uniqueKeys); for (UniqueKey uniqueKey : uniqueKeys) { List ingredientSubstitutes = itemManager.getIngredientSubstitutes(uniqueKey.key()); 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 860d66187..380ae3bca 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 @@ -11,6 +11,7 @@ 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.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -37,12 +38,12 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { } public PlacementInfo placementInfo() { - return placementInfo; + return this.placementInfo; } @Override public List> ingredientsInUse() { - return ingredients; + return this.ingredients; } @SuppressWarnings("unchecked") diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/PlacementInfo.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/PlacementInfo.java index 69b8f6b43..df5c07a93 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/PlacementInfo.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/PlacementInfo.java @@ -19,7 +19,7 @@ public class PlacementInfo { IntList intList = new IntArrayList(i); for (int j = 0; j < i; j++) { Ingredient ingredient = ingredients.get(j); - if (ingredient.isEmpty()) { + if (ingredient == null || ingredient.isEmpty()) { return new PlacementInfo<>(List.of(), IntList.of()); } intList.add(j); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java b/core/src/main/java/net/momirealms/craftengine/core/util/TickersList.java similarity index 87% rename from core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java rename to core/src/main/java/net/momirealms/craftengine/core/util/TickersList.java index 4ff57411a..ed7ded3c9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/BlockEntityTickersList.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/TickersList.java @@ -6,10 +6,8 @@ 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 @@ -20,26 +18,17 @@ import java.util.Collection; * 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 { +public final class TickersList 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() { + public TickersList() { 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 * 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 60098466b..df9472248 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 @@ -7,7 +7,7 @@ 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.util.TickersList; 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; @@ -25,9 +25,9 @@ public abstract class CEWorld { protected final WorldHeight worldHeightAccessor; protected List pendingLightSections = new ArrayList<>(); protected final Set lightSections = ConcurrentHashMap.newKeySet(128); - protected final BlockEntityTickersList tickingSyncBlockEntities = new BlockEntityTickersList(); + protected final TickersList syncTickingBlockEntities = new TickersList<>(); protected final List pendingSyncTickingBlockEntities = new ArrayList<>(); - protected final BlockEntityTickersList tickingAsyncBlockEntities = new BlockEntityTickersList(); + protected final TickersList asyncTickingBlockEntities = new TickersList<>(); protected final List pendingAsyncTickingBlockEntities = new ArrayList<>(); protected volatile boolean isTickingSyncBlockEntities = false; protected volatile boolean isTickingAsyncBlockEntities = false; @@ -51,19 +51,15 @@ public abstract class CEWorld { public void setTicking(boolean ticking) { if (ticking) { - if (this.syncTickTask == null || this.syncTickTask.cancelled()) { + 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()) { + 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()) { + if (this.syncTickTask != null && !this.syncTickTask.cancelled()) this.syncTickTask.cancel(); - } - if (this.asyncTickTask != null && !this.asyncTickTask.cancelled()) { + if (this.asyncTickTask != null && !this.asyncTickTask.cancelled()) this.asyncTickTask.cancel(); - } } } @@ -194,39 +190,39 @@ public abstract class CEWorld { public abstract void updateLight(); - public void addSyncBlockEntityTicker(TickingBlockEntity ticker) { + public synchronized void addSyncBlockEntityTicker(TickingBlockEntity ticker) { if (this.isTickingSyncBlockEntities) { this.pendingSyncTickingBlockEntities.add(ticker); } else { - this.tickingSyncBlockEntities.add(ticker); + this.syncTickingBlockEntities.add(ticker); } } - public void addAsyncBlockEntityTicker(TickingBlockEntity ticker) { + public synchronized void addAsyncBlockEntityTicker(TickingBlockEntity ticker) { if (this.isTickingAsyncBlockEntities) { this.pendingAsyncTickingBlockEntities.add(ticker); } else { - this.tickingAsyncBlockEntities.add(ticker); + this.asyncTickingBlockEntities.add(ticker); } } protected void tickSyncBlockEntities() { this.isTickingSyncBlockEntities = true; if (!this.pendingSyncTickingBlockEntities.isEmpty()) { - this.tickingSyncBlockEntities.addAll(this.pendingSyncTickingBlockEntities); + this.syncTickingBlockEntities.addAll(this.pendingSyncTickingBlockEntities); this.pendingSyncTickingBlockEntities.clear(); } - if (!this.tickingSyncBlockEntities.isEmpty()) { - Object[] entities = this.tickingSyncBlockEntities.elements(); - for (int i = 0, size = this.tickingSyncBlockEntities.size(); i < size; i++) { + if (!this.syncTickingBlockEntities.isEmpty()) { + Object[] entities = this.syncTickingBlockEntities.elements(); + for (int i = 0, size = this.syncTickingBlockEntities.size(); i < size; i++) { TickingBlockEntity entity = (TickingBlockEntity) entities[i]; if (entity.isValid()) { entity.tick(); } else { - this.tickingSyncBlockEntities.markAsRemoved(i); + this.syncTickingBlockEntities.markAsRemoved(i); } } - this.tickingSyncBlockEntities.removeMarkedEntries(); + this.syncTickingBlockEntities.removeMarkedEntries(); } this.isTickingSyncBlockEntities = false; } @@ -234,20 +230,20 @@ public abstract class CEWorld { protected void tickAsyncBlockEntities() { this.isTickingAsyncBlockEntities = true; if (!this.pendingAsyncTickingBlockEntities.isEmpty()) { - this.tickingAsyncBlockEntities.addAll(this.pendingAsyncTickingBlockEntities); + this.asyncTickingBlockEntities.addAll(this.pendingAsyncTickingBlockEntities); this.pendingAsyncTickingBlockEntities.clear(); } - if (!this.tickingAsyncBlockEntities.isEmpty()) { - Object[] entities = this.tickingAsyncBlockEntities.elements(); - for (int i = 0, size = this.tickingAsyncBlockEntities.size(); i < size; i++) { + if (!this.asyncTickingBlockEntities.isEmpty()) { + Object[] entities = this.asyncTickingBlockEntities.elements(); + for (int i = 0, size = this.asyncTickingBlockEntities.size(); i < size; i++) { TickingBlockEntity entity = (TickingBlockEntity) entities[i]; if (entity.isValid()) { entity.tick(); } else { - this.tickingAsyncBlockEntities.markAsRemoved(i); + this.asyncTickingBlockEntities.markAsRemoved(i); } } - this.tickingAsyncBlockEntities.removeMarkedEntries(); + this.asyncTickingBlockEntities.removeMarkedEntries(); } this.isTickingAsyncBlockEntities = false; } From 35b63b163031b5af47dce8eba47756d1bc28e0dc Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 20:34:46 +0800 Subject: [PATCH 113/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E8=A1=8C=E4=B8=BA=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/entity/furniture/BukkitFurnitureManager.java | 1 - .../bukkit/plugin/network/BukkitNetworkManager.java | 6 ++++-- .../resources/default/configuration/templates/events.yml | 4 ++++ .../core/entity/furniture/AbstractFurnitureManager.java | 1 - .../craftengine/core/item/recipe/CustomShapelessRecipe.java | 1 - 5 files changed, 8 insertions(+), 5 deletions(-) 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 6a441f42f..e5c9087f6 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 @@ -13,7 +13,6 @@ import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; import net.momirealms.craftengine.core.entity.furniture.tick.FurnitureTicker; -import net.momirealms.craftengine.core.entity.furniture.tick.TickingFurniture; import net.momirealms.craftengine.core.entity.furniture.tick.TickingFurnitureImpl; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.sound.SoundData; 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 24a96b9f9..113268a15 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 @@ -3813,12 +3813,14 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes if (cancellable.isCancelled()) { return; } - // 不处理调试棒 if (itemInHand.vanillaId().equals(ItemKeys.DEBUG_STICK)) { return; } - + // 已经有过交互了 + if (serverPlayer.lastSuccessfulInteractionTick() == serverPlayer.gameTicks()) { + return; + } // 必须从网络包层面处理,否则无法获取交互的具体实体 if (serverPlayer.isSecondaryUseActive() && !itemInHand.isEmpty() && hitBox.config().canUseItemOn()) { Optional> optionalCustomItem = itemInHand.getCustomItem(); diff --git a/common-files/src/main/resources/resources/default/configuration/templates/events.yml b/common-files/src/main/resources/resources/default/configuration/templates/events.yml index fd93462b9..cf28a87f6 100644 --- a/common-files/src/main/resources/resources/default/configuration/templates/events.yml +++ b/common-files/src/main/resources/resources/default/configuration/templates/events.yml @@ -21,7 +21,9 @@ templates: conditions: - type: expression expression: + - type: "!has_item" functions: + - type: update_interaction_tick - type: rotate_furniture degree: 90 on-success: @@ -47,9 +49,11 @@ templates: conditions: - type: expression expression: + - type: "!has_item" - type: "!match_furniture_variant" variants: ${blacklist_variants:-null} functions: + - type: update_interaction_tick - type: rotate_furniture degree: 45 on-success: 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 038b240e2..b4460723c 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 @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.entity.furniture; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import net.momirealms.craftengine.core.block.entity.tick.TickingBlockEntity; import net.momirealms.craftengine.core.entity.furniture.behavior.FurnitureBehaviorTypes; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs; 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 380ae3bca..f2f2a4340 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 @@ -11,7 +11,6 @@ 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.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; From 8c744dedf77ac35569176fd1d855fd10d8d10a42 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 23:16:42 +0800 Subject: [PATCH 114/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=AD=BB=E4=BA=A1?= =?UTF-8?q?=E4=BF=9D=E7=95=99/=E6=8D=9F=E6=AF=81=E7=89=A9=E5=93=81?= =?UTF-8?q?=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../item/listener/ItemEventListener.java | 123 ++++++++++++++++-- .../core/item/AbstractItemManager.java | 28 +++- .../craftengine/core/item/ItemSettings.java | 30 +++++ .../item/modifier/ComponentsModifier.java | 2 +- gradle.properties | 2 +- 5 files changed, 173 insertions(+), 12 deletions(-) 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 4d7e6e374..5d3d4afa8 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 @@ -21,6 +21,7 @@ import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.ItemSettings; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.item.context.UseOnContext; @@ -54,20 +55,16 @@ import org.bukkit.event.enchantment.PrepareItemEnchantEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.entity.FoodLevelChangeEvent; +import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerItemConsumeEvent; -import org.bukkit.inventory.EnchantingInventory; -import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; public class ItemEventListener implements Listener { private final BukkitCraftEngine plugin; @@ -630,4 +627,114 @@ public class ItemEventListener implements Listener { } event.setCurrentItem((ItemStack) result.finalItem().getItem()); } + + @SuppressWarnings("DuplicatedCode") + @EventHandler(ignoreCancelled = true) + public void onPlayerDeath(PlayerDeathEvent event) { + BukkitItemManager instance = BukkitItemManager.instance(); + + // 处理损毁物品 + if (event.getKeepInventory()) { + if (!instance.featureFlag$destroyOnDeathChance()) return; + + Random random = ThreadLocalRandom.current(); + PlayerInventory inventory = event.getPlayer().getInventory(); + for (ItemStack item : inventory.getContents()) { + if (item == null) continue; + + Optional> optional = instance.wrap(item).getCustomItem(); + if (optional.isEmpty()) continue; + + CustomItem customItem = optional.get(); + ItemSettings settings = customItem.settings(); + float destroyChance = settings.destroyOnDeathChance(); + if (destroyChance <= 0f) continue; + + int totalAmount = item.getAmount(); + int destroyCount = 0; + + for (int i = 0; i < totalAmount; i++) { + float rand = random.nextFloat(); + // 判断是否损毁 + if (destroyChance > 0f && rand < destroyChance) { + destroyCount++; + } + } + if (destroyCount != 0) { + item.setAmount(totalAmount - destroyCount); + } + } + } + // 处理保留 + 损毁物品 + else { + if (!instance.featureFlag$keepOnDeathChance() && !instance.featureFlag$destroyOnDeathChance()) return; + Random random = ThreadLocalRandom.current(); + + List itemsToKeep = event.getItemsToKeep(); + List itemsToDrop = event.getDrops(); + + Iterator iterator = itemsToDrop.iterator(); + + while (iterator.hasNext()) { + ItemStack item = iterator.next(); + Optional> optional = instance.wrap(item).getCustomItem(); + if (optional.isEmpty()) continue; + + CustomItem customItem = optional.get(); + ItemSettings settings = customItem.settings(); + + float destroyChance = settings.destroyOnDeathChance(); + float keepChance = settings.keepOnDeathChance(); + + // 如果没有效果,跳过 + if (destroyChance <= 0f && keepChance <= 0f) continue; + + int totalAmount = item.getAmount(); + + int keepCount = 0; + int destroyCount = 0; + int dropCount = 0; + + for (int i = 0; i < totalAmount; i++) { + float rand = random.nextFloat(); + + // 先判断是否损毁 + if (destroyChance > 0f && rand < destroyChance) { + destroyCount++; + } + // 然后判断是否保留(在未损毁的物品中) + else if (keepChance > 0f && rand < (destroyChance + keepChance)) { + keepCount++; + } + // 否则掉落 + else { + dropCount++; + } + } + + // 处理结果 + if (destroyCount == totalAmount) { + iterator.remove(); + continue; + } + + if (keepCount == 0 && dropCount == 0) { + // 实际上不会发生这种情况 + continue; + } + + if (keepCount > 0) { + ItemStack keepItem = item.clone(); + keepItem.setAmount(keepCount); + itemsToKeep.add(keepItem); + } + + if (dropCount > 0) { + item.setAmount(dropCount); + } else { + iterator.remove(); + } + } + } + } } 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 1af81c706..a119bcf42 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 @@ -67,6 +67,9 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl // 替代配方材料 protected final Map> ingredientSubstitutes = new HashMap<>(); + protected boolean featureFlag$keepOnDeathChance = false; + protected boolean featureFlag$destroyOnDeathChance = false; + protected AbstractItemManager(CraftEngine plugin) { super(plugin); this.itemParser = new ItemParser(); @@ -133,6 +136,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl @Override public void unload() { super.clearModelsToGenerate(); + this.clearFeatureFlags(); this.customItemsById.clear(); this.customItemsByPath.clear(); this.cachedCustomItemSuggestions.clear(); @@ -147,6 +151,11 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl this.ingredientSubstitutes.clear(); } + private void clearFeatureFlags() { + this.featureFlag$keepOnDeathChance = false; + this.featureFlag$destroyOnDeathChance = false; + } + @Override public Map equipments() { return Collections.unmodifiableMap(this.equipments); @@ -205,12 +214,13 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl this.cachedTotemSuggestions.add(Suggestion.suggestion(id.toString())); } // tags - Set tags = customItem.settings().tags(); + ItemSettings settings = customItem.settings(); + Set tags = settings.tags(); for (Key tag : tags) { this.customItemTags.computeIfAbsent(tag, k -> new ArrayList<>()).add(customItem.uniqueId()); } // ingredient substitutes - List substitutes = customItem.settings().ingredientSubstitutes(); + List substitutes = settings.ingredientSubstitutes(); if (!substitutes.isEmpty()) { for (Key key : substitutes) { if (VANILLA_ITEMS.contains(key)) { @@ -218,6 +228,12 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl } } } + if (settings.keepOnDeathChance != 0) { + this.featureFlag$keepOnDeathChance = true; + } + if (settings.destroyOnDeathChance != 0) { + this.featureFlag$destroyOnDeathChance = true; + } } return true; } @@ -317,6 +333,14 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl return VANILLA_ITEMS.contains(item); } + public boolean featureFlag$keepOnDeathChance() { + return featureFlag$keepOnDeathChance; + } + + public boolean featureFlag$destroyOnDeathChance() { + return featureFlag$destroyOnDeathChance; + } + protected abstract CustomItem.Builder createPlatformItemBuilder(UniqueKey id, Key material, Key clientBoundMaterial); protected abstract void registerArmorTrimPattern(Collection equipments); 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 64a67c8dd..00c6601a2 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 @@ -47,6 +47,8 @@ public class ItemSettings { Color dyeColor; @Nullable Color fireworkColor; + float keepOnDeathChance = 0f; + float destroyOnDeathChance = 0f; Map, Object> customData = new IdentityHashMap<>(4); private ItemSettings() {} @@ -109,6 +111,8 @@ public class ItemSettings { newSettings.dyeColor = settings.dyeColor; newSettings.fireworkColor = settings.fireworkColor; newSettings.ingredientSubstitutes = settings.ingredientSubstitutes; + newSettings.keepOnDeathChance = settings.keepOnDeathChance; + newSettings.destroyOnDeathChance = settings.destroyOnDeathChance; newSettings.customData = new IdentityHashMap<>(settings.customData); return newSettings; } @@ -231,6 +235,14 @@ public class ItemSettings { return this.compostProbability; } + public float keepOnDeathChance() { + return this.keepOnDeathChance; + } + + public float destroyOnDeathChance() { + return this.destroyOnDeathChance; + } + public ItemSettings fireworkColor(Color color) { this.fireworkColor = color; return this; @@ -331,6 +343,16 @@ public class ItemSettings { return this; } + public ItemSettings keepOnDeathChance(float keepChance) { + this.keepOnDeathChance = keepChance; + return this; + } + + public ItemSettings destroyOnDeathChance(float destroyChance) { + this.destroyOnDeathChance = destroyChance; + return this; + } + @FunctionalInterface public interface Modifier { @@ -361,6 +383,14 @@ public class ItemSettings { boolean bool = ResourceConfigUtils.getAsBoolean(value, "enchantable"); return settings -> settings.canEnchant(bool); })); + registerFactory("keep-on-death-chance", (value -> { + float chance = ResourceConfigUtils.getAsFloat(value, "keep-on-death-chance"); + return settings -> settings.keepOnDeathChance(MiscUtils.clamp(chance, 0, 1)); + })); + registerFactory("destroy-on-death-chance", (value -> { + float chance = ResourceConfigUtils.getAsFloat(value, "destroy-on-death-chance"); + return settings -> settings.destroyOnDeathChance(MiscUtils.clamp(chance, 0, 1)); + })); registerFactory("renameable", (value -> { boolean bool = ResourceConfigUtils.getAsBoolean(value, "renameable"); return settings -> settings.renameable(bool); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java index bb4213a32..57381eed6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentsModifier.java @@ -31,7 +31,7 @@ public class ComponentsModifier implements ItemDataModifier { } public List> components() { - return arguments; + return this.arguments; } private Tag parseValue(Object value) { diff --git a/gradle.properties b/gradle.properties index 1b2eee6a6..96cd697b5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -38,7 +38,7 @@ zstd_version=1.5.7-6 commons_io_version=2.21.0 commons_lang3_version=3.20.0 sparrow_nbt_version=0.10.6 -sparrow_util_version=0.67 +sparrow_util_version=0.68 fastutil_version=8.5.18 netty_version=4.1.128.Final joml_version=1.10.8 From 5003fa4cf27160cefe589d5aaaf8046e29b7cc7c Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 23:16:52 +0800 Subject: [PATCH 115/135] Update gradle.properties --- gradle.properties | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle.properties b/gradle.properties index 96cd697b5..496c67425 100644 --- a/gradle.properties +++ b/gradle.properties @@ -59,9 +59,9 @@ concurrent_util_version=0.0.3 bucket4j_version=8.15.0 # Proxy settings -systemProp.socks.proxyHost=127.0.0.1 -systemProp.socks.proxyPort=7890 -systemProp.http.proxyHost=127.0.0.1 -systemProp.http.proxyPort=7890 -systemProp.https.proxyHost=127.0.0.1 -systemProp.https.proxyPort=7890 \ No newline at end of file +#systemProp.socks.proxyHost=127.0.0.1 +#systemProp.socks.proxyPort=7890 +#systemProp.http.proxyHost=127.0.0.1 +#systemProp.http.proxyPort=7890 +#systemProp.https.proxyHost=127.0.0.1 +#systemProp.https.proxyPort=7890 \ No newline at end of file From d5bbbc49032e8272564a3d4c646cdb6e824d7226 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 23:23:01 +0800 Subject: [PATCH 116/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=BD=9C=E5=9C=A8?= =?UTF-8?q?=E7=9A=84=E6=95=B0=E7=BB=84=E8=B6=8A=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/core/world/chunk/client/ClientChunk.java | 2 ++ gradle.properties | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java index 03e0ac6ad..b99d3a574 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/client/ClientChunk.java @@ -22,6 +22,7 @@ public class ClientChunk { public boolean isOccluding(int x, int y, int z) { if (this.sections == null) return false; int index = sectionIndex(SectionPos.blockToSectionCoord(y)); + if (index < 0 || index >= this.sections.length) return false; ClientSection section = this.sections[index]; if (section == null) return false; return section.isOccluding((y & 15) << 8 | (z & 15) << 4 | x & 15); @@ -30,6 +31,7 @@ public class ClientChunk { public void setOccluding(int x, int y, int z, boolean occluding) { if (this.sections == null) return; int index = sectionIndex(SectionPos.blockToSectionCoord(y)); + if (index < 0 || index >= this.sections.length) return; ClientSection section = this.sections[index]; if (section == null) return; section.setOccluding((y & 15) << 8 | (z & 15) << 4 | x & 15, occluding); diff --git a/gradle.properties b/gradle.properties index 496c67425..bf950c905 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings -project_version=0.0.65.17 +project_version=0.0.65.18 config_version=60 lang_version=43 project_group=net.momirealms From 2f8351d75e89b03ef0978db26e40adbcd7a23e00 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 23:27:11 +0800 Subject: [PATCH 117/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=A3=B0=E9=9F=B3?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E9=AA=8C=E8=AF=81sound=20event?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../momirealms/craftengine/core/pack/AbstractPackManager.java | 4 ++++ 1 file changed, 4 insertions(+) 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 8ec309b93..4508ceaa0 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 @@ -1419,6 +1419,10 @@ public abstract class AbstractPackManager implements PackManager { oggToSoundEvents.put(Key.of(primitive.getAsString()), soundKey); } } else if (sound instanceof JsonObject soundObj && soundObj.has("name")) { + if (soundObj.has("type")) { + String type = soundObj.get("type").getAsString(); + if (!type.equals("file")) continue; + } String name = soundObj.get("name").getAsString(); oggToSoundEvents.put(Key.of(name), soundKey); } From 5395dc4ea93f6fba2c3c5f1b4595c7023aa210a5 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 5 Dec 2025 23:31:43 +0800 Subject: [PATCH 118/135] Update EntityCullingThread.java --- .../core/plugin/entityculling/EntityCullingThread.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java index 7eb6f1e69..5586bc51d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java @@ -54,7 +54,7 @@ public class EntityCullingThread { Debugger.ENTITY_CULLING.debug(() -> value); } } catch (Exception e) { - CraftEngine.instance().logger().severe("Failed to run entity culling tick: " + e.getMessage()); + CraftEngine.instance().logger().severe("Failed to run entity culling tick", e); } finally { this.isRunning.set(false); } From 465eebe15aba61b00d8c13e08a4bb490be20ac56 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 6 Dec 2025 00:58:17 +0800 Subject: [PATCH 119/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E6=8E=89=E8=90=BD=E7=89=A9=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- .../handler/CommonItemPacketHandler.java | 68 ++++++++++++++++++- common-files/src/main/resources/config.yml | 4 ++ .../craftengine/core/item/ItemSettings.java | 38 +++++++++++ .../core/plugin/config/Config.java | 12 ++++ .../text/minimessage/CustomTagResolver.java | 34 ++++++++++ .../core/util/LegacyChatFormatter.java | 26 +++++++ 7 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/CustomTagResolver.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/LegacyChatFormatter.java diff --git a/README.md b/README.md index 2f45c4583..1780802cf 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ repositories { ``` ```kotlin dependencies { - compileOnly("net.momirealms:craft-engine-core:0.0.65") - compileOnly("net.momirealms:craft-engine-bukkit:0.0.65") + compileOnly("net.momirealms:craft-engine-core:0.0.66") + compileOnly("net.momirealms:craft-engine-bukkit:0.0.66") } ``` \ No newline at end of file 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 f9b57d6d3..0afd02080 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 @@ -1,19 +1,33 @@ package net.momirealms.craftengine.bukkit.plugin.network.handler; +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.entity.data.ItemEntityData; 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.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.EntityDataUtils; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.CustomItem; +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.ContextHolder; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; +import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; +import net.momirealms.craftengine.core.plugin.text.minimessage.CustomTagResolver; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.ArrayUtils; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import org.bukkit.inventory.ItemStack; import java.util.List; +import java.util.Map; import java.util.Optional; public class CommonItemPacketHandler implements EntityPacketHandler { @@ -27,11 +41,14 @@ public class CommonItemPacketHandler implements EntityPacketHandler { int id = buf.readVarInt(); boolean changed = false; List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); + Component nameToShow = null; for (int i = 0; i < packedItems.size(); i++) { Object packedItem = packedItems.get(i); int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); if (entityDataId != EntityDataUtils.UNSAFE_ITEM_DATA_ID) continue; Object nmsItemStack = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); + + // 可能是其他插件导致的问题 if (!CoreReflections.clazz$ItemStack.isInstance(nmsItemStack)) { long time = System.currentTimeMillis(); if (time - lastWarningTime > 5000) { @@ -42,15 +59,64 @@ public class CommonItemPacketHandler implements EntityPacketHandler { } continue; } + + // 处理 drop-display 物品设置 ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack); + Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); + Optional> optionalCustomItem = wrappedItem.getCustomItem(); + String showName = null; + if (optionalCustomItem.isPresent()) { + showName = optionalCustomItem.get().settings().dropDisplay(); + } else if (Config.enableDefaultDropDisplay()) { + showName = Config.defaultDropDisplayFormat(); + } + + // 如果设定了自定义展示名 + if (showName != null) { + PlayerOptionalContext context = NetworkTextReplaceContext.of(user, ContextHolder.builder() + .withParameter(DirectContextParameters.COUNT, itemStack.getAmount())); + Optional optionalHoverComponent = wrappedItem.hoverNameComponent(); + Component hoverComponent; + if (optionalHoverComponent.isPresent()) { + Map tokens = CraftEngine.instance().fontManager().matchTags(AdventureHelper.componentToNbt(optionalHoverComponent.get())); + if (tokens.isEmpty()) { + hoverComponent = optionalHoverComponent.get(); + } else { + hoverComponent = AdventureHelper.replaceText(optionalHoverComponent.get(), tokens, context); + } + } else { + hoverComponent = Component.translatable(itemStack.translationKey()); + } + // 展示名称为空,则显示其hover name + if (showName.isEmpty()) { + nameToShow = hoverComponent; + } + // 显示自定义格式的名字 + else { + nameToShow = AdventureHelper.miniMessage().deserialize( + showName, + ArrayUtils.appendElementToArrayTail(context.tagResolvers(), new CustomTagResolver("name", hoverComponent)) + ); + } + } + + // 转换为客户端侧物品 Optional optional = BukkitItemManager.instance().s2c(itemStack, user); - if (optional.isEmpty()) continue; + if (optional.isEmpty()) { + break; + } changed = true; itemStack = optional.get(); Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack))); break; } + // 添加自定义显示名 + if (nameToShow != null) { + changed = true; + packedItems.add(ItemEntityData.CustomNameVisible.createEntityData(true)); + packedItems.add(ItemEntityData.CustomName.createEntityData(Optional.of(ComponentUtils.adventureToMinecraft(nameToShow)))); + } if (changed) { event.setChanged(true); buf.clear(); diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 64b7eef86..26716aa71 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -250,6 +250,10 @@ item: default: 10000 overrides: paper: 20000 + # Toggle whether to display the names of dropped items by default and configure the display format. This setting can be overridden for individual items. + default-drop-display: + enable: false + format: "x " equipment: # The sacrificed-vanilla-armor argument determines which vanilla armor gets completely removed (loses all its trims) 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 00c6601a2..ed04a975e 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 @@ -49,6 +49,10 @@ public class ItemSettings { Color fireworkColor; float keepOnDeathChance = 0f; float destroyOnDeathChance = 0f; + @Nullable + String dropDisplay = Config.defaultDropDisplayFormat(); + @Nullable + LegacyChatFormatter glowColor = null; Map, Object> customData = new IdentityHashMap<>(4); private ItemSettings() {} @@ -113,6 +117,8 @@ public class ItemSettings { newSettings.ingredientSubstitutes = settings.ingredientSubstitutes; newSettings.keepOnDeathChance = settings.keepOnDeathChance; newSettings.destroyOnDeathChance = settings.destroyOnDeathChance; + newSettings.glowColor = settings.glowColor; + newSettings.dropDisplay = settings.dropDisplay; newSettings.customData = new IdentityHashMap<>(settings.customData); return newSettings; } @@ -243,6 +249,16 @@ public class ItemSettings { return this.destroyOnDeathChance; } + @Nullable + public LegacyChatFormatter glowColor() { + return this.glowColor; + } + + @Nullable + public String dropDisplay() { + return this.dropDisplay; + } + public ItemSettings fireworkColor(Color color) { this.fireworkColor = color; return this; @@ -293,6 +309,11 @@ public class ItemSettings { return this; } + public ItemSettings dropDisplay(String showName) { + this.dropDisplay = showName; + return this; + } + public ItemSettings projectileMeta(ProjectileMeta projectileMeta) { this.projectileMeta = projectileMeta; return this; @@ -353,6 +374,11 @@ public class ItemSettings { return this; } + public ItemSettings glowColor(LegacyChatFormatter chatFormatter) { + this.glowColor = chatFormatter; + return this; + } + @FunctionalInterface public interface Modifier { @@ -395,6 +421,18 @@ public class ItemSettings { boolean bool = ResourceConfigUtils.getAsBoolean(value, "renameable"); return settings -> settings.renameable(bool); })); + registerFactory("drop-display", (value -> { + if (value instanceof String name) { + return settings -> settings.dropDisplay(name); + } else { + boolean bool = ResourceConfigUtils.getAsBoolean(value, "drop-display"); + return settings -> settings.dropDisplay(bool ? "" : null); + } + })); + registerFactory("glow-color", (value -> { + LegacyChatFormatter chatFormatter = ResourceConfigUtils.getAsEnum(value, LegacyChatFormatter.class, LegacyChatFormatter.WHITE); + return settings -> settings.glowColor(chatFormatter); + })); registerFactory("anvil-repair-item", (value -> { List anvilRepairItemList = ResourceConfigUtils.parseConfigAsList(value, material -> { int amount = ResourceConfigUtils.getAsInt(material.getOrDefault("amount", 0), "amount"); 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 df09a8b08..a00dc1f0e 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 @@ -193,6 +193,8 @@ public class Config { protected Map item$custom_model_data_starting_value$overrides; protected boolean item$always_use_item_model; protected String item$default_material = ""; + protected boolean item$default_drop_display$enable = false; + protected String item$default_drop_display$format = null; protected String equipment$sacrificed_vanilla_armor$type; protected Key equipment$sacrificed_vanilla_armor$asset_id; @@ -471,6 +473,8 @@ public class Config { item$custom_model_data_starting_value$default = config.getInt("item.custom-model-data-starting-value.default", 10000); item$always_use_item_model = config.getBoolean("item.always-use-item-model", true) && VersionHelper.isOrAbove1_21_2(); item$default_material = config.getString("item.default-material", ""); + item$default_drop_display$enable = config.getBoolean("item.default-drop-display.enable", false); + item$default_drop_display$format = item$default_drop_display$enable ? config.getString("item.default-drop-display.format", "x "): null; Section customModelDataOverridesSection = config.getSection("item.custom-model-data-starting-value.overrides"); if (customModelDataOverridesSection != null) { @@ -1175,6 +1179,14 @@ public class Config { return instance.resource_pack$optimization$texture$zopfli_iterations; } + public static boolean enableDefaultDropDisplay() { + return instance.item$default_drop_display$enable; + } + + public static String defaultDropDisplayFormat() { + return instance.item$default_drop_display$format; + } + public static boolean enableEntityCulling() { return instance.client_optimization$entity_culling$enable; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/CustomTagResolver.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/CustomTagResolver.java new file mode 100644 index 000000000..1d66bf4d7 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/CustomTagResolver.java @@ -0,0 +1,34 @@ +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; +import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class CustomTagResolver implements TagResolver { + private final String name; + private final Component replacement; + + public CustomTagResolver(String name, Component replacement) { + this.name = name; + this.replacement = replacement; + } + + @Override + @Nullable + public Tag resolve(@NotNull String name, @NotNull ArgumentQueue arguments, @NotNull Context ctx) throws ParsingException { + if (!has(name)) { + return null; + } + return Tag.selfClosingInserting(this.replacement); + } + + @Override + public boolean has(@NotNull String name) { + return this.name.equals(name); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/LegacyChatFormatter.java b/core/src/main/java/net/momirealms/craftengine/core/util/LegacyChatFormatter.java new file mode 100644 index 000000000..5f71615ca --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/LegacyChatFormatter.java @@ -0,0 +1,26 @@ +package net.momirealms.craftengine.core.util; + +public enum LegacyChatFormatter { + BLACK, + DARK_BLUE, + DARK_GREEN, + DARK_AQUA, + DARK_RED, + DARK_PURPLE, + GOLD, + GRAY, + DARK_GRAY, + BLUE, + GREEN, + AQUA, + RED, + LIGHT_PURPLE, + YELLOW, + WHITE, + OBFUSCATED, + BOLD, + STRIKETHROUGH, + UNDERLINE, + ITALIC, + RESET; +} From bd02b9d46e235fe04dee95616c495dde52dde498 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 6 Dec 2025 01:08:06 +0800 Subject: [PATCH 120/135] =?UTF-8?q?=E8=B0=83=E6=8D=A2=E5=A4=84=E7=90=86?= =?UTF-8?q?=E9=A1=BA=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handler/CommonItemPacketHandler.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) 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 0afd02080..3df058747 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 @@ -60,8 +60,19 @@ public class CommonItemPacketHandler implements EntityPacketHandler { continue; } - // 处理 drop-display 物品设置 ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack); + + // 转换为客户端侧物品 + Optional optional = BukkitItemManager.instance().s2c(itemStack, user); + if (optional.isPresent()) { + changed = true; + itemStack = optional.get(); + Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); + packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack))); + } + + // 处理 drop-display 物品设置 + // 一定要处理经历过客户端侧组件修改的物品 Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); Optional> optionalCustomItem = wrappedItem.getCustomItem(); String showName = null; @@ -78,12 +89,7 @@ public class CommonItemPacketHandler implements EntityPacketHandler { Optional optionalHoverComponent = wrappedItem.hoverNameComponent(); Component hoverComponent; if (optionalHoverComponent.isPresent()) { - Map tokens = CraftEngine.instance().fontManager().matchTags(AdventureHelper.componentToNbt(optionalHoverComponent.get())); - if (tokens.isEmpty()) { - hoverComponent = optionalHoverComponent.get(); - } else { - hoverComponent = AdventureHelper.replaceText(optionalHoverComponent.get(), tokens, context); - } + hoverComponent = optionalHoverComponent.get(); } else { hoverComponent = Component.translatable(itemStack.translationKey()); } @@ -100,15 +106,6 @@ public class CommonItemPacketHandler implements EntityPacketHandler { } } - // 转换为客户端侧物品 - Optional optional = BukkitItemManager.instance().s2c(itemStack, user); - if (optional.isEmpty()) { - break; - } - changed = true; - itemStack = optional.get(); - Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); - packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack))); break; } // 添加自定义显示名 From 30a6ad848a1432e8bf26913ea59b811a676e3328 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Sat, 6 Dec 2025 01:48:02 +0800 Subject: [PATCH 121/135] =?UTF-8?q?=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/translations/zh_cn.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index f31e79875..e1f1bebad 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -238,6 +238,8 @@ warning.config.furniture.element.item_display.missing_item: "在文件 < warning.config.furniture.settings.unknown: "在文件 发现问题 - 家具 '' 使用了未知的设置类型 ''" warning.config.furniture.hitbox.invalid_type: "在文件 发现问题 - 家具 '' 使用了无效的碰撞箱类型 ''" warning.config.furniture.hitbox.custom.invalid_entity: "在文件 发现问题 - 家具 '' 的自定义碰撞箱使用了无效的实体类型 ''" +warning.config.furniture.behavior.missing_type: "在文件 发现问题 - 家具 '' 的行为配置缺少必需的 'type' 参数" +warning.config.furniture.behavior.invalid_type: "在文件 发现问题 - 家具 '' 使用了无效的行为类型 ''" warning.config.item.duplicate: "在文件 发现问题 - 重复的物品 '' 请检查其他文件中是否存在相同配置" warning.config.item.settings.unknown: "在文件 发现问题 - 物品 '' 使用了未知的设置类型 ''" warning.config.item.settings.invulnerable.invalid_damage_source: "在文件 发现问题 - 物品 '' 物品使用了未知的伤害来源类型 '' 允许的来源: []" From 054444b6588a18ed17e08b2e79031e4844725728 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 6 Dec 2025 02:32:32 +0800 Subject: [PATCH 122/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AE=B6=E5=85=B7?= =?UTF-8?q?=E5=8F=91=E5=85=89=E5=92=8C=E7=89=A9=E5=93=81=E5=8F=91=E5=85=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../element/ItemDisplayFurnitureElement.java | 13 +- .../ItemDisplayFurnitureElementConfig.java | 23 ++- .../bukkit/plugin/BukkitCraftEngine.java | 3 + .../handler/CommonItemPacketHandler.java | 142 +++++++++++------- .../bukkit/CraftBukkitReflections.java | 8 + .../reflection/minecraft/CoreReflections.java | 7 + .../bukkit/util/EntityDataUtils.java | 1 + .../bukkit/world/score/BukkitTeamManager.java | 76 ++++++++++ .../craftengine/core/entity/Glowing.java | 10 ++ .../furniture/AbstractFurnitureManager.java | 10 ++ .../core/item/AbstractItemManager.java | 3 + .../craftengine/core/plugin/CraftEngine.java | 20 +++ .../craftengine/core/plugin/Plugin.java | 9 ++ .../core/world/score/TeamManager.java | 10 ++ gradle.properties | 2 +- 15 files changed, 275 insertions(+), 62 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/score/BukkitTeamManager.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/Glowing.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/score/TeamManager.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java index ae884d0c7..cbd6a4c05 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java @@ -4,10 +4,12 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; 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.bukkit.world.score.BukkitTeamManager; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.util.LegacyChatFormatter; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; @@ -21,6 +23,7 @@ public class ItemDisplayFurnitureElement implements FurnitureElement { private final int entityId; private final Object despawnPacket; private final FurnitureColorSource colorSource; + private final LegacyChatFormatter glowColor; public ItemDisplayFurnitureElement(Furniture furniture, ItemDisplayFurnitureElementConfig config) { this.config = config; @@ -30,18 +33,26 @@ public class ItemDisplayFurnitureElement implements FurnitureElement { this.position = new WorldPosition(furniturePos.world, position.x, position.y, position.z, furniturePos.xRot, furniturePos.yRot); this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(entityId); }}); this.colorSource = furniture.dataAccessor.getColorSource(); + this.glowColor = config.glowColor; } @Override public void show(Player player) { + UUID uuid = UUID.randomUUID(); player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - this.entityId, UUID.randomUUID(), + this.entityId, uuid, this.position.x, this.position.y, this.position.z, 0, this.position.yRot, MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 ), FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadata.apply(player, this.colorSource)) )), false); + if (this.glowColor != null) { + Object team = BukkitTeamManager.instance().getTeamByColor(this.glowColor); + if (team != null) { + FastNMS.INSTANCE.method$ClientboundSetPlayerTeamPacket$createMultiplePlayerPacket(team, List.of(uuid.toString()), true); + } + } } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java index caa972e9b..8e6a16aae 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -1,8 +1,10 @@ package net.momirealms.craftengine.bukkit.entity.furniture.element; import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.core.entity.Glowing; import net.momirealms.craftengine.core.entity.display.Billboard; import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; import net.momirealms.craftengine.core.entity.furniture.Furniture; @@ -14,9 +16,11 @@ import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.item.data.FireworkExplosion; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.LegacyChatFormatter; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; @@ -26,7 +30,7 @@ import java.util.Map; import java.util.Optional; import java.util.function.BiFunction; -public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig { +public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig, Glowing { public static final Factory FACTORY = new Factory(); public final BiFunction> metadata; public final Key itemId; @@ -41,6 +45,7 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig public final float shadowRadius; public final float shadowStrength; public final boolean applyDyedColor; + public final LegacyChatFormatter glowColor; public ItemDisplayFurnitureElementConfig(Key itemId, Vector3f scale, @@ -53,7 +58,8 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig Billboard billboard, float shadowRadius, float shadowStrength, - boolean applyDyedColor) { + boolean applyDyedColor, + @Nullable LegacyChatFormatter glowColor) { this.scale = scale; this.position = position; this.translation = translation; @@ -66,6 +72,7 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig this.shadowStrength = shadowStrength; this.applyDyedColor = applyDyedColor; this.itemId = itemId; + this.glowColor = glowColor; BiFunction> itemFunction = (player, colorSource) -> { Item wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player); if (applyDyedColor && colorSource != null && wrappedItem != null) { @@ -82,6 +89,9 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig }; this.metadata = (player, source) -> { List dataValues = new ArrayList<>(); + if (glowColor != null) { + BaseEntityData.SharedFlags.addEntityData((byte) 0x40, dataValues); + } ItemDisplayEntityData.DisplayedItem.addEntityData(itemFunction.apply(player, source).getLiteralObject(), dataValues); ItemDisplayEntityData.Scale.addEntityData(this.scale, dataValues); ItemDisplayEntityData.RotationLeft.addEntityData(this.rotation, dataValues); @@ -142,6 +152,12 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig return this.itemId; } + @Nullable + @Override + public LegacyChatFormatter glowColor() { + return this.glowColor; + } + @Override public ItemDisplayFurnitureElement create(@NotNull Furniture furniture) { return new ItemDisplayFurnitureElement(furniture, this); @@ -165,7 +181,8 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig ResourceConfigUtils.getAsEnum(arguments.get("billboard"), Billboard.class, Billboard.FIXED), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength"), - applyDyedColor + applyDyedColor, + ResourceConfigUtils.getAsEnum(arguments.get("glow-color"), LegacyChatFormatter.class, null) ); } } 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 e141aa000..df1f0b1d5 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 @@ -27,6 +27,7 @@ import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.sound.BukkitSoundManager; import net.momirealms.craftengine.bukkit.util.EventUtils; import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; +import net.momirealms.craftengine.bukkit.world.score.BukkitTeamManager; import net.momirealms.craftengine.core.item.ItemManager; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender; @@ -192,6 +193,8 @@ public class BukkitCraftEngine extends CraftEngine { super.seatManager = new BukkitSeatManager(this); // 初始化家具管理器 super.furnitureManager = new BukkitFurnitureManager(this); + // 初始化队伍管理器 + super.teamManager = new BukkitTeamManager(this); // 注册默认的parser this.registerDefaultParsers(); // 完成加载 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 3df058747..b835c2c8f 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 @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.plugin.network.handler; import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.entity.data.ItemEntityData; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; @@ -8,9 +9,11 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.EntityDataUtils; +import net.momirealms.craftengine.bukkit.world.score.BukkitTeamManager; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemSettings; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.context.ContextHolder; @@ -19,15 +22,14 @@ import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.plugin.text.minimessage.CustomTagResolver; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.ArrayUtils; import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import net.momirealms.craftengine.core.util.LegacyChatFormatter; import org.bukkit.inventory.ItemStack; import java.util.List; -import java.util.Map; import java.util.Optional; public class CommonItemPacketHandler implements EntityPacketHandler { @@ -42,71 +44,97 @@ public class CommonItemPacketHandler implements EntityPacketHandler { boolean changed = false; List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); Component nameToShow = null; + LegacyChatFormatter glowColor = null; for (int i = 0; i < packedItems.size(); i++) { Object packedItem = packedItems.get(i); int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); - if (entityDataId != EntityDataUtils.UNSAFE_ITEM_DATA_ID) continue; - Object nmsItemStack = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); + if (entityDataId == EntityDataUtils.UNSAFE_ITEM_DATA_ID) { + Object nmsItemStack = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); - // 可能是其他插件导致的问题 - if (!CoreReflections.clazz$ItemStack.isInstance(nmsItemStack)) { - long time = System.currentTimeMillis(); - if (time - lastWarningTime > 5000) { - BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; - CraftEngine.instance().logger().severe("An issue was detected while applying item-related entity data for '" + serverPlayer.name() + - "'. Please execute the command '/ce debug entity-id " + serverPlayer.world().name() + " " + id + "' and provide a screenshot for further investigation."); - lastWarningTime = time; + // 可能是其他插件导致的问题 + if (!CoreReflections.clazz$ItemStack.isInstance(nmsItemStack)) { + long time = System.currentTimeMillis(); + if (time - lastWarningTime > 5000) { + BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; + CraftEngine.instance().logger().severe("An issue was detected while applying item-related entity data for '" + serverPlayer.name() + + "'. Please execute the command '/ce debug entity-id " + serverPlayer.world().name() + " " + id + "' and provide a screenshot for further investigation."); + lastWarningTime = time; + } + continue; + } + + ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack); + + // 转换为客户端侧物品 + Optional optional = BukkitItemManager.instance().s2c(itemStack, user); + if (optional.isPresent()) { + changed = true; + itemStack = optional.get(); + Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); + packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack))); + } + + // 处理 drop-display 物品设置 + // 一定要处理经历过客户端侧组件修改的物品 + Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); + Optional> optionalCustomItem = wrappedItem.getCustomItem(); + String showName = null; + if (optionalCustomItem.isPresent()) { + ItemSettings settings = optionalCustomItem.get().settings(); + showName = settings.dropDisplay(); + glowColor = settings.glowColor(); + } else if (Config.enableDefaultDropDisplay()) { + showName = Config.defaultDropDisplayFormat(); + } + + // 如果设定了自定义展示名 + if (showName != null) { + PlayerOptionalContext context = NetworkTextReplaceContext.of(user, ContextHolder.builder() + .withParameter(DirectContextParameters.COUNT, itemStack.getAmount())); + Optional optionalHoverComponent = wrappedItem.hoverNameComponent(); + Component hoverComponent; + if (optionalHoverComponent.isPresent()) { + hoverComponent = optionalHoverComponent.get(); + } else { + hoverComponent = Component.translatable(itemStack.translationKey()); + } + // 展示名称为空,则显示其hover name + if (showName.isEmpty()) { + nameToShow = hoverComponent; + } + // 显示自定义格式的名字 + else { + nameToShow = AdventureHelper.miniMessage().deserialize( + showName, + ArrayUtils.appendElementToArrayTail(context.tagResolvers(), new CustomTagResolver("name", hoverComponent)) + ); + } } - continue; } - - ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack); - - // 转换为客户端侧物品 - Optional optional = BukkitItemManager.instance().s2c(itemStack, user); - if (optional.isPresent()) { + } + if (glowColor != null) { + Object teamByColor = BukkitTeamManager.instance().getTeamByColor(glowColor); + if (teamByColor != null) { changed = true; - itemStack = optional.get(); - Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); - packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack))); - } - - // 处理 drop-display 物品设置 - // 一定要处理经历过客户端侧组件修改的物品 - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - Optional> optionalCustomItem = wrappedItem.getCustomItem(); - String showName = null; - if (optionalCustomItem.isPresent()) { - showName = optionalCustomItem.get().settings().dropDisplay(); - } else if (Config.enableDefaultDropDisplay()) { - showName = Config.defaultDropDisplayFormat(); - } - - // 如果设定了自定义展示名 - if (showName != null) { - PlayerOptionalContext context = NetworkTextReplaceContext.of(user, ContextHolder.builder() - .withParameter(DirectContextParameters.COUNT, itemStack.getAmount())); - Optional optionalHoverComponent = wrappedItem.hoverNameComponent(); - Component hoverComponent; - if (optionalHoverComponent.isPresent()) { - hoverComponent = optionalHoverComponent.get(); - } else { - hoverComponent = Component.translatable(itemStack.translationKey()); + outer: { + for (int i = 0; i < packedItems.size(); i++) { + Object packedItem = packedItems.get(i); + int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); + if (entityDataId == BaseEntityData.SharedFlags.id()) { + byte flags = (Byte) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); + flags |= (byte) 0x40; + packedItems.set(i, BaseEntityData.SharedFlags.createEntityData(flags)); + break outer; + } + } + packedItems.add(BaseEntityData.SharedFlags.createEntityData((byte) 0x40)); } - // 展示名称为空,则显示其hover name - if (showName.isEmpty()) { - nameToShow = hoverComponent; - } - // 显示自定义格式的名字 - else { - nameToShow = AdventureHelper.miniMessage().deserialize( - showName, - ArrayUtils.appendElementToArrayTail(context.tagResolvers(), new CustomTagResolver("name", hoverComponent)) - ); + Object entityLookup = FastNMS.INSTANCE.method$ServerLevel$getEntityLookup(user.clientSideWorld().serverWorld()); + Object entity = FastNMS.INSTANCE.method$EntityLookup$get(entityLookup, id); + if (entity != null) { + user.sendPacket(FastNMS.INSTANCE.method$ClientboundSetPlayerTeamPacket$createMultiplePlayerPacket(teamByColor, List.of(FastNMS.INSTANCE.method$Entity$getUUID(entity).toString()), true), false); } } - - break; } // 添加自定义显示名 if (nameToShow != null) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/bukkit/CraftBukkitReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/bukkit/CraftBukkitReflections.java index 06546717e..e330f24ab 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/bukkit/CraftBukkitReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/bukkit/CraftBukkitReflections.java @@ -392,4 +392,12 @@ public final class CraftBukkitReflections { public static final Field field$MinecraftMerchant$title = requireNonNull( ReflectionUtils.getDeclaredField(clazz$CraftMerchantCustom$MinecraftMerchant, CoreReflections.clazz$Component, 0) ); + + public static final Class clazz$CraftTeam = requireNonNull( + ReflectionUtils.getClazz(BukkitReflectionUtils.assembleCBClass("scoreboard.CraftTeam")) + ); + + public static final Field field$CraftTeam$team = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$CraftTeam, CoreReflections.clazz$PlayerTeam, 0) + ); } 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 5d4d7286b..661b445e0 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 @@ -4638,4 +4638,11 @@ public final class CoreReflections { public static final Field field$EntityDimensions$fixed = requireNonNull( ReflectionUtils.getDeclaredField(clazz$EntityDimensions, boolean.class, 0) ); + + public static final Class clazz$PlayerTeam = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "world.scores.ScoreboardTeam", + "world.scores.PlayerTeam" + ) + ); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityDataUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityDataUtils.java index 1f03ba9ed..30a913296 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityDataUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityDataUtils.java @@ -10,6 +10,7 @@ public final class EntityDataUtils { private static final int LEFT_ALIGNMENT = 0x08; // 8 private static final int RIGHT_ALIGNMENT = 0x10; // 16 public static final int UNSAFE_ITEM_DATA_ID = 8; // 正常来说应该通过定义 Data 获取 id 这样的做法未经验证可能不安全 + public static final int SHARED_FLAGS_ID = 0; // 正常来说应该通过定义 Data 获取 id 这样的做法未经验证可能不安全 public static byte encodeTextDisplayMask(boolean hasShadow, boolean isSeeThrough, boolean useDefaultBackground, int alignment) { int bitMask = 0; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/score/BukkitTeamManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/score/BukkitTeamManager.java new file mode 100644 index 000000000..49fb993ad --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/score/BukkitTeamManager.java @@ -0,0 +1,76 @@ +package net.momirealms.craftengine.bukkit.world.score; + +import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; +import net.momirealms.craftengine.core.util.LegacyChatFormatter; +import net.momirealms.craftengine.core.world.score.TeamManager; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.scoreboard.Scoreboard; +import org.bukkit.scoreboard.Team; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class BukkitTeamManager implements TeamManager { + private static BukkitTeamManager instance; + private final BukkitCraftEngine plugin; + protected Set colorsInUse = new HashSet<>(); + private final Map teamByColor = new EnumMap<>(LegacyChatFormatter.class); + private boolean changed = false; + + public BukkitTeamManager(BukkitCraftEngine plugin) { + this.plugin = plugin; + instance = this; + } + + public static BukkitTeamManager instance() { + return instance; + } + + @Override + public void setColorInUse(LegacyChatFormatter color) { + this.colorsInUse.add(color); + this.changed = true; + } + + @Override + public void unload() { + this.changed = !this.colorsInUse.isEmpty(); + this.colorsInUse.clear(); + this.teamByColor.clear(); + } + + @Nullable + public Object getTeamByColor(LegacyChatFormatter color) { + return this.teamByColor.get(color); + } + + @SuppressWarnings("deprecation") + @Override + public void runDelayedSyncTasks() { + if (!this.changed) { + return; + } + Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard(); + for (LegacyChatFormatter color : LegacyChatFormatter.values()) { + Team team = scoreboard.getTeam(TEAM_PREFIX + color.name().toLowerCase(Locale.ROOT)); + if (this.colorsInUse.contains(color)) { + if (team == null) { + team = scoreboard.registerNewTeam(TEAM_PREFIX + color.name().toLowerCase(Locale.ROOT)); + team.setColor(ChatColor.valueOf(color.name())); + } + try { + Object nmsTeam = CraftBukkitReflections.field$CraftTeam$team.get(team); + this.teamByColor.put(color, nmsTeam); + } catch (ReflectiveOperationException e) { + this.plugin.logger().warn("Could not get nms team", e); + } + } else { + if (team != null) { + team.unregister(); + } + } + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/Glowing.java b/core/src/main/java/net/momirealms/craftengine/core/entity/Glowing.java new file mode 100644 index 000000000..71877e393 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/Glowing.java @@ -0,0 +1,10 @@ +package net.momirealms.craftengine.core.entity; + +import net.momirealms.craftengine.core.util.LegacyChatFormatter; +import org.jetbrains.annotations.Nullable; + +public interface Glowing { + + @Nullable + LegacyChatFormatter glowColor(); +} 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 b4460723c..364281ce7 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 @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.entity.furniture; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.momirealms.craftengine.core.entity.Glowing; import net.momirealms.craftengine.core.entity.furniture.behavior.FurnitureBehaviorTypes; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs; @@ -219,6 +220,14 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { Optional optionalLootSpawnOffset = Optional.ofNullable(variantArguments.get("loot-spawn-offset")).map(it -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset")); List> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elements"), FurnitureElementConfigs::fromMap); + // 收集发光颜色类型 + for (FurnitureElementConfig element : elements) { + if (element instanceof Glowing glowing) { + Optional.ofNullable(glowing.glowColor()).ifPresent(color -> AbstractFurnitureManager.this.plugin.teamManager().setColorInUse(color)); + } + } + + // 外部模型 Optional externalModel; if (variantArguments.containsKey("model-engine")) { externalModel = Optional.of(plugin.compatibilityManager().createModel("ModelEngine", variantArguments.get("model-engine").toString())); @@ -228,6 +237,7 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { externalModel = Optional.empty(); } + // 碰撞箱配置 List> hitboxes = ResourceConfigUtils.parseConfigAsList(variantArguments.get("hitboxes"), FurnitureHitBoxTypes::fromMap); if (hitboxes.isEmpty() && externalModel.isEmpty()) { hitboxes = List.of(defaultHitBox()); 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 a119bcf42..6eacd6503 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 @@ -234,6 +234,9 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl if (settings.destroyOnDeathChance != 0) { this.featureFlag$destroyOnDeathChance = true; } + if (settings.glowColor != null) { + this.plugin.teamManager().setColorInUse(settings.glowColor); + } } return true; } 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 3075ea81e..68fea801d 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 @@ -41,6 +41,7 @@ import net.momirealms.craftengine.core.plugin.scheduler.SchedulerAdapter; import net.momirealms.craftengine.core.sound.SoundManager; import net.momirealms.craftengine.core.util.CompletableFutures; import net.momirealms.craftengine.core.world.WorldManager; +import net.momirealms.craftengine.core.world.score.TeamManager; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.Logger; import org.jetbrains.annotations.ApiStatus; @@ -82,6 +83,7 @@ public abstract class CraftEngine implements Plugin { protected ProjectileManager projectileManager; protected SeatManager seatManager; protected EntityCullingManager entityCullingManager; + protected TeamManager teamManager; private final PluginTaskRegistry beforeEnableTaskRegistry = new PluginTaskRegistry(); private final PluginTaskRegistry afterEnableTaskRegistry = new PluginTaskRegistry(); @@ -164,6 +166,7 @@ public abstract class CraftEngine implements Plugin { this.projectileManager.reload(); this.seatManager.reload(); this.entityCullingManager.reload(); + this.teamManager.reload(); } private void runDelayTasks(boolean reloadRecipe) { @@ -236,6 +239,8 @@ public abstract class CraftEngine implements Plugin { if (reloadRecipe) { this.recipeManager.runDelayedSyncTasks(); } + // 同步修改队伍 + this.teamManager.runDelayedSyncTasks(); long time4 = System.currentTimeMillis(); long syncTime = time4 - time3; this.reloadEventDispatcher.accept(this); @@ -569,6 +574,21 @@ public abstract class CraftEngine implements Plugin { return projectileManager; } + @Override + public EntityCullingManager entityCullingManager() { + return entityCullingManager; + } + + @Override + public TeamManager teamManager() { + return teamManager; + } + + @Override + public SeatManager seatManager() { + return seatManager; + } + @Override public Platform platform() { return platform; 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 1813c2bd8..d4aae96e9 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 @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.advancement.AdvancementManager; import net.momirealms.craftengine.core.block.BlockManager; import net.momirealms.craftengine.core.entity.furniture.FurnitureManager; import net.momirealms.craftengine.core.entity.projectile.ProjectileManager; +import net.momirealms.craftengine.core.entity.seat.SeatManager; import net.momirealms.craftengine.core.font.FontManager; import net.momirealms.craftengine.core.item.ItemManager; import net.momirealms.craftengine.core.item.recipe.RecipeManager; @@ -16,6 +17,7 @@ import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.template.TemplateManager; import net.momirealms.craftengine.core.plugin.context.GlobalVariableManager; import net.momirealms.craftengine.core.plugin.dependency.DependencyManager; +import net.momirealms.craftengine.core.plugin.entityculling.EntityCullingManager; import net.momirealms.craftengine.core.plugin.gui.GuiManager; import net.momirealms.craftengine.core.plugin.gui.category.ItemBrowserManager; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; @@ -24,6 +26,7 @@ import net.momirealms.craftengine.core.plugin.network.NetworkManager; import net.momirealms.craftengine.core.plugin.scheduler.SchedulerAdapter; import net.momirealms.craftengine.core.sound.SoundManager; import net.momirealms.craftengine.core.world.WorldManager; +import net.momirealms.craftengine.core.world.score.TeamManager; import java.io.File; import java.io.InputStream; @@ -97,5 +100,11 @@ public interface Plugin { ProjectileManager projectileManager(); + EntityCullingManager entityCullingManager(); + + TeamManager teamManager(); + + SeatManager seatManager(); + Platform platform(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/score/TeamManager.java b/core/src/main/java/net/momirealms/craftengine/core/world/score/TeamManager.java new file mode 100644 index 000000000..af772e294 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/score/TeamManager.java @@ -0,0 +1,10 @@ +package net.momirealms.craftengine.core.world.score; + +import net.momirealms.craftengine.core.plugin.Manageable; +import net.momirealms.craftengine.core.util.LegacyChatFormatter; + +public interface TeamManager extends Manageable { + String TEAM_PREFIX = "ce_"; + + void setColorInUse(LegacyChatFormatter color); +} diff --git a/gradle.properties b/gradle.properties index bf950c905..b510a3cca 100644 --- a/gradle.properties +++ b/gradle.properties @@ -48,7 +48,7 @@ byte_buddy_version=1.18.1 ahocorasick_version=0.6.3 snake_yaml_version=2.5 anti_grief_version=1.0.5 -nms_helper_version=1.0.142 +nms_helper_version=1.0.145 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.38.7 From 8cdced204b846dbd4e5a7f3396738e6b84379597 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 6 Dec 2025 03:02:24 +0800 Subject: [PATCH 123/135] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E5=8F=91=E5=85=89?= =?UTF-8?q?=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../element/ItemDisplayFurnitureElement.java | 12 ++---------- .../ItemDisplayFurnitureElementConfig.java | 15 +++++++-------- .../craftengine/core/entity/Glowing.java | 10 ---------- .../furniture/AbstractFurnitureManager.java | 8 -------- 4 files changed, 9 insertions(+), 36 deletions(-) delete mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/Glowing.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java index cbd6a4c05..b63daf007 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java @@ -23,7 +23,7 @@ public class ItemDisplayFurnitureElement implements FurnitureElement { private final int entityId; private final Object despawnPacket; private final FurnitureColorSource colorSource; - private final LegacyChatFormatter glowColor; + private final UUID uuid = UUID.randomUUID(); public ItemDisplayFurnitureElement(Furniture furniture, ItemDisplayFurnitureElementConfig config) { this.config = config; @@ -33,26 +33,18 @@ public class ItemDisplayFurnitureElement implements FurnitureElement { this.position = new WorldPosition(furniturePos.world, position.x, position.y, position.z, furniturePos.xRot, furniturePos.yRot); this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(entityId); }}); this.colorSource = furniture.dataAccessor.getColorSource(); - this.glowColor = config.glowColor; } @Override public void show(Player player) { - UUID uuid = UUID.randomUUID(); player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - this.entityId, uuid, + this.entityId, this.uuid, this.position.x, this.position.y, this.position.z, 0, this.position.yRot, MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 ), FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadata.apply(player, this.colorSource)) )), false); - if (this.glowColor != null) { - Object team = BukkitTeamManager.instance().getTeamByColor(this.glowColor); - if (team != null) { - FastNMS.INSTANCE.method$ClientboundSetPlayerTeamPacket$createMultiplePlayerPacket(team, List.of(uuid.toString()), true); - } - } } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java index 8e6a16aae..1b265b269 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -4,7 +4,6 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; -import net.momirealms.craftengine.core.entity.Glowing; import net.momirealms.craftengine.core.entity.display.Billboard; import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; import net.momirealms.craftengine.core.entity.furniture.Furniture; @@ -15,8 +14,8 @@ import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.item.data.FireworkExplosion; +import net.momirealms.craftengine.core.util.Color; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.LegacyChatFormatter; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -30,7 +29,7 @@ import java.util.Map; import java.util.Optional; import java.util.function.BiFunction; -public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig, Glowing { +public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig { public static final Factory FACTORY = new Factory(); public final BiFunction> metadata; public final Key itemId; @@ -45,7 +44,7 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig public final float shadowRadius; public final float shadowStrength; public final boolean applyDyedColor; - public final LegacyChatFormatter glowColor; + public final Color glowColor; public ItemDisplayFurnitureElementConfig(Key itemId, Vector3f scale, @@ -59,7 +58,7 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig float shadowRadius, float shadowStrength, boolean applyDyedColor, - @Nullable LegacyChatFormatter glowColor) { + @Nullable Color glowColor) { this.scale = scale; this.position = position; this.translation = translation; @@ -91,6 +90,7 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig List dataValues = new ArrayList<>(); if (glowColor != null) { BaseEntityData.SharedFlags.addEntityData((byte) 0x40, dataValues); + ItemDisplayEntityData.GlowColorOverride.addEntityData(glowColor.color(), dataValues); } ItemDisplayEntityData.DisplayedItem.addEntityData(itemFunction.apply(player, source).getLiteralObject(), dataValues); ItemDisplayEntityData.Scale.addEntityData(this.scale, dataValues); @@ -153,8 +153,7 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig } @Nullable - @Override - public LegacyChatFormatter glowColor() { + public Color glowColor() { return this.glowColor; } @@ -182,7 +181,7 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength"), applyDyedColor, - ResourceConfigUtils.getAsEnum(arguments.get("glow-color"), LegacyChatFormatter.class, null) + Optional.ofNullable(arguments.get("glow-color")).map(it -> Color.fromStrings(it.toString().split(","))).orElse(null) ); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/Glowing.java b/core/src/main/java/net/momirealms/craftengine/core/entity/Glowing.java deleted file mode 100644 index 71877e393..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/Glowing.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.momirealms.craftengine.core.entity; - -import net.momirealms.craftengine.core.util.LegacyChatFormatter; -import org.jetbrains.annotations.Nullable; - -public interface Glowing { - - @Nullable - LegacyChatFormatter glowColor(); -} 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 364281ce7..d439e8ea3 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 @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.entity.furniture; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import net.momirealms.craftengine.core.entity.Glowing; import net.momirealms.craftengine.core.entity.furniture.behavior.FurnitureBehaviorTypes; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs; @@ -220,13 +219,6 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { Optional optionalLootSpawnOffset = Optional.ofNullable(variantArguments.get("loot-spawn-offset")).map(it -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset")); List> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elements"), FurnitureElementConfigs::fromMap); - // 收集发光颜色类型 - for (FurnitureElementConfig element : elements) { - if (element instanceof Glowing glowing) { - Optional.ofNullable(glowing.glowColor()).ifPresent(color -> AbstractFurnitureManager.this.plugin.teamManager().setColorInUse(color)); - } - } - // 外部模型 Optional externalModel; if (variantArguments.containsKey("model-engine")) { From 1e01956ba943f6ef8a0f6f1162104d917aa51885 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 6 Dec 2025 03:15:57 +0800 Subject: [PATCH 124/135] =?UTF-8?q?=E9=81=BF=E5=85=8D=E5=85=B6=E4=BB=96?= =?UTF-8?q?=E5=BC=B9=E5=B0=84=E7=89=A9=E5=BA=94=E7=94=A8=E5=8F=91=E5=85=89?= =?UTF-8?q?=E5=92=8C=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/network/BukkitNetworkManager.java | 4 +- .../handler/CommonItemPacketHandler.java | 127 +++------------- .../network/handler/ItemPacketHandler.java | 141 ++++++++++++++++++ 3 files changed, 161 insertions(+), 111 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemPacketHandler.java 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 113268a15..5546b0734 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 @@ -3900,7 +3900,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes this.handlers[MEntityTypes.BLOCK_DISPLAY$registryId] = simpleAddEntityHandler(BlockDisplayPacketHandler.INSTANCE); this.handlers[MEntityTypes.TEXT_DISPLAY$registryId] = simpleAddEntityHandler(TextDisplayPacketHandler.INSTANCE); this.handlers[MEntityTypes.ARMOR_STAND$registryId] = simpleAddEntityHandler(ArmorStandPacketHandler.INSTANCE); - this.handlers[MEntityTypes.ITEM$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + this.handlers[MEntityTypes.ITEM$registryId] = simpleAddEntityHandler(ItemPacketHandler.INSTANCE); this.handlers[MEntityTypes.ITEM_FRAME$registryId] = simpleAddEntityHandler(ItemFramePacketHandler.INSTANCE); this.handlers[MEntityTypes.GLOW_ITEM_FRAME$registryId] = simpleAddEntityHandler(ItemFramePacketHandler.INSTANCE); this.handlers[MEntityTypes.ENDERMAN$registryId] = simpleAddEntityHandler(EndermanPacketHandler.INSTANCE); @@ -3927,7 +3927,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes this.handlers[MEntityTypes.TNT$registryId] = simpleAddEntityHandler(PrimedTNTPacketHandler.INSTANCE); } if (VersionHelper.isOrAbove1_20_5()) { - this.handlers[MEntityTypes.OMINOUS_ITEM_SPAWNER$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + this.handlers[MEntityTypes.OMINOUS_ITEM_SPAWNER$registryId] = simpleAddEntityHandler(ItemPacketHandler.INSTANCE); } this.handlers[MEntityTypes.FALLING_BLOCK$registryId] = (user, event) -> { FriendlyByteBuf buf = event.getBuffer(); 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 b835c2c8f..4e264198e 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 @@ -1,32 +1,16 @@ package net.momirealms.craftengine.bukkit.plugin.network.handler; -import net.kyori.adventure.text.Component; -import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; -import net.momirealms.craftengine.bukkit.entity.data.ItemEntityData; 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.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.EntityDataUtils; -import net.momirealms.craftengine.bukkit.world.score.BukkitTeamManager; import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.item.CustomItem; -import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.ItemSettings; 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.parameter.DirectContextParameters; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; -import net.momirealms.craftengine.core.plugin.text.minimessage.CustomTagResolver; -import net.momirealms.craftengine.core.util.AdventureHelper; -import net.momirealms.craftengine.core.util.ArrayUtils; import net.momirealms.craftengine.core.util.FriendlyByteBuf; -import net.momirealms.craftengine.core.util.LegacyChatFormatter; import org.bukkit.inventory.ItemStack; import java.util.List; @@ -43,104 +27,29 @@ public class CommonItemPacketHandler implements EntityPacketHandler { int id = buf.readVarInt(); boolean changed = false; List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); - Component nameToShow = null; - LegacyChatFormatter glowColor = null; for (int i = 0; i < packedItems.size(); i++) { Object packedItem = packedItems.get(i); int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); - if (entityDataId == EntityDataUtils.UNSAFE_ITEM_DATA_ID) { - Object nmsItemStack = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); - - // 可能是其他插件导致的问题 - if (!CoreReflections.clazz$ItemStack.isInstance(nmsItemStack)) { - long time = System.currentTimeMillis(); - if (time - lastWarningTime > 5000) { - BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; - CraftEngine.instance().logger().severe("An issue was detected while applying item-related entity data for '" + serverPlayer.name() + - "'. Please execute the command '/ce debug entity-id " + serverPlayer.world().name() + " " + id + "' and provide a screenshot for further investigation."); - lastWarningTime = time; - } - continue; - } - - ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack); - - // 转换为客户端侧物品 - Optional optional = BukkitItemManager.instance().s2c(itemStack, user); - if (optional.isPresent()) { - changed = true; - itemStack = optional.get(); - Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); - packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack))); - } - - // 处理 drop-display 物品设置 - // 一定要处理经历过客户端侧组件修改的物品 - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - Optional> optionalCustomItem = wrappedItem.getCustomItem(); - String showName = null; - if (optionalCustomItem.isPresent()) { - ItemSettings settings = optionalCustomItem.get().settings(); - showName = settings.dropDisplay(); - glowColor = settings.glowColor(); - } else if (Config.enableDefaultDropDisplay()) { - showName = Config.defaultDropDisplayFormat(); - } - - // 如果设定了自定义展示名 - if (showName != null) { - PlayerOptionalContext context = NetworkTextReplaceContext.of(user, ContextHolder.builder() - .withParameter(DirectContextParameters.COUNT, itemStack.getAmount())); - Optional optionalHoverComponent = wrappedItem.hoverNameComponent(); - Component hoverComponent; - if (optionalHoverComponent.isPresent()) { - hoverComponent = optionalHoverComponent.get(); - } else { - hoverComponent = Component.translatable(itemStack.translationKey()); - } - // 展示名称为空,则显示其hover name - if (showName.isEmpty()) { - nameToShow = hoverComponent; - } - // 显示自定义格式的名字 - else { - nameToShow = AdventureHelper.miniMessage().deserialize( - showName, - ArrayUtils.appendElementToArrayTail(context.tagResolvers(), new CustomTagResolver("name", hoverComponent)) - ); - } + if (entityDataId != EntityDataUtils.UNSAFE_ITEM_DATA_ID) continue; + Object nmsItemStack = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); + if (!CoreReflections.clazz$ItemStack.isInstance(nmsItemStack)) { + long time = System.currentTimeMillis(); + if (time - lastWarningTime > 5000) { + BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user; + CraftEngine.instance().logger().severe("An issue was detected while applying item-related entity data for '" + serverPlayer.name() + + "'. Please execute the command '/ce debug entity-id " + serverPlayer.world().name() + " " + id + "' and provide a screenshot for further investigation."); + lastWarningTime = time; } + continue; } - } - if (glowColor != null) { - Object teamByColor = BukkitTeamManager.instance().getTeamByColor(glowColor); - if (teamByColor != null) { - changed = true; - outer: { - for (int i = 0; i < packedItems.size(); i++) { - Object packedItem = packedItems.get(i); - int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); - if (entityDataId == BaseEntityData.SharedFlags.id()) { - byte flags = (Byte) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); - flags |= (byte) 0x40; - packedItems.set(i, BaseEntityData.SharedFlags.createEntityData(flags)); - break outer; - } - } - packedItems.add(BaseEntityData.SharedFlags.createEntityData((byte) 0x40)); - } - Object entityLookup = FastNMS.INSTANCE.method$ServerLevel$getEntityLookup(user.clientSideWorld().serverWorld()); - Object entity = FastNMS.INSTANCE.method$EntityLookup$get(entityLookup, id); - if (entity != null) { - user.sendPacket(FastNMS.INSTANCE.method$ClientboundSetPlayerTeamPacket$createMultiplePlayerPacket(teamByColor, List.of(FastNMS.INSTANCE.method$Entity$getUUID(entity).toString()), true), false); - } - } - } - // 添加自定义显示名 - if (nameToShow != null) { + ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack); + Optional optional = BukkitItemManager.instance().s2c(itemStack, user); + if (optional.isEmpty()) continue; changed = true; - packedItems.add(ItemEntityData.CustomNameVisible.createEntityData(true)); - packedItems.add(ItemEntityData.CustomName.createEntityData(Optional.of(ComponentUtils.adventureToMinecraft(nameToShow)))); + itemStack = optional.get(); + Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); + packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack))); + break; } if (changed) { event.setChanged(true); @@ -150,4 +59,4 @@ public class CommonItemPacketHandler implements EntityPacketHandler { FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf); } } -} +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemPacketHandler.java new file mode 100644 index 000000000..6f01fdc82 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemPacketHandler.java @@ -0,0 +1,141 @@ +package net.momirealms.craftengine.bukkit.plugin.network.handler; + +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; +import net.momirealms.craftengine.bukkit.entity.data.ItemEntityData; +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.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.bukkit.util.EntityDataUtils; +import net.momirealms.craftengine.bukkit.world.score.BukkitTeamManager; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.CustomItem; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemSettings; +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.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; +import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; +import net.momirealms.craftengine.core.plugin.text.minimessage.CustomTagResolver; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.ArrayUtils; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import net.momirealms.craftengine.core.util.LegacyChatFormatter; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Optional; + +public class ItemPacketHandler implements EntityPacketHandler { + public static final ItemPacketHandler INSTANCE = new ItemPacketHandler(); + + @Override + public void handleSetEntityData(Player user, ByteBufPacketEvent event) { + if (Config.disableItemOperations()) return; + FriendlyByteBuf buf = event.getBuffer(); + int id = buf.readVarInt(); + boolean changed = false; + List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); + Component nameToShow = null; + LegacyChatFormatter glowColor = null; + for (int i = 0; i < packedItems.size(); i++) { + Object packedItem = packedItems.get(i); + int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); + if (entityDataId == ItemEntityData.Item.id()) { + Object nmsItemStack = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); + ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack); + + // 转换为客户端侧物品 + Optional optional = BukkitItemManager.instance().s2c(itemStack, user); + if (optional.isPresent()) { + changed = true; + itemStack = optional.get(); + Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); + packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack))); + } + + // 处理 drop-display 物品设置 + // 一定要处理经历过客户端侧组件修改的物品 + Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); + Optional> optionalCustomItem = wrappedItem.getCustomItem(); + String showName = null; + if (optionalCustomItem.isPresent()) { + ItemSettings settings = optionalCustomItem.get().settings(); + showName = settings.dropDisplay(); + glowColor = settings.glowColor(); + } else if (Config.enableDefaultDropDisplay()) { + showName = Config.defaultDropDisplayFormat(); + } + + // 如果设定了自定义展示名 + if (showName != null) { + PlayerOptionalContext context = NetworkTextReplaceContext.of(user, ContextHolder.builder() + .withParameter(DirectContextParameters.COUNT, itemStack.getAmount())); + Optional optionalHoverComponent = wrappedItem.hoverNameComponent(); + Component hoverComponent; + if (optionalHoverComponent.isPresent()) { + hoverComponent = optionalHoverComponent.get(); + } else { + hoverComponent = Component.translatable(itemStack.translationKey()); + } + // 展示名称为空,则显示其hover name + if (showName.isEmpty()) { + nameToShow = hoverComponent; + } + // 显示自定义格式的名字 + else { + nameToShow = AdventureHelper.miniMessage().deserialize( + showName, + ArrayUtils.appendElementToArrayTail(context.tagResolvers(), new CustomTagResolver("name", hoverComponent)) + ); + } + } + + break; + } + } + if (glowColor != null) { + Object teamByColor = BukkitTeamManager.instance().getTeamByColor(glowColor); + if (teamByColor != null) { + changed = true; + outer: { + for (int i = 0; i < packedItems.size(); i++) { + Object packedItem = packedItems.get(i); + int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); + if (entityDataId == BaseEntityData.SharedFlags.id()) { + byte flags = (Byte) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); + flags |= (byte) 0x40; + packedItems.set(i, BaseEntityData.SharedFlags.createEntityData(flags)); + break outer; + } + } + packedItems.add(BaseEntityData.SharedFlags.createEntityData((byte) 0x40)); + } + Object entityLookup = FastNMS.INSTANCE.method$ServerLevel$getEntityLookup(user.clientSideWorld().serverWorld()); + Object entity = FastNMS.INSTANCE.method$EntityLookup$get(entityLookup, id); + if (entity != null) { + user.sendPacket(FastNMS.INSTANCE.method$ClientboundSetPlayerTeamPacket$createMultiplePlayerPacket(teamByColor, List.of(FastNMS.INSTANCE.method$Entity$getUUID(entity).toString()), true), false); + } + } + } + // 添加自定义显示名 + if (nameToShow != null) { + changed = true; + packedItems.add(ItemEntityData.CustomNameVisible.createEntityData(true)); + packedItems.add(ItemEntityData.CustomName.createEntityData(Optional.of(ComponentUtils.adventureToMinecraft(nameToShow)))); + } + if (changed) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(id); + FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf); + } + } +} From a2d83cefb37cf23b1ad7c7f57de5bfedefc04e17 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 6 Dec 2025 04:03:12 +0800 Subject: [PATCH 125/135] =?UTF-8?q?=E6=8F=90=E5=8D=87=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common-files/src/main/resources/config.yml | 5 +++++ gradle.properties | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 26716aa71..8252c48c1 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -576,6 +576,11 @@ client-optimization: bucket-size: 1000 restore-per-tick: 25 +# [Premium Exclusive] +bedrock-edition-support: + enable: false + player-prefix: "!" + # Enables or disables debug mode debug: common: false diff --git a/gradle.properties b/gradle.properties index b510a3cca..29c7148dc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings -project_version=0.0.65.18 +project_version=0.0.65.19 config_version=60 lang_version=43 project_group=net.momirealms From 0b057fa86952aebc75040643f7593e7fbd892b9d Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Sat, 6 Dec 2025 05:27:25 +0800 Subject: [PATCH 126/135] =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ItemDisplayFurnitureElementConfig.java | 35 +++++++++++++++++-- .../bukkit/util/EntityDataUtils.java | 1 - 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java index 1b265b269..00ec59e3c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -45,6 +45,9 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig public final float shadowStrength; public final boolean applyDyedColor; public final Color glowColor; + public final int blockLight; + public final int skyLight; + public final float viewRange; public ItemDisplayFurnitureElementConfig(Key itemId, Vector3f scale, @@ -58,7 +61,10 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig float shadowRadius, float shadowStrength, boolean applyDyedColor, - @Nullable Color glowColor) { + @Nullable Color glowColor, + int blockLight, + int skyLight, + float viewRange) { this.scale = scale; this.position = position; this.translation = translation; @@ -72,6 +78,9 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig this.applyDyedColor = applyDyedColor; this.itemId = itemId; this.glowColor = glowColor; + this.blockLight = blockLight; + this.skyLight = skyLight; + this.viewRange = viewRange; BiFunction> itemFunction = (player, colorSource) -> { Item wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player); if (applyDyedColor && colorSource != null && wrappedItem != null) { @@ -89,7 +98,7 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig this.metadata = (player, source) -> { List dataValues = new ArrayList<>(); if (glowColor != null) { - BaseEntityData.SharedFlags.addEntityData((byte) 0x40, dataValues); + ItemDisplayEntityData.SharedFlags.addEntityData((byte) 0x40, dataValues); ItemDisplayEntityData.GlowColorOverride.addEntityData(glowColor.color(), dataValues); } ItemDisplayEntityData.DisplayedItem.addEntityData(itemFunction.apply(player, source).getLiteralObject(), dataValues); @@ -100,6 +109,10 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig ItemDisplayEntityData.DisplayType.addEntityData(this.displayContext.id(), dataValues); ItemDisplayEntityData.ShadowRadius.addEntityData(this.shadowRadius, dataValues); ItemDisplayEntityData.ShadowStrength.addEntityData(this.shadowStrength, dataValues); + if (this.blockLight != -1 && this.skyLight != -1) { + ItemDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues); + } + ItemDisplayEntityData.ViewRange.addEntityData(this.viewRange, dataValues); return dataValues; }; } @@ -157,6 +170,18 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig return this.glowColor; } + public int blockLight() { + return this.blockLight; + } + + public int skyLight() { + return this.skyLight; + } + + public float viewRange() { + return this.viewRange; + } + @Override public ItemDisplayFurnitureElement create(@NotNull Furniture furniture) { return new ItemDisplayFurnitureElement(furniture, this); @@ -168,6 +193,7 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig public ItemDisplayFurnitureElementConfig create(Map arguments) { Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.furniture.element.item_display.missing_item")); boolean applyDyedColor = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color"); + Map brightness = ResourceConfigUtils.getAsMap(arguments.getOrDefault("brightness", Map.of()), "brightness"); return new ItemDisplayFurnitureElementConfig( itemId, ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), @@ -181,7 +207,10 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength"), applyDyedColor, - Optional.ofNullable(arguments.get("glow-color")).map(it -> Color.fromStrings(it.toString().split(","))).orElse(null) + Optional.ofNullable(arguments.get("glow-color")).map(it -> Color.fromStrings(it.toString().split(","))).orElse(null), + ResourceConfigUtils.getAsInt(brightness.getOrDefault("block-light", -1), "block-light"), + ResourceConfigUtils.getAsInt(brightness.getOrDefault("sky-light", -1), "sky-light"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("view-range", 1f), "view-range") ); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityDataUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityDataUtils.java index 30a913296..1f03ba9ed 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityDataUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityDataUtils.java @@ -10,7 +10,6 @@ public final class EntityDataUtils { private static final int LEFT_ALIGNMENT = 0x08; // 8 private static final int RIGHT_ALIGNMENT = 0x10; // 16 public static final int UNSAFE_ITEM_DATA_ID = 8; // 正常来说应该通过定义 Data 获取 id 这样的做法未经验证可能不安全 - public static final int SHARED_FLAGS_ID = 0; // 正常来说应该通过定义 Data 获取 id 这样的做法未经验证可能不安全 public static byte encodeTextDisplayMask(boolean hasShadow, boolean isSeeThrough, boolean useDefaultBackground, int alignment) { int bitMask = 0; From 17c9b567ffb155fa25770a678a25c122f760abef Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Sat, 6 Dec 2025 06:44:12 +0800 Subject: [PATCH 127/135] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=86=97=E4=BD=99?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/core/entity/furniture/CustomFurniture.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java index 6a4b89499..de43db298 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java @@ -48,7 +48,6 @@ public interface CustomFurniture { if (optionalVariant.isPresent()) { variantName = optionalVariant.get(); } else { - @SuppressWarnings("deprecation") Optional optionalAnchorType = accessor.anchorType(); if (optionalAnchorType.isPresent()) { variantName = optionalAnchorType.get().name().toLowerCase(Locale.ROOT); From 4f058e088e78f41da8871a1fabcbfb565997fdb2 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Sat, 6 Dec 2025 06:49:41 +0800 Subject: [PATCH 128/135] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/core/entity/furniture/CustomFurniture.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java index de43db298..abfd362bf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/CustomFurniture.java @@ -28,11 +28,11 @@ public interface CustomFurniture { Map variants(); default FurnitureVariant anyVariant() { - return variants().values().stream().findFirst().get(); + return variants().values().iterator().next(); } default String anyVariantName() { - return variants().keySet().stream().findFirst().get(); + return variants().keySet().iterator().next(); } @Nullable From aab16be43c0adeeefbe410d0655ca592b5601280 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 6 Dec 2025 17:20:34 +0800 Subject: [PATCH 129/135] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/entity/BukkitEntity.java | 2 +- .../bukkit/plugin/user/BukkitServerPlayer.java | 18 +++++++++--------- .../craftengine/bukkit/util/PlayerUtils.java | 8 ++++---- .../craftengine/core/entity/Entity.java | 2 +- .../core/entity/furniture/Furniture.java | 2 +- .../craftengine/core/plugin/config/Config.java | 7 +++++++ .../plugin/config/template/ArgumentString.java | 2 +- 7 files changed, 24 insertions(+), 17 deletions(-) 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 3d4780635..efda7a2c5 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 @@ -46,7 +46,7 @@ public class BukkitEntity extends AbstractEntity { } @Override - public int entityID() { + public int entityId() { return platformEntity().getEntityId(); } 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 f42c9713d..af3c1a1a1 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 @@ -588,7 +588,7 @@ public class BukkitServerPlayer extends Player { } if (Config.predictBreaking() && this.eyeLocation != null && !this.isDestroyingCustomBlock && !unsureEyeLocation.equals(this.eyeLocation)) { // if it's not destroying blocks, we do predict - if ((gameTicks() + entityID()) % Config.predictBreakingInterval() == 0) { + if ((gameTicks() + entityId()) % Config.predictBreakingInterval() == 0) { this.predictNextBlockToMine(); } } @@ -807,7 +807,7 @@ public class BukkitServerPlayer extends Player { if (VersionHelper.isOrAbove1_20_5()) { Object serverPlayer = serverPlayer(); Object attributeInstance = CoreReflections.methodHandle$ServerPlayer$getAttributeMethod.invokeExact(serverPlayer, MAttributeHolders.BLOCK_BREAK_SPEED); - Object newPacket = NetworkReflections.methodHandle$ClientboundUpdateAttributesPacket0Constructor.invokeExact(entityID(), (List) Lists.newArrayList(attributeInstance)); + Object newPacket = NetworkReflections.methodHandle$ClientboundUpdateAttributesPacket0Constructor.invokeExact(entityId(), (List) Lists.newArrayList(attributeInstance)); sendPacket(newPacket, true); } else { resetEffect(MMobEffects.MINING_FATIGUE); @@ -819,11 +819,11 @@ public class BukkitServerPlayer extends Player { CoreReflections.constructor$AttributeModifier.newInstance(KeyUtils.toResourceLocation(Key.DEFAULT_NAMESPACE, "custom_hardness"), -9999d, CoreReflections.instance$AttributeModifier$Operation$ADD_VALUE) : CoreReflections.constructor$AttributeModifier.newInstance(UUID.randomUUID(), Key.DEFAULT_NAMESPACE + ":custom_hardness", -9999d, CoreReflections.instance$AttributeModifier$Operation$ADD_VALUE); Object attributeSnapshot = NetworkReflections.constructor$ClientboundUpdateAttributesPacket$AttributeSnapshot.newInstance(MAttributeHolders.BLOCK_BREAK_SPEED, 1d, Lists.newArrayList(attributeModifier)); - Object newPacket = NetworkReflections.constructor$ClientboundUpdateAttributesPacket1.newInstance(entityID(), Lists.newArrayList(attributeSnapshot)); + Object newPacket = NetworkReflections.constructor$ClientboundUpdateAttributesPacket1.newInstance(entityId(), Lists.newArrayList(attributeSnapshot)); sendPacket(newPacket, true); } else { - Object fatiguePacket = MobEffectUtils.createPacket(MMobEffects.MINING_FATIGUE, entityID(), (byte) 9, -1, false, false, false); - Object hastePacket = MobEffectUtils.createPacket(MMobEffects.HASTE, entityID(), (byte) 0, -1, false, false, false); + Object fatiguePacket = MobEffectUtils.createPacket(MMobEffects.MINING_FATIGUE, entityId(), (byte) 9, -1, false, false, false); + Object hastePacket = MobEffectUtils.createPacket(MMobEffects.HASTE, entityId(), (byte) 0, -1, false, false, false); sendPackets(List.of(fatiguePacket, hastePacket), true); } } @@ -863,9 +863,9 @@ public class BukkitServerPlayer extends Player { Object effectInstance = CoreReflections.method$ServerPlayer$getEffect.invoke(serverPlayer(), mobEffect); Object packet; if (effectInstance != null) { - packet = NetworkReflections.constructor$ClientboundUpdateMobEffectPacket.newInstance(entityID(), effectInstance); + packet = NetworkReflections.constructor$ClientboundUpdateMobEffectPacket.newInstance(entityId(), effectInstance); } else { - packet = NetworkReflections.constructor$ClientboundRemoveMobEffectPacket.newInstance(entityID(), mobEffect); + packet = NetworkReflections.constructor$ClientboundRemoveMobEffectPacket.newInstance(entityId(), mobEffect); } sendPacket(packet, true); } @@ -975,7 +975,7 @@ public class BukkitServerPlayer extends Player { } private void broadcastDestroyProgress(org.bukkit.entity.Player player, BlockPos hitPos, Object blockPos, int stage) { - Object packet = FastNMS.INSTANCE.constructor$ClientboundBlockDestructionPacket(Integer.MAX_VALUE - entityID(), blockPos, stage); + Object packet = FastNMS.INSTANCE.constructor$ClientboundBlockDestructionPacket(Integer.MAX_VALUE - entityId(), blockPos, stage); for (org.bukkit.entity.Player other : player.getWorld().getPlayers()) { Location otherLocation = other.getLocation(); double d0 = (double) hitPos.x() - otherLocation.getX(); @@ -1036,7 +1036,7 @@ public class BukkitServerPlayer extends Player { } @Override - public int entityID() { + public int entityId() { return platformPlayer().getEntityId(); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java index ee138d4d8..96de3b3c8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java @@ -71,20 +71,20 @@ public final class PlayerUtils { Object previousOffHandItem = player.getItemInHand(InteractionHand.OFF_HAND).getLiteralObject(); if (isMainHandTotem) { packets.add(NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance( - player.entityID(), List.of(Pair.of(CoreReflections.instance$EquipmentSlot$MAINHAND, BukkitItemManager.instance().uniqueEmptyItem().item().getLiteralObject())) + player.entityId(), List.of(Pair.of(CoreReflections.instance$EquipmentSlot$MAINHAND, BukkitItemManager.instance().uniqueEmptyItem().item().getLiteralObject())) )); } packets.add(NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance( - player.entityID(), List.of(Pair.of(CoreReflections.instance$EquipmentSlot$OFFHAND, totemItem)) + player.entityId(), List.of(Pair.of(CoreReflections.instance$EquipmentSlot$OFFHAND, totemItem)) )); packets.add(NetworkReflections.constructor$ClientboundEntityEventPacket.newInstance(player.serverPlayer(), (byte) 35)); if (isMainHandTotem) { packets.add(NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance( - player.entityID(), List.of(Pair.of(CoreReflections.instance$EquipmentSlot$MAINHAND, previousMainHandItem.getLiteralObject())) + player.entityId(), List.of(Pair.of(CoreReflections.instance$EquipmentSlot$MAINHAND, previousMainHandItem.getLiteralObject())) )); } packets.add(NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance( - player.entityID(), List.of(Pair.of(CoreReflections.instance$EquipmentSlot$OFFHAND, previousOffHandItem)) + player.entityId(), List.of(Pair.of(CoreReflections.instance$EquipmentSlot$OFFHAND, previousOffHandItem)) )); if (sound != null || silent) { packets.add(NetworkReflections.constructor$ClientboundStopSoundPacket.newInstance( diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java index 81ab02cf7..7acf7a186 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/Entity.java @@ -27,7 +27,7 @@ public interface Entity { float yRot(); - int entityID(); + int entityId(); World world(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index 98033012d..18e3d467c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -274,7 +274,7 @@ public abstract class Furniture implements Cullable { } public int entityId() { - return this.metaDataEntity.entityID(); + return this.metaDataEntity.entityId(); } public boolean hasExternalModel() { 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 a00dc1f0e..c38549237 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 @@ -215,6 +215,9 @@ public class Config { protected int client_optimization$entity_culling$rate_limiting$bucket_size; protected int client_optimization$entity_culling$rate_limiting$restore_per_tick; + protected boolean bedrock_edition_support$enable; + protected String bedrock_edition_support$player_prefix; + public Config(CraftEngine plugin) { this.plugin = plugin; this.configVersion = PluginProperties.getValue("config"); @@ -589,6 +592,10 @@ public class Config { client_optimization$entity_culling$rate_limiting$bucket_size = config.getInt("client-optimization.entity-culling.rate-limiting.bucket-size", 300); client_optimization$entity_culling$rate_limiting$restore_per_tick = config.getInt("client-optimization.entity-culling.rate-limiting.restore-per-tick", 5); + // bedrock support + bedrock_edition_support$enable = config.getBoolean("bedrock-edition-support.enable", true); + bedrock_edition_support$player_prefix = config.getString("bedrock-edition-support.player-prefix", "!"); + firstTime = false; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java index 3394bed15..71336065d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/ArgumentString.java @@ -152,7 +152,7 @@ public interface ArgumentString { @Override public boolean equals(Object o) { - if (!(o instanceof Complex that)) return false; + if (!(o instanceof Complex2 that)) return false; return this.rawText.equals(that.rawText); } From e2170f54ba5a20b0348ee928db158b37ee7d8783 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Sat, 6 Dec 2025 19:03:07 +0800 Subject: [PATCH 130/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=9F=BA=E5=B2=A9?= =?UTF-8?q?=E7=89=88=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bukkit/compatibility/build.gradle.kts | 5 +++ .../BukkitCompatibilityManager.java | 22 ++++++++++ .../compatibility/bedrock/FloodgateUtils.java | 14 +++++++ .../compatibility/bedrock/GeyserUtils.java | 14 +++++++ .../compatibility/item/MMOItemsSource.java | 4 +- .../worldedit/WorldEditBlockRegister.java | 3 +- bukkit/paper-loader/build.gradle.kts | 4 ++ .../advancement/BukkitAdvancementManager.java | 5 ++- .../block/behavior/BukkitBlockBehavior.java | 3 +- .../element/ItemBlockEntityElementConfig.java | 5 +++ .../ItemDisplayBlockEntityElementConfig.java | 37 +++++++++++++++- .../TextDisplayBlockEntityElementConfig.java | 42 ++++++++++++++++--- .../entity/furniture/BukkitFurniture.java | 5 ++- .../element/ItemDisplayFurnitureElement.java | 2 +- .../ItemDisplayFurnitureElementConfig.java | 38 +++++++++-------- .../hitbox/CustomFurnitureHitbox.java | 3 +- .../hitbox/InteractionFurnitureHitbox.java | 3 +- .../DebugGenerateInternalAssetsCommand.java | 3 +- .../plugin/injector/BlockStateGenerator.java | 2 +- .../bukkit/util/InteractUtils.java | 8 ++-- .../core/block/parser/BlockStateParser.java | 13 +++--- .../core/pack/AbstractPackManager.java | 12 +++--- .../core/pack/host/impl/AlistHost.java | 2 +- .../core/pack/host/impl/DropboxHost.java | 2 +- .../core/pack/host/impl/ExternalHost.java | 3 +- .../core/pack/host/impl/GitLabHost.java | 8 ++-- .../core/pack/host/impl/LobFileHost.java | 15 +++---- .../core/pack/host/impl/OneDriveHost.java | 11 ++--- .../pack/host/impl/SelfHostHttpServer.java | 16 +++++-- .../model/special/ShulkerBoxSpecialModel.java | 2 +- .../core/pack/obfuscation/ObfB.java | 3 +- .../compatibility/CompatibilityManager.java | 2 + .../entityculling/EntityCullingThread.java | 4 +- .../core/util/MarkedArrayList.java | 3 ++ .../craftengine/core/util/MarkedHashMap.java | 3 ++ .../craftengine/core/util/SkullUtils.java | 3 +- 36 files changed, 241 insertions(+), 83 deletions(-) create mode 100644 bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/bedrock/FloodgateUtils.java create mode 100644 bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/bedrock/GeyserUtils.java diff --git a/bukkit/compatibility/build.gradle.kts b/bukkit/compatibility/build.gradle.kts index 13d93e5cd..af75e84e9 100644 --- a/bukkit/compatibility/build.gradle.kts +++ b/bukkit/compatibility/build.gradle.kts @@ -18,6 +18,7 @@ repositories { maven("https://jitpack.io") // sxitem slimefun maven("https://repo.codemc.io/repository/maven-public/") // quickshop maven("https://repo.nexomc.com/releases/") // nexo + maven("https://repo.opencollab.dev/main/") // geyser } dependencies { @@ -88,6 +89,10 @@ dependencies { compileOnly("io.github.Slimefun:Slimefun4:RC-32") // QuickShop compileOnly("com.ghostchu:quickshop-api:6.2.0.10") + // Geyser + compileOnly("org.geysermc.geyser:api:2.9.0-SNAPSHOT") + // Floodgate + compileOnly("org.geysermc.floodgate:api:2.2.4-SNAPSHOT") } 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 c56a998aa..35e2717f0 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 @@ -3,6 +3,8 @@ package net.momirealms.craftengine.bukkit.compatibility; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.block.entity.renderer.element.BukkitBlockEntityElementConfigs; +import net.momirealms.craftengine.bukkit.compatibility.bedrock.FloodgateUtils; +import net.momirealms.craftengine.bukkit.compatibility.bedrock.GeyserUtils; import net.momirealms.craftengine.bukkit.compatibility.item.*; import net.momirealms.craftengine.bukkit.compatibility.legacy.slimeworld.LegacySlimeFormatStorageAdaptor; import net.momirealms.craftengine.bukkit.compatibility.leveler.*; @@ -55,6 +57,8 @@ public class BukkitCompatibilityManager implements CompatibilityManager { private final Map tagResolverProviders; private TagResolverProvider[] tagResolverProviderArray = null; private boolean hasPlaceholderAPI; + private boolean hasGeyser; + private boolean hasFloodgate; public BukkitCompatibilityManager(BukkitCraftEngine plugin) { this.plugin = plugin; @@ -110,6 +114,12 @@ public class BukkitCompatibilityManager implements CompatibilityManager { EventConditions.register(worldGuardRegion, new AlwaysFalseCondition.FactoryImpl<>()); LootConditions.register(worldGuardRegion, new AlwaysFalseCondition.FactoryImpl<>()); } + if (this.isPluginEnabled("Geyser-Spigot")) { + this.hasGeyser = true; + } + if (this.isPluginEnabled("floodgate")) { + this.hasFloodgate = true; + } } @Override @@ -368,4 +378,16 @@ public class BukkitCompatibilityManager implements CompatibilityManager { } return resolvers; } + + @Override + public boolean isBedrockPlayer(Player player) { + UUID uuid = player.uuid(); + if (this.hasFloodgate) { + return FloodgateUtils.isFloodgatePlayer(uuid); + } + if (this.hasGeyser) { + return GeyserUtils.isGeyserPlayer(uuid); + } + return uuid.version() == 0; + } } diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/bedrock/FloodgateUtils.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/bedrock/FloodgateUtils.java new file mode 100644 index 000000000..57969e68d --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/bedrock/FloodgateUtils.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.bukkit.compatibility.bedrock; + +import org.geysermc.floodgate.api.FloodgateApi; + +import java.util.UUID; + +public final class FloodgateUtils { + + private FloodgateUtils() {} + + public static boolean isFloodgatePlayer(UUID uuid) { + return FloodgateApi.getInstance().isFloodgatePlayer(uuid); + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/bedrock/GeyserUtils.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/bedrock/GeyserUtils.java new file mode 100644 index 000000000..f62bfc243 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/bedrock/GeyserUtils.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.bukkit.compatibility.bedrock; + +import org.geysermc.api.Geyser; + +import java.util.UUID; + +public final class GeyserUtils { + + private GeyserUtils() {} + + public static boolean isGeyserPlayer(UUID uuid) { + return Geyser.api().isBedrockPlayer(uuid); + } +} diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MMOItemsSource.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MMOItemsSource.java index b22e5595a..02ec55ab3 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MMOItemsSource.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MMOItemsSource.java @@ -9,6 +9,8 @@ import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; +import java.util.Locale; + import static java.util.Objects.requireNonNull; public class MMOItemsSource implements ExternalItemSource { @@ -25,7 +27,7 @@ public class MMOItemsSource implements ExternalItemSource { split = split[0].split("_", 2); } if (split.length == 1) return new ItemStack(Material.AIR); - MMOItem mmoItem = MMOItems.plugin.getMMOItem(Type.get(split[0]), split[1].toUpperCase()); + MMOItem mmoItem = MMOItems.plugin.getMMOItem(Type.get(split[0]), split[1].toUpperCase(Locale.ROOT)); return mmoItem == null ? new ItemStack(Material.AIR) : requireNonNull(mmoItem.newBuilder().build()); } 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 be5b1f2ed..fdbfbeb8f 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 @@ -16,6 +16,7 @@ import net.momirealms.craftengine.core.util.ReflectionUtils; import org.bukkit.Material; import java.lang.reflect.Field; +import java.util.Locale; import java.util.Set; import java.util.stream.Stream; @@ -63,7 +64,7 @@ public class WorldEditBlockRegister { } if (!input.contains(":")) { - String lowerSearch = input.toLowerCase(); + String lowerSearch = input.toLowerCase(Locale.ROOT); return Stream.concat( namespacesInUse.stream().filter(n -> n.startsWith(lowerSearch)).map(n -> n + ":"), BlockStateParser.fillSuggestions(input).stream() diff --git a/bukkit/paper-loader/build.gradle.kts b/bukkit/paper-loader/build.gradle.kts index ce8513840..aba556f45 100644 --- a/bukkit/paper-loader/build.gradle.kts +++ b/bukkit/paper-loader/build.gradle.kts @@ -77,6 +77,10 @@ paper { register("ViaVersion") { required = false } register("QuickShop-Hikari") { required = false } + // Geyser + register("Geyser-Spigot") { required = false } + register("floodgate") { required = false } + // AdvancedSlimePaper register("SlimeWorldPlugin") { required = false } register("SlimeWorldManager") { required = false } 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 5d98d2130..9421777da 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 @@ -17,6 +17,7 @@ import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.VersionHelper; import java.nio.file.Path; @@ -98,8 +99,8 @@ public final class BukkitAdvancementManager extends AbstractAdvancementManager { 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<>()); + NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, new ArrayList<>(), MiscUtils.init(new HashSet<>(), s -> s.add(resourceLocation)), new HashMap<>(), true) : + NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, new ArrayList<>(), MiscUtils.init(new HashSet<>(), s -> s.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); 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 9bc29f2da..79f31a798 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 @@ -13,6 +13,7 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.util.*; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.HashMap; @@ -22,7 +23,7 @@ import java.util.concurrent.Callable; import java.util.function.BiConsumer; public class BukkitBlockBehavior extends AbstractBlockBehavior { - private static final Map>> HARD_CODED_PROPERTY_DATA = new HashMap<>(); + private static final Map>> HARD_CODED_PROPERTY_DATA = new HashMap<>(); static { HARD_CODED_PROPERTY_DATA.put("axis", (behavior, property) -> { @SuppressWarnings("unchecked") diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java index fc6ca3df7..51c9c661e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java @@ -75,6 +75,11 @@ public class ItemBlockEntityElementConfig implements BlockEntityElementConfig { @Override 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 index a8b02c0b0..426d71406 100644 --- 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 @@ -9,16 +9,19 @@ import net.momirealms.craftengine.core.entity.display.Billboard; import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.util.Color; 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.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Function; public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementConfig { @@ -35,6 +38,10 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo private final Billboard billboard; private final float shadowRadius; private final float shadowStrength; + private final Color glowColor; + private final int blockLight; + private final int skyLight; + private final float viewRange; public ItemDisplayBlockEntityElementConfig(Function> item, Vector3f scale, @@ -46,7 +53,11 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo ItemDisplayContext displayContext, Billboard billboard, float shadowRadius, - float shadowStrength) { + float shadowStrength, + @Nullable Color glowColor, + int blockLight, + int skyLight, + float viewRange) { this.item = item; this.scale = scale; this.position = position; @@ -58,8 +69,16 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo this.billboard = billboard; this.shadowRadius = shadowRadius; this.shadowStrength = shadowStrength; + this.glowColor = glowColor; + this.blockLight = blockLight; + this.skyLight = skyLight; + this.viewRange = viewRange; this.lazyMetadataPacket = player -> { List dataValues = new ArrayList<>(); + if (glowColor != null) { + ItemDisplayEntityData.SharedFlags.addEntityData((byte) 0x40, dataValues); + ItemDisplayEntityData.GlowColorOverride.addEntityData(glowColor.color(), dataValues); + } ItemDisplayEntityData.DisplayedItem.addEntityData(item.apply(player).getLiteralObject(), dataValues); ItemDisplayEntityData.Scale.addEntityData(this.scale, dataValues); ItemDisplayEntityData.RotationLeft.addEntityData(this.rotation, dataValues); @@ -68,6 +87,10 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo ItemDisplayEntityData.DisplayType.addEntityData(this.displayContext.id(), dataValues); ItemDisplayEntityData.ShadowRadius.addEntityData(this.shadowRadius, dataValues); ItemDisplayEntityData.ShadowStrength.addEntityData(this.shadowStrength, dataValues); + if (this.blockLight != -1 && this.skyLight != -1) { + ItemDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues); + } + ItemDisplayEntityData.ViewRange.addEntityData(this.viewRange, dataValues); return dataValues; }; } @@ -157,11 +180,17 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo Objects.equal(rotation, that.rotation); } + @Override + public int hashCode() { + return Objects.hashCode(xRot, yRot, position, translation, rotation); + } + public static class Factory implements BlockEntityElementConfigFactory { @Override public ItemDisplayBlockEntityElementConfig create(Map arguments) { Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item_display.missing_item")); + Map brightness = ResourceConfigUtils.getAsMap(arguments.getOrDefault("brightness", Map.of()), "brightness"); return new ItemDisplayBlockEntityElementConfig( player -> BukkitItemManager.instance().createWrappedItem(itemId, player), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), @@ -173,7 +202,11 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo ResourceConfigUtils.getAsEnum(ResourceConfigUtils.get(arguments, "display-context", "display-transform"), ItemDisplayContext.class, ItemDisplayContext.NONE), ResourceConfigUtils.getAsEnum(arguments.get("billboard"), Billboard.class, Billboard.FIXED), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"), - ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength") + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength"), + Optional.ofNullable(arguments.get("glow-color")).map(it -> Color.fromStrings(it.toString().split(","))).orElse(null), + ResourceConfigUtils.getAsInt(brightness.getOrDefault("block-light", -1), "block-light"), + ResourceConfigUtils.getAsInt(brightness.getOrDefault("sky-light", -1), "sky-light"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("view-range", 1f), "view-range") ); } } 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 index 5b96a9611..9f3075164 100644 --- 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 @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.block.entity.renderer.element; import com.google.common.base.Objects; import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; @@ -10,16 +11,15 @@ import net.momirealms.craftengine.core.entity.display.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.Color; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.World; +import org.jetbrains.annotations.Nullable; 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.*; import java.util.function.Function; public class TextDisplayBlockEntityElementConfig implements BlockEntityElementConfig { @@ -33,6 +33,10 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo private final float yRot; private final Quaternionf rotation; private final Billboard billboard; + public final Color glowColor; + public final int blockLight; + public final int skyLight; + public final float viewRange; public TextDisplayBlockEntityElementConfig(String text, Vector3f scale, @@ -41,7 +45,11 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo float xRot, float yRot, Quaternionf rotation, - Billboard billboard) { + Billboard billboard, + @Nullable Color glowColor, + int blockLight, + int skyLight, + float viewRange) { this.text = text; this.scale = scale; this.position = position; @@ -50,13 +58,25 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo this.yRot = yRot; this.rotation = rotation; this.billboard = billboard; + this.glowColor = glowColor; + this.blockLight = blockLight; + this.skyLight = skyLight; + this.viewRange = viewRange; this.lazyMetadataPacket = player -> { List dataValues = new ArrayList<>(); + if (glowColor != null) { + ItemDisplayEntityData.SharedFlags.addEntityData((byte) 0x40, dataValues); + ItemDisplayEntityData.GlowColorOverride.addEntityData(glowColor.color(), dataValues); + } TextDisplayEntityData.Text.addEntityData(ComponentUtils.adventureToMinecraft(text(player)), dataValues); TextDisplayEntityData.Scale.addEntityData(this.scale, dataValues); TextDisplayEntityData.RotationLeft.addEntityData(this.rotation, dataValues); TextDisplayEntityData.BillboardConstraints.addEntityData(this.billboard.id(), dataValues); TextDisplayEntityData.Translation.addEntityData(this.translation, dataValues); + if (this.blockLight != -1 && this.skyLight != -1) { + ItemDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues); + } + ItemDisplayEntityData.ViewRange.addEntityData(this.viewRange, dataValues); return dataValues; }; } @@ -134,11 +154,17 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo Objects.equal(rotation, that.rotation); } + @Override + public int hashCode() { + return Objects.hashCode(xRot, yRot, position, translation, rotation); + } + public static class Factory implements BlockEntityElementConfigFactory { @Override public TextDisplayBlockEntityElementConfig create(Map arguments) { String text = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("text"), "warning.config.block.state.entity_renderer.text_display.missing_text"); + Map brightness = ResourceConfigUtils.getAsMap(arguments.getOrDefault("brightness", Map.of()), "brightness"); return new TextDisplayBlockEntityElementConfig( text, ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), @@ -147,7 +173,11 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo 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)) + Billboard.valueOf(arguments.getOrDefault("billboard", "fixed").toString().toUpperCase(Locale.ROOT)), + Optional.ofNullable(arguments.get("glow-color")).map(it -> Color.fromStrings(it.toString().split(","))).orElse(null), + ResourceConfigUtils.getAsInt(brightness.getOrDefault("block-light", -1), "block-light"), + ResourceConfigUtils.getAsInt(brightness.getOrDefault("sky-light", -1), "sky-light"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("view-range", 1f), "view-range") ); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index ab51b86ff..237f8de2a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -9,6 +9,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityType import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.QuaternionUtils; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; @@ -115,7 +116,7 @@ public class BukkitFurniture extends Furniture { BukkitFurnitureManager.instance().invalidateFurniture(this); super.clearColliders(); this.location = LocationUtils.toLocation(position); - Object removePacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(itemDisplay.getEntityId()); }}); + Object removePacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(MiscUtils.init(new IntArrayList(), l -> l.add(itemDisplay.getEntityId()))); for (Player player : itemDisplay.getTrackedPlayers()) { BukkitAdaptors.adapt(player).sendPacket(removePacket, false); } @@ -142,7 +143,7 @@ public class BukkitFurniture extends Furniture { protected void refresh() { ItemDisplay itemDisplay = this.metaEntity.get(); if (itemDisplay == null) return; - Object removePacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(itemDisplay.getEntityId()); }}); + Object removePacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(MiscUtils.init(new IntArrayList(), l -> l.add(itemDisplay.getEntityId()))); Object addPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(itemDisplay.getEntityId(), itemDisplay.getUniqueId(), itemDisplay.getX(), itemDisplay.getY(), itemDisplay.getZ(), itemDisplay.getPitch(), itemDisplay.getYaw(), MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0); for (Player player : itemDisplay.getTrackedPlayers()) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java index b63daf007..dd30f8c02 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java @@ -43,7 +43,7 @@ public class ItemDisplayFurnitureElement implements FurnitureElement { this.position.x, this.position.y, this.position.z, 0, this.position.yRot, MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 ), - FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadata.apply(player, this.colorSource)) + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadata().apply(player, this.colorSource)) )), false); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java index 00ec59e3c..51addbc7b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -31,23 +31,23 @@ import java.util.function.BiFunction; public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig { public static final Factory FACTORY = new Factory(); - public final BiFunction> metadata; - public final Key itemId; - public final Vector3f scale; - public final Vector3f position; - public final Vector3f translation; - public final float xRot; - public final float yRot; - public final Quaternionf rotation; - public final ItemDisplayContext displayContext; - public final Billboard billboard; - public final float shadowRadius; - public final float shadowStrength; - public final boolean applyDyedColor; - public final Color glowColor; - public final int blockLight; - public final int skyLight; - public final float viewRange; + private final BiFunction> metadata; + private final Key itemId; + 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; + private final float shadowRadius; + private final float shadowStrength; + private final boolean applyDyedColor; + private final Color glowColor; + private final int blockLight; + private final int skyLight; + private final float viewRange; public ItemDisplayFurnitureElementConfig(Key itemId, Vector3f scale, @@ -161,6 +161,10 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig return this.applyDyedColor; } + public BiFunction> metadata() { + return this.metadata; + } + public Key itemId() { return this.itemId; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java index 900909794..da193062f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java @@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; @@ -54,7 +55,7 @@ public class CustomFurnitureHitbox extends AbstractFurnitureHitBox { } this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); this.part = new FurnitureHitboxPart(entityId, aabb, pos, false); - this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(entityId); }}); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(MiscUtils.init(new IntArrayList(), l -> l.add(entityId))); this.entityId = entityId; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java index 4b4a34110..454b2eb59 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/InteractionFurnitureHitbox.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.entity.furniture.Collider; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; @@ -40,7 +41,7 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox { FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, config.cachedValues()) )); this.part = new FurnitureHitboxPart(interactionId, aabb, pos, config.responsive()); - this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(interactionId); }}); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(MiscUtils.init(new IntArrayList(), l -> l.add(interactionId))); this.entityId = interactionId; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGenerateInternalAssetsCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGenerateInternalAssetsCommand.java index c92fcea03..70201e12f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGenerateInternalAssetsCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGenerateInternalAssetsCommand.java @@ -13,6 +13,7 @@ import org.incendo.cloud.parser.standard.StringParser; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; @@ -139,7 +140,7 @@ public class DebugGenerateInternalAssetsCommand extends BukkitCommandFeature callback) { try (InputStream inputStream = Files.newInputStream(folder.resolve("_list.json"))) { String s = prefix.isEmpty() ? "" : (prefix + "/"); - JsonObject listJson = JsonParser.parseReader(new InputStreamReader(inputStream)).getAsJsonObject(); + JsonObject listJson = JsonParser.parseReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).getAsJsonObject(); JsonArray fileList = listJson.getAsJsonArray("files"); for (JsonElement element : fileList) { if (element instanceof JsonPrimitive primitive) { 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 2c2e4d036..bad247fea 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 @@ -181,7 +181,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().literalObject(); + return state.with(waterloggedProperty, (Boolean) args[1]).customBlockState().literalObject(); } } 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 dc44ac1a9..ca2d08ad8 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 @@ -38,15 +38,13 @@ import org.bukkit.block.data.Directional; import org.bukkit.block.data.Levelled; import org.bukkit.block.data.Lightable; import org.bukkit.block.data.type.*; +import org.bukkit.block.data.type.Observer; import org.bukkit.entity.*; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; public final class InteractUtils { private static final Map, BlockData, BlockHitResult, Boolean>> INTERACTIONS = new HashMap<>(); @@ -806,7 +804,7 @@ public final class InteractUtils { if (entity instanceof Sheep sheep && sheep.readyToBeSheared() && ArrayUtils.contains(ItemKeys.DYES, item)) { DyeColor sheepColor = sheep.getColor(); if (sheepColor != null) { - String color = sheepColor.name().toLowerCase(); + String color = sheepColor.name().toLowerCase(Locale.ROOT); return !Key.of(color + "_dye").equals(id); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/parser/BlockStateParser.java b/core/src/main/java/net/momirealms/craftengine/core/block/parser/BlockStateParser.java index fd6cdd6dd..bc544e428 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/parser/BlockStateParser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/parser/BlockStateParser.java @@ -10,10 +10,7 @@ import net.momirealms.craftengine.core.util.StringReader; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collection; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; +import java.util.*; public final class BlockStateParser { private static final char START = '['; @@ -33,7 +30,7 @@ public final class BlockStateParser { private Property property; public BlockStateParser(String data, int cursor) { - this.reader = StringReader.simple(data.toLowerCase()); + this.reader = StringReader.simple(data.toLowerCase(Locale.ROOT)); this.reader.setCursor(cursor); this.cursor = cursor; this.replaceCursor = cursor; @@ -116,7 +113,7 @@ public final class BlockStateParser { suggestPropertyName(); return; } - if (used.contains(property.name().toLowerCase())) return; + if (used.contains(property.name().toLowerCase(Locale.ROOT))) return; used.add(input); reader.skipWhitespace(); @@ -159,7 +156,7 @@ public final class BlockStateParser { if (!reader.getRemaining().isEmpty()) return; String front = readPrefix(); for (Property p : properties) { - if (!used.contains(p.name().toLowerCase()) && p.name().toLowerCase().startsWith(input)) { + if (!used.contains(p.name().toLowerCase(Locale.ROOT)) && p.name().toLowerCase(Locale.ROOT).startsWith(input)) { this.suggestions.add(front + p.name() + EQUAL); } } @@ -172,7 +169,7 @@ public final class BlockStateParser { private void suggestValue() { for (Object val : property.possibleValues()) { - this.suggestions.add(readPrefix() + val.toString().toLowerCase()); + this.suggestions.add(readPrefix() + val.toString().toLowerCase(Locale.ROOT)); } } 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 4508ceaa0..fc211d329 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 @@ -167,7 +167,7 @@ public abstract class AbstractPackManager implements PackManager { } this.initInternalData(); try (InputStream inputStream = plugin.resourceStream("internal/atlases/blocks.json")) { - this.vanillaAtlas = JsonParser.parseReader(new InputStreamReader(inputStream)).getAsJsonObject(); + this.vanillaAtlas = JsonParser.parseReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).getAsJsonObject(); } catch (IOException e) { throw new RuntimeException("Failed to read internal/atlases/blocks.json", e); } @@ -221,7 +221,7 @@ public abstract class AbstractPackManager implements PackManager { private void loadModernItemModel(String path, BiConsumer callback) { try (InputStream inputStream = this.plugin.resourceStream(path)) { if (inputStream != null) { - JsonObject allModelsItems = JsonParser.parseReader(new InputStreamReader(inputStream)).getAsJsonObject(); + JsonObject allModelsItems = JsonParser.parseReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).getAsJsonObject(); for (Map.Entry entry : allModelsItems.entrySet()) { if (entry.getValue() instanceof JsonObject modelJson) { callback.accept(Key.of(entry.getKey()), ModernItemModel.fromJson(modelJson)); @@ -236,7 +236,7 @@ public abstract class AbstractPackManager implements PackManager { private void loadInternalData(String path, BiConsumer callback) { try (InputStream inputStream = this.plugin.resourceStream(path)) { if (inputStream != null) { - JsonObject allModelsItems = JsonParser.parseReader(new InputStreamReader(inputStream)).getAsJsonObject(); + JsonObject allModelsItems = JsonParser.parseReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).getAsJsonObject(); for (Map.Entry entry : allModelsItems.entrySet()) { if (entry.getValue() instanceof JsonObject modelJson) { callback.accept(Key.of(entry.getKey()), modelJson); @@ -251,7 +251,7 @@ public abstract class AbstractPackManager implements PackManager { private void loadInternalList(String path, Consumer callback) { try (InputStream inputStream = this.plugin.resourceStream(path)) { if (inputStream != null) { - JsonArray listJson = JsonParser.parseReader(new InputStreamReader(inputStream)).getAsJsonArray(); + JsonArray listJson = JsonParser.parseReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).getAsJsonArray(); for (JsonElement element : listJson) { if (element instanceof JsonPrimitive primitiveJson) { callback.accept(Key.of("minecraft", primitiveJson.getAsString())); @@ -1777,7 +1777,7 @@ public abstract class AbstractPackManager implements PackManager { if (Files.exists(atlasPath) && Files.isRegularFile(atlasPath)) { try { previousAtlasSources = GsonHelper.readJsonFile(atlasPath).getAsJsonObject().getAsJsonArray("sources"); - } catch (Exception ignored) { + } catch (ClassCastException | IllegalStateException | IOException | JsonParseException ignored) { } } @@ -2241,7 +2241,7 @@ public abstract class AbstractPackManager implements PackManager { plugin.logger().warn("Failed to load internal/sounds.json"); return; } - soundTemplate = JsonParser.parseReader(new InputStreamReader(inputStream)).getAsJsonObject(); + soundTemplate = JsonParser.parseReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).getAsJsonObject(); } catch (IOException e) { plugin.logger().warn("Failed to load internal/sounds.json", e); return; diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java index 8b36730a6..ecd57bc95 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/AlistHost.java @@ -82,7 +82,7 @@ public class AlistHost implements ResourcePackHost { if (!Files.exists(cachePath) || !Files.isRegularFile(cachePath)) return; try (InputStream is = Files.newInputStream(cachePath)) { Map cache = GsonHelper.get().fromJson( - new InputStreamReader(is), + new InputStreamReader(is, StandardCharsets.UTF_8), new TypeToken>(){}.getType() ); this.cachedSha1 = cache.get("sha1"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java index 98b65751b..ccafa796a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/DropboxHost.java @@ -219,7 +219,7 @@ public class DropboxHost implements ResourcePackHost { } String credentials = this.appKey + ":" + this.appSecret; - String authHeader = "Basic " + Base64.getEncoder().encodeToString(credentials.getBytes()); + String authHeader = "Basic " + Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8)); try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) { HttpRequest request = HttpRequest.newBuilder() diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/ExternalHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/ExternalHost.java index dec6095cd..9d59c88ac 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/ExternalHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/ExternalHost.java @@ -7,6 +7,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.util.Key; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.List; import java.util.Map; @@ -52,7 +53,7 @@ public class ExternalHost implements ResourcePackHost { } String uuid = Optional.ofNullable(arguments.get("uuid")).map(String::valueOf).orElse(null); if (uuid == null || uuid.isEmpty()) { - uuid = UUID.nameUUIDFromBytes(url.getBytes()).toString(); + uuid = UUID.nameUUIDFromBytes(url.getBytes(StandardCharsets.UTF_8)).toString(); } UUID hostUUID = UUID.fromString(uuid); String sha1 = arguments.getOrDefault("sha1", "").toString(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java index d2e4fd58b..4f8211779 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/GitLabHost.java @@ -50,7 +50,7 @@ public class GitLabHost implements ResourcePackHost { if (!Files.exists(cachePath) || !Files.isRegularFile(cachePath)) return; try (InputStream is = Files.newInputStream(cachePath)) { Map cache = GsonHelper.get().fromJson( - new InputStreamReader(is), + new InputStreamReader(is, StandardCharsets.UTF_8), new TypeToken>(){}.getType() ); this.url = cache.get("url"); @@ -155,13 +155,13 @@ public class GitLabHost implements ResourcePackHost { String filePartHeader = "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"file\"; filename=\"" + filePath.getFileName() + "\"\r\n" + "Content-Type: application/octet-stream\r\n\r\n"; - parts.add(filePartHeader.getBytes()); + parts.add(filePartHeader.getBytes(StandardCharsets.UTF_8)); parts.add(Files.readAllBytes(filePath)); - parts.add("\r\n".getBytes()); + parts.add("\r\n".getBytes(StandardCharsets.UTF_8)); String endBoundary = "--" + boundary + "--\r\n"; - parts.add(endBoundary.getBytes()); + parts.add(endBoundary.getBytes(StandardCharsets.UTF_8)); return HttpRequest.BodyPublishers.ofByteArrays(parts); } 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 89db7002d..88d3d6c36 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 @@ -22,6 +22,7 @@ import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; @@ -62,7 +63,7 @@ public class LobFileHost implements ResourcePackHost { if (!Files.exists(cachePath) || !Files.isRegularFile(cachePath)) return; try (InputStream is = Files.newInputStream(cachePath)) { Map cache = GsonHelper.get().fromJson( - new InputStreamReader(is), + new InputStreamReader(is, StandardCharsets.UTF_8), new TypeToken>(){}.getType() ); this.url = cache.get("url"); @@ -191,8 +192,8 @@ public class LobFileHost implements ResourcePackHost { MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256"); try (InputStream is = Files.newInputStream(path); - DigestInputStream dis = new DigestInputStream(is, sha1Digest)) { - DigestInputStream dis2 = new DigestInputStream(dis, sha256Digest); + DigestInputStream dis = new DigestInputStream(is, sha1Digest); + DigestInputStream dis2 = new DigestInputStream(dis, sha256Digest)) { while (dis2.read() != -1) ; @@ -207,18 +208,18 @@ public class LobFileHost implements ResourcePackHost { String filePartHeader = "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"file\"; filename=\"" + filePath.getFileName() + "\"\r\n" + "Content-Type: application/octet-stream\r\n\r\n"; - parts.add(filePartHeader.getBytes()); + parts.add(filePartHeader.getBytes(StandardCharsets.UTF_8)); parts.add(Files.readAllBytes(filePath)); - parts.add("\r\n".getBytes()); + parts.add("\r\n".getBytes(StandardCharsets.UTF_8)); String sha256Part = "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"sha_256\"\r\n\r\n" + sha256Hash + "\r\n"; - parts.add(sha256Part.getBytes()); + parts.add(sha256Part.getBytes(StandardCharsets.UTF_8)); String endBoundary = "--" + boundary + "--\r\n"; - parts.add(endBoundary.getBytes()); + parts.add(endBoundary.getBytes(StandardCharsets.UTF_8)); return HttpRequest.BodyPublishers.ofByteArrays(parts); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java index 3fb95aa92..2665818ac 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/OneDriveHost.java @@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedException; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.*; +import org.jetbrains.annotations.NotNull; import java.io.FileNotFoundException; import java.io.IOException; @@ -34,7 +35,7 @@ public class OneDriveHost implements ResourcePackHost { private final String clientSecret; private final ProxySelector proxy; private final String uploadPath; - private Tuple refreshToken; + private Tuple<@NotNull String, @NotNull String, @NotNull Date> refreshToken; private String sha1; private String fileId; @@ -66,13 +67,13 @@ public class OneDriveHost implements ResourcePackHost { if (!Files.exists(cachePath) || !Files.isRegularFile(cachePath)) return; try (InputStream is = Files.newInputStream(cachePath)) { Map cache = GsonHelper.get().fromJson( - new InputStreamReader(is), + new InputStreamReader(is, StandardCharsets.UTF_8), new TypeToken>(){}.getType() ); this.refreshToken = Tuple.of( - cache.get("refresh-token"), - cache.get("access-token"), + Objects.requireNonNull(cache.get("refresh-token")), + Objects.requireNonNull(cache.get("access-token")), new Date(Long.parseLong(cache.get("refresh-token-expires-in")))); this.sha1 = cache.get("sha1"); this.fileId = cache.get("file-id"); @@ -188,7 +189,7 @@ public class OneDriveHost implements ResourcePackHost { if (this.refreshToken == null || this.refreshToken.mid().isEmpty() || this.refreshToken.right().before(new Date())) { try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) { String formData = "client_id=" + URLEncoder.encode(this.clientId, StandardCharsets.UTF_8) + - "&client_secret=" + URLEncoder.encode(this.clientSecret, StandardCharsets.UTF_8) + + "&client_secret=" + URLEncoder.encode(Objects.requireNonNull(this.clientSecret), StandardCharsets.UTF_8) + "&refresh_token=" + URLEncoder.encode(this.refreshToken.left(), StandardCharsets.UTF_8) + "&grant_type=refresh_token" + "&scope=Files.ReadWrite.All+offline_access"; diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java index 29563b49c..925a9dcdf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java @@ -42,8 +42,8 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -public class SelfHostHttpServer { - private static SelfHostHttpServer instance; +public final class SelfHostHttpServer { + private static volatile SelfHostHttpServer instance; private final Cache oneTimePackUrls = Caffeine.newBuilder() .maximumSize(1024) .scheduler(Scheduler.systemScheduler()) @@ -86,9 +86,19 @@ public class SelfHostHttpServer { private EventLoopGroup workerGroup; private Channel serverChannel; + private SelfHostHttpServer() { + if (instance != null) { + throw new IllegalStateException("SelfHostHttpServer is already initialized."); + } + } + public static SelfHostHttpServer instance() { if (instance == null) { - instance = new SelfHostHttpServer(); + synchronized (SelfHostHttpServer.class) { + if (instance == null) { + instance = new SelfHostHttpServer(); + } + } } return instance; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java index 7df3f9449..20e67ca70 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java @@ -54,7 +54,7 @@ public class ShulkerBoxSpecialModel implements SpecialModel { public SpecialModel create(Map arguments) { float openness = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("openness", 0), "openness"); String texture = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.shulker_box.missing_texture"); - Direction orientation = Optional.ofNullable(arguments.get("orientation")).map(String::valueOf).map(String::toUpperCase).map(Direction::valueOf).orElse(null); + Direction orientation = Optional.ofNullable(arguments.get("orientation")).map(String::valueOf).map(s -> s.toUpperCase(Locale.ROOT)).map(Direction::valueOf).orElse(null); if (openness > 1 || openness < 0) { throw new LocalizedResourceConfigException("warning.config.item.model.special.shulker_box.invalid_openness", String.valueOf(openness)); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfB.java b/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfB.java index eb1fc463e..19e5ba0f3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfB.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/obfuscation/ObfB.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; +import java.util.Locale; import java.util.Objects; import java.util.concurrent.ThreadLocalRandom; @@ -81,7 +82,7 @@ public final class ObfB { } private static String 雷雷宝宝打肚肚(String input) { - return input.replace('\\', '/').toLowerCase(); + return input.replace('\\', '/').toLowerCase(Locale.ROOT); } private static String[] 因为都叫你小学生(String path) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java index 0c0c82726..1a89da040 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/compatibility/CompatibilityManager.java @@ -42,4 +42,6 @@ public interface CompatibilityManager { void executeMMSkill(String skill, float power, Player player); TagResolver[] createExternalTagResolvers(Context context); + + boolean isBedrockPlayer(Player player); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java index 5586bc51d..f0d52c08c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCullingThread.java @@ -40,8 +40,8 @@ public class EntityCullingThread { long startTime = System.nanoTime(); for (Player player : CraftEngine.instance().networkManager().onlineUsers()) { - // 使用绝对值确保非负,使用 threads 而不是 threads-1 确保均匀分布 - if (Math.abs(player.uuid().hashCode()) % this.threads == this.id) { + // 使用位运算确保非负,使用 threads 而不是 threads-1 确保均匀分布 + if ((player.uuid().hashCode() & 0x7FFFFFFF) % this.threads == this.id) { player.entityCullingTick(); processed++; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MarkedArrayList.java b/core/src/main/java/net/momirealms/craftengine/core/util/MarkedArrayList.java index 1d1e57d24..fa1a87226 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MarkedArrayList.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MarkedArrayList.java @@ -2,10 +2,13 @@ package net.momirealms.craftengine.core.util; import org.jetbrains.annotations.NotNull; +import java.io.Serial; import java.util.ArrayList; import java.util.Collection; public class MarkedArrayList extends ArrayList { + @Serial + private static final long serialVersionUID = 1L; public MarkedArrayList() { } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MarkedHashMap.java b/core/src/main/java/net/momirealms/craftengine/core/util/MarkedHashMap.java index 5acda9dda..29128754e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MarkedHashMap.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MarkedHashMap.java @@ -1,6 +1,9 @@ package net.momirealms.craftengine.core.util; +import java.io.Serial; import java.util.HashMap; public class MarkedHashMap extends HashMap { + @Serial + private static final long serialVersionUID = 1L; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SkullUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/SkullUtils.java index a74d6273f..e1d753c86 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SkullUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SkullUtils.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.util; +import java.nio.charset.StandardCharsets; import java.util.Base64; public final class SkullUtils { @@ -8,7 +9,7 @@ public final class SkullUtils { public static String identifierFromBase64(String base64) { byte[] decodedBytes = Base64.getDecoder().decode(base64); - String decodedString = new String(decodedBytes); + String decodedString = new String(decodedBytes, StandardCharsets.UTF_8); int urlStartIndex = decodedString.indexOf("\"url\":\"") + 7; int urlEndIndex = decodedString.indexOf("\"", urlStartIndex); String textureUrl = decodedString.substring(urlStartIndex, urlEndIndex); From b1d91cb0f04759fba60dc97f61917bc7ae42f166 Mon Sep 17 00:00:00 2001 From: jhqwqmc Date: Sat, 6 Dec 2025 19:34:35 +0800 Subject: [PATCH 131/135] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compatibility/item/MMOItemsSource.java | 4 ++- .../ItemDisplayBlockEntityElementConfig.java | 8 ++++- .../TextDisplayBlockEntityElementConfig.java | 8 ++++- .../element/ItemDisplayFurnitureElement.java | 2 +- .../ItemDisplayFurnitureElementConfig.java | 34 +++++++++---------- .../plugin/injector/BlockStateGenerator.java | 2 +- .../pack/host/impl/SelfHostHttpServer.java | 8 ++--- .../core/util/MarkedArrayList.java | 3 -- .../craftengine/core/util/MarkedHashMap.java | 3 -- 9 files changed, 38 insertions(+), 34 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MMOItemsSource.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MMOItemsSource.java index 02ec55ab3..9cfc2ce73 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MMOItemsSource.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MMOItemsSource.java @@ -27,7 +27,9 @@ public class MMOItemsSource implements ExternalItemSource { split = split[0].split("_", 2); } if (split.length == 1) return new ItemStack(Material.AIR); - MMOItem mmoItem = MMOItems.plugin.getMMOItem(Type.get(split[0]), split[1].toUpperCase(Locale.ROOT)); + // 这里与使用和mmoitems相同的转换id方法 + String mmoItemId = split[1].toUpperCase().replace("-", "_").replace(" ", "_"); + MMOItem mmoItem = MMOItems.plugin.getMMOItem(Type.get(split[0]), mmoItemId); return mmoItem == null ? new ItemStack(Material.AIR) : requireNonNull(mmoItem.newBuilder().build()); } 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 index 426d71406..296b59051 100644 --- 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 @@ -182,7 +182,13 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo @Override public int hashCode() { - return Objects.hashCode(xRot, yRot, position, translation, rotation); + int result = 17; + result = 31 * result + Double.hashCode(xRot); + result = 31 * result + Double.hashCode(yRot); + result = 31 * result + (position != null ? position.hashCode() : 0); + result = 31 * result + (translation != null ? translation.hashCode() : 0); + result = 31 * result + (rotation != null ? rotation.hashCode() : 0); + return result; } public static class Factory implements BlockEntityElementConfigFactory { 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 index 9f3075164..3709a8d71 100644 --- 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 @@ -156,7 +156,13 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo @Override public int hashCode() { - return Objects.hashCode(xRot, yRot, position, translation, rotation); + int result = 17; + result = 31 * result + Double.hashCode(xRot); + result = 31 * result + Double.hashCode(yRot); + result = 31 * result + (position != null ? position.hashCode() : 0); + result = 31 * result + (translation != null ? translation.hashCode() : 0); + result = 31 * result + (rotation != null ? rotation.hashCode() : 0); + return result; } public static class Factory implements BlockEntityElementConfigFactory { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java index dd30f8c02..b63daf007 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java @@ -43,7 +43,7 @@ public class ItemDisplayFurnitureElement implements FurnitureElement { this.position.x, this.position.y, this.position.z, 0, this.position.yRot, MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 ), - FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadata().apply(player, this.colorSource)) + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadata.apply(player, this.colorSource)) )), false); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java index 51addbc7b..7b94bcd76 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -31,23 +31,23 @@ import java.util.function.BiFunction; public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig { public static final Factory FACTORY = new Factory(); - private final BiFunction> metadata; - private final Key itemId; - 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; - private final float shadowRadius; - private final float shadowStrength; - private final boolean applyDyedColor; - private final Color glowColor; - private final int blockLight; - private final int skyLight; - private final float viewRange; + public final BiFunction> metadata; + public final Key itemId; + public final Vector3f scale; + public final Vector3f position; + public final Vector3f translation; + public final float xRot; + public final float yRot; + public final Quaternionf rotation; + public final ItemDisplayContext displayContext; + public final Billboard billboard; + public final float shadowRadius; + public final float shadowStrength; + public final boolean applyDyedColor; + public final Color glowColor; + public final int blockLight; + public final int skyLight; + public final float viewRange; public ItemDisplayFurnitureElementConfig(Key itemId, Vector3f scale, 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 bad247fea..2c2e4d036 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BlockStateGenerator.java @@ -181,7 +181,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().literalObject(); + return state.with(waterloggedProperty, (boolean) args[1]).customBlockState().literalObject(); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java index 925a9dcdf..74aa8fc9b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java @@ -43,7 +43,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; public final class SelfHostHttpServer { - private static volatile SelfHostHttpServer instance; + private static SelfHostHttpServer instance; private final Cache oneTimePackUrls = Caffeine.newBuilder() .maximumSize(1024) .scheduler(Scheduler.systemScheduler()) @@ -94,11 +94,7 @@ public final class SelfHostHttpServer { public static SelfHostHttpServer instance() { if (instance == null) { - synchronized (SelfHostHttpServer.class) { - if (instance == null) { - instance = new SelfHostHttpServer(); - } - } + instance = new SelfHostHttpServer(); } return instance; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MarkedArrayList.java b/core/src/main/java/net/momirealms/craftengine/core/util/MarkedArrayList.java index fa1a87226..1d1e57d24 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MarkedArrayList.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MarkedArrayList.java @@ -2,13 +2,10 @@ package net.momirealms.craftengine.core.util; import org.jetbrains.annotations.NotNull; -import java.io.Serial; import java.util.ArrayList; import java.util.Collection; public class MarkedArrayList extends ArrayList { - @Serial - private static final long serialVersionUID = 1L; public MarkedArrayList() { } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MarkedHashMap.java b/core/src/main/java/net/momirealms/craftengine/core/util/MarkedHashMap.java index 29128754e..5acda9dda 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MarkedHashMap.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MarkedHashMap.java @@ -1,9 +1,6 @@ package net.momirealms.craftengine.core.util; -import java.io.Serial; import java.util.HashMap; public class MarkedHashMap extends HashMap { - @Serial - private static final long serialVersionUID = 1L; } From 1566ac89c98033ce8aecc2620e3fc47d1c70dab4 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 6 Dec 2025 20:14:47 +0800 Subject: [PATCH 132/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AE=9E=E4=BD=93?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E8=B7=9D=E7=A6=BB=E3=80=81=E7=9B=94=E7=94=B2?= =?UTF-8?q?=E6=9E=B6=E5=85=83=E7=B4=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compatibility/item/MMOItemsSource.java | 2 - .../bukkit/api/BukkitAdaptors.java | 14 ++ .../element/ArmorStandBlockEntityElement.java | 69 ++++++++++ .../ArmorStandBlockEntityElementConfig.java | 130 ++++++++++++++++++ .../BukkitBlockEntityElementConfigs.java | 1 + .../element/ItemBlockEntityElementConfig.java | 11 +- .../ItemDisplayBlockEntityElementConfig.java | 19 +-- .../TextDisplayBlockEntityElementConfig.java | 19 +-- .../bukkit/entity/data/ArmorStandData.java | 10 ++ .../element/ItemDisplayFurnitureElement.java | 2 - .../ItemDisplayFurnitureElementConfig.java | 3 +- .../plugin/command/BukkitCommandManager.java | 3 +- ...isplayEntityViewDistanceScaleCommand.java} | 20 +-- .../SetEntityCullingDistanceScaleCommand.java | 42 ++++++ .../network/handler/ItemPacketHandler.java | 4 - .../plugin/user/BukkitServerPlayer.java | 24 +++- common-files/src/main/resources/commands.yml | 12 +- common-files/src/main/resources/config.yml | 5 - .../src/main/resources/translations/en.yml | 3 +- .../src/main/resources/translations/zh_cn.yml | 3 +- .../element/BlockEntityElementConfigs.java | 1 + .../core/entity/player/Player.java | 6 +- .../core/plugin/config/Config.java | 8 ++ .../core/plugin/locale/MessageConstants.java | 3 +- gradle.properties | 16 +-- 25 files changed, 339 insertions(+), 91 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElement.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElementConfig.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ArmorStandData.java rename bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/{SetEntityViewDistanceScaleCommand.java => SetDisplayEntityViewDistanceScaleCommand.java} (59%) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityCullingDistanceScaleCommand.java diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MMOItemsSource.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MMOItemsSource.java index 9cfc2ce73..68f5831af 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MMOItemsSource.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/MMOItemsSource.java @@ -9,8 +9,6 @@ import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; -import java.util.Locale; - import static java.util.Objects.requireNonNull; public class MMOItemsSource implements ExternalItemSource { 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 c667f832e..092a25cad 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 @@ -1,14 +1,17 @@ package net.momirealms.craftengine.bukkit.api; import net.momirealms.craftengine.bukkit.entity.BukkitEntity; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.bukkit.world.BukkitWorld; +import net.momirealms.craftengine.core.item.Item; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; public final class BukkitAdaptors { @@ -62,4 +65,15 @@ public final class BukkitAdaptors { public static BukkitExistingBlock adapt(@NotNull final Block block) { return new BukkitExistingBlock(block); } + + /** + * Adapts a Bukkit ItemStack to a CraftEngine wrapped item + * + * @param item the Bukkit ItemStack to adapt, must not be null + * @return a non-null Item instance wrapping the provided item + */ + @NotNull + public static Item adapt(@NotNull final ItemStack item) { + return BukkitItemManager.instance().wrap(item); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElement.java new file mode 100644 index 000000000..dee58c501 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElement.java @@ -0,0 +1,69 @@ +package net.momirealms.craftengine.bukkit.block.entity.renderer.element; + +import com.mojang.datafixers.util.Pair; +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 ArmorStandBlockEntityElement implements BlockEntityElement { + public final ArmorStandBlockEntityElementConfig config; + public final Object cachedSpawnPacket; + public final Object cachedDespawnPacket; + public final Object cachedUpdatePosPacket; + public final int entityId; + + public ArmorStandBlockEntityElement(ArmorStandBlockEntityElementConfig config, BlockPos pos) { + this(config, pos, CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(), false); + } + + public ArmorStandBlockEntityElement(ArmorStandBlockEntityElementConfig config, BlockPos pos, int entityId, boolean posChanged) { + 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.ARMOR_STAND, 0, CoreReflections.instance$Vec3$Zero, config.yRot() + ); + this.config = config; + this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId)); + this.entityId = entityId; + this.cachedUpdatePosPacket = posChanged ? FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(this.entityId, pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, config.yRot(), config.xRot(), false) : null; + } + + @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))), false); + player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEquipmentPacket(this.entityId, List.of( + Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, this.config.item(player).getLiteralObject()) + )), false); + } + + @Override + public void transform(Player player) { + if (this.cachedUpdatePosPacket != null) { + player.sendPackets(List.of( + this.cachedUpdatePosPacket, + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player)), + FastNMS.INSTANCE.constructor$ClientboundSetEquipmentPacket(this.entityId, List.of( + Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, this.config.item(player).getLiteralObject()) + )) + ), false); + } else { + player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player)), false); + player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEquipmentPacket(this.entityId, List.of( + Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, this.config.item(player).getLiteralObject()) + )), false); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElementConfig.java new file mode 100644 index 000000000..6953c474f --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElementConfig.java @@ -0,0 +1,130 @@ +package net.momirealms.craftengine.bukkit.block.entity.renderer.element; + +import com.google.common.base.Objects; +import net.momirealms.craftengine.bukkit.entity.data.ArmorStandData; +import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +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.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.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +public class ArmorStandBlockEntityElementConfig 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 float xRot; + private final float yRot; + private final boolean small; + + public ArmorStandBlockEntityElementConfig(Function> item, + Vector3f scale, + Vector3f position, + float xRot, + float yRot, + boolean small) { + this.item = item; + this.scale = scale; + this.position = position; + this.xRot = xRot; + this.yRot = yRot; + this.small = small; + this.lazyMetadataPacket = player -> { + List dataValues = new ArrayList<>(2); + BaseEntityData.SharedFlags.addEntityData((byte) 0x20, dataValues); + if (small) { + ArmorStandData.ArmorStandFlags.addEntityData((byte) 0x01, dataValues); + } + return dataValues; + }; + } + + @Override + public ArmorStandBlockEntityElement create(World world, BlockPos pos) { + return new ArmorStandBlockEntityElement(this, pos); + } + + @Override + public ArmorStandBlockEntityElement create(World world, BlockPos pos, ArmorStandBlockEntityElement previous) { + return new ArmorStandBlockEntityElement(this, pos, previous.entityId, + previous.config.yRot != this.yRot || + previous.config.xRot != this.xRot || + !previous.config.position.equals(this.position) + ); + } + + @Override + public ArmorStandBlockEntityElement createExact(World world, BlockPos pos, ArmorStandBlockEntityElement previous) { + if (!previous.config.isSamePosition(this)) { + return null; + } + return new ArmorStandBlockEntityElement(this, pos, previous.entityId, false); + } + + @Override + public Class elementClass() { + return ArmorStandBlockEntityElement.class; + } + + public Item item(Player player) { + return this.item.apply(player); + } + + public Vector3f scale() { + return this.scale; + } + + public Vector3f position() { + return this.position; + } + + public float yRot() { + return this.yRot; + } + + public float xRot() { + return this.xRot; + } + + public boolean small() { + return this.small; + } + + public List metadataValues(Player player) { + return this.lazyMetadataPacket.apply(player); + } + + public boolean isSamePosition(ArmorStandBlockEntityElementConfig that) { + return Float.compare(xRot, that.xRot) == 0 && + Float.compare(yRot, that.yRot) == 0 && + Objects.equal(position, that.position); + } + + public static class Factory implements BlockEntityElementConfigFactory { + + @Override + public ArmorStandBlockEntityElementConfig create(Map arguments) { + Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.armor_stand.missing_item")); + return new ArmorStandBlockEntityElementConfig( + player -> BukkitItemManager.instance().createWrappedItem(itemId, player), + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("small", false), "small") + ); + } + } +} 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 index ee182e392..7d7ad348a 100644 --- 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 @@ -8,6 +8,7 @@ public class BukkitBlockEntityElementConfigs extends BlockEntityElementConfigs { register(ITEM_DISPLAY, ItemDisplayBlockEntityElementConfig.FACTORY); register(TEXT_DISPLAY, TextDisplayBlockEntityElementConfig.FACTORY); register(ITEM, ItemBlockEntityElementConfig.FACTORY); + register(ARMOR_STAND, ArmorStandBlockEntityElementConfig.FACTORY); } private BukkitBlockEntityElementConfigs() {} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java index 51c9c661e..de5f09f40 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java @@ -46,7 +46,7 @@ public class ItemBlockEntityElementConfig implements BlockEntityElementConfig { @Override 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 index 296b59051..6b4c3e2e6 100644 --- 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 @@ -90,7 +90,7 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo if (this.blockLight != -1 && this.skyLight != -1) { ItemDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues); } - ItemDisplayEntityData.ViewRange.addEntityData(this.viewRange, dataValues); + ItemDisplayEntityData.ViewRange.addEntityData((float) (this.viewRange * player.displayEntityViewDistance()), dataValues); return dataValues; }; } @@ -111,7 +111,7 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo @Override public ItemDisplayBlockEntityElement createExact(World world, BlockPos pos, ItemDisplayBlockEntityElement previous) { - if (!previous.config.equals(this)) { + if (!previous.config.isSamePosition(this)) { return null; } return new ItemDisplayBlockEntityElement(this, pos, previous.entityId, false); @@ -170,9 +170,7 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo return this.lazyMetadataPacket.apply(player); } - @Override - public boolean equals(Object o) { - if (!(o instanceof ItemDisplayBlockEntityElementConfig that)) return false; + public boolean isSamePosition(ItemDisplayBlockEntityElementConfig that) { return Float.compare(xRot, that.xRot) == 0 && Float.compare(yRot, that.yRot) == 0 && Objects.equal(position, that.position) && @@ -180,17 +178,6 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo Objects.equal(rotation, that.rotation); } - @Override - public int hashCode() { - int result = 17; - result = 31 * result + Double.hashCode(xRot); - result = 31 * result + Double.hashCode(yRot); - result = 31 * result + (position != null ? position.hashCode() : 0); - result = 31 * result + (translation != null ? translation.hashCode() : 0); - result = 31 * result + (rotation != null ? rotation.hashCode() : 0); - return result; - } - public static class Factory implements BlockEntityElementConfigFactory { @Override 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 index 3709a8d71..dc6fa84d9 100644 --- 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 @@ -76,7 +76,7 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo if (this.blockLight != -1 && this.skyLight != -1) { ItemDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues); } - ItemDisplayEntityData.ViewRange.addEntityData(this.viewRange, dataValues); + ItemDisplayEntityData.ViewRange.addEntityData((float) (this.viewRange * player.displayEntityViewDistance()), dataValues); return dataValues; }; } @@ -97,7 +97,7 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo @Override public TextDisplayBlockEntityElement createExact(World world, BlockPos pos, TextDisplayBlockEntityElement previous) { - if (!previous.config.equals(this)) { + if (!previous.config.isSamePosition(this)) { return null; } return new TextDisplayBlockEntityElement(this, pos, previous.entityId, false); @@ -144,9 +144,7 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo return this.lazyMetadataPacket.apply(player); } - @Override - public boolean equals(Object o) { - if (!(o instanceof TextDisplayBlockEntityElementConfig that)) return false; + public boolean isSamePosition(TextDisplayBlockEntityElementConfig that) { return Float.compare(xRot, that.xRot) == 0 && Float.compare(yRot, that.yRot) == 0 && Objects.equal(position, that.position) && @@ -154,17 +152,6 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo Objects.equal(rotation, that.rotation); } - @Override - public int hashCode() { - int result = 17; - result = 31 * result + Double.hashCode(xRot); - result = 31 * result + Double.hashCode(yRot); - result = 31 * result + (position != null ? position.hashCode() : 0); - result = 31 * result + (translation != null ? translation.hashCode() : 0); - result = 31 * result + (rotation != null ? rotation.hashCode() : 0); - return result; - } - public static class Factory implements BlockEntityElementConfigFactory { @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ArmorStandData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ArmorStandData.java new file mode 100644 index 000000000..b57c49046 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ArmorStandData.java @@ -0,0 +1,10 @@ +package net.momirealms.craftengine.bukkit.entity.data; + +public class ArmorStandData extends LivingEntityData { + public static final ArmorStandData ArmorStandFlags = new ArmorStandData<>(ArmorStandData.class, EntityDataValue.Serializers$BYTE, (byte) 0); + // rotations + + public ArmorStandData(Class clazz, Object serializer, T defaultValue) { + super(clazz, serializer, defaultValue); + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java index b63daf007..8557e6493 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java @@ -4,12 +4,10 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; 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.bukkit.world.score.BukkitTeamManager; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.util.LegacyChatFormatter; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java index 7b94bcd76..d5658154c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.bukkit.entity.furniture.element; import it.unimi.dsi.fastutil.ints.IntArrayList; -import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.core.entity.display.Billboard; @@ -112,7 +111,7 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig if (this.blockLight != -1 && this.skyLight != -1) { ItemDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues); } - ItemDisplayEntityData.ViewRange.addEntityData(this.viewRange, dataValues); + ItemDisplayEntityData.ViewRange.addEntityData((float) (this.viewRange * player.displayEntityViewDistance()), dataValues); return dataValues; }; } 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 533032261..5f44c5689 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 @@ -42,7 +42,8 @@ public class BukkitCommandManager extends AbstractCommandManager new SearchUsageAdminCommand(this, plugin), new TestCommand(this, plugin), new SetLocaleCommand(this, plugin), - new SetEntityViewDistanceScaleCommand(this, plugin), + new SetDisplayEntityViewDistanceScaleCommand(this, plugin), + new SetEntityCullingDistanceScaleCommand(this, plugin), new ToggleEntityCullingCommand(this, plugin), new UnsetLocaleCommand(this, plugin), new DebugGetBlockStateRegistryIdCommand(this, plugin), diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetDisplayEntityViewDistanceScaleCommand.java similarity index 59% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetDisplayEntityViewDistanceScaleCommand.java index d8a9d0d9a..e10907455 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetDisplayEntityViewDistanceScaleCommand.java @@ -1,14 +1,12 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.FlagKeys; -import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.locale.MessageConstants; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -16,9 +14,9 @@ import org.incendo.cloud.Command; import org.incendo.cloud.bukkit.parser.PlayerParser; import org.incendo.cloud.parser.standard.DoubleParser; -public class SetEntityViewDistanceScaleCommand extends BukkitCommandFeature { +public class SetDisplayEntityViewDistanceScaleCommand extends BukkitCommandFeature { - public SetEntityViewDistanceScaleCommand(CraftEngineCommandManager commandManager, CraftEngine plugin) { + public SetDisplayEntityViewDistanceScaleCommand(CraftEngineCommandManager commandManager, CraftEngine plugin) { super(commandManager, plugin); } @@ -29,24 +27,16 @@ public class SetEntityViewDistanceScaleCommand extends BukkitCommandFeature { - if (!Config.enableEntityCulling()) { - plugin().senderFactory().wrap(context.sender()).sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); - return; - } - if (Config.entityCullingViewDistance() <= 0) { - plugin().senderFactory().wrap(context.sender()).sendMessage(Component.text("View distance is not enabled on this server").color(NamedTextColor.RED)); - return; - } Player player = context.get("player"); double scale = context.get("scale"); BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); - serverPlayer.setEntityCullingViewDistanceScale(scale); - handleFeedback(context, MessageConstants.COMMAND_ENTITY_VIEW_DISTANCE_SCALE_SET_SUCCESS, Component.text(scale), Component.text(player.getName())); + serverPlayer.setDisplayEntityViewDistanceScale(scale); + handleFeedback(context, MessageConstants.COMMAND_DISPLAY_ENTITY_VIEW_DISTANCE_SCALE_SET_SUCCESS, Component.text(scale), Component.text(player.getName())); }); } @Override public String getFeatureID() { - return "set_entity_view_distance_scale"; + return "set_display_entity_view_distance_scale"; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityCullingDistanceScaleCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityCullingDistanceScaleCommand.java new file mode 100644 index 000000000..89968985e --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityCullingDistanceScaleCommand.java @@ -0,0 +1,42 @@ +package net.momirealms.craftengine.bukkit.plugin.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; +import net.momirealms.craftengine.core.plugin.command.FlagKeys; +import net.momirealms.craftengine.core.plugin.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.bukkit.parser.PlayerParser; +import org.incendo.cloud.parser.standard.DoubleParser; + +public class SetEntityCullingDistanceScaleCommand extends BukkitCommandFeature { + + public SetEntityCullingDistanceScaleCommand(CraftEngineCommandManager commandManager, CraftEngine plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { + return builder + .flag(FlagKeys.SILENT_FLAG) + .required("player", PlayerParser.playerParser()) + .required("scale", DoubleParser.doubleParser(0.125, 8)) + .handler(context -> { + Player player = context.get("player"); + double scale = context.get("scale"); + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); + serverPlayer.setEntityCullingDistanceScale(scale); + handleFeedback(context, MessageConstants.COMMAND_ENTITY_CULLING_DISTANCE_SCALE_SET_SUCCESS, Component.text(scale), Component.text(player.getName())); + }); + } + + @Override + public String getFeatureID() { + return "set_entity_culling_distance_scale"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemPacketHandler.java index 6f01fdc82..78e62a2d1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemPacketHandler.java @@ -5,16 +5,12 @@ import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; import net.momirealms.craftengine.bukkit.entity.data.ItemEntityData; 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.bukkit.util.ComponentUtils; -import net.momirealms.craftengine.bukkit.util.EntityDataUtils; import net.momirealms.craftengine.bukkit.world.score.BukkitTeamManager; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.CustomItem; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemSettings; -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; 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 af3c1a1a1..b29efe890 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 @@ -79,7 +79,8 @@ import java.util.function.Predicate; public class BukkitServerPlayer extends Player { public static final Key SELECTED_LOCALE_KEY = Key.of("craftengine:locale"); - public static final Key ENTITY_CULLING_VIEW_DISTANCE_SCALE = Key.of("craftengine:entity_culling_view_distance_scale"); + public static final Key ENTITY_CULLING_DISTANCE_SCALE = Key.of("craftengine:entity_culling_distance_scale"); + public static final Key DISPLAY_ENTITY_VIEW_DISTANCE_SCALE = Key.of("craftengine:display_entity_view_distance_scale"); public static final Key ENABLE_ENTITY_CULLING = Key.of("craftengine:enable_entity_culling"); public static final Key ENABLE_FURNITURE_DEBUG = Key.of("craftengine:enable_furniture_debug"); private final BukkitCraftEngine plugin; @@ -165,6 +166,8 @@ public class BukkitServerPlayer extends Player { private BukkitFurniture lastHitFurniture; // 缓存的tick private int lastHitFurnitureTick; + // 控制展示实体可见距离 + private double displayEntityViewDistance; public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) { this.channel = channel; @@ -190,7 +193,8 @@ public class BukkitServerPlayer extends Player { this.isNameVerified = true; byte[] bytes = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(CooldownData.COOLDOWN_KEY), PersistentDataType.BYTE_ARRAY); String locale = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(SELECTED_LOCALE_KEY), PersistentDataType.STRING); - Double scale = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENTITY_CULLING_VIEW_DISTANCE_SCALE), PersistentDataType.DOUBLE); + Double scale = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENTITY_CULLING_DISTANCE_SCALE), PersistentDataType.DOUBLE); + this.displayEntityViewDistance = Optional.ofNullable(player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(DISPLAY_ENTITY_VIEW_DISTANCE_SCALE), PersistentDataType.DOUBLE)).orElse(1d); this.enableEntityCulling = Optional.ofNullable(player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENABLE_ENTITY_CULLING), PersistentDataType.BOOLEAN)).orElse(true); this.enableFurnitureDebug = Optional.ofNullable(player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(ENABLE_FURNITURE_DEBUG), PersistentDataType.BOOLEAN)).orElse(false); this.culling.setDistanceScale(Optional.ofNullable(scale).orElse(1.0)); @@ -1400,10 +1404,22 @@ public class BukkitServerPlayer extends Player { } @Override - public void setEntityCullingViewDistanceScale(double value) { + public void setEntityCullingDistanceScale(double value) { value = Math.min(Math.max(0.125, value), 8); this.culling.setDistanceScale(value); - platformPlayer().getPersistentDataContainer().set(KeyUtils.toNamespacedKey(ENTITY_CULLING_VIEW_DISTANCE_SCALE), PersistentDataType.DOUBLE, value); + platformPlayer().getPersistentDataContainer().set(KeyUtils.toNamespacedKey(ENTITY_CULLING_DISTANCE_SCALE), PersistentDataType.DOUBLE, value); + } + + @Override + public void setDisplayEntityViewDistanceScale(double value) { + value = Math.min(Math.max(0.125, value), 8); + this.displayEntityViewDistance = value; + platformPlayer().getPersistentDataContainer().set(KeyUtils.toNamespacedKey(DISPLAY_ENTITY_VIEW_DISTANCE_SCALE), PersistentDataType.DOUBLE, value); + } + + @Override + public double displayEntityViewDistance() { + return this.displayEntityViewDistance; } @Override diff --git a/common-files/src/main/resources/commands.yml b/common-files/src/main/resources/commands.yml index 24e0bcccf..e9929cd06 100644 --- a/common-files/src/main/resources/commands.yml +++ b/common-files/src/main/resources/commands.yml @@ -129,11 +129,17 @@ unset_locale: usage: - /ce feature locale unset -set_entity_view_distance_scale: +set_display_entity_view_distance_scale: enable: true - permission: ce.command.admin.set_entity_view_distance_scale + permission: ce.command.admin.set_display_entity_view_distance_scale usage: - - /ce feature entity-view-distance-scale set + - /ce feature display-entity-view-distance-scale set + +set_entity_culling_distance_scale: + enable: true + permission: ce.command.admin.set_entity_culling_distance_scale + usage: + - /ce feature entity-culling-distance-scale set toggle_entity_culling: enable: true diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 8252c48c1..26716aa71 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -576,11 +576,6 @@ client-optimization: bucket-size: 1000 restore-per-tick: 25 -# [Premium Exclusive] -bedrock-edition-support: - enable: false - player-prefix: "!" - # Enables or disables debug mode debug: common: false diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index e42ae799d..1a1ebdd0c 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -88,7 +88,8 @@ command.send_resource_pack.success.multiple: "Send resource packs to Invalid locale format: " command.locale.set.success: "Selected locale has been set to for " command.locale.unset.success: "Cleared selected locale for " -command.entity_view_distance_scale.set.success: "Entity view distance scale updated to for " +command.display_entity_view_distance_scale.set.success: "Display entity view distance scale updated to for " +command.entity_culling_distance_scale.set.success: "Entity culling distance scale updated to for " command.entity_culling.toggle.success: "Entity culling status updated to for " 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." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index e1f1bebad..4219c13b6 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -88,7 +88,8 @@ command.send_resource_pack.success.multiple: "发送资源包给 command.locale.set.failure: "区域设置格式无效: " command.locale.set.success: "已为 更新选定区域设置为 " command.locale.unset.success: "已清除 的选定区域设置" -command.entity_view_distance_scale.set.success: "已为 的实体可见距离百分比设置为 " +command.display_entity_view_distance_scale.set.success: "已为 的展示实体可见距离百分比设置为 " +command.entity_culling_distance_scale.set.success: "已为 的实体剔除距离百分比设置为 " command.entity_culling.toggle.success: "已为 的实体剔除状态设置为 " warning.network.resource_pack.unverified_uuid: "玩家 使用未经服务器验证的 UUID () 尝试请求获取资源包" warning.config.pack.duplicated_files: "发现重复文件 请通过 config.yml 的 'resource-pack.duplicated-files-handler' 部分解决" 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 index c355c4eb2..a267612c9 100644 --- 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 @@ -14,6 +14,7 @@ public abstract 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 final Key ITEM = Key.of("craftengine:item"); + public static final Key ARMOR_STAND = Key.of("craftengine:armor_stand"); public static void register(Key key, BlockEntityElementConfigFactory type) { ((WritableRegistry>) BuiltInRegistries.BLOCK_ENTITY_ELEMENT_TYPE) 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 b8792bde0..f7f85bdf5 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 @@ -192,7 +192,11 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void setSelectedLocale(@Nullable Locale locale); - public abstract void setEntityCullingViewDistanceScale(double value); + public abstract void setEntityCullingDistanceScale(double value); + + public abstract void setDisplayEntityViewDistanceScale(double value); + + public abstract double displayEntityViewDistance(); public abstract void setEnableEntityCulling(boolean enable); 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 c38549237..f343ce3f3 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 @@ -1222,6 +1222,14 @@ public class Config { return instance.client_optimization$entity_culling$ray_tracing; } + public static boolean enableBedrockEditionSupport() { + return instance.bedrock_edition_support$enable; + } + + public static String bedrockEditionPlayerPrefix() { + return instance.bedrock_edition_support$player_prefix; + } + public YamlDocument loadOrCreateYamlData(String fileName) { Path path = this.plugin.dataFolderPath().resolve(fileName); if (!Files.exists(path)) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java index f8cfbd946..fafd81f68 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java @@ -41,6 +41,7 @@ public interface MessageConstants { TranslatableComponent.Builder COMMAND_ITEM_CLEAR_FAILED_MULTIPLE = Component.translatable().key("command.item.clear.failed.multiple"); TranslatableComponent.Builder COMMAND_ITEM_CLEAR_TEST_SINGLE = Component.translatable().key("command.item.clear.test.single"); TranslatableComponent.Builder COMMAND_ITEM_CLEAR_TEST_MULTIPLE = Component.translatable().key("command.item.clear.test.multiple"); - TranslatableComponent.Builder COMMAND_ENTITY_VIEW_DISTANCE_SCALE_SET_SUCCESS = Component.translatable().key("command.entity_view_distance_scale.set.success"); + TranslatableComponent.Builder COMMAND_DISPLAY_ENTITY_VIEW_DISTANCE_SCALE_SET_SUCCESS = Component.translatable().key("command.display_entity_view_distance_scale.set.success"); + TranslatableComponent.Builder COMMAND_ENTITY_CULLING_DISTANCE_SCALE_SET_SUCCESS = Component.translatable().key("command.entity_culling_distance_scale.set.success"); TranslatableComponent.Builder COMMAND_TOGGLE_ENTITY_CULLING_SUCCESS = Component.translatable().key("command.entity_culling.toggle.success"); } diff --git a/gradle.properties b/gradle.properties index 29c7148dc..2a2f5f0f6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings -project_version=0.0.65.19 +project_version=0.0.66 config_version=60 lang_version=43 project_group=net.momirealms @@ -48,7 +48,7 @@ byte_buddy_version=1.18.1 ahocorasick_version=0.6.3 snake_yaml_version=2.5 anti_grief_version=1.0.5 -nms_helper_version=1.0.145 +nms_helper_version=1.0.146 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.38.7 @@ -59,9 +59,9 @@ concurrent_util_version=0.0.3 bucket4j_version=8.15.0 # Proxy settings -#systemProp.socks.proxyHost=127.0.0.1 -#systemProp.socks.proxyPort=7890 -#systemProp.http.proxyHost=127.0.0.1 -#systemProp.http.proxyPort=7890 -#systemProp.https.proxyHost=127.0.0.1 -#systemProp.https.proxyPort=7890 \ No newline at end of file +systemProp.socks.proxyHost=127.0.0.1 +systemProp.socks.proxyPort=7890 +systemProp.http.proxyHost=127.0.0.1 +systemProp.http.proxyPort=7890 +systemProp.https.proxyHost=127.0.0.1 +systemProp.https.proxyPort=7890 \ No newline at end of file From dfeff51f65cb5a3dc0ee79da9a6f8a528be445e8 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 6 Dec 2025 21:25:09 +0800 Subject: [PATCH 133/135] =?UTF-8?q?=E6=B7=BB=E5=8A=A0text=20display?= =?UTF-8?q?=E5=AE=B6=E5=85=B7=E5=85=83=E7=B4=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ItemDisplayBlockEntityElementConfig.java | 6 + .../TextDisplayBlockEntityElementConfig.java | 50 +++- .../entity/data/TextDisplayEntityData.java | 36 +++ .../BukkitFurnitureElementConfigs.java | 1 + .../element/ItemDisplayFurnitureElement.java | 3 +- .../ItemDisplayFurnitureElementConfig.java | 35 +-- .../element/TextDisplayFurnitureElement.java | 61 +++++ .../TextDisplayFurnitureElementConfig.java | 252 ++++++++++++++++++ .../src/main/resources/translations/en.yml | 1 + .../src/main/resources/translations/zh_cn.yml | 1 + .../element/BlockEntityElementConfigs.java | 9 +- .../entity/display/TextDisplayAlignment.java | 8 + .../element/FurnitureElementConfigs.java | 10 +- 13 files changed, 437 insertions(+), 36 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElement.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElementConfig.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/display/TextDisplayAlignment.java 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 index 6b4c3e2e6..a4bca98cc 100644 --- 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 @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.block.entity.renderer.element; import com.google.common.base.Objects; import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; +import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; @@ -78,6 +79,9 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo if (glowColor != null) { ItemDisplayEntityData.SharedFlags.addEntityData((byte) 0x40, dataValues); ItemDisplayEntityData.GlowColorOverride.addEntityData(glowColor.color(), dataValues); + } else { + ItemDisplayEntityData.SharedFlags.addEntityData((byte) 0x0, dataValues); + ItemDisplayEntityData.GlowColorOverride.addEntityData(-1, dataValues); } ItemDisplayEntityData.DisplayedItem.addEntityData(item.apply(player).getLiteralObject(), dataValues); ItemDisplayEntityData.Scale.addEntityData(this.scale, dataValues); @@ -89,6 +93,8 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo ItemDisplayEntityData.ShadowStrength.addEntityData(this.shadowStrength, dataValues); if (this.blockLight != -1 && this.skyLight != -1) { ItemDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues); + } else { + ItemDisplayEntityData.BrightnessOverride.addEntityData(-1, dataValues); } ItemDisplayEntityData.ViewRange.addEntityData((float) (this.viewRange * player.displayEntityViewDistance()), dataValues); return dataValues; 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 index dc6fa84d9..10b04468f 100644 --- 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 @@ -8,6 +8,7 @@ import net.momirealms.craftengine.bukkit.util.ComponentUtils; 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.display.Billboard; +import net.momirealms.craftengine.core.entity.display.TextDisplayAlignment; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.util.AdventureHelper; @@ -37,6 +38,13 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo public final int blockLight; public final int skyLight; public final float viewRange; + public final int lineWidth; + public final int backgroundColor; + public final byte opacity; + public final boolean hasShadow; + public final boolean isSeeThrough; + public final boolean useDefaultBackgroundColor; + public final TextDisplayAlignment alignment; public TextDisplayBlockEntityElementConfig(String text, Vector3f scale, @@ -49,7 +57,14 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo @Nullable Color glowColor, int blockLight, int skyLight, - float viewRange) { + float viewRange, + int lineWidth, + int backgroundColor, + byte opacity, + boolean hasShadow, + boolean isSeeThrough, + boolean useDefaultBackgroundColor, + TextDisplayAlignment alignment) { this.text = text; this.scale = scale; this.position = position; @@ -62,21 +77,37 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo this.blockLight = blockLight; this.skyLight = skyLight; this.viewRange = viewRange; + this.lineWidth = lineWidth; + this.backgroundColor = backgroundColor; + this.opacity = opacity; + this.hasShadow = hasShadow; + this.useDefaultBackgroundColor = useDefaultBackgroundColor; + this.alignment = alignment; + this.isSeeThrough = isSeeThrough; this.lazyMetadataPacket = player -> { List dataValues = new ArrayList<>(); if (glowColor != null) { - ItemDisplayEntityData.SharedFlags.addEntityData((byte) 0x40, dataValues); - ItemDisplayEntityData.GlowColorOverride.addEntityData(glowColor.color(), dataValues); + TextDisplayEntityData.SharedFlags.addEntityData((byte) 0x40, dataValues); + TextDisplayEntityData.GlowColorOverride.addEntityData(glowColor.color(), dataValues); + } else { + TextDisplayEntityData.SharedFlags.addEntityData((byte) 0x0, dataValues); + TextDisplayEntityData.GlowColorOverride.addEntityData(-1, dataValues); } TextDisplayEntityData.Text.addEntityData(ComponentUtils.adventureToMinecraft(text(player)), dataValues); TextDisplayEntityData.Scale.addEntityData(this.scale, dataValues); TextDisplayEntityData.RotationLeft.addEntityData(this.rotation, dataValues); TextDisplayEntityData.BillboardConstraints.addEntityData(this.billboard.id(), dataValues); TextDisplayEntityData.Translation.addEntityData(this.translation, dataValues); + TextDisplayEntityData.LineWidth.addEntityData(this.lineWidth, dataValues); + TextDisplayEntityData.BackgroundColor.addEntityData(this.backgroundColor, dataValues); + TextDisplayEntityData.TextOpacity.addEntityData(this.opacity, dataValues); + TextDisplayEntityData.TextDisplayMasks.addEntityData(TextDisplayEntityData.encodeMask(this.hasShadow, this.isSeeThrough, this.useDefaultBackgroundColor, this.alignment), dataValues); if (this.blockLight != -1 && this.skyLight != -1) { - ItemDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues); + TextDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues); + } else { + TextDisplayEntityData.BrightnessOverride.addEntityData(-1, dataValues); } - ItemDisplayEntityData.ViewRange.addEntityData((float) (this.viewRange * player.displayEntityViewDistance()), dataValues); + TextDisplayEntityData.ViewRange.addEntityData((float) (this.viewRange * player.displayEntityViewDistance()), dataValues); return dataValues; }; } @@ -170,7 +201,14 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo Optional.ofNullable(arguments.get("glow-color")).map(it -> Color.fromStrings(it.toString().split(","))).orElse(null), ResourceConfigUtils.getAsInt(brightness.getOrDefault("block-light", -1), "block-light"), ResourceConfigUtils.getAsInt(brightness.getOrDefault("sky-light", -1), "sky-light"), - ResourceConfigUtils.getAsFloat(arguments.getOrDefault("view-range", 1f), "view-range") + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("view-range", 1f), "view-range"), + ResourceConfigUtils.getAsInt(arguments.getOrDefault("line-width", 200), "line-width"), + ResourceConfigUtils.getOrDefault(arguments.get("background-color"), o -> Color.fromStrings(o.toString().split(",")).color(), 0x40000000), + (byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("text-opacity", -1), "text-opacity"), + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("has-shadow", false), "has-shadow"), + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("is-see-through", false), "is-see-through"), + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("use-default-background-color", false), "use-default-background-color"), + ResourceConfigUtils.getAsEnum(arguments.get("alignment"), TextDisplayAlignment.class, TextDisplayAlignment.CENTER) ); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/TextDisplayEntityData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/TextDisplayEntityData.java index 73a801d3b..840dde225 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/TextDisplayEntityData.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/TextDisplayEntityData.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.entity.data; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.core.entity.display.TextDisplayAlignment; public class TextDisplayEntityData extends DisplayEntityData { public static final TextDisplayEntityData Text = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$COMPONENT, CoreReflections.instance$Component$empty); @@ -12,4 +13,39 @@ public class TextDisplayEntityData extends DisplayEntityData { public TextDisplayEntityData(Class clazz, Object serializer, T defaultValue) { super(clazz, serializer, defaultValue); } + + public static final int HAS_SHADOW = 0x01; + public static final int IS_SEE_THROUGH = 0x02; + public static final int USE_DEFAULT_BG_COLOR = 0x04; + private static final int LEFT_ALIGNMENT = 0x08; // 8 + private static final int RIGHT_ALIGNMENT = 0x10; // 16 + + public static byte encodeMask(boolean hasShadow, boolean isSeeThrough, boolean useDefaultBackground, TextDisplayAlignment alignment) { + int bitMask = 0; + + if (hasShadow) { + bitMask |= HAS_SHADOW; + } + if (isSeeThrough) { + bitMask |= IS_SEE_THROUGH; + } + if (useDefaultBackground) { + bitMask |= USE_DEFAULT_BG_COLOR; + } + + switch (alignment) { + case CENTER: // CENTER + break; + case LEFT: // LEFT + bitMask |= LEFT_ALIGNMENT; + break; + case RIGHT: // RIGHT + bitMask |= RIGHT_ALIGNMENT; + break; + default: + throw new IllegalArgumentException("Invalid alignment value"); + } + + return (byte) bitMask; + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java index 4ce2d57f2..aac2804da 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java @@ -6,6 +6,7 @@ public class BukkitFurnitureElementConfigs extends FurnitureElementConfigs { static { register(ITEM_DISPLAY, ItemDisplayFurnitureElementConfig.FACTORY); + register(TEXT_DISPLAY, TextDisplayFurnitureElementConfig.FACTORY); } private BukkitFurnitureElementConfigs() {} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java index 8557e6493..b21d957e0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElement.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; @@ -29,7 +30,7 @@ public class ItemDisplayFurnitureElement implements FurnitureElement { WorldPosition furniturePos = furniture.position(); Vec3d position = Furniture.getRelativePosition(furniturePos, config.position()); this.position = new WorldPosition(furniturePos.world, position.x, position.y, position.z, furniturePos.xRot, furniturePos.yRot); - this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(entityId); }}); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(MiscUtils.init(new IntArrayList(), a -> a.add(entityId))); this.colorSource = furniture.dataAccessor.getColorSource(); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java index d5658154c..dabbfdc1f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -101,17 +101,17 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig ItemDisplayEntityData.GlowColorOverride.addEntityData(glowColor.color(), dataValues); } ItemDisplayEntityData.DisplayedItem.addEntityData(itemFunction.apply(player, source).getLiteralObject(), dataValues); - ItemDisplayEntityData.Scale.addEntityData(this.scale, dataValues); - ItemDisplayEntityData.RotationLeft.addEntityData(this.rotation, dataValues); - ItemDisplayEntityData.BillboardConstraints.addEntityData(this.billboard.id(), dataValues); - ItemDisplayEntityData.Translation.addEntityData(this.translation, dataValues); - ItemDisplayEntityData.DisplayType.addEntityData(this.displayContext.id(), dataValues); - ItemDisplayEntityData.ShadowRadius.addEntityData(this.shadowRadius, dataValues); - ItemDisplayEntityData.ShadowStrength.addEntityData(this.shadowStrength, 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); + ItemDisplayEntityData.ShadowRadius.addEntityDataIfNotDefaultValue(this.shadowRadius, dataValues); + ItemDisplayEntityData.ShadowStrength.addEntityDataIfNotDefaultValue(this.shadowStrength, dataValues); if (this.blockLight != -1 && this.skyLight != -1) { ItemDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues); } - ItemDisplayEntityData.ViewRange.addEntityData((float) (this.viewRange * player.displayEntityViewDistance()), dataValues); + ItemDisplayEntityData.ViewRange.addEntityDataIfNotDefaultValue((float) (this.viewRange * player.displayEntityViewDistance()), dataValues); return dataValues; }; } @@ -217,23 +217,4 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig ); } } - - @Override - public String toString() { - return "ItemDisplayFurnitureElementConfig{" + - "metadata=" + metadata + - ", itemId=" + itemId + - ", scale=" + scale + - ", position=" + position + - ", translation=" + translation + - ", xRot=" + xRot + - ", yRot=" + yRot + - ", rotation=" + rotation + - ", displayContext=" + displayContext + - ", billboard=" + billboard + - ", shadowRadius=" + shadowRadius + - ", shadowStrength=" + shadowStrength + - ", applyDyedColor=" + applyDyedColor + - '}'; - } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElement.java new file mode 100644 index 000000000..eb2771d4b --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElement.java @@ -0,0 +1,61 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.element; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +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.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; + +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +public class TextDisplayFurnitureElement implements FurnitureElement { + private final TextDisplayFurnitureElementConfig config; + private final WorldPosition position; + private final int entityId; + private final Object despawnPacket; + private final UUID uuid = UUID.randomUUID(); + + public TextDisplayFurnitureElement(Furniture furniture, TextDisplayFurnitureElementConfig config) { + this.config = config; + this.entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + WorldPosition furniturePos = furniture.position(); + Vec3d position = Furniture.getRelativePosition(furniturePos, config.position()); + this.position = new WorldPosition(furniturePos.world, position.x, position.y, position.z, furniturePos.xRot, furniturePos.yRot); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(MiscUtils.init(new IntArrayList(), a -> a.add(entityId))); + } + + @Override + public void show(Player player) { + player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of( + FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + this.entityId, this.uuid, + this.position.x, this.position.y, this.position.z, 0, this.position.yRot, + MEntityTypes.TEXT_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 + ), + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadata.apply(player)) + )), false); + } + + @Override + public void hide(Player player) { + player.sendPacket(this.despawnPacket, false); + } + + @Override + public int[] virtualEntityIds() { + return new int[] {this.entityId}; + } + + @Override + public void collectVirtualEntityId(Consumer collector) { + collector.accept(this.entityId); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElementConfig.java new file mode 100644 index 000000000..5c2ae4b7e --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElementConfig.java @@ -0,0 +1,252 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.element; + +import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData; +import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.core.entity.display.Billboard; +import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; +import net.momirealms.craftengine.core.entity.display.TextDisplayAlignment; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.Color; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +public class TextDisplayFurnitureElementConfig implements FurnitureElementConfig { + public static final Factory FACTORY = new Factory(); + public final Function> metadata; + public final String text; + public final Vector3f scale; + public final Vector3f position; + public final Vector3f translation; + public final float xRot; + public final float yRot; + public final Quaternionf rotation; + public final ItemDisplayContext displayContext; + public final Billboard billboard; + public final float shadowRadius; + public final float shadowStrength; + public final Color glowColor; + public final int blockLight; + public final int skyLight; + public final float viewRange; + public final int lineWidth; + public final int backgroundColor; + public final byte opacity; + public final boolean hasShadow; + public final boolean isSeeThrough; + public final boolean useDefaultBackgroundColor; + public final TextDisplayAlignment alignment; + + public TextDisplayFurnitureElementConfig(String text, + Vector3f scale, + Vector3f position, + Vector3f translation, + float xRot, + float yRot, + Quaternionf rotation, + ItemDisplayContext displayContext, + Billboard billboard, + float shadowRadius, + float shadowStrength, + @Nullable Color glowColor, + int blockLight, + int skyLight, + float viewRange, + int lineWidth, + int backgroundColor, + byte opacity, + boolean hasShadow, + boolean isSeeThrough, + boolean useDefaultBackgroundColor, + TextDisplayAlignment alignment) { + this.text = text; + 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.shadowRadius = shadowRadius; + this.shadowStrength = shadowStrength; + this.glowColor = glowColor; + this.blockLight = blockLight; + this.skyLight = skyLight; + this.viewRange = viewRange; + this.lineWidth = lineWidth; + this.backgroundColor = backgroundColor; + this.opacity = opacity; + this.hasShadow = hasShadow; + this.useDefaultBackgroundColor = useDefaultBackgroundColor; + this.alignment = alignment; + this.isSeeThrough = isSeeThrough; + this.metadata = (player) -> { + List dataValues = new ArrayList<>(); + if (glowColor != null) { + TextDisplayEntityData.SharedFlags.addEntityData((byte) 0x40, dataValues); + TextDisplayEntityData.GlowColorOverride.addEntityData(glowColor.color(), 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); + TextDisplayEntityData.ShadowRadius.addEntityDataIfNotDefaultValue(this.shadowRadius, dataValues); + TextDisplayEntityData.ShadowStrength.addEntityDataIfNotDefaultValue(this.shadowStrength, dataValues); + TextDisplayEntityData.Text.addEntityData(ComponentUtils.adventureToMinecraft(AdventureHelper.miniMessage().deserialize(this.text, NetworkTextReplaceContext.of(player).tagResolvers())), dataValues); + TextDisplayEntityData.LineWidth.addEntityDataIfNotDefaultValue(this.lineWidth, dataValues); + TextDisplayEntityData.BackgroundColor.addEntityDataIfNotDefaultValue(this.backgroundColor, dataValues); + TextDisplayEntityData.TextOpacity.addEntityDataIfNotDefaultValue(this.opacity, dataValues); + TextDisplayEntityData.TextDisplayMasks.addEntityDataIfNotDefaultValue(TextDisplayEntityData.encodeMask(this.hasShadow, this.isSeeThrough, this.useDefaultBackgroundColor, this.alignment), dataValues); + if (this.blockLight != -1 && this.skyLight != -1) { + TextDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues); + } + TextDisplayEntityData.ViewRange.addEntityDataIfNotDefaultValue((float) (this.viewRange * player.displayEntityViewDistance()), dataValues); + return dataValues; + }; + } + + public Vector3f scale() { + return this.scale; + } + + public Vector3f position() { + return this.position; + } + + public Vector3f translation() { + return this.translation; + } + + public float xRot() { + return this.xRot; + } + + public float yRot() { + return this.yRot; + } + + public Quaternionf rotation() { + return this.rotation; + } + + public ItemDisplayContext displayContext() { + return this.displayContext; + } + + public Billboard billboard() { + return this.billboard; + } + + public float shadowRadius() { + return this.shadowRadius; + } + + public float shadowStrength() { + return this.shadowStrength; + } + + public Function> metadata() { + return this.metadata; + } + + @Nullable + public Color glowColor() { + return this.glowColor; + } + + public int blockLight() { + return this.blockLight; + } + + public int skyLight() { + return this.skyLight; + } + + public float viewRange() { + return this.viewRange; + } + + public String text() { + return this.text; + } + + public int lineWidth() { + return this.lineWidth; + } + + public int backgroundColor() { + return this.backgroundColor; + } + + public byte opacity() { + return this.opacity; + } + + public boolean hasShadow() { + return this.hasShadow; + } + + public boolean isSeeThrough() { + return this.isSeeThrough; + } + + public boolean useDefaultBackgroundColor() { + return this.useDefaultBackgroundColor; + } + + public TextDisplayAlignment alignment() { + return this.alignment; + } + + @Override + public TextDisplayFurnitureElement create(@NotNull Furniture furniture) { + return new TextDisplayFurnitureElement(furniture, this); + } + + public static class Factory implements FurnitureElementConfigFactory { + + @Override + public TextDisplayFurnitureElementConfig create(Map arguments) { + String text = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("text"), "warning.config.furniture.element.text_display.missing_text"); + Map brightness = ResourceConfigUtils.getAsMap(arguments.getOrDefault("brightness", Map.of()), "brightness"); + return new TextDisplayFurnitureElementConfig( + text, + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0f), "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"), + ResourceConfigUtils.getAsEnum(ResourceConfigUtils.get(arguments, "display-context", "display-transform"), ItemDisplayContext.class, ItemDisplayContext.NONE), + ResourceConfigUtils.getAsEnum(arguments.get("billboard"), Billboard.class, Billboard.FIXED), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength"), + Optional.ofNullable(arguments.get("glow-color")).map(it -> Color.fromStrings(it.toString().split(","))).orElse(null), + ResourceConfigUtils.getAsInt(brightness.getOrDefault("block-light", -1), "block-light"), + ResourceConfigUtils.getAsInt(brightness.getOrDefault("sky-light", -1), "sky-light"), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("view-range", 1f), "view-range"), + ResourceConfigUtils.getAsInt(arguments.getOrDefault("line-width", 200), "line-width"), + ResourceConfigUtils.getOrDefault(arguments.get("background-color"), o -> Color.fromStrings(o.toString().split(",")).color(), 0x40000000), + (byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("text-opacity", -1), "text-opacity"), + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("has-shadow", false), "has-shadow"), + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("is-see-through", false), "is-see-through"), + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("use-default-background-color", false), "use-default-background-color"), + ResourceConfigUtils.getAsEnum(arguments.get("alignment"), TextDisplayAlignment.class, TextDisplayAlignment.CENTER) + ); + } + } +} diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 1a1ebdd0c..25492ff33 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -236,6 +236,7 @@ warning.config.furniture.duplicate: "Issue found in file - Dupli warning.config.furniture.missing_variants: "Issue found in file - The furniture '' is missing the required 'variants' argument." warning.config.furniture.element.invalid_type: "Issue found in file - The furniture '' is using an invalid element type ''." warning.config.furniture.element.item_display.missing_item: "Issue found in file - The furniture '' is missing the required 'item' argument for 'item_display' element." +warning.config.furniture.element.text_display.missing_text: "Issue found in file - The furniture '' is missing the required 'text' argument for 'text_display' element." warning.config.furniture.settings.unknown: "Issue found in file - The furniture '' is using an unknown setting type ''." warning.config.furniture.hitbox.invalid_type: "Issue found in file - The furniture '' is using an invalid hitbox type ''." warning.config.furniture.hitbox.custom.invalid_entity: "Issue found in file - The furniture '' is using a custom hitbox with invalid entity type ''." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 4219c13b6..74b33e5a4 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -236,6 +236,7 @@ warning.config.furniture.duplicate: "在文件 发现问题 - warning.config.furniture.missing_variants: "在文件 发现问题 - 家具 '' 缺少必需的 'variants' 参数" warning.config.furniture.element.invalid_type: "在文件 发现问题 - 家具 '' 使用了无效的元素类型 ''" warning.config.furniture.element.item_display.missing_item: "在文件 发现问题 - 家具 '' 的 'item_display' 元素缺少必需的 'item' 参数" +warning.config.furniture.element.text_display.missing_text: "在文件 发现问题 - 家具 '' 的 'text_display' 元素缺少必需的 'text' 参数" warning.config.furniture.settings.unknown: "在文件 发现问题 - 家具 '' 使用了未知的设置类型 ''" warning.config.furniture.hitbox.invalid_type: "在文件 发现问题 - 家具 '' 使用了无效的碰撞箱类型 ''" warning.config.furniture.hitbox.custom.invalid_entity: "在文件 发现问题 - 家具 '' 的自定义碰撞箱使用了无效的实体类型 ''" 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 index a267612c9..380ece537 100644 --- 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 @@ -22,7 +22,14 @@ public abstract class BlockEntityElementConfigs { } 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); + Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(it -> Key.withDefaultNamespace(it, "craftengine")).orElse(null); + if (type == null) { + if (arguments.containsKey("text")) { + type = TEXT_DISPLAY; + } else { + type = ITEM_DISPLAY; + } + } @SuppressWarnings("unchecked") BlockEntityElementConfigFactory factory = (BlockEntityElementConfigFactory) BuiltInRegistries.BLOCK_ENTITY_ELEMENT_TYPE.getValue(type); if (factory == null) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/display/TextDisplayAlignment.java b/core/src/main/java/net/momirealms/craftengine/core/entity/display/TextDisplayAlignment.java new file mode 100644 index 000000000..17c79fb26 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/display/TextDisplayAlignment.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.entity.display; + +public enum TextDisplayAlignment { + CENTER, + LEFT, + RIGHT +} + diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java index 7247cdf0e..10a7b4d09 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/element/FurnitureElementConfigs.java @@ -14,6 +14,7 @@ public class FurnitureElementConfigs { public static final Key ITEM_DISPLAY = Key.of("craftengine:item_display"); public static final Key TEXT_DISPLAY = Key.of("craftengine:text_display"); public static final Key ITEM = Key.of("craftengine:item"); + public static final Key ARMOR_STAND = Key.of("craftengine:armor_stand"); public static void register(Key key, FurnitureElementConfigFactory type) { ((WritableRegistry>) BuiltInRegistries.FURNITURE_ELEMENT_TYPE) @@ -21,7 +22,14 @@ public class FurnitureElementConfigs { } public static FurnitureElementConfig fromMap(Map arguments) { - Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(it -> Key.withDefaultNamespace(it, "craftengine")).orElse(ITEM_DISPLAY); + Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(it -> Key.withDefaultNamespace(it, "craftengine")).orElse(null); + if (type == null) { + if (arguments.containsKey("text")) { + type = TEXT_DISPLAY; + } else { + type = ITEM_DISPLAY; + } + } @SuppressWarnings("unchecked") FurnitureElementConfigFactory factory = (FurnitureElementConfigFactory) BuiltInRegistries.FURNITURE_ELEMENT_TYPE.getValue(type); if (factory == null) { From adf34c860f5b02c6e5edec821ae6ab76c65763ac Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 6 Dec 2025 22:04:59 +0800 Subject: [PATCH 134/135] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=AE=B6=E5=85=B7?= =?UTF-8?q?=E7=9A=84=E6=B8=B2=E6=9F=93=E5=85=83=E7=B4=A0=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ArmorStandBlockEntityElementConfig.java | 26 +++-- .../element/ItemBlockEntityElementConfig.java | 22 ++-- .../ItemDisplayBlockEntityElementConfig.java | 26 +++-- .../TextDisplayBlockEntityElementConfig.java | 9 +- .../element/ArmorStandFurnitureElement.java | 61 ++++++++++ .../ArmorStandFurnitureElementConfig.java | 109 ++++++++++++++++++ .../BukkitFurnitureElementConfigs.java | 2 + .../ItemDisplayFurnitureElementConfig.java | 6 +- .../element/ItemFurnitureElement.java | 78 +++++++++++++ .../element/ItemFurnitureElementConfig.java | 93 +++++++++++++++ .../element/TextDisplayFurnitureElement.java | 1 - .../TextDisplayFurnitureElementConfig.java | 3 +- .../src/main/resources/translations/en.yml | 4 + .../src/main/resources/translations/zh_cn.yml | 4 + 14 files changed, 407 insertions(+), 37 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElement.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElementConfig.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemFurnitureElement.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemFurnitureElementConfig.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElementConfig.java index 6953c474f..f20b8100c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElementConfig.java @@ -8,10 +8,12 @@ import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityEl import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; 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.bukkit.inventory.ItemStack; import org.joml.Vector3f; import java.util.ArrayList; @@ -22,20 +24,20 @@ import java.util.function.Function; public class ArmorStandBlockEntityElementConfig implements BlockEntityElementConfig { public static final Factory FACTORY = new Factory(); private final Function> lazyMetadataPacket; - private final Function> item; - private final Vector3f scale; + private final Key itemId; + private final float scale; private final Vector3f position; private final float xRot; private final float yRot; private final boolean small; - public ArmorStandBlockEntityElementConfig(Function> item, - Vector3f scale, + public ArmorStandBlockEntityElementConfig(Key itemId, + float scale, Vector3f position, float xRot, float yRot, boolean small) { - this.item = item; + this.itemId = itemId; this.scale = scale; this.position = position; this.xRot = xRot; @@ -79,10 +81,15 @@ public class ArmorStandBlockEntityElementConfig implements BlockEntityElementCon } public Item item(Player player) { - return this.item.apply(player); + Item wrappedItem = BukkitItemManager.instance().createWrappedItem(this.itemId, player); + return wrappedItem == null ? BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, player) : wrappedItem ; } - public Vector3f scale() { + public Key itemId() { + return this.itemId; + } + + public float scale() { return this.scale; } @@ -116,10 +123,9 @@ public class ArmorStandBlockEntityElementConfig implements BlockEntityElementCon @Override public ArmorStandBlockEntityElementConfig create(Map arguments) { - Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.armor_stand.missing_item")); return new ArmorStandBlockEntityElementConfig( - player -> BukkitItemManager.instance().createWrappedItem(itemId, player), - ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), + Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.armor_stand.missing_item")), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1f), "scale"), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java index de5f09f40..f49fd65f5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java @@ -6,29 +6,36 @@ import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityEl import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; 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.bukkit.inventory.ItemStack; import org.joml.Vector3f; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Function; public class ItemBlockEntityElementConfig implements BlockEntityElementConfig { public static final Factory FACTORY = new Factory(); private final Function> lazyMetadataPacket; - private final Function> item; + private final Key itemId; private final Vector3f position; - public ItemBlockEntityElementConfig(Function> item, Vector3f position) { - this.item = item; + public ItemBlockEntityElementConfig(Key itemId, Vector3f position) { + this.itemId = itemId; this.position = position; this.lazyMetadataPacket = player -> { List dataValues = new ArrayList<>(); - ItemEntityData.Item.addEntityData(item.apply(player).getLiteralObject(), dataValues); + Item wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player); + if (wrappedItem == null) { + wrappedItem = Objects.requireNonNull(BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, player)); + } + ItemEntityData.Item.addEntityData(wrappedItem.getLiteralObject(), dataValues); ItemEntityData.NoGravity.addEntityData(true, dataValues); return dataValues; }; @@ -61,8 +68,8 @@ public class ItemBlockEntityElementConfig implements BlockEntityElementConfig item(Player player) { - return this.item.apply(player); + public Key itemId() { + return itemId; } public List metadataValues(Player player) { @@ -77,9 +84,8 @@ public class ItemBlockEntityElementConfig implements BlockEntityElementConfig arguments) { - Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item_display.missing_item")); return new ItemBlockEntityElementConfig( - player -> BukkitItemManager.instance().createWrappedItem(itemId, player), + Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item.missing_item")), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position") ); } 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 index a4bca98cc..ec00776e8 100644 --- 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 @@ -2,7 +2,6 @@ package net.momirealms.craftengine.bukkit.block.entity.renderer.element; import com.google.common.base.Objects; import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; -import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory; @@ -10,11 +9,13 @@ import net.momirealms.craftengine.core.entity.display.Billboard; import net.momirealms.craftengine.core.entity.display.ItemDisplayContext; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.util.Color; 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.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; @@ -28,7 +29,7 @@ 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 Key itemId; private final Vector3f scale; private final Vector3f position; private final Vector3f translation; @@ -44,7 +45,7 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo private final int skyLight; private final float viewRange; - public ItemDisplayBlockEntityElementConfig(Function> item, + public ItemDisplayBlockEntityElementConfig(Key itemId, Vector3f scale, Vector3f position, Vector3f translation, @@ -59,7 +60,7 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo int blockLight, int skyLight, float viewRange) { - this.item = item; + this.itemId = itemId; this.scale = scale; this.position = position; this.translation = translation; @@ -83,7 +84,11 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo ItemDisplayEntityData.SharedFlags.addEntityData((byte) 0x0, dataValues); ItemDisplayEntityData.GlowColorOverride.addEntityData(-1, dataValues); } - ItemDisplayEntityData.DisplayedItem.addEntityData(item.apply(player).getLiteralObject(), dataValues); + Item wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player); + if (wrappedItem == null) { + wrappedItem = java.util.Objects.requireNonNull(BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, player)); + } + ItemDisplayEntityData.DisplayedItem.addEntityData(wrappedItem.getLiteralObject(), dataValues); ItemDisplayEntityData.Scale.addEntityData(this.scale, dataValues); ItemDisplayEntityData.RotationLeft.addEntityData(this.rotation, dataValues); ItemDisplayEntityData.BillboardConstraints.addEntityData(this.billboard.id(), dataValues); @@ -128,8 +133,12 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo return ItemDisplayBlockEntityElement.class; } - public Item item(Player player) { - return this.item.apply(player); + public Color glowColor() { + return glowColor; + } + + public Key itemId() { + return itemId; } public Vector3f scale() { @@ -188,10 +197,9 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo @Override public ItemDisplayBlockEntityElementConfig create(Map arguments) { - Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item_display.missing_item")); Map brightness = ResourceConfigUtils.getAsMap(arguments.getOrDefault("brightness", Map.of()), "brightness"); return new ItemDisplayBlockEntityElementConfig( - player -> BukkitItemManager.instance().createWrappedItem(itemId, player), + Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item_display.missing_item")), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), ResourceConfigUtils.getAsVector3f(arguments.get("translation"), "translation"), 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 index 10b04468f..3c27ed7d8 100644 --- 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 @@ -2,7 +2,6 @@ package net.momirealms.craftengine.bukkit.block.entity.renderer.element; import com.google.common.base.Objects; import net.kyori.adventure.text.Component; -import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; @@ -10,7 +9,7 @@ import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityEl import net.momirealms.craftengine.core.entity.display.Billboard; import net.momirealms.craftengine.core.entity.display.TextDisplayAlignment; import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.Color; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -139,8 +138,12 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo return TextDisplayBlockEntityElement.class; } + public String text() { + return text; + } + public Component text(Player player) { - return AdventureHelper.miniMessage().deserialize(this.text, PlayerOptionalContext.of(player).tagResolvers()); + return AdventureHelper.miniMessage().deserialize(this.text, NetworkTextReplaceContext.of(player).tagResolvers()); } public Vector3f scale() { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElement.java new file mode 100644 index 000000000..ba6495647 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElement.java @@ -0,0 +1,61 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.element; + +import com.mojang.datafixers.util.Pair; +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.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; + +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +public class ArmorStandFurnitureElement implements FurnitureElement { + private final ArmorStandFurnitureElementConfig config; + public final int entityId; + private final FurnitureColorSource colorSource; + public final Object cachedSpawnPacket; + public final Object cachedDespawnPacket; + + public ArmorStandFurnitureElement(Furniture furniture, ArmorStandFurnitureElementConfig config) { + this.config = config; + this.entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + WorldPosition furniturePos = furniture.position(); + Vec3d position = Furniture.getRelativePosition(furniturePos, config.position()); + this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityId, UUID.randomUUID(), position.x, position.y, position.z, + furniturePos.xRot, furniturePos.yRot, MEntityTypes.ARMOR_STAND, 0, CoreReflections.instance$Vec3$Zero, furniturePos.yRot + ); + this.colorSource = furniture.dataAccessor.getColorSource(); + this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId)); + } + + @Override + public void show(Player player) { + player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadata.apply(player))), false); + player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEquipmentPacket(this.entityId, List.of( + Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, this.config.item(player, this.colorSource).getLiteralObject()) + )), false); + } + + @Override + public void hide(Player player) { + player.sendPacket(this.cachedDespawnPacket, false); + } + + @Override + public int[] virtualEntityIds() { + return new int[] {this.entityId}; + } + + @Override + public void collectVirtualEntityId(Consumer collector) { + collector.accept(this.entityId); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElementConfig.java new file mode 100644 index 000000000..f24d95a48 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElementConfig.java @@ -0,0 +1,109 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.element; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.entity.data.ArmorStandData; +import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; +import net.momirealms.craftengine.core.item.data.FireworkExplosion; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +public class ArmorStandFurnitureElementConfig implements FurnitureElementConfig { + public static final Factory FACTORY = new Factory(); + public final Function> metadata; + public final Key itemId; + public final float scale; + public final boolean applyDyedColor; + public final Vector3f position; + public final boolean small; + + public ArmorStandFurnitureElementConfig(Key itemId, + float scale, + Vector3f position, + boolean applyDyedColor, + boolean small) { + this.position = position; + this.applyDyedColor = applyDyedColor; + this.small = small; + this.scale = scale; + this.itemId = itemId; + this.metadata = (player) -> { + List dataValues = new ArrayList<>(2); + BaseEntityData.SharedFlags.addEntityData((byte) 0x20, dataValues); + if (small) { + ArmorStandData.ArmorStandFlags.addEntityData((byte) 0x01, dataValues); + } + return dataValues; + }; + } + + public Item item(Player player, FurnitureColorSource colorSource) { + Item wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player); + if (applyDyedColor && colorSource != null && wrappedItem != null) { + Optional.ofNullable(colorSource.dyedColor()).ifPresent(wrappedItem::dyedColor); + Optional.ofNullable(colorSource.fireworkColors()).ifPresent(colors -> wrappedItem.fireworkExplosion(new FireworkExplosion( + FireworkExplosion.Shape.SMALL_BALL, + new IntArrayList(colors), + new IntArrayList(), + false, + false + ))); + } + return Optional.ofNullable(wrappedItem).orElseGet(() -> BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null)); + } + + public float scale() { + return scale; + } + + public boolean small() { + return small; + } + + public Vector3f position() { + return this.position; + } + + public boolean applyDyedColor() { + return this.applyDyedColor; + } + + public Key itemId() { + return this.itemId; + } + + @Override + public ArmorStandFurnitureElement create(@NotNull Furniture furniture) { + return new ArmorStandFurnitureElement(furniture, this); + } + + public static class Factory implements FurnitureElementConfigFactory { + + @Override + public ArmorStandFurnitureElementConfig create(Map arguments) { + return new ArmorStandFurnitureElementConfig( + Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.furniture.element.armor_stand.missing_item")), + ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1f), "scale"), + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0f), "position"), + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color"), + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("small", false), "small") + ); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java index aac2804da..fc328fa1d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/BukkitFurnitureElementConfigs.java @@ -7,6 +7,8 @@ public class BukkitFurnitureElementConfigs extends FurnitureElementConfigs { static { register(ITEM_DISPLAY, ItemDisplayFurnitureElementConfig.FACTORY); register(TEXT_DISPLAY, TextDisplayFurnitureElementConfig.FACTORY); + register(ITEM, ItemFurnitureElementConfig.FACTORY); + register(ARMOR_STAND, ArmorStandFurnitureElementConfig.FACTORY); } private BukkitFurnitureElementConfigs() {} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java index dabbfdc1f..3a4c9d38e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemDisplayFurnitureElementConfig.java @@ -194,11 +194,9 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig @Override public ItemDisplayFurnitureElementConfig create(Map arguments) { - Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.furniture.element.item_display.missing_item")); - boolean applyDyedColor = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color"); Map brightness = ResourceConfigUtils.getAsMap(arguments.getOrDefault("brightness", Map.of()), "brightness"); return new ItemDisplayFurnitureElementConfig( - itemId, + Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.furniture.element.item_display.missing_item")), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0f), "position"), ResourceConfigUtils.getAsVector3f(arguments.get("translation"), "translation"), @@ -209,7 +207,7 @@ public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig ResourceConfigUtils.getAsEnum(arguments.get("billboard"), Billboard.class, Billboard.FIXED), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength"), - applyDyedColor, + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color"), Optional.ofNullable(arguments.get("glow-color")).map(it -> Color.fromStrings(it.toString().split(","))).orElse(null), ResourceConfigUtils.getAsInt(brightness.getOrDefault("block-light", -1), "block-light"), ResourceConfigUtils.getAsInt(brightness.getOrDefault("sky-light", -1), "sky-light"), diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemFurnitureElement.java new file mode 100644 index 000000000..26621dd33 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemFurnitureElement.java @@ -0,0 +1,78 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.element; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +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.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.WorldPosition; + +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +public class ItemFurnitureElement implements FurnitureElement { + private final ItemFurnitureElementConfig config; + public final int entityId1; + public final int entityId2; + private final Object despawnPacket; + private final FurnitureColorSource colorSource; + public final Object cachedSpawnPacket1; + public final Object cachedSpawnPacket2; + public final Object cachedRidePacket; + + public ItemFurnitureElement(Furniture furniture, ItemFurnitureElementConfig config) { + this.config = config; + this.entityId1 = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + this.entityId2 = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + WorldPosition furniturePos = furniture.position(); + Vec3d position = Furniture.getRelativePosition(furniturePos, config.position()); + this.cachedSpawnPacket1 = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityId1, UUID.randomUUID(), position.x, position.y, position.z, + 0, 0, MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0 + ); + this.cachedSpawnPacket2 = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityId2, UUID.randomUUID(), position.x, position.y, position.z, + 0, 0, MEntityTypes.ITEM, 0, CoreReflections.instance$Vec3$Zero, 0 + ); + this.cachedRidePacket = FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityId1, entityId2); + this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(MiscUtils.init(new IntArrayList(), + a -> { + a.add(entityId1); + a.add(entityId2); + } + )); + this.colorSource = furniture.dataAccessor.getColorSource(); + } + + @Override + public void show(Player player) { + player.sendPackets(List.of( + this.cachedSpawnPacket1, + this.cachedSpawnPacket2, + this.cachedRidePacket, + FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId2, this.config.metadata().apply(player, this.colorSource) + )), false); + } + + @Override + public void hide(Player player) { + player.sendPacket(this.despawnPacket, false); + } + + @Override + public int[] virtualEntityIds() { + return new int[] {this.entityId1, this.entityId2}; + } + + @Override + public void collectVirtualEntityId(Consumer collector) { + collector.accept(this.entityId1); + collector.accept(this.entityId2); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemFurnitureElementConfig.java new file mode 100644 index 000000000..af3808b69 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ItemFurnitureElementConfig.java @@ -0,0 +1,93 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.element; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.momirealms.craftengine.bukkit.entity.data.ItemEntityData; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig; +import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemKeys; +import net.momirealms.craftengine.core.item.data.FireworkExplosion; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiFunction; + +public class ItemFurnitureElementConfig implements FurnitureElementConfig { + public static final Factory FACTORY = new Factory(); + public final BiFunction> metadata; + public final Key itemId; + public final boolean applyDyedColor; + public final Vector3f position; + + public ItemFurnitureElementConfig(Key itemId, + Vector3f position, + boolean applyDyedColor) { + this.position = position; + this.applyDyedColor = applyDyedColor; + this.itemId = itemId; + BiFunction> itemFunction = (player, colorSource) -> { + Item wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player); + if (applyDyedColor && colorSource != null && wrappedItem != null) { + Optional.ofNullable(colorSource.dyedColor()).ifPresent(wrappedItem::dyedColor); + Optional.ofNullable(colorSource.fireworkColors()).ifPresent(colors -> wrappedItem.fireworkExplosion(new FireworkExplosion( + FireworkExplosion.Shape.SMALL_BALL, + new IntArrayList(colors), + new IntArrayList(), + false, + false + ))); + } + return Optional.ofNullable(wrappedItem).orElseGet(() -> BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null)); + }; + this.metadata = (player, source) -> { + List dataValues = new ArrayList<>(); + ItemEntityData.Item.addEntityData(itemFunction.apply(player, source).getLiteralObject(), dataValues); + ItemEntityData.NoGravity.addEntityData(true, dataValues); + return dataValues; + }; + } + + public Vector3f position() { + return this.position; + } + + public boolean applyDyedColor() { + return this.applyDyedColor; + } + + public BiFunction> metadata() { + return this.metadata; + } + + public Key itemId() { + return this.itemId; + } + + @Override + public ItemFurnitureElement create(@NotNull Furniture furniture) { + return new ItemFurnitureElement(furniture, this); + } + + public static class Factory implements FurnitureElementConfigFactory { + + @Override + public ItemFurnitureElementConfig create(Map arguments) { + return new ItemFurnitureElementConfig( + Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.furniture.element.item.missing_item")), + ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0f), "position"), + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color") + ); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElement.java index eb2771d4b..a68e1c483 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElement.java @@ -5,7 +5,6 @@ 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.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.util.MiscUtils; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElementConfig.java index 5c2ae4b7e..614efbad3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/TextDisplayFurnitureElementConfig.java @@ -221,10 +221,9 @@ public class TextDisplayFurnitureElementConfig implements FurnitureElementConfig @Override public TextDisplayFurnitureElementConfig create(Map arguments) { - String text = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("text"), "warning.config.furniture.element.text_display.missing_text"); Map brightness = ResourceConfigUtils.getAsMap(arguments.getOrDefault("brightness", Map.of()), "brightness"); return new TextDisplayFurnitureElementConfig( - text, + ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("text"), "warning.config.furniture.element.text_display.missing_text"), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0f), "position"), ResourceConfigUtils.getAsVector3f(arguments.get("translation"), "translation"), diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 25492ff33..edac2828b 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -237,6 +237,8 @@ warning.config.furniture.missing_variants: "Issue found in file warning.config.furniture.element.invalid_type: "Issue found in file - The furniture '' is using an invalid element type ''." warning.config.furniture.element.item_display.missing_item: "Issue found in file - The furniture '' is missing the required 'item' argument for 'item_display' element." warning.config.furniture.element.text_display.missing_text: "Issue found in file - The furniture '' is missing the required 'text' argument for 'text_display' element." +warning.config.furniture.element.item.missing_item: "Issue found in file - The furniture '' is missing the required 'item' argument for 'item' element." +warning.config.furniture.element.armor_stand.missing_item: "Issue found in file - The furniture '' is missing the required 'item' argument for 'armor_stand' element." warning.config.furniture.settings.unknown: "Issue found in file - The furniture '' is using an unknown setting type ''." warning.config.furniture.hitbox.invalid_type: "Issue found in file - The furniture '' is using an invalid hitbox type ''." warning.config.furniture.hitbox.custom.invalid_entity: "Issue found in file - The furniture '' is using a custom hitbox with invalid entity type ''." @@ -351,6 +353,8 @@ warning.config.block.state.entity_renderer.item_display.missing_item: "I 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.entity_renderer.armor_stand.missing_item: "Issue found in file - The block '' is missing the required 'item' argument for 'armor_stand' entity renderer." +warning.config.block.state.entity_renderer.item.missing_item: "Issue found in file - The block '' is missing the required 'item' argument for 'item' entity renderer." warning.config.block.state.variant.invalid_appearance: "Issue found in file - The block '' has an error that the variant '' is using a non-existing appearance ''." warning.config.block.state.invalid_vanilla: "Issue found in file - The block '' is using an invalid vanilla block state ''." warning.config.block.state.invalid_auto_state: "Issue found in file - The block '' is using an invalid auto-state ''. Allowed values: []." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 74b33e5a4..5ea020fd7 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -237,6 +237,8 @@ warning.config.furniture.missing_variants: "在文件 发现问 warning.config.furniture.element.invalid_type: "在文件 发现问题 - 家具 '' 使用了无效的元素类型 ''" warning.config.furniture.element.item_display.missing_item: "在文件 发现问题 - 家具 '' 的 'item_display' 元素缺少必需的 'item' 参数" warning.config.furniture.element.text_display.missing_text: "在文件 发现问题 - 家具 '' 的 'text_display' 元素缺少必需的 'text' 参数" +warning.config.furniture.element.item.missing_item: "在文件 发现问题 - 家具 '' 的 'item' 元素缺少必需的 'item' 参数" +warning.config.furniture.element.armor_stand.missing_item: "在文件 发现问题 - 家具 '' 的 'armor_stand' 元素缺少必需的 'item' 参数" warning.config.furniture.settings.unknown: "在文件 发现问题 - 家具 '' 使用了未知的设置类型 ''" warning.config.furniture.hitbox.invalid_type: "在文件 发现问题 - 家具 '' 使用了无效的碰撞箱类型 ''" warning.config.furniture.hitbox.custom.invalid_entity: "在文件 发现问题 - 家具 '' 的自定义碰撞箱使用了无效的实体类型 ''" @@ -351,6 +353,8 @@ warning.config.block.state.entity_renderer.item_display.missing_item: " warning.config.block.state.entity_renderer.text_display.missing_text: "在文件 发现问题 - 方块 '' 缺少 'text_display' 实体渲染器所需的 'text' 参数" warning.config.block.state.entity_renderer.better_model.missing_model: "在文件 发现问题 - 方块 '' 缺少 'better_model' 实体渲染器所需的 'model' 参数" warning.config.block.state.entity_renderer.model_engine.missing_model: "在文件 发现问题 - 方块 '' 缺少 'model_engine' 实体渲染器所需的 'model' 参数" +warning.config.block.state.entity_renderer.armor_stand.missing_item: "在文件 发现问题 - 方块 '' 缺少 'armor_stand' 实体渲染器所需的 'item' 参数" +warning.config.block.state.entity_renderer.item.missing_item: "在文件 发现问题 - 方块 '' 缺少 'item' 实体渲染器所需的 'item' 参数" warning.config.block.state.variant.invalid_appearance: "在文件 发现问题 - 方块 '' 的变体 '' 使用了不存在的 appearance ''" warning.config.block.state.invalid_vanilla: "在文件 发现问题 - 方块 '' 使用了无效的原版方块状态 ''" warning.config.block.state.invalid_auto_state: "在文件 发现问题 - 方块 '' 使用了无效的自动状态 ''. 允许的值: []" From 283d5e86c1ce16d15444a00c70710b3f74e8da0e Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 6 Dec 2025 23:02:56 +0800 Subject: [PATCH 135/135] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=AE=B6=E5=85=B7?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E5=85=83=E7=B4=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../element/ArmorStandBlockEntityElement.java | 31 +++++++++++++- .../ArmorStandBlockEntityElementConfig.java | 42 ++++++++++++++----- .../element/ItemBlockEntityElementConfig.java | 6 +-- .../ItemDisplayBlockEntityElementConfig.java | 32 +++++++------- .../TextDisplayBlockEntityElementConfig.java | 18 ++++---- .../element/ArmorStandFurnitureElement.java | 34 +++++++++++++-- .../ArmorStandFurnitureElementConfig.java | 26 ++++++++++-- .../hitbox/CustomFurnitureHitbox.java | 12 ++---- .../hitbox/HappyGhastFurnitureHitbox.java | 12 ++---- .../hitbox/ShulkerFurnitureHitbox.java | 10 ++--- .../reflection/minecraft/CoreReflections.java | 11 +++-- .../plugin/user/BukkitServerPlayer.java | 3 +- .../core/block/AbstractBlockManager.java | 6 +++ .../furniture/AbstractFurnitureManager.java | 8 ++++ .../craftengine/core/world/Glowing.java | 10 +++++ gradle.properties | 2 +- 16 files changed, 182 insertions(+), 81 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/world/Glowing.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElement.java index dee58c501..1d6909905 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElement.java @@ -4,21 +4,29 @@ import com.mojang.datafixers.util.Pair; 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.MAttributeHolders; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.bukkit.world.score.BukkitTeamManager; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.BlockPos; import org.joml.Vector3f; +import java.util.Collections; import java.util.List; import java.util.UUID; +import java.util.function.Consumer; public class ArmorStandBlockEntityElement implements BlockEntityElement { public final ArmorStandBlockEntityElementConfig config; public final Object cachedSpawnPacket; public final Object cachedDespawnPacket; public final Object cachedUpdatePosPacket; + public final Object cachedScalePacket; + public final Object cachedTeamPacket; public final int entityId; + public final UUID uuid = UUID.randomUUID(); public ArmorStandBlockEntityElement(ArmorStandBlockEntityElementConfig config, BlockPos pos) { this(config, pos, CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(), false); @@ -27,13 +35,28 @@ public class ArmorStandBlockEntityElement implements BlockEntityElement { public ArmorStandBlockEntityElement(ArmorStandBlockEntityElementConfig config, BlockPos pos, int entityId, boolean posChanged) { 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, + entityId, this.uuid, pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, config.xRot(), config.yRot(), MEntityTypes.ARMOR_STAND, 0, CoreReflections.instance$Vec3$Zero, config.yRot() ); this.config = config; this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId)); this.entityId = entityId; this.cachedUpdatePosPacket = posChanged ? FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(this.entityId, pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, config.yRot(), config.xRot(), false) : null; + if (VersionHelper.isOrAbove1_20_5() && config.scale() != 1) { + Object attributeIns = FastNMS.INSTANCE.constructor$AttributeInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); + FastNMS.INSTANCE.method$AttributeInstance$setBaseValue(attributeIns, config.scale()); + this.cachedScalePacket = FastNMS.INSTANCE.constructor$ClientboundUpdateAttributesPacket(entityId, Collections.singletonList(attributeIns)); + } else { + this.cachedScalePacket = null; + } + Object teamPacket = null; + if (config.glowColor != null) { + Object teamByColor = BukkitTeamManager.instance().getTeamByColor(config.glowColor); + if (teamByColor != null) { + teamPacket = FastNMS.INSTANCE.method$ClientboundSetPlayerTeamPacket$createMultiplePlayerPacket(teamByColor, List.of(this.uuid.toString()), true); + } + } + this.cachedTeamPacket = teamPacket; } @Override @@ -47,6 +70,12 @@ public class ArmorStandBlockEntityElement implements BlockEntityElement { player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEquipmentPacket(this.entityId, List.of( Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, this.config.item(player).getLiteralObject()) )), false); + if (this.cachedDespawnPacket != null) { + player.sendPacket(this.cachedDespawnPacket, false); + } + if (this.cachedTeamPacket != null) { + player.sendPacket(this.cachedTeamPacket, false); + } } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElementConfig.java index f20b8100c..f0e99b90b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ArmorStandBlockEntityElementConfig.java @@ -10,10 +10,13 @@ import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.LegacyChatFormatter; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.Glowing; import net.momirealms.craftengine.core.world.World; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; import java.util.ArrayList; @@ -21,23 +24,26 @@ import java.util.List; import java.util.Map; import java.util.function.Function; -public class ArmorStandBlockEntityElementConfig implements BlockEntityElementConfig { +public class ArmorStandBlockEntityElementConfig implements BlockEntityElementConfig, Glowing { public static final Factory FACTORY = new Factory(); - private final Function> lazyMetadataPacket; - private final Key itemId; - private final float scale; - private final Vector3f position; - private final float xRot; - private final float yRot; - private final boolean small; + public final Function> lazyMetadataPacket; + public final Key itemId; + public final float scale; + public final Vector3f position; + public final float xRot; + public final float yRot; + public final boolean small; + public final LegacyChatFormatter glowColor; public ArmorStandBlockEntityElementConfig(Key itemId, float scale, Vector3f position, float xRot, float yRot, - boolean small) { + boolean small, + LegacyChatFormatter glowColor) { this.itemId = itemId; + this.glowColor = glowColor; this.scale = scale; this.position = position; this.xRot = xRot; @@ -45,7 +51,11 @@ public class ArmorStandBlockEntityElementConfig implements BlockEntityElementCon this.small = small; this.lazyMetadataPacket = player -> { List dataValues = new ArrayList<>(2); - BaseEntityData.SharedFlags.addEntityData((byte) 0x20, dataValues); + if (glowColor != null) { + BaseEntityData.SharedFlags.addEntityData((byte) 0x60, dataValues); + } else { + BaseEntityData.SharedFlags.addEntityData((byte) 0x20, dataValues); + } if (small) { ArmorStandData.ArmorStandFlags.addEntityData((byte) 0x01, dataValues); } @@ -53,6 +63,12 @@ public class ArmorStandBlockEntityElementConfig implements BlockEntityElementCon }; } + @Nullable + @Override + public LegacyChatFormatter glowColor() { + return this.glowColor; + } + @Override public ArmorStandBlockEntityElement create(World world, BlockPos pos) { return new ArmorStandBlockEntityElement(this, pos); @@ -60,6 +76,9 @@ public class ArmorStandBlockEntityElementConfig implements BlockEntityElementCon @Override public ArmorStandBlockEntityElement create(World world, BlockPos pos, ArmorStandBlockEntityElement previous) { + if (previous.config.scale != scale || previous.config.glowColor != glowColor) { + return null; + } return new ArmorStandBlockEntityElement(this, pos, previous.entityId, previous.config.yRot != this.yRot || previous.config.xRot != this.xRot || @@ -129,7 +148,8 @@ public class ArmorStandBlockEntityElementConfig implements BlockEntityElementCon ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"), ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"), - ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("small", false), "small") + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("small", false), "small"), + ResourceConfigUtils.getAsEnum(arguments.get("glow-color"), LegacyChatFormatter.class, null) ); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java index f49fd65f5..dadbd2d96 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/ItemBlockEntityElementConfig.java @@ -22,9 +22,9 @@ import java.util.function.Function; public class ItemBlockEntityElementConfig implements BlockEntityElementConfig { public static final Factory FACTORY = new Factory(); - private final Function> lazyMetadataPacket; - private final Key itemId; - private final Vector3f position; + public final Function> lazyMetadataPacket; + public final Key itemId; + public final Vector3f position; public ItemBlockEntityElementConfig(Key itemId, Vector3f position) { this.itemId = itemId; 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 index ec00776e8..3dbd0ca1a 100644 --- 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 @@ -28,22 +28,22 @@ import java.util.function.Function; public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementConfig { public static final Factory FACTORY = new Factory(); - private final Function> lazyMetadataPacket; - private final Key itemId; - 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; - private final float shadowRadius; - private final float shadowStrength; - private final Color glowColor; - private final int blockLight; - private final int skyLight; - private final float viewRange; + public final Function> lazyMetadataPacket; + public final Key itemId; + public final Vector3f scale; + public final Vector3f position; + public final Vector3f translation; + public final float xRot; + public final float yRot; + public final Quaternionf rotation; + public final ItemDisplayContext displayContext; + public final Billboard billboard; + public final float shadowRadius; + public final float shadowStrength; + public final Color glowColor; + public final int blockLight; + public final int skyLight; + public final float viewRange; public ItemDisplayBlockEntityElementConfig(Key itemId, Vector3f scale, 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 index 3c27ed7d8..fcdc44fea 100644 --- 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 @@ -24,15 +24,15 @@ 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 final Function> lazyMetadataPacket; + public final String text; + public final Vector3f scale; + public final Vector3f position; + public final Vector3f translation; + public final float xRot; + public final float yRot; + public final Quaternionf rotation; + public final Billboard billboard; public final Color glowColor; public final int blockLight; public final int skyLight; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElement.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElement.java index ba6495647..e25917dd5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElement.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElement.java @@ -4,24 +4,31 @@ import com.mojang.datafixers.util.Pair; 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.MAttributeHolders; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.bukkit.world.score.BukkitTeamManager; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource; import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; +import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.function.Consumer; public class ArmorStandFurnitureElement implements FurnitureElement { private final ArmorStandFurnitureElementConfig config; - public final int entityId; private final FurnitureColorSource colorSource; public final Object cachedSpawnPacket; public final Object cachedDespawnPacket; + public final Object cachedScalePacket; + public final Object cachedTeamPacket; + public final int entityId; + public final UUID uuid = UUID.randomUUID(); public ArmorStandFurnitureElement(Furniture furniture, ArmorStandFurnitureElementConfig config) { this.config = config; @@ -29,11 +36,26 @@ public class ArmorStandFurnitureElement implements FurnitureElement { WorldPosition furniturePos = furniture.position(); Vec3d position = Furniture.getRelativePosition(furniturePos, config.position()); this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( - entityId, UUID.randomUUID(), position.x, position.y, position.z, + this.entityId, this.uuid, position.x, position.y, position.z, furniturePos.xRot, furniturePos.yRot, MEntityTypes.ARMOR_STAND, 0, CoreReflections.instance$Vec3$Zero, furniturePos.yRot ); this.colorSource = furniture.dataAccessor.getColorSource(); - this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId)); + this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(this.entityId)); + if (VersionHelper.isOrAbove1_20_5() && config.scale != 1) { + Object attributeIns = FastNMS.INSTANCE.constructor$AttributeInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); + FastNMS.INSTANCE.method$AttributeInstance$setBaseValue(attributeIns, config.scale()); + this.cachedScalePacket = FastNMS.INSTANCE.constructor$ClientboundUpdateAttributesPacket(this.entityId, Collections.singletonList(attributeIns)); + } else { + this.cachedScalePacket = null; + } + Object teamPacket = null; + if (config.glowColor != null) { + Object teamByColor = BukkitTeamManager.instance().getTeamByColor(config.glowColor); + if (teamByColor != null) { + teamPacket = FastNMS.INSTANCE.method$ClientboundSetPlayerTeamPacket$createMultiplePlayerPacket(teamByColor, List.of(this.uuid.toString()), true); + } + } + this.cachedTeamPacket = teamPacket; } @Override @@ -42,6 +64,12 @@ public class ArmorStandFurnitureElement implements FurnitureElement { player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEquipmentPacket(this.entityId, List.of( Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, this.config.item(player, this.colorSource).getLiteralObject()) )), false); + if (this.cachedScalePacket != null) { + player.sendPacket(this.cachedScalePacket, false); + } + if (this.cachedTeamPacket != null) { + player.sendPacket(this.cachedTeamPacket, false); + } } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElementConfig.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElementConfig.java index f24d95a48..ab2f09849 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElementConfig.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/element/ArmorStandFurnitureElementConfig.java @@ -13,9 +13,12 @@ import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemKeys; import net.momirealms.craftengine.core.item.data.FireworkExplosion; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.LegacyChatFormatter; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.Glowing; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; import java.util.ArrayList; @@ -24,7 +27,7 @@ import java.util.Map; import java.util.Optional; import java.util.function.Function; -public class ArmorStandFurnitureElementConfig implements FurnitureElementConfig { +public class ArmorStandFurnitureElementConfig implements FurnitureElementConfig, Glowing { public static final Factory FACTORY = new Factory(); public final Function> metadata; public final Key itemId; @@ -32,23 +35,31 @@ public class ArmorStandFurnitureElementConfig implements FurnitureElementConfig< public final boolean applyDyedColor; public final Vector3f position; public final boolean small; + public final LegacyChatFormatter glowColor; public ArmorStandFurnitureElementConfig(Key itemId, float scale, Vector3f position, boolean applyDyedColor, - boolean small) { + boolean small, + LegacyChatFormatter glowColor) { this.position = position; this.applyDyedColor = applyDyedColor; this.small = small; this.scale = scale; this.itemId = itemId; + this.glowColor = glowColor; this.metadata = (player) -> { List dataValues = new ArrayList<>(2); - BaseEntityData.SharedFlags.addEntityData((byte) 0x20, dataValues); + if (glowColor != null) { + BaseEntityData.SharedFlags.addEntityData((byte) 0x60, dataValues); + } else { + BaseEntityData.SharedFlags.addEntityData((byte) 0x20, dataValues); + } if (small) { ArmorStandData.ArmorStandFlags.addEntityData((byte) 0x01, dataValues); } + return dataValues; }; } @@ -68,6 +79,12 @@ public class ArmorStandFurnitureElementConfig implements FurnitureElementConfig< return Optional.ofNullable(wrappedItem).orElseGet(() -> BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null)); } + @Nullable + @Override + public LegacyChatFormatter glowColor() { + return this.glowColor; + } + public float scale() { return scale; } @@ -102,7 +119,8 @@ public class ArmorStandFurnitureElementConfig implements FurnitureElementConfig< ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1f), "scale"), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0f), "position"), ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color"), - ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("small", false), "small") + ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("small", false), "small"), + ResourceConfigUtils.getAsEnum(arguments.get("glow-color"), LegacyChatFormatter.class, null) ); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java index da193062f..7e2caafed 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/CustomFurnitureHitbox.java @@ -4,12 +4,10 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; import net.momirealms.craftengine.bukkit.nms.FastNMS; 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.NetworkReflections; import net.momirealms.craftengine.core.entity.furniture.Collider; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.Vec3d; @@ -45,13 +43,9 @@ public class CustomFurnitureHitbox extends AbstractFurnitureHitBox { )); packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, config.cachedValues())); if (VersionHelper.isOrAbove1_20_5()) { - try { - Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); - CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale()); - packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityId, Collections.singletonList(attributeInstance))); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to apply scale attribute", e); - } + Object attributeIns = FastNMS.INSTANCE.constructor$AttributeInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); + FastNMS.INSTANCE.method$AttributeInstance$setBaseValue(attributeIns, config.scale()); + packets.add(FastNMS.INSTANCE.constructor$ClientboundUpdateAttributesPacket(entityId, Collections.singletonList(attributeIns))); } this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); this.part = new FurnitureHitboxPart(entityId, aabb, pos, false); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java index e5306701a..313e896b9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/HappyGhastFurnitureHitbox.java @@ -5,12 +5,10 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; 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.MEntityTypes; -import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.core.entity.furniture.Collider; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart; import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; @@ -43,13 +41,9 @@ public class HappyGhastFurnitureHitbox extends AbstractFurnitureHitBox { this.packets = new ArrayList<>(3); this.packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, config.cachedValues())); if (config.scale() != 1) { - try { - Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); - CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale()); - this.packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(this.entityId, Collections.singletonList(attributeInstance))); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to apply scale attribute", e); - } + Object attributeIns = FastNMS.INSTANCE.constructor$AttributeInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); + FastNMS.INSTANCE.method$AttributeInstance$setBaseValue(attributeIns, config.scale()); + this.packets.add(FastNMS.INSTANCE.constructor$ClientboundUpdateAttributesPacket(this.entityId, Collections.singletonList(attributeIns))); } this.packets.add(FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(this.entityId, this.pos.x, this.pos.y, this.pos.z, 0, position.yRot, false)); this.collider = createCollider(furniture.world(), this.pos, aabb, config.hardCollision(), config.blocksBuilding(), config.canBeHitByProjectile()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java index ac88d288d..b55a65872 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerFurnitureHitbox.java @@ -75,13 +75,9 @@ public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox { } } if (VersionHelper.isOrAbove1_20_5() && config.scale() != 1) { - try { - Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); - CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale()); - packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[1], Collections.singletonList(attributeInstance))); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to apply scale attribute", e); - } + Object attributeIns = FastNMS.INSTANCE.constructor$AttributeInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); + FastNMS.INSTANCE.method$AttributeInstance$setBaseValue(attributeIns, config.scale()); + packets.add(FastNMS.INSTANCE.constructor$ClientboundUpdateAttributesPacket(this.entityIds[1], Collections.singletonList(attributeIns))); } config.spawner().accept(entityIds, position.world(), x, y, z, yaw, offset, packets::add, colliders::add, parts::add); this.parts = parts; 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 661b445e0..c1f11650e 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 @@ -24,7 +24,6 @@ import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; -import java.util.function.Consumer; import java.util.function.Function; import static java.util.Objects.requireNonNull; @@ -2863,12 +2862,12 @@ public final class CoreReflections { ); // 1.20.5+ - public static final Constructor constructor$AttributeInstance = - ReflectionUtils.getConstructor(clazz$AttributeInstance, clazz$Holder, Consumer.class); +// public static final Constructor constructor$AttributeInstance = +// ReflectionUtils.getConstructor(clazz$AttributeInstance, clazz$Holder, Consumer.class); - public static final Method method$AttributeInstance$setBaseValue = requireNonNull( - ReflectionUtils.getMethod(clazz$AttributeInstance, void.class, double.class) - ); +// public static final Method method$AttributeInstance$setBaseValue = requireNonNull( +// ReflectionUtils.getMethod(clazz$AttributeInstance, void.class, double.class) +// ); public static final Class clazz$Rotation = requireNonNull( BukkitReflectionUtils.findReobfOrMojmapClass( 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 b29efe890..d9ebbbe69 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 @@ -811,8 +811,7 @@ public class BukkitServerPlayer extends Player { if (VersionHelper.isOrAbove1_20_5()) { Object serverPlayer = serverPlayer(); Object attributeInstance = CoreReflections.methodHandle$ServerPlayer$getAttributeMethod.invokeExact(serverPlayer, MAttributeHolders.BLOCK_BREAK_SPEED); - Object newPacket = NetworkReflections.methodHandle$ClientboundUpdateAttributesPacket0Constructor.invokeExact(entityId(), (List) Lists.newArrayList(attributeInstance)); - sendPacket(newPacket, true); + sendPacket(FastNMS.INSTANCE.constructor$ClientboundUpdateAttributesPacket(entityId(), Lists.newArrayList(attributeInstance)), true); } else { resetEffect(MMobEffects.MINING_FATIGUE); resetEffect(MMobEffects.HASTE); 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 9f620cd82..cc1b2fff5 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 @@ -39,6 +39,7 @@ import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.*; +import net.momirealms.craftengine.core.world.Glowing; import net.momirealms.craftengine.core.world.collision.AABB; import net.momirealms.sparrow.nbt.CompoundTag; import org.incendo.cloud.suggestion.Suggestion; @@ -744,6 +745,11 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem if (arguments == null) return Optional.empty(); List> blockEntityElementConfigs = ResourceConfigUtils.parseConfigAsList(arguments, BlockEntityElementConfigs::fromMap); if (blockEntityElementConfigs.isEmpty()) return Optional.empty(); + for (BlockEntityElementConfig blockEntityElementConfig : blockEntityElementConfigs) { + if (blockEntityElementConfig instanceof Glowing glowing && glowing.glowColor() != null) { + AbstractBlockManager.this.plugin.teamManager().setColorInUse(glowing.glowColor()); + } + } return Optional.of(blockEntityElementConfigs.toArray(new BlockEntityElementConfig[0])); } 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 d439e8ea3..3bf2300a1 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 @@ -19,6 +19,7 @@ import net.momirealms.craftengine.core.plugin.entityculling.CullingData; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; import net.momirealms.craftengine.core.util.*; +import net.momirealms.craftengine.core.world.Glowing; import org.incendo.cloud.suggestion.Suggestion; import org.joml.Vector3f; @@ -219,6 +220,13 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { Optional optionalLootSpawnOffset = Optional.ofNullable(variantArguments.get("loot-spawn-offset")).map(it -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset")); List> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elements"), FurnitureElementConfigs::fromMap); + // 收集颜色 + for (FurnitureElementConfig element : elements) { + if (element instanceof Glowing glowing && glowing.glowColor() != null) { + AbstractFurnitureManager.this.plugin.teamManager().setColorInUse(glowing.glowColor()); + } + } + // 外部模型 Optional externalModel; if (variantArguments.containsKey("model-engine")) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/Glowing.java b/core/src/main/java/net/momirealms/craftengine/core/world/Glowing.java new file mode 100644 index 000000000..b237e34b5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/Glowing.java @@ -0,0 +1,10 @@ +package net.momirealms.craftengine.core.world; + +import net.momirealms.craftengine.core.util.LegacyChatFormatter; +import org.jetbrains.annotations.Nullable; + +public interface Glowing { + + @Nullable + LegacyChatFormatter glowColor(); +} diff --git a/gradle.properties b/gradle.properties index 2a2f5f0f6..1488dee0e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -48,7 +48,7 @@ byte_buddy_version=1.18.1 ahocorasick_version=0.6.3 snake_yaml_version=2.5 anti_grief_version=1.0.5 -nms_helper_version=1.0.146 +nms_helper_version=1.0.148 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.38.7