9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-25 09:59:20 +00:00

添加物品名称和描述的预解析

This commit is contained in:
XiaoMoMi
2025-08-17 17:51:57 +08:00
parent e7ca6a8a3c
commit 90d67b09aa
17 changed files with 271 additions and 24 deletions

View File

@@ -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<Integer, Object> stateId2BlockHolder;
// This map is used to change the block states that are not necessarily needed into a certain block state
private Map<Integer, Integer> blockAppearanceMapper;
// Used to automatically arrange block states for strings such as minecraft:note_block:0
private Map<Key, List<Integer>> blockAppearanceArranger;
private Map<Key, List<Integer>> realBlockArranger;
// Record the amount of real blocks by block type
private Map<Key, Integer> 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;
}

View File

@@ -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<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(newState);
CESection section = holder.ceSection();

View File

@@ -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())));

View File

@@ -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

View File

@@ -239,7 +239,6 @@ warning.config.block.state.unavailable_vanilla: "<yellow>Problem in Datei <arg:0
warning.config.block.state.invalid_vanilla_id: "<yellow>Problem in Datei <arg:0> gefunden - Der Block '<arg:1>' verwendet einen Vanilla-Blockzustand '<arg:2>', der den verfügbaren Slot-Bereich '0~<arg:3>' überschreitet.</yellow>"
warning.config.block.state.conflict: "<yellow>Problem in Datei <arg:0> gefunden - Der Block '<arg:1>' verwendet einen Vanilla-Blockzustand '<arg:2>', der bereits von '<arg:3>' belegt ist.</yellow>"
warning.config.block.state.bind_failed: "<yellow>Problem in Datei <arg:0> gefunden - Der Block '<arg:1>' konnte den echten Blockzustand für '<arg:2>' nicht binden, da der Zustand von '<arg:3>' belegt ist.</yellow>"
warning.config.block.state.missing_model: "<yellow>Problem in Datei <arg:0> gefunden - Dem Block '<arg:1>' fehlt das erforderliche 'model' oder 'models'-Argument.</yellow>"
warning.config.block.state.invalid_real_id: "<yellow>Problem in Datei <arg:0> gefunden - Der Block '<arg:1>' verwendet einen echten Blockzustand '<arg:2>', der den verfügbaren Slot-Bereich '0~<arg:3>' überschreitet. Erwägen Sie, weitere echte Zustände in 'additional-real-blocks.yml' hinzuzufügen, wenn die Slots aufgebraucht sind.</yellow>"
warning.config.block.state.model.missing_path: "<yellow>Problem in Datei <arg:0> gefunden - Dem Block '<arg:1>' fehlt die erforderliche 'path'-Option für 'model'.</yellow>"
warning.config.block.state.model.invalid_path: "<yellow>Problem in Datei <arg:0> gefunden - Der Block '<arg:1>' hat ein 'path'-Argument '<arg:2>', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"

View File

@@ -261,7 +261,6 @@ warning.config.block.state.unavailable_vanilla: "<yellow>Issue found in file <ar
warning.config.block.state.invalid_vanilla_id: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using a vanilla block state '<arg:2>' that exceeds the available slot range '0~<arg:3>'.</yellow>"
warning.config.block.state.conflict: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using a vanilla block state '<arg:2>' that has been occupied by '<arg:3>'.</yellow>"
warning.config.block.state.bind_failed: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' failed to bind real block state for '<arg:2>' as the state has been occupied by '<arg:3>'.</yellow>"
warning.config.block.state.missing_model: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'model' or 'models' argument.</yellow>"
warning.config.block.state.invalid_real_id: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is using a real block state '<arg:2>' that exceeds the available slot range '0~<arg:3>'. Consider adding more real states in 'additional-real-blocks.yml' if the slots are used up.</yellow>"
warning.config.block.state.model.missing_path: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'path' option for 'model'.</yellow>"
warning.config.block.state.model.invalid_path: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' has a 'path' argument '<arg:2>' that contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"

View File

@@ -187,7 +187,6 @@ warning.config.block.state.unavailable_vanilla: "<yellow>Problema encontrado en
warning.config.block.state.invalid_vanilla_id: "<yellow>Problema encontrado en el archivo <arg:0> - El bloque '<arg:1>' está usando un estado de bloque vanilla '<arg:2>' que excede el rango de slots disponible '0~<arg:3>'.</yellow>"
warning.config.block.state.conflict: "<yellow>Problema encontrado en el archivo <arg:0> - El bloque '<arg:1>' está usando un estado de bloque vanilla '<arg:2>' que está ocupado por '<arg:3>'.</yellow>"
warning.config.block.state.bind_failed: "<yellow>Problema encontrado en el archivo <arg:0> - El bloque '<arg:1>' falló al vincular el estado de bloque real para '<arg:2>' porque está ocupado por el estado '<arg:3>'.</yellow>"
warning.config.block.state.missing_model: "<yellow>Problema encontrado en el archivo <arg:0> - El bloque '<arg:1>' carece del argumento requerido 'model' o 'models'.</yellow>"
warning.config.block.state.invalid_real_id: "<yellow>Problema encontrado en el archivo <arg:0> - El bloque '<arg:1>' está usando un estado de bloque real '<arg:2>' que excede el rango de slots disponible '0~<arg:3>'. Si los slots están usados, considera agregar más estados reales a 'additional-real-blocks.yml'.</yellow>"
warning.config.block.state.model.missing_path: "<yellow>Problema encontrado en el archivo <arg:0> - El bloque '<arg:1>' carece de la opción requerida 'path' para 'model'.</yellow>"
warning.config.block.state.model.invalid_path: "<yellow>Problema encontrado en el archivo <arg:0> - El bloque '<arg:1>' tiene un argumento 'path' '<arg:2>' que contiene caracteres prohibidos. Por favor lee https://minecraft.wiki/w/Resource_location#Legal_characters</yellow>"

View File

@@ -237,7 +237,6 @@ warning.config.block.state.unavailable_vanilla: "<yellow>Проблема най
warning.config.block.state.invalid_vanilla_id: "<yellow>Проблема найдена в файле <arg:0> - Блок '<arg:1>' использует состояние ванильного блока '<arg:2>', что превышает доступный диапазон слотов '0~<arg:3>'.</yellow>"
warning.config.block.state.conflict: "<yellow>Проблема найдена в файле <arg:0> - Блок '<arg:1>' использует состояние ванильного блока '<arg:2>' которое занято '<arg:3>'.</yellow>"
warning.config.block.state.bind_failed: "<yellow>Проблема найдена в файле <arg:0> - Блоку '<arg:1>' не удалось привязать реальное состояние блока для '<arg:2>', так как состояние занято '<arg:3>'.</yellow>"
warning.config.block.state.missing_model: "<yellow>Проблема найдена в файле <arg:0> - В блоке '<arg:1>' отсутствует необходимый 'model' или 'models' аргумент.</yellow>"
warning.config.block.state.invalid_real_id: "<yellow>Проблема найдена в файле <arg:0> - Блок '<arg:1>' использует реальное состояние блока '<arg:2>', которое превышает доступный диапазон слотов '0~<arg:3>'. Рассмотрите возможность добавления большего количества реальных состояний в 'additional-real-blocks.yml' если слоты израсходованы.</yellow>"
warning.config.block.state.model.missing_path: "<yellow>Проблема найдена в файле <arg:0> - В блоке '<arg:1>' отсутствует необходимый 'path' опция для 'model'.</yellow>"
warning.config.block.state.model.invalid_path: "<yellow>Проблема найдена в файле <arg:0> - Блок '<arg:1>' имеет 'path' аргумент '<arg:2>' содержит недопустимые символы. Пожалуйста, прочтите https://minecraft.wiki/w/Resource_location#Legal_characters.</yellow>"

View File

@@ -185,7 +185,6 @@ warning.config.block.state.unavailable_vanilla: "<yellow><arg:0> dosyasında sor
warning.config.block.state.invalid_vanilla_id: "<yellow><arg:0> dosyasında sorun bulundu - '<arg:1>' bloğu, mevcut yuva aralığı '0~<arg:3>' aşan bir vanilya blok durumu '<arg:2>' kullanıyor.</yellow>"
warning.config.block.state.conflict: "<yellow><arg:0> dosyasında sorun bulundu - '<arg:1>' bloğu, '<arg:3>' tarafından işgal edilmiş bir vanilya blok durumu '<arg:2>' kullanıyor.</yellow>"
warning.config.block.state.bind_failed: "<yellow><arg:0> dosyasında sorun bulundu - '<arg:1>' bloğu, durum '<arg:3>' tarafından işgal edildiği için '<arg:2>' için gerçek blok durumu bağlamada başarısız oldu.</yellow>"
warning.config.block.state.missing_model: "<yellow><arg:0> dosyasında sorun bulundu - '<arg:1>' bloğu gerekli 'model' veya 'models' argümanı eksik.</yellow>"
warning.config.block.state.invalid_real_id: "<yellow><arg:0> dosyasında sorun bulundu - '<arg:1>' bloğu, mevcut yuva aralığı '0~<arg:3>' aşan bir gerçek blok durumu '<arg:2>' kullanıyor. Yuvalar kullanılmışsa, 'additional-real-blocks.yml' dosyasına daha fazla gerçek durum eklemeyi düşünün.</yellow>"
warning.config.block.state.model.missing_path: "<yellow><arg:0> dosyasında sorun bulundu - '<arg:1>' bloğu, 'model' için gerekli 'path' seçeneği eksik.</yellow>"
warning.config.block.state.model.invalid_path: "<yellow><arg:0> dosyasında sorun bulundu - '<arg:1>' bloğunun, yasak karakterler içeren bir 'path' argümanı '<arg:2>' var. Lütfen https://minecraft.wiki/w/Resource_location#Legal_characters sayfasını okuyun.</yellow>"

View File

@@ -261,7 +261,6 @@ warning.config.block.state.unavailable_vanilla: "<yellow>在文件 <arg:0> 发
warning.config.block.state.invalid_vanilla_id: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用的原版方块状态 '<arg:2>' 超出可用槽位范围 '0~<arg:3>'</yellow>"
warning.config.block.state.conflict: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用的原版方块状态 '<arg:2>' 已被 '<arg:3>' 占用</yellow>"
warning.config.block.state.bind_failed: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 无法为 '<arg:2>' 绑定真实方块状态 因该状态已被 '<arg:3>' 占用</yellow>"
warning.config.block.state.missing_model: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 缺少必需的 'model' 或 'models' 参数</yellow>"
warning.config.block.state.invalid_real_id: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 使用的真实方块状态 '<arg:2>' 超出可用槽位范围 '0~<arg:3>' 如果槽位已用尽 请在 additional-real-blocks.yml 中添加更多真实状态</yellow>"
warning.config.block.state.model.missing_path: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'model' 缺少必需的 'path' 选项</yellow>"
warning.config.block.state.model.invalid_path: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'path' 参数 '<arg:2>' 包含非法字符 请参考 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</yellow>"

View File

@@ -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<Integer, List<String>> clientBoundTags = Map.of();
protected Map<Integer, List<String>> previousClientBoundTags = Map.of();
// Used to automatically arrange block states for strings such as minecraft:note_block:0
protected Map<Key, List<Integer>> blockAppearanceArranger;
protected Map<Key, List<Integer>> 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<String, Object> section) {
// 获取方块设置
BlockSettings settings = BlockSettings.fromMap(id, MiscUtils.castToMap(section.get("settings"), true));
//
// 读取基础外观配置
Map<String, Property<?>> properties;
Map<String, Integer> appearances;
Map<String, BlockStateVariant> variants;
// 读取states区域
Map<String, Object> 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<String, Integer> parseBlockAppearances(Map<String, Object> appearancesSection) {
Map<String, Integer> appearances = new HashMap<>();
for (Map.Entry<String, Object> entry : appearancesSection.entrySet()) {
Map<String, Object> 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<String, Property<?>> parseBlockProperties(Map<String, Object> propertiesSection) {
Map<String, Property<?>> properties = new HashMap<>();
for (Map.Entry<String, Object> 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<JsonObject> 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<String, Object> 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<String, Object> 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<Integer> 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;
}
}
}

View File

@@ -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<I> implements SimpleNetworkItemDataModifier<I> {
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<I> implements SimpleNetworkItemDataModifier<I> {
} else {
this.argument = argument;
}
this.line = FormattedLine.create(this.argument);
}
public String customName() {
@@ -36,7 +39,7 @@ public class CustomNameModifier<I> implements SimpleNetworkItemDataModifier<I> {
@Override
public Item<I> apply(Item<I> item, ItemBuildContext context) {
item.customNameComponent(AdventureHelper.miniMessage().deserialize(this.argument, context.tagResolvers()));
item.customNameComponent(this.line.parse(context));
return item;
}

View File

@@ -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;

View File

@@ -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<I> implements SimpleNetworkItemDataModifier<I> {
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<I> implements SimpleNetworkItemDataModifier<I> {
@Override
public Item<I> apply(Item<I> item, ItemBuildContext context) {
item.itemNameComponent(AdventureHelper.miniMessage().deserialize(this.argument, context.tagResolvers()));
item.itemNameComponent(this.line.parse(context));
return item;
}

View File

@@ -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<Component> apply(Stream<Component> lore, ItemBuildContext context) {
return this.operation.function.apply(lore, context, this);
}
public Stream<Component> parseAsStream(ItemBuildContext context) {
Stream<Component> parsed = Arrays.stream(this.content).map(string -> AdventureHelper.miniMessage().deserialize(string, context.tagResolvers()));
Stream<Component> parsed = Arrays.stream(this.content).map(line -> line.parse(context));
return this.split ? parsed.map(AdventureHelper::splitLines).flatMap(List::stream) : parsed;
}
public List<Component> parseAsList(ItemBuildContext context) {
return this.parseAsStream(context).collect(Collectors.toList());
return this.parseAsStream(context).toList();
}
public enum Operation {

View File

@@ -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<I> extends SimpleNetworkItemDataModifier<I>
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("<!i>" + line) : FormattedLine.create(line))
.toArray(FormattedLine[]::new)));
}
List<LoreModificationHolder> modifications = new ArrayList<>(rawLoreData.size() + 1);
@@ -74,7 +78,9 @@ public sealed interface LoreModifier<I> extends SimpleNetworkItemDataModifier<I>
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("<!i>" + line) : FormattedLine.create(line))
.toArray(FormattedLine[]::new)), lastPriority));
}
}
modifications.sort(LoreModificationHolder::compareTo);

View File

@@ -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());
}
}
}