From 90d67b09aad3a7e750165bdc23260a17310ec0ca Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sun, 17 Aug 2025 17:51:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=89=A9=E5=93=81=E5=90=8D?= =?UTF-8?q?=E7=A7=B0=E5=92=8C=E6=8F=8F=E8=BF=B0=E7=9A=84=E9=A2=84=E8=A7=A3?= =?UTF-8?q?=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/block/BukkitBlockManager.java | 9 +- .../plugin/injector/WorldStorageInjector.java | 3 +- .../plugin/user/BukkitServerPlayer.java | 1 + common-files/src/main/resources/config.yml | 4 +- .../src/main/resources/translations/de.yml | 1 - .../src/main/resources/translations/en.yml | 1 - .../src/main/resources/translations/es.yml | 1 - .../src/main/resources/translations/ru_ru.yml | 1 - .../src/main/resources/translations/tr.yml | 1 - .../src/main/resources/translations/zh_cn.yml | 1 - .../core/block/AbstractBlockManager.java | 167 +++++++++++++++++- .../item/modifier/CustomNameModifier.java | 5 +- .../core/item/modifier/ItemDataModifiers.java | 2 +- .../core/item/modifier/ItemNameModifier.java | 5 +- .../item/modifier/lore/LoreModification.java | 8 +- .../core/item/modifier/lore/LoreModifier.java | 10 +- .../text/minimessage/FormattedLine.java | 75 ++++++++ 17 files changed, 271 insertions(+), 24 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/FormattedLine.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 cc347e0bb..47d6cf89e 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 @@ -47,7 +47,6 @@ import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.Registry; -import org.bukkit.block.BlockType; import org.bukkit.block.data.BlockData; import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; @@ -80,9 +79,6 @@ public final class BukkitBlockManager extends AbstractBlockManager { private Map stateId2BlockHolder; // This map is used to change the block states that are not necessarily needed into a certain block state private Map blockAppearanceMapper; - // Used to automatically arrange block states for strings such as minecraft:note_block:0 - private Map> blockAppearanceArranger; - private Map> realBlockArranger; // Record the amount of real blocks by block type private Map registeredRealBlockSlots; // A set of blocks that sounds have been removed @@ -125,6 +121,11 @@ public final class BukkitBlockManager extends AbstractBlockManager { this.resetPacketConsumers(); } + @Override + public String stateRegistryIdToStateSNBT(int id) { + return BlockStateUtils.idToBlockState(id).toString(); + } + public static BukkitBlockManager instance() { return instance; } 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 ce3a33ac8..1fdc089d0 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 @@ -177,7 +177,6 @@ public final class WorldStorageInjector { return section; } - public static class SetBlockStateInterceptor { public static final SetBlockStateInterceptor INSTANCE = new SetBlockStateInterceptor(); @@ -215,7 +214,7 @@ public final class WorldStorageInjector { } } - protected static void compareAndUpdateBlockState(int x, int y, int z, Object newState, Object previousState, InjectedHolder holder) { + private static void compareAndUpdateBlockState(int x, int y, int z, Object newState, Object previousState, InjectedHolder holder) { try { Optional optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(newState); CESection section = holder.ceSection(); 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 a56e78b01..523e9cd92 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 @@ -188,6 +188,7 @@ public class BukkitServerPlayer extends Player { }; } + @SuppressWarnings("UnstableApiUsage") @Override public void setGameMode(GameMode gameMode) { platformPlayer().setGameMode(Objects.requireNonNull(org.bukkit.GameMode.getByValue(gameMode.id()))); diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 66e7060f0..2d4cc5ea7 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -380,10 +380,10 @@ chunk-system: compression-method: 4 # Settings for injection injection: - # Requires a restart to apply + # Requires a restart to apply. # SECTION: Inject the LevelChunkSection # PALETTE: Inject the PalettedContainer - target: SECTION + target: PALETTE # Enables faster injection method # Note: May not work with certain server forks that alter chunk class structure (In most cases it won't conflict) use-fast-method: true diff --git a/common-files/src/main/resources/translations/de.yml b/common-files/src/main/resources/translations/de.yml index 3cbda8827..3e4619163 100644 --- a/common-files/src/main/resources/translations/de.yml +++ b/common-files/src/main/resources/translations/de.yml @@ -239,7 +239,6 @@ warning.config.block.state.unavailable_vanilla: "Problem in Datei Problem in Datei gefunden - Der Block '' verwendet einen Vanilla-Blockzustand '', der den verfügbaren Slot-Bereich '0~' überschreitet." warning.config.block.state.conflict: "Problem in Datei gefunden - Der Block '' verwendet einen Vanilla-Blockzustand '', der bereits von '' belegt ist." warning.config.block.state.bind_failed: "Problem in Datei gefunden - Der Block '' konnte den echten Blockzustand für '' nicht binden, da der Zustand von '' belegt ist." -warning.config.block.state.missing_model: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'model' oder 'models'-Argument." warning.config.block.state.invalid_real_id: "Problem in Datei gefunden - Der Block '' verwendet einen echten Blockzustand '', der den verfügbaren Slot-Bereich '0~' überschreitet. Erwägen Sie, weitere echte Zustände in 'additional-real-blocks.yml' hinzuzufügen, wenn die Slots aufgebraucht sind." warning.config.block.state.model.missing_path: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'path'-Option für 'model'." warning.config.block.state.model.invalid_path: "Problem in Datei gefunden - Der Block '' hat ein 'path'-Argument '', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index dc3cdb95c..6244a83e1 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -261,7 +261,6 @@ warning.config.block.state.unavailable_vanilla: "Issue found in file Issue found in file - The block '' is using a vanilla block state '' that exceeds the available slot range '0~'." warning.config.block.state.conflict: "Issue found in file - The block '' is using a vanilla block state '' that has been occupied by ''." warning.config.block.state.bind_failed: "Issue found in file - The block '' failed to bind real block state for '' as the state has been occupied by ''." -warning.config.block.state.missing_model: "Issue found in file - The block '' is missing the required 'model' or 'models' argument." warning.config.block.state.invalid_real_id: "Issue found in file - The block '' is using a real block state '' that exceeds the available slot range '0~'. Consider adding more real states in 'additional-real-blocks.yml' if the slots are used up." warning.config.block.state.model.missing_path: "Issue found in file - The block '' is missing the required 'path' option for 'model'." warning.config.block.state.model.invalid_path: "Issue found in file - The block '' has a 'path' argument '' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." diff --git a/common-files/src/main/resources/translations/es.yml b/common-files/src/main/resources/translations/es.yml index 4306e0579..b26e3f80d 100644 --- a/common-files/src/main/resources/translations/es.yml +++ b/common-files/src/main/resources/translations/es.yml @@ -187,7 +187,6 @@ warning.config.block.state.unavailable_vanilla: "Problema encontrado en warning.config.block.state.invalid_vanilla_id: "Problema encontrado en el archivo - El bloque '' está usando un estado de bloque vanilla '' que excede el rango de slots disponible '0~'." warning.config.block.state.conflict: "Problema encontrado en el archivo - El bloque '' está usando un estado de bloque vanilla '' que está ocupado por ''." warning.config.block.state.bind_failed: "Problema encontrado en el archivo - El bloque '' falló al vincular el estado de bloque real para '' porque está ocupado por el estado ''." -warning.config.block.state.missing_model: "Problema encontrado en el archivo - El bloque '' carece del argumento requerido 'model' o 'models'." warning.config.block.state.invalid_real_id: "Problema encontrado en el archivo - El bloque '' está usando un estado de bloque real '' que excede el rango de slots disponible '0~'. Si los slots están usados, considera agregar más estados reales a 'additional-real-blocks.yml'." warning.config.block.state.model.missing_path: "Problema encontrado en el archivo - El bloque '' carece de la opción requerida 'path' para 'model'." warning.config.block.state.model.invalid_path: "Problema encontrado en el archivo - El bloque '' tiene un argumento 'path' '' que contiene caracteres prohibidos. Por favor lee https://minecraft.wiki/w/Resource_location#Legal_characters" diff --git a/common-files/src/main/resources/translations/ru_ru.yml b/common-files/src/main/resources/translations/ru_ru.yml index 626e314d6..60036cb99 100644 --- a/common-files/src/main/resources/translations/ru_ru.yml +++ b/common-files/src/main/resources/translations/ru_ru.yml @@ -237,7 +237,6 @@ warning.config.block.state.unavailable_vanilla: "Проблема най warning.config.block.state.invalid_vanilla_id: "Проблема найдена в файле - Блок '' использует состояние ванильного блока '', что превышает доступный диапазон слотов '0~'." warning.config.block.state.conflict: "Проблема найдена в файле - Блок '' использует состояние ванильного блока '' которое занято ''." warning.config.block.state.bind_failed: "Проблема найдена в файле - Блоку '' не удалось привязать реальное состояние блока для '', так как состояние занято ''." -warning.config.block.state.missing_model: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'model' или 'models' аргумент." warning.config.block.state.invalid_real_id: "Проблема найдена в файле - Блок '' использует реальное состояние блока '', которое превышает доступный диапазон слотов '0~'. Рассмотрите возможность добавления большего количества реальных состояний в 'additional-real-blocks.yml' если слоты израсходованы." warning.config.block.state.model.missing_path: "Проблема найдена в файле - В блоке '' отсутствует необходимый 'path' опция для 'model'." warning.config.block.state.model.invalid_path: "Проблема найдена в файле - Блок '' имеет 'path' аргумент '' содержит недопустимые символы. Пожалуйста, прочтите https://minecraft.wiki/w/Resource_location#Legal_characters." diff --git a/common-files/src/main/resources/translations/tr.yml b/common-files/src/main/resources/translations/tr.yml index b1d73071c..0704fb7f2 100644 --- a/common-files/src/main/resources/translations/tr.yml +++ b/common-files/src/main/resources/translations/tr.yml @@ -185,7 +185,6 @@ warning.config.block.state.unavailable_vanilla: " dosyasında sor warning.config.block.state.invalid_vanilla_id: " dosyasında sorun bulundu - '' bloğu, mevcut yuva aralığı '0~' aşan bir vanilya blok durumu '' kullanıyor." warning.config.block.state.conflict: " dosyasında sorun bulundu - '' bloğu, '' tarafından işgal edilmiş bir vanilya blok durumu '' kullanıyor." warning.config.block.state.bind_failed: " dosyasında sorun bulundu - '' bloğu, durum '' tarafından işgal edildiği için '' için gerçek blok durumu bağlamada başarısız oldu." -warning.config.block.state.missing_model: " dosyasında sorun bulundu - '' bloğu gerekli 'model' veya 'models' argümanı eksik." warning.config.block.state.invalid_real_id: " dosyasında sorun bulundu - '' bloğu, mevcut yuva aralığı '0~' aşan bir gerçek blok durumu '' kullanıyor. Yuvalar kullanılmışsa, 'additional-real-blocks.yml' dosyasına daha fazla gerçek durum eklemeyi düşünün." warning.config.block.state.model.missing_path: " dosyasında sorun bulundu - '' bloğu, 'model' için gerekli 'path' seçeneği eksik." warning.config.block.state.model.invalid_path: " dosyasında sorun bulundu - '' bloğunun, yasak karakterler içeren bir 'path' argümanı '' var. Lütfen https://minecraft.wiki/w/Resource_location#Legal_characters sayfasını okuyun." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index da8ae3198..93b136d5a 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -261,7 +261,6 @@ warning.config.block.state.unavailable_vanilla: "在文件 发 warning.config.block.state.invalid_vanilla_id: "在文件 发现问题 - 方块 '' 使用的原版方块状态 '' 超出可用槽位范围 '0~'" warning.config.block.state.conflict: "在文件 发现问题 - 方块 '' 使用的原版方块状态 '' 已被 '' 占用" warning.config.block.state.bind_failed: "在文件 发现问题 - 方块 '' 无法为 '' 绑定真实方块状态 因该状态已被 '' 占用" -warning.config.block.state.missing_model: "在文件 发现问题 - 方块 '' 缺少必需的 'model' 或 'models' 参数" warning.config.block.state.invalid_real_id: "在文件 发现问题 - 方块 '' 使用的真实方块状态 '' 超出可用槽位范围 '0~' 如果槽位已用尽 请在 additional-real-blocks.yml 中添加更多真实状态" warning.config.block.state.model.missing_path: "在文件 发现问题 - 方块 '' 的 'model' 缺少必需的 'path' 选项" warning.config.block.state.model.invalid_path: "在文件 发现问题 - 方块 '' 的 'path' 参数 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" 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 36862c946..70dfad312 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/AbstractBlockManager.java @@ -1,18 +1,24 @@ package net.momirealms.craftengine.core.block; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.momirealms.craftengine.core.block.properties.Properties; +import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.ResourceLocation; import net.momirealms.craftengine.core.pack.model.generation.AbstractModelGenerator; +import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.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 org.incendo.cloud.suggestion.Suggestion; import org.jetbrains.annotations.NotNull; @@ -41,6 +47,9 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem // client side block tags protected Map> clientBoundTags = Map.of(); protected Map> previousClientBoundTags = Map.of(); + // Used to automatically arrange block states for strings such as minecraft:note_block:0 + protected Map> blockAppearanceArranger; + protected Map> realBlockArranger; protected AbstractBlockManager(CraftEngine plugin) { super(plugin); @@ -139,6 +148,8 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem protected abstract int getBlockRegistryId(Key id); + public abstract String stateRegistryIdToStateSNBT(int id); + public class BlockParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[]{"blocks", "block"}; @@ -179,7 +190,161 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem private void parseCustomBlock(Pack pack, Path path, Key id, Map section) { // 获取方块设置 BlockSettings settings = BlockSettings.fromMap(id, MiscUtils.castToMap(section.get("settings"), true)); - // + // 读取基础外观配置 + Map> properties; + Map appearances; + Map variants; + // 读取states区域 + Map stateSection = MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow( + ResourceConfigUtils.get(section, "state", "states"), "warning.config.block.missing_state"), true); + boolean singleState = !stateSection.containsKey("properties"); + // 单方块状态 + if (singleState) { + int internalId = ResourceConfigUtils.getAsInt(ResourceConfigUtils.requireNonNullOrThrow( + stateSection.get("id"), "warning.config.block.state.missing_real_id"), "id"); + // 获取原版外观的注册表id + int appearanceId = pluginFormattedBlockStateToRegistryId(ResourceConfigUtils.requireNonEmptyStringOrThrow( + stateSection.get("state"), "warning.config.block.state.missing_state")); + // 为原版外观赋予外观模型并检查模型冲突 + this.arrangeModelForStateAndVerify(appearanceId, ResourceConfigUtils.get(stateSection, "model", "models")); + // 设置参数 + properties = Map.of(); + appearances = Map.of("", appearanceId); + variants = Map.of("", new BlockStateVariant("", settings, internalId)); + } + // 多方块状态 + else { + properties = parseBlockProperties(ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("properties"), "warning.config.block.state.missing_properties"), "properties")); + appearances = parseBlockAppearances(ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("appearances"), "warning.config.block.state.missing_appearances"), "appearances")); + + } + } + + private Map parseBlockAppearances(Map appearancesSection) { + Map appearances = new HashMap<>(); + for (Map.Entry entry : appearancesSection.entrySet()) { + Map appearanceSection = ResourceConfigUtils.getAsMap(entry.getValue(), entry.getKey()); + int appearanceId = pluginFormattedBlockStateToRegistryId(ResourceConfigUtils.requireNonEmptyStringOrThrow( + appearanceSection.get("state"), "warning.config.block.state.missing_state")); + this.arrangeModelForStateAndVerify(appearanceId, ResourceConfigUtils.get(appearanceSection, "model", "models")); + appearances.put(entry.getKey(), appearanceId); + } + return appearances; + } + + @NotNull + private Map> parseBlockProperties(Map propertiesSection) { + Map> properties = new HashMap<>(); + for (Map.Entry entry : propertiesSection.entrySet()) { + Property property = Properties.fromMap(entry.getKey(), ResourceConfigUtils.getAsMap(entry.getValue(), entry.getKey())); + properties.put(entry.getKey(), property); + } + return properties; + } + + private void arrangeModelForStateAndVerify(int registryId, Object modelOrModels) { + // 如果没有配置models + if (modelOrModels == null) { + return; + } + // 获取variants + List variants; + if (modelOrModels instanceof String model) { + JsonObject json = new JsonObject(); + json.addProperty("model", model); + variants = Collections.singletonList(json); + } else { + variants = ResourceConfigUtils.parseConfigAsList(modelOrModels, this::parseAppearanceModelSectionAsJson); + if (variants.isEmpty()) { + return; + } + } + // 拆分方块id与属性 + String blockState = stateRegistryIdToStateSNBT(registryId); + Key blockId = Key.of(blockState.substring(blockState.indexOf('{') + 1, blockState.lastIndexOf('}'))); + String propertyNBT = blockState.substring(blockState.indexOf('[') + 1, blockState.lastIndexOf(']')); + // 结合variants + JsonElement combinedVariant = GsonHelper.combine(variants); + JsonElement previous = AbstractBlockManager.this.blockStateOverrides.computeIfAbsent(blockId, k -> new HashMap<>()).put(propertyNBT, combinedVariant); + if (previous != null && !previous.equals(combinedVariant)) { + // todo 播报可能的冲突 + plugin.logger().warn("warning 1"); + } + } + + private JsonObject parseAppearanceModelSectionAsJson(Map section) { + JsonObject json = new JsonObject(); + String modelPath = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("path"), "warning.config.block.state.model.missing_path"); + if (!ResourceLocation.isValid(modelPath)) { + throw new LocalizedResourceConfigException("warning.config.block.state.model.invalid_path", modelPath); + } + json.addProperty("model", modelPath); + if (section.containsKey("x")) + json.addProperty("x", ResourceConfigUtils.getAsInt(section.get("x"), "x")); + if (section.containsKey("y")) + json.addProperty("y", ResourceConfigUtils.getAsInt(section.get("y"), "y")); + if (section.containsKey("uvlock")) json.addProperty("uvlock", ResourceConfigUtils.getAsBoolean(section.get("uvlock"), "uvlock")); + if (section.containsKey("weight")) + json.addProperty("weight", ResourceConfigUtils.getAsInt(section.get("weight"), "weight")); + Map generationMap = MiscUtils.castToMap(section.get("generation"), true); + if (generationMap != null) { + prepareModelGeneration(ModelGeneration.of(Key.of(modelPath), generationMap)); + } + return json; + } + + // 从方块外观的state里获取其原版方块的state id + private int pluginFormattedBlockStateToRegistryId(String blockState) { + // 五种合理情况 + // minecraft:note_block:10 + // note_block:10 + // minecraft:note_block[xxx=xxx] + // note_block[xxx=xxx] + // minecraft:barrier + String[] split = blockState.split(":", 3); + if (split.length >= 4) { + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState); + } + int registryId; + String stateOrId = split[split.length - 1]; + boolean isId = false; + int arrangerIndex = 0; + try { + arrangerIndex = Integer.parseInt(stateOrId); + if (arrangerIndex < 0) { + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState); + } + isId = true; + } catch (NumberFormatException ignored) { + } + // 如果末尾是id,则至少长度为2 + if (isId) { + if (split.length == 1) { + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState); + } + // 获取原版方块的id + Key block = split.length == 2 ? Key.of(split[0]) : Key.of(split[0], split[1]); + try { + List arranger = blockAppearanceArranger.get(block); + if (arranger == null) { + throw new LocalizedResourceConfigException("warning.config.block.state.unavailable_vanilla", blockState); + } + if (arrangerIndex >= arranger.size()) { + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla_id", blockState, String.valueOf(arranger.size() - 1)); + } + registryId = arranger.get(arrangerIndex); + } catch (NumberFormatException e) { + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", e, blockState); + } + } else { + // 其他情况则是完整的方块 + BlockStateWrapper packedBlockState = createPackedBlockState(blockState); + if (packedBlockState == null || !packedBlockState.isVanillaBlock()) { + throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState); + } + registryId = packedBlockState.registryId(); + } + return registryId; } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomNameModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomNameModifier.java index 8989c2225..4d4aaa318 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomNameModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomNameModifier.java @@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.text.minimessage.FormattedLine; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.Nullable; @@ -12,6 +13,7 @@ import org.jetbrains.annotations.Nullable; public class CustomNameModifier implements SimpleNetworkItemDataModifier { public static final Factory FACTORY = new Factory<>(); private final String argument; + private final FormattedLine line; public CustomNameModifier(String argument) { if (Config.addNonItalicTag()) { @@ -23,6 +25,7 @@ public class CustomNameModifier implements SimpleNetworkItemDataModifier { } else { this.argument = argument; } + this.line = FormattedLine.create(this.argument); } public String customName() { @@ -36,7 +39,7 @@ public class CustomNameModifier implements SimpleNetworkItemDataModifier { @Override public Item apply(Item item, ItemBuildContext context) { - item.customNameComponent(AdventureHelper.miniMessage().deserialize(this.argument, context.tagResolvers())); + item.customNameComponent(this.line.parse(context)); return item; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java index cbb6436e4..d2eb6a9c4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemDataModifiers.java @@ -1,9 +1,9 @@ package net.momirealms.craftengine.core.item.modifier; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; -import net.momirealms.craftengine.core.item.modifier.lore.OverwritableLoreModifier; import net.momirealms.craftengine.core.item.modifier.lore.DynamicLoreModifier; import net.momirealms.craftengine.core.item.modifier.lore.LoreModifier; +import net.momirealms.craftengine.core.item.modifier.lore.OverwritableLoreModifier; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Registries; import net.momirealms.craftengine.core.registry.WritableRegistry; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemNameModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemNameModifier.java index 20728b979..fefb4696d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemNameModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemNameModifier.java @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.ItemDataModifierFactory; +import net.momirealms.craftengine.core.plugin.text.minimessage.FormattedLine; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.Nullable; @@ -11,9 +12,11 @@ import org.jetbrains.annotations.Nullable; public class ItemNameModifier implements SimpleNetworkItemDataModifier { public static final Factory FACTORY = new Factory<>(); private final String argument; + private final FormattedLine line; public ItemNameModifier(String argument) { this.argument = argument; + this.line = FormattedLine.create(argument); } public String itemName() { @@ -27,7 +30,7 @@ public class ItemNameModifier implements SimpleNetworkItemDataModifier { @Override public Item apply(Item item, ItemBuildContext context) { - item.itemNameComponent(AdventureHelper.miniMessage().deserialize(this.argument, context.tagResolvers())); + item.itemNameComponent(this.line.parse(context)); return item; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModification.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModification.java index 0b6b8ce34..d033da602 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModification.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModification.java @@ -2,28 +2,28 @@ package net.momirealms.craftengine.core.item.modifier.lore; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.plugin.text.minimessage.FormattedLine; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.TriFunction; import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; import java.util.stream.Stream; // todo 可以考虑未来添加条件系统 -public record LoreModification(Operation operation, boolean split, String[] content) { +public record LoreModification(Operation operation, boolean split, FormattedLine[] content) { public Stream apply(Stream lore, ItemBuildContext context) { return this.operation.function.apply(lore, context, this); } public Stream parseAsStream(ItemBuildContext context) { - Stream parsed = Arrays.stream(this.content).map(string -> AdventureHelper.miniMessage().deserialize(string, context.tagResolvers())); + Stream parsed = Arrays.stream(this.content).map(line -> line.parse(context)); return this.split ? parsed.map(AdventureHelper::splitLines).flatMap(List::stream) : parsed; } public List parseAsList(ItemBuildContext context) { - return this.parseAsStream(context).collect(Collectors.toList()); + return this.parseAsStream(context).toList(); } public enum Operation { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModifier.java index 6e5f3411a..2068fbcc9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/lore/LoreModifier.java @@ -8,6 +8,8 @@ import net.momirealms.craftengine.core.item.ItemDataModifierFactory; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.item.modifier.ItemDataModifiers; import net.momirealms.craftengine.core.item.modifier.SimpleNetworkItemDataModifier; +import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.text.minimessage.FormattedLine; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -63,7 +65,9 @@ public sealed interface LoreModifier extends SimpleNetworkItemDataModifier rawLore[i] = o.toString(); } } - return new SingleLoreModifier<>(new LoreModification(LoreModification.Operation.APPEND, false, rawLore)); + return new SingleLoreModifier<>(new LoreModification(LoreModification.Operation.APPEND, false, + Arrays.stream(rawLore).map(line -> Config.addNonItalicTag() ? FormattedLine.create("" + line) : FormattedLine.create(line)) + .toArray(FormattedLine[]::new))); } List modifications = new ArrayList<>(rawLoreData.size() + 1); @@ -74,7 +78,9 @@ public sealed interface LoreModifier extends SimpleNetworkItemDataModifier LoreModification.Operation operation = ResourceConfigUtils.getAsEnum(Optional.ofNullable(complexLore.get("operation")).map(String::valueOf).orElse(null), LoreModification.Operation.class, LoreModification.Operation.APPEND); lastPriority = Optional.ofNullable(complexLore.get("priority")).map(it -> ResourceConfigUtils.getAsInt(it, "priority")).orElse(lastPriority); boolean split = ResourceConfigUtils.getAsBoolean(complexLore.get("split-lines"), "split-lines"); - modifications.add(new LoreModificationHolder(new LoreModification(operation, split, content), lastPriority)); + modifications.add(new LoreModificationHolder(new LoreModification(operation, split, + Arrays.stream(content).map(line -> Config.addNonItalicTag() ? FormattedLine.create("" + line) : FormattedLine.create(line)) + .toArray(FormattedLine[]::new)), lastPriority)); } } modifications.sort(LoreModificationHolder::compareTo); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/FormattedLine.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/FormattedLine.java new file mode 100644 index 000000000..a5a2b6d20 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/FormattedLine.java @@ -0,0 +1,75 @@ +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 net.momirealms.craftengine.core.util.AdventureHelper; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface FormattedLine { + TagResolver[] CUSTOM_RESOLVERS = new TagResolver[]{ + createDummyResolvers("expr"), + createDummyResolvers("image"), + createDummyResolvers("arg"), + createDummyResolvers("shift"), + createDummyResolvers("i18n"), + createDummyResolvers("global"), + createDummyResolvers("papi"), + createDummyResolvers("rel_papi") + }; + + Component parse(net.momirealms.craftengine.core.plugin.context.Context context); + + private static TagResolver createDummyResolvers(String tag) { + return new TagResolver() { + @Override + public boolean has(@NotNull String name) { + return tag.equals(name); + } + + @Override + public @Nullable Tag resolve(@NotNull String name, @NotNull ArgumentQueue arguments, @NotNull Context ctx) throws ParsingException { + return null; + } + }; + } + + static FormattedLine create(String line) { + if (line.equals(AdventureHelper.customMiniMessage().stripTags(line, CUSTOM_RESOLVERS))) { + return new PreParsedLine(AdventureHelper.miniMessage().deserialize(line)); + } else { + return new DynamicLine(line); + } + } + + class PreParsedLine implements FormattedLine { + private final Component parsed; + + public PreParsedLine(Component parsed) { + this.parsed = parsed; + } + + @Override + public Component parse(net.momirealms.craftengine.core.plugin.context.Context context) { + return this.parsed; + } + } + + class DynamicLine implements FormattedLine { + private final String content; + + public DynamicLine(String content) { + this.content = content; + } + + @Override + public Component parse(net.momirealms.craftengine.core.plugin.context.Context context) { + return AdventureHelper.miniMessage().deserialize(this.content, context.tagResolvers()); + } + } +} +