diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml b/bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml new file mode 100644 index 000000000..77f21e382 --- /dev/null +++ b/bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml @@ -0,0 +1,26 @@ +templates: + default:emoji/basic: + content: "'>" + default:emoji/addition_info: + content: "'>{text}" + +emoji: + default:emoji_location: + template: "default:emoji/addition_info" + arguments: + text: "" + overrides: + image: "default:icons:0:0" + permission: emoji.location + keywords: + - ":location:" + - ":pos:" + default:emoji_time: + template: "default:emoji/addition_info" + arguments: + text: "" + overrides: + image: "default:icons:0:1" + permission: emoji.time + keywords: + - ":time:" \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/i18n.yml b/bukkit/loader/src/main/resources/resources/default/configuration/i18n.yml index 51b178e1b..fe6db7c36 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/i18n.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/i18n.yml @@ -39,6 +39,9 @@ i18n: category.topaz: "Topaz" category.furniture: "Furniture" category.misc: "Misc" + emoji.tip: "Use '' to send the '' emoji" + emoji.time: "Current time: " + emoji.location: "Current coordinates: ,," zh_cn: item.chinese_lantern: "灯笼" item.fairy_flower: "仙灵花" @@ -78,4 +81,7 @@ i18n: category.palm_tree: "棕榈树" category.topaz: "黄玉" category.furniture: "家具" - category.misc: "杂项" \ No newline at end of file + category.misc: "杂项" + emoji.tip: "使用''来发送表情''" + emoji.time: "当前时间: " + emoji.location: "当前坐标: ,," \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/translations/en.yml b/bukkit/loader/src/main/resources/translations/en.yml index d2c95dfb1..007676fe9 100644 --- a/bukkit/loader/src/main/resources/translations/en.yml +++ b/bukkit/loader/src/main/resources/translations/en.yml @@ -102,4 +102,7 @@ warning.config.block.state.model.lack_path: "Issue found in file warning.config.block.state.model.invalid_resource_location: "Issue found in file - The block '' has a 'path' argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" warning.config.model.generation.conflict: "Issue found in file - Failed to generate model for '' as two or more configurations attempt to generate different json models with the same path: ''" warning.config.model.generation.texture.invalid_resource_location: "Issue found in file - The config '' has a '' texture argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" -warning.config.model.generation.parent.invalid_resource_location: "Issue found in file - The config '' has a parent argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" \ No newline at end of file +warning.config.model.generation.parent.invalid_resource_location: "Issue found in file - The config '' has a parent argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" +warning.config.emoji.lack_keywords: "Issue found in file - The emoji '' is missing the required 'keywords' argument." +warning.config.emoji.duplicated: "Issue found in file - Duplicated emoji ''." +warning.config.emoji.invalid_image: "Issue found in file - The emoji '' has an invalid 'image' argument ''." \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/translations/zh_cn.yml b/bukkit/loader/src/main/resources/translations/zh_cn.yml index 822f32e54..66acc8f30 100644 --- a/bukkit/loader/src/main/resources/translations/zh_cn.yml +++ b/bukkit/loader/src/main/resources/translations/zh_cn.yml @@ -102,4 +102,7 @@ warning.config.block.state.model.lack_path: "在文件 中发现 warning.config.block.state.model.invalid_resource_location: "在文件 中发现问题 - 方块 '' 的 'path' 路径参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" warning.config.model.generation.conflict: "在文件 中发现问题 - 无法为 '' 生成模型,多个配置尝试用相同路径 '' 生成不同的JSON模型" warning.config.model.generation.texture.invalid_resource_location: "在文件 中发现问题 - 配置项 '' 的 '' 纹理参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" -warning.config.model.generation.parent.invalid_resource_location: "在文件 中发现问题 - 配置项 '' 的父模型参数 [] 包含非法字符,请参考资源路径规范: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" \ No newline at end of file +warning.config.model.generation.parent.invalid_resource_location: "在文件 中发现问题 - 配置项 '' 的父模型参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" +warning.config.emoji.lack_keywords: "在文件 中发现问题 - 表情 '' 缺少必要的 'keywords' 配置" +warning.config.emoji.duplicated: "在文件 中发现问题 - 表情 '' 重复定义" +warning.config.emoji.invalid_image: "在文件 中发现问题 - 表情 '' 使用了无效的 'image' 图片参数 ''." \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 483bbab9f..d193d46aa 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -2,7 +2,6 @@ package net.momirealms.craftengine.bukkit.font; import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent; import io.papermc.paper.event.player.AsyncChatDecorateEvent; -import io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.ComponentUtils; @@ -17,11 +16,10 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; -import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import java.lang.reflect.InvocationTargetException; import java.util.Map; -import java.util.function.Consumer; public class BukkitFontManager extends AbstractFontManager implements Listener { private final BukkitCraftEngine plugin; @@ -59,10 +57,12 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public void onCommand(PlayerCommandPreprocessEvent event) { if (!Config.filterCommand()) return; - if (event.getPlayer().hasPermission(FontManager.BYPASS_COMMAND)) { - return; + if (!event.getPlayer().hasPermission(FontManager.BYPASS_COMMAND)) { + IllegalCharacterProcessResult result = processIllegalCharacters(event.getMessage()); + if (result.has()) { + event.setMessage(result.newText()); + } } - runIfContainsIllegalCharacter(event.getMessage(), event::setMessage); } @SuppressWarnings("UnstableApiUsage") @@ -71,23 +71,28 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { if (player == null) return; try { Object originalMessage = Reflections.field$AsyncChatDecorateEvent$originalMessage.get(event); - String jsonMessage = ComponentUtils.paperAdventureToJson(originalMessage); + String rawJsonMessage = ComponentUtils.paperAdventureToJson(originalMessage); + String jsonMessage = replaceJsonEmoji(rawJsonMessage, this.plugin.adapt(player)); + boolean hasChanged = !rawJsonMessage.equals(jsonMessage); if (!player.hasPermission(FontManager.BYPASS_CHAT)) { - runIfContainsIllegalCharacter(jsonMessage, (json) -> { - Object component = ComponentUtils.jsonToPaperAdventure(json); - try { - Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - }); + IllegalCharacterProcessResult result = processIllegalCharacters(jsonMessage); + if (result.has()) { + Object component = ComponentUtils.jsonToPaperAdventure(result.newText()); + Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); + } else if (hasChanged) { + Object component = ComponentUtils.jsonToPaperAdventure(jsonMessage); + Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); + } + } else if (hasChanged) { + Object component = ComponentUtils.jsonToPaperAdventure(jsonMessage); + Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); } - } catch (IllegalAccessException e) { + } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } } - private void runIfContainsIllegalCharacter(String raw, Consumer callback) { + private IllegalCharacterProcessResult processIllegalCharacters(String raw) { boolean hasIllegal = false; // replace illegal image usage Map tokens = matchTags(raw); @@ -113,11 +118,24 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { hasIllegal = true; } } + if (hasIllegal) { - callback.accept(new String(newCodepoints, 0, newCodepoints.length)); + return IllegalCharacterProcessResult.has(new String(newCodepoints, 0, newCodepoints.length)); } } else if (hasIllegal) { - callback.accept(raw); + return IllegalCharacterProcessResult.has(raw); + } + return IllegalCharacterProcessResult.not(); + } + + public record IllegalCharacterProcessResult(boolean has, String newText) { + + public static IllegalCharacterProcessResult has(String newText) { + return new IllegalCharacterProcessResult(true, newText); + } + + public static IllegalCharacterProcessResult not() { + return new IllegalCharacterProcessResult(false, null); } } } 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 b8ecc62bf..df2e565b8 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 @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.font; import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.ResourceLocation; @@ -8,6 +9,8 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.*; +import net.momirealms.craftengine.core.util.context.ContextHolder; +import net.momirealms.craftengine.core.util.context.PlayerContext; import org.ahocorasick.trie.Token; import org.ahocorasick.trie.Trie; @@ -28,8 +31,10 @@ public abstract class AbstractFontManager implements FontManager { private final EmojiParser emojiParser; private OffsetFont offsetFont; - private Trie trie; + private Trie imageTagTrie; + private Trie emojiKeywordTrie; private Map tagMapper; + private Map emojiMapper; public AbstractFontManager(CraftEngine plugin) { this.plugin = plugin; @@ -49,15 +54,16 @@ public abstract class AbstractFontManager implements FontManager { this.fonts.clear(); this.images.clear(); this.illegalChars.clear(); + this.emojis.clear(); } @Override public Map matchTags(String json) { - if (this.trie == null) { + if (this.imageTagTrie == null) { return Collections.emptyMap(); } Map tags = new HashMap<>(); - for (Token token : this.trie.tokenize(json)) { + for (Token token : this.imageTagTrie.tokenize(json)) { if (token.isMatch()) { tags.put(token.getFragment(), this.tagMapper.get(token.getFragment())); } @@ -67,11 +73,11 @@ public abstract class AbstractFontManager implements FontManager { @Override public String stripTags(String text) { - if (this.trie == null) { + if (this.imageTagTrie == null) { return text; } StringBuilder builder = new StringBuilder(); - for (Token token : this.trie.tokenize(text)) { + for (Token token : this.imageTagTrie.tokenize(text)) { if (token.isMatch()) { builder.append("*"); } else { @@ -81,6 +87,58 @@ public abstract class AbstractFontManager implements FontManager { return builder.toString(); } + @Override + public String replaceMiniMessageEmoji(String miniMessage, Player player) { + if (this.emojiKeywordTrie == null) { + return miniMessage; + } + for (Token token : this.emojiKeywordTrie.tokenize(miniMessage)) { + if (!token.isMatch()) continue; + Emoji emoji = this.emojiMapper.get(token.getFragment()); + if (emoji == null) continue; + Component content = AdventureHelper.miniMessage().deserialize( + emoji.content(), + PlayerContext.of(player, ContextHolder.builder() + .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) + .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) + .build()).tagResolvers() + ); + miniMessage = miniMessage.replace(token.getFragment(), AdventureHelper.componentToMiniMessage(content)); + } + return miniMessage; + } + + @Override + public String replaceJsonEmoji(String jsonText, Player player) { + if (this.emojiKeywordTrie == null) { + return jsonText; + } + Map emojis = new HashMap<>(); + for (Token token : this.emojiKeywordTrie.tokenize(jsonText)) { + if (token.isMatch()) { + emojis.put(token.getFragment(), this.emojiMapper.get(token.getFragment())); + } + } + if (emojis.isEmpty()) return jsonText; + Component component = AdventureHelper.jsonToComponent(jsonText); + for (Map.Entry entry : emojis.entrySet()) { + Emoji emoji = entry.getValue(); + if (player != null && emoji.permission() != null && !player.hasPermission(emoji.permission())) { + continue; + } + component = component.replaceText(builder -> builder.matchLiteral(entry.getKey()) + .replacement( + AdventureHelper.miniMessage().deserialize( + emoji.content(), + PlayerContext.of(player, ContextHolder.builder() + .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) + .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) + .build()).tagResolvers()) + )); + } + return AdventureHelper.componentToJson(component); + } + @Override public ConfigSectionParser[] parsers() { return new ConfigSectionParser[] {this.imageParser, this.emojiParser}; @@ -89,10 +147,24 @@ public abstract class AbstractFontManager implements FontManager { @Override public void delayedLoad() { Optional.ofNullable(this.fonts.get(DEFAULT_FONT)).ifPresent(font -> this.illegalChars.addAll(font.codepointsInUse())); - this.buildTrie(); + this.buildImageTagTrie(); + this.buildEmojiKeywordsTrie(); } - private void buildTrie() { + private void buildEmojiKeywordsTrie() { + this.emojiMapper = new HashMap<>(); + for (Emoji emoji : this.emojis.values()) { + for (String keyword : emoji.keywords()) { + this.emojiMapper.put(keyword, emoji); + } + } + this.emojiKeywordTrie = Trie.builder() + .ignoreOverlaps() + .addKeywords(this.emojiMapper.keySet()) + .build(); + } + + private void buildImageTagTrie() { this.tagMapper = new HashMap<>(); for (BitmapImage image : this.images.values()) { String id = image.id().toString(); @@ -109,7 +181,7 @@ public abstract class AbstractFontManager implements FontManager { this.tagMapper.put("", AdventureHelper.miniMessage().deserialize(this.offsetFont.createOffset(i, FormatUtils::miniMessageFont))); this.tagMapper.put("\\", Component.text("")); } - this.trie = Trie.builder() + this.imageTagTrie = Trie.builder() .ignoreOverlaps() .addKeywords(this.tagMapper.keySet()) .build(); @@ -180,7 +252,47 @@ public abstract class AbstractFontManager implements FontManager { @Override public void parseSection(Pack pack, Path path, Key id, Map section) { - + if (emojis.containsKey(id)) { + TranslationManager.instance().log("warning.config.emoji.duplicated", path.toString(), id.toString()); + return; + } + String permission = (String) section.get("permission"); + Object keywordsRaw = section.get("keywords"); + if (keywordsRaw == null) { + TranslationManager.instance().log("warning.config.emoji.lack_keywords", path.toString(), id.toString()); + return; + } + List keywords = MiscUtils.getAsStringList(keywordsRaw); + String content = section.getOrDefault("content", "").toString(); + String image = null; + if (section.containsKey("image")) { + String rawImage = section.get("image").toString(); + String[] split = rawImage.split(":"); + if (split.length == 2) { + Key imageId = new Key(split[0], split[1]); + Optional bitmapImage = bitmapImageByImageId(imageId); + if (bitmapImage.isPresent()) { + image = bitmapImage.get().miniMessage(0, 0); + } else { + TranslationManager.instance().log("warning.config.emoji.invalid_image", path.toString(), id.toString(), rawImage); + return; + } + } else if (split.length == 4) { + Key imageId = new Key(split[0], split[1]); + Optional bitmapImage = bitmapImageByImageId(imageId); + if (bitmapImage.isPresent()) { + image = bitmapImage.get().miniMessage(Integer.parseInt(split[2]), Integer.parseInt(split[3])); + } else { + TranslationManager.instance().log("warning.config.emoji.invalid_image", path.toString(), id.toString(), rawImage); + return; + } + } else { + TranslationManager.instance().log("warning.config.emoji.invalid_image", path.toString(), id.toString(), rawImage); + return; + } + } + Emoji emoji = new Emoji(content, permission, image, keywords); + emojis.put(id, emoji); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/Emoji.java b/core/src/main/java/net/momirealms/craftengine/core/font/Emoji.java index 3b76f27f7..81a51429b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/Emoji.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/Emoji.java @@ -1,25 +1,27 @@ package net.momirealms.craftengine.core.font; -import net.momirealms.craftengine.core.util.Key; - import javax.annotation.Nullable; +import java.util.List; public class Emoji { - private final Key font; - private final String image; + private final String content; private final String permission; + private final String image; + private final List keywords; - public Emoji(Key font, String image, String permission) { - this.font = font; + public Emoji(String content, String permission, String image, List keywords) { + this.content = content; this.image = image; this.permission = permission; + this.keywords = keywords; } - public Key font() { - return font; + public String content() { + return content; } - public String image() { + @Nullable + public String emojiImage() { return image; } @@ -27,4 +29,8 @@ public class Emoji { public String permission() { return permission; } + + public List keywords() { + return keywords; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/EmojiParameters.java b/core/src/main/java/net/momirealms/craftengine/core/font/EmojiParameters.java new file mode 100644 index 000000000..83c91536d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/font/EmojiParameters.java @@ -0,0 +1,9 @@ +package net.momirealms.craftengine.core.font; + +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.context.ContextKey; + +public class EmojiParameters { + public static final ContextKey KEYWORD = new ContextKey<>(Key.of("keyword")); + public static final ContextKey EMOJI = new ContextKey<>(Key.of("emoji")); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java index 456947d4e..7fd53b4d1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.font; import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; import net.momirealms.craftengine.core.util.CharacterUtils; @@ -23,6 +24,10 @@ public interface FontManager extends Manageable { ConfigSectionParser[] parsers(); + String replaceMiniMessageEmoji(String miniMessage, Player player); + + String replaceJsonEmoji(String jsonText, Player player); + boolean isDefaultFontInUse(); boolean isIllegalCodepoint(int codepoint); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java index 2c77dcbd9..5f7ccfd0a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java @@ -1,43 +1,20 @@ package net.momirealms.craftengine.core.item; -import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.plugin.text.minimessage.*; import net.momirealms.craftengine.core.util.context.ContextHolder; +import net.momirealms.craftengine.core.util.context.PlayerContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class ItemBuildContext implements MiniMessageTextContext { +public class ItemBuildContext extends PlayerContext { public static final ItemBuildContext EMPTY = new ItemBuildContext(null, ContextHolder.EMPTY); - private final Player player; - private final ContextHolder contexts; - private TagResolver[] tagResolvers; public ItemBuildContext(@Nullable Player player, @NotNull ContextHolder contexts) { - this.player = player; - this.contexts = contexts; + super(player, contexts); } @NotNull public static ItemBuildContext of(@Nullable Player player, @NotNull ContextHolder contexts) { return new ItemBuildContext(player, contexts); } - - @Nullable - public Player player() { - return this.player; - } - - @NotNull - public ContextHolder contexts() { - return this.contexts; - } - - @NotNull - public TagResolver[] tagResolvers() { - if (this.tagResolvers == null) { - this.tagResolvers = new TagResolver[]{ShiftTag.INSTANCE, ImageTag.INSTANCE, new PlaceholderTag(this.player), new I18NTag(this), new NamedArgumentTag(this)}; - } - return this.tagResolvers; - } } 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 c99a83be3..a94426c90 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 @@ -300,6 +300,8 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/resourcepack/pack.png"); // templates plugin.saveResource("resources/default/configuration/templates.yml"); + // emoji + plugin.saveResource("resources/default/configuration/emoji.yml"); // i18n plugin.saveResource("resources/default/configuration/i18n.yml"); // block_name 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 81460d345..aacfc4f47 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 @@ -16,6 +16,7 @@ import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.context.ContextHolder; +import net.momirealms.craftengine.core.util.context.PlayerContext; import java.nio.file.Path; import java.util.*; @@ -175,7 +176,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }) .build() - .title(AdventureHelper.miniMessage().deserialize(Constants.BROWSER_TITLE, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers())) + .title(AdventureHelper.miniMessage().deserialize(Constants.BROWSER_TITLE, PlayerContext.of(player, ContextHolder.EMPTY).tagResolvers())) .refresh() .open(player); } @@ -290,7 +291,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }) .build() - .title(AdventureHelper.miniMessage().deserialize(Constants.CATEGORY_TITLE, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers())) + .title(AdventureHelper.miniMessage().deserialize(Constants.CATEGORY_TITLE, PlayerContext.of(player, ContextHolder.EMPTY).tagResolvers())) .refresh() .open(player); } @@ -351,7 +352,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }) .build() - .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_NONE_TITLE, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers())) + .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_NONE_TITLE, PlayerContext.of(player, ContextHolder.EMPTY).tagResolvers())) .refresh() .open(player); } @@ -576,7 +577,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }) .build() - .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_SMITHING_TRANSFORM_TITLE, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers())) + .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_SMITHING_TRANSFORM_TITLE, PlayerContext.of(player, ContextHolder.EMPTY).tagResolvers())) .refresh() .open(player); } @@ -709,7 +710,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }) .build() - .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_STONECUTTING_TITLE, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers())) + .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_STONECUTTING_TITLE, PlayerContext.of(player, ContextHolder.EMPTY).tagResolvers())) .refresh() .open(player); } @@ -859,7 +860,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }) .build() - .title(AdventureHelper.miniMessage().deserialize(title, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers())) + .title(AdventureHelper.miniMessage().deserialize(title, PlayerContext.of(player, ContextHolder.EMPTY).tagResolvers())) .refresh() .open(player); } @@ -1054,7 +1055,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }) .build() - .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_CRAFTING_TITLE, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers())) + .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_CRAFTING_TITLE, PlayerContext.of(player, ContextHolder.EMPTY).tagResolvers())) .refresh() .open(player); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/NamedArgumentTag.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/NamedArgumentTag.java index e63f82acd..67f2b335f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/NamedArgumentTag.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/NamedArgumentTag.java @@ -28,15 +28,12 @@ public class NamedArgumentTag implements TagResolver { if (!has(name)) { return null; } - String argumentKey = arguments.popOr("No argument key provided").toString(); - ContextKey key = ContextKey.of(Key.of(argumentKey)); if (!this.context.contexts().has(key)) { throw ctx.newException("Invalid argument key", arguments); } - - return Tag.inserting(AdventureHelper.miniMessage().deserialize(this.context.contexts().getOrThrow(key), this.context.tagResolvers())); + return Tag.selfClosingInserting(AdventureHelper.miniMessage().deserialize(this.context.contexts().getOrThrow(key), this.context.tagResolvers())); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java index 2a365d14d..d69c82d01 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java @@ -126,6 +126,10 @@ public class AdventureHelper { return getInstance().miniMessageStrict.serialize(getInstance().gsonComponentSerializer.deserialize(json)); } + public static String componentToMiniMessage(Component component) { + return getInstance().miniMessageStrict.serialize(component); + } + /** * Converts a JSON string to a Component. * diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/context/PlayerContext.java b/core/src/main/java/net/momirealms/craftengine/core/util/context/PlayerContext.java new file mode 100644 index 000000000..1104abb65 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/context/PlayerContext.java @@ -0,0 +1,42 @@ +package net.momirealms.craftengine.core.util.context; + +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.text.minimessage.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PlayerContext implements MiniMessageTextContext { + public static final PlayerContext EMPTY = new PlayerContext(null, ContextHolder.EMPTY); + private final Player player; + private final ContextHolder contexts; + private TagResolver[] tagResolvers; + + public PlayerContext(@Nullable Player player, @NotNull ContextHolder contexts) { + this.player = player; + this.contexts = contexts; + } + + @NotNull + public static PlayerContext of(@Nullable Player player, @NotNull ContextHolder contexts) { + return new PlayerContext(player, contexts); + } + + @Nullable + public Player player() { + return this.player; + } + + @NotNull + public ContextHolder contexts() { + return this.contexts; + } + + @NotNull + public TagResolver[] tagResolvers() { + if (this.tagResolvers == null) { + this.tagResolvers = new TagResolver[]{ShiftTag.INSTANCE, ImageTag.INSTANCE, new PlaceholderTag(this.player), new I18NTag(this), new NamedArgumentTag(this)}; + } + return this.tagResolvers; + } +}