From 182555b70cba1686ec13efe27049f12ab6020abd Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 11 Apr 2025 20:58:41 +0800 Subject: [PATCH 1/3] Update emoji.yml --- .../main/resources/resources/default/configuration/emoji.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml b/bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml index 77f21e382..755db9585 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml @@ -1,8 +1,8 @@ templates: default:emoji/basic: - content: "'>" + content: "'>" default:emoji/addition_info: - content: "'>{text}" + content: "'>{text}" emoji: default:emoji_location: From af7713534cd496f51bf905ef2a8723dfde1565fe Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 11 Apr 2025 22:47:48 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=AE=9E=E9=AA=8C=E6=80=A7=E6=94=B9?= =?UTF-8?q?=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/font/BukkitFontManager.java | 81 +++-------- .../core/font/AbstractFontManager.java | 128 +++++++++++++----- .../font/EmojiComponentProcessResult.java | 14 ++ .../core/font/EmojiTextProcessResult.java | 12 ++ .../craftengine/core/font/FontManager.java | 30 +++- 5 files changed, 168 insertions(+), 97 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/font/EmojiComponentProcessResult.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/font/EmojiTextProcessResult.java 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 0ffafddc4..3d31a83e4 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 @@ -11,17 +11,14 @@ import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils; import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.font.AbstractFontManager; -import net.momirealms.craftengine.core.font.Emoji; -import net.momirealms.craftengine.core.font.EmojiParameters; +import net.momirealms.craftengine.core.font.EmojiComponentProcessResult; +import net.momirealms.craftengine.core.font.EmojiTextProcessResult; import net.momirealms.craftengine.core.font.FontManager; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.CharacterUtils; import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.util.context.ContextHolder; -import net.momirealms.craftengine.core.util.context.PlayerContext; -import org.ahocorasick.trie.Token; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -36,10 +33,11 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.view.AnvilView; -import org.jetbrains.annotations.NotNull; import java.lang.reflect.InvocationTargetException; -import java.util.*; +import java.util.List; +import java.util.Map; +import java.util.UUID; public class BukkitFontManager extends AbstractFontManager implements Listener { private final BukkitCraftEngine plugin; @@ -127,9 +125,12 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { } if (renameText == null || renameText.isEmpty()) return; - + IllegalCharacterProcessResult processResult = processIllegalCharacters(renameText); + if (processResult.has()) { + renameText = processResult.newText(); + } Component itemName = Component.text(renameText); - EmojiReplaceProcessResult replaceProcessResult = processEmojiNoCopy(player, itemName, renameText); + EmojiComponentProcessResult replaceProcessResult = replaceComponentEmoji(itemName, plugin.adapt(player), renameText); if (replaceProcessResult.changed()) { Item wrapped = this.plugin.itemManager().wrap(result); wrapped.customName(AdventureHelper.componentToJson(replaceProcessResult.newText())); @@ -145,7 +146,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { JsonElement json = ComponentUtils.paperAdventureToJsonElement(lines.get(i)); if (json == null) continue; Component line = AdventureHelper.jsonElementToComponent(json); - EmojiReplaceProcessResult result = processEmojiNoCopy(player, line); + EmojiComponentProcessResult result = replaceComponentEmoji(line, plugin.adapt(player)); if (result.changed()) { try { Reflections.method$SignChangeEvent$line.invoke(event, i, ComponentUtils.jsonElementToPaperAdventure(AdventureHelper.componentToJsonElement(result.newText()))); @@ -175,7 +176,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { for (int i = 0; i < pages.size(); i++) { JsonElement json = ComponentUtils.paperAdventureToJsonElement(pages.get(i)); Component page = AdventureHelper.jsonElementToComponent(json); - EmojiReplaceProcessResult result = processEmojiNoCopy(player, page); + EmojiComponentProcessResult result = replaceComponentEmoji(page, plugin.adapt(player)); if (result.changed()) { changed = true; try { @@ -197,19 +198,19 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { try { Object originalMessage = Reflections.field$AsyncChatDecorateEvent$originalMessage.get(event); String rawJsonMessage = ComponentUtils.paperAdventureToJson(originalMessage); - String jsonMessage = replaceJsonEmoji(rawJsonMessage, this.plugin.adapt(player)); - boolean hasChanged = !rawJsonMessage.equals(jsonMessage); + EmojiTextProcessResult processResult = replaceJsonEmoji(rawJsonMessage, this.plugin.adapt(player)); + boolean hasChanged = processResult.replaced(); if (!player.hasPermission(FontManager.BYPASS_CHAT)) { - IllegalCharacterProcessResult result = processIllegalCharacters(jsonMessage); + IllegalCharacterProcessResult result = processIllegalCharacters(processResult.text()); if (result.has()) { Object component = ComponentUtils.jsonToPaperAdventure(result.newText()); Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); } else if (hasChanged) { - Object component = ComponentUtils.jsonToPaperAdventure(jsonMessage); + Object component = ComponentUtils.jsonToPaperAdventure(processResult.text()); Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); } } else if (hasChanged) { - Object component = ComponentUtils.jsonToPaperAdventure(jsonMessage); + Object component = ComponentUtils.jsonToPaperAdventure(processResult.text()); Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); } } catch (IllegalAccessException | InvocationTargetException e) { @@ -217,43 +218,6 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { } } - private EmojiReplaceProcessResult processEmojiNoCopy(Player player, Component text) { - return this.processEmojiNoCopy(player, text, AdventureHelper.plainTextContent(text)); - } - - private EmojiReplaceProcessResult processEmojiNoCopy(Player player, Component text, @NotNull String raw) { - boolean replaced = false; - Set processed = new HashSet<>(); - int times = 0; - for (Token token : super.emojiKeywordTrie.tokenize(raw)) { - if (!token.isMatch()) - continue; - String fragment = token.getFragment(); - if (processed.contains(fragment)) - continue; - Emoji emoji = super.emojiMapper.get(token.getFragment()); - if (emoji == null || (emoji.permission() != null && !player.hasPermission(Objects.requireNonNull(emoji.permission())))) - continue; - text = text.replaceText(builder -> builder.matchLiteral(token.getFragment()) - .replacement(AdventureHelper.miniMessage().deserialize( - emoji.content(), - PlayerContext.of(plugin.adapt(player), - ContextHolder.builder() - .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) - .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) - .build() - ).tagResolvers() - ))); - replaced = true; - processed.add(fragment); - if (++times >= Config.maxEmojisPerParse()) { - break; - } - } - if (!replaced) return EmojiReplaceProcessResult.failed(); - return EmojiReplaceProcessResult.success(text); - } - private IllegalCharacterProcessResult processIllegalCharacters(String raw) { boolean hasIllegal = false; // replace illegal image usage @@ -300,15 +264,4 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { return new IllegalCharacterProcessResult(false, null); } } - - public record EmojiReplaceProcessResult(boolean changed, Component newText) { - - public static EmojiReplaceProcessResult success(Component newText) { - return new EmojiReplaceProcessResult(true, newText); - } - - public static EmojiReplaceProcessResult failed() { - return new EmojiReplaceProcessResult(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 dc3cd1e69..a98476665 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 @@ -14,11 +14,15 @@ 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; +import org.jetbrains.annotations.NotNull; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; public abstract class AbstractFontManager implements FontManager { private final CraftEngine plugin; @@ -104,14 +108,20 @@ public abstract class AbstractFontManager implements FontManager { } @Override - public String replaceMiniMessageEmoji(String miniMessage, Player player) { - if (this.emojiKeywordTrie == null) { - return miniMessage; + public EmojiTextProcessResult replaceMiniMessageEmoji(@NotNull String miniMessage, Player player, int maxTimes) { + if (this.emojiKeywordTrie == null || maxTimes <= 0) { + return EmojiTextProcessResult.notReplaced(miniMessage); } + Map replacements = new HashMap<>(); for (Token token : this.emojiKeywordTrie.tokenize(miniMessage)) { - if (!token.isMatch()) continue; - Emoji emoji = this.emojiMapper.get(token.getFragment()); - if (emoji == null) continue; + if (!token.isMatch()) + continue; + String fragment = token.getFragment(); + if (replacements.containsKey(fragment)) + continue; + Emoji emoji = this.emojiMapper.get(fragment); + if (emoji == null || (player != null && emoji.permission() != null && !player.hasPermission(emoji.permission()))) + continue; Component content = AdventureHelper.miniMessage().deserialize( emoji.content(), PlayerContext.of(player, ContextHolder.builder() @@ -119,40 +129,96 @@ public abstract class AbstractFontManager implements FontManager { .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) .build()).tagResolvers() ); - miniMessage = miniMessage.replace(token.getFragment(), AdventureHelper.componentToMiniMessage(content)); + replacements.put(fragment, AdventureHelper.componentToMiniMessage(content)); } - return miniMessage; + if (replacements.isEmpty()) return EmojiTextProcessResult.notReplaced(miniMessage); + String regex = replacements.keySet().stream() + .map(Pattern::quote) + .collect(Collectors.joining("|")); + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(miniMessage); + StringBuilder sb = new StringBuilder(); + int count = 0; + while (matcher.find() && count < maxTimes) { + String key = matcher.group(); + String replacement = replacements.get(key); + if (replacement != null) { + matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement)); + count++; + } else { + // should not reach this + matcher.appendReplacement(sb, Matcher.quoteReplacement(matcher.group())); + } + } + matcher.appendTail(sb); + return EmojiTextProcessResult.replaced(sb.toString()); } @Override - public String replaceJsonEmoji(String jsonText, Player player) { + public EmojiTextProcessResult replaceJsonEmoji(@NotNull String jsonText, Player player, int maxTimes) { if (this.emojiKeywordTrie == null) { - return jsonText; + return EmojiTextProcessResult.notReplaced(jsonText); } - Map emojis = new HashMap<>(); + 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())) { + if (!token.isMatch()) 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()) - )); + String fragment = token.getFragment(); + if (emojis.containsKey(fragment)) continue; + Emoji emoji = this.emojiMapper.get(fragment); + if (emoji == null || (player != null && emoji.permission() != null && !player.hasPermission(emoji.permission()))) + continue; + emojis.put(fragment, AdventureHelper.miniMessage().deserialize( + emoji.content(), + PlayerContext.of(player, ContextHolder.builder() + .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) + .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) + .build()).tagResolvers()) + ); + if (emojis.size() >= maxTimes) break; } - return AdventureHelper.componentToJson(component); + if (emojis.isEmpty()) return EmojiTextProcessResult.notReplaced(jsonText); + Component component = AdventureHelper.jsonToComponent(jsonText); + String patternString = emojis.keySet().stream() + .map(Pattern::quote) + .collect(Collectors.joining("|")); + component = component.replaceText(builder -> builder.times(maxTimes) + .match(Pattern.compile(patternString)) + .replacement((result, b) -> emojis.get(result.group()))); + return EmojiTextProcessResult.replaced(AdventureHelper.componentToJson(component)); + } + + @Override + public EmojiComponentProcessResult replaceComponentEmoji(@NotNull Component text, Player player, @NotNull String raw, int maxTimes) { + Map emojis = new HashMap<>(); + for (Token token : this.emojiKeywordTrie.tokenize(raw)) { + if (!token.isMatch()) + continue; + String fragment = token.getFragment(); + if (emojis.containsKey(fragment)) + continue; + Emoji emoji = this.emojiMapper.get(token.getFragment()); + if (emoji == null || (player != null && emoji.permission() != null && !player.hasPermission(Objects.requireNonNull(emoji.permission())))) + continue; + emojis.put(fragment, AdventureHelper.miniMessage().deserialize( + emoji.content(), + PlayerContext.of(player, + ContextHolder.builder() + .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) + .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) + .build() + ).tagResolvers() + )); + if (emojis.size() >= maxTimes) break; + } + if (emojis.isEmpty()) return EmojiComponentProcessResult.failed(); + String patternString = emojis.keySet().stream() + .map(Pattern::quote) + .collect(Collectors.joining("|")); + text = text.replaceText(builder -> builder.times(maxTimes) + .match(Pattern.compile(patternString)) + .replacement((result, b) -> emojis.get(result.group()))); + return EmojiComponentProcessResult.success(text); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/EmojiComponentProcessResult.java b/core/src/main/java/net/momirealms/craftengine/core/font/EmojiComponentProcessResult.java new file mode 100644 index 000000000..dbffb0998 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/font/EmojiComponentProcessResult.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.core.font; + +import net.kyori.adventure.text.Component; + +public record EmojiComponentProcessResult(boolean changed, Component newText) { + + public static EmojiComponentProcessResult success(Component newText) { + return new EmojiComponentProcessResult(true, newText); + } + + public static EmojiComponentProcessResult failed() { + return new EmojiComponentProcessResult(false, null); + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/EmojiTextProcessResult.java b/core/src/main/java/net/momirealms/craftengine/core/font/EmojiTextProcessResult.java new file mode 100644 index 000000000..8813cbdf0 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/font/EmojiTextProcessResult.java @@ -0,0 +1,12 @@ +package net.momirealms.craftengine.core.font; + +public record EmojiTextProcessResult(boolean replaced, String text) { + + public static EmojiTextProcessResult replaced(String text) { + return new EmojiTextProcessResult(true, text); + } + + public static EmojiTextProcessResult notReplaced(String text) { + return new EmojiTextProcessResult(false, text); + } +} 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 ca001f040..62f4eafe6 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 @@ -3,10 +3,14 @@ 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.Config; import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.CharacterUtils; import net.momirealms.craftengine.core.util.FormatUtils; import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Map; @@ -23,11 +27,33 @@ public interface FontManager extends Manageable { String stripTags(String text); + default EmojiComponentProcessResult replaceComponentEmoji(@NotNull Component text, @Nullable Player player) { + return replaceComponentEmoji(text, player, Config.maxEmojisPerParse()); + } + + default EmojiComponentProcessResult replaceComponentEmoji(@NotNull Component text, @Nullable Player player, int maxTimes) { + return replaceComponentEmoji(text, player, AdventureHelper.plainTextContent(text), maxTimes); + } + + default EmojiComponentProcessResult replaceComponentEmoji(@NotNull Component text, @Nullable Player player, String raw) { + return replaceComponentEmoji(text, player, raw, Config.maxEmojisPerParse()); + } + + EmojiComponentProcessResult replaceComponentEmoji(@NotNull Component text, @Nullable Player player, @NotNull String raw, int maxTimes); + ConfigSectionParser[] parsers(); - String replaceMiniMessageEmoji(String miniMessage, Player player); + default EmojiTextProcessResult replaceMiniMessageEmoji(@NotNull String miniMessage, @Nullable Player player) { + return replaceMiniMessageEmoji(miniMessage, player, Config.maxEmojisPerParse()); + } - String replaceJsonEmoji(String jsonText, Player player); + EmojiTextProcessResult replaceMiniMessageEmoji(@NotNull String miniMessage, @Nullable Player player, int maxTimes); + + default EmojiTextProcessResult replaceJsonEmoji(@NotNull String json, @Nullable Player player) { + return replaceJsonEmoji(json, player, Config.maxEmojisPerParse()); + } + + EmojiTextProcessResult replaceJsonEmoji(@NotNull String jsonText, @Nullable Player player, int maxTimes); boolean isDefaultFontInUse(); From b1f63d8a316ec72f51abe3b209c6d16972979ad1 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 11 Apr 2025 23:03:53 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=86=8D=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/font/BukkitFontManager.java | 60 +------------------ .../plugin/network/PacketConsumers.java | 49 +++++---------- .../core/font/AbstractFontManager.java | 53 +++++++++++----- .../craftengine/core/font/FontManager.java | 8 ++- .../font/IllegalCharacterProcessResult.java | 12 ++++ 5 files changed, 73 insertions(+), 109 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/font/IllegalCharacterProcessResult.java 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 3d31a83e4..676e8909f 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 @@ -10,10 +10,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils; import net.momirealms.craftengine.bukkit.util.Reflections; -import net.momirealms.craftengine.core.font.AbstractFontManager; -import net.momirealms.craftengine.core.font.EmojiComponentProcessResult; -import net.momirealms.craftengine.core.font.EmojiTextProcessResult; -import net.momirealms.craftengine.core.font.FontManager; +import net.momirealms.craftengine.core.font.*; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.AdventureHelper; @@ -95,7 +92,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { if (!event.getPlayer().hasPermission(FontManager.BYPASS_COMMAND)) { IllegalCharacterProcessResult result = processIllegalCharacters(event.getMessage()); if (result.has()) { - event.setMessage(result.newText()); + event.setMessage(result.text()); } } } @@ -125,10 +122,6 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { } if (renameText == null || renameText.isEmpty()) return; - IllegalCharacterProcessResult processResult = processIllegalCharacters(renameText); - if (processResult.has()) { - renameText = processResult.newText(); - } Component itemName = Component.text(renameText); EmojiComponentProcessResult replaceProcessResult = replaceComponentEmoji(itemName, plugin.adapt(player), renameText); if (replaceProcessResult.changed()) { @@ -203,7 +196,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { if (!player.hasPermission(FontManager.BYPASS_CHAT)) { IllegalCharacterProcessResult result = processIllegalCharacters(processResult.text()); if (result.has()) { - Object component = ComponentUtils.jsonToPaperAdventure(result.newText()); + Object component = ComponentUtils.jsonToPaperAdventure(result.text()); Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); } else if (hasChanged) { Object component = ComponentUtils.jsonToPaperAdventure(processResult.text()); @@ -217,51 +210,4 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { throw new RuntimeException(e); } } - - private IllegalCharacterProcessResult processIllegalCharacters(String raw) { - boolean hasIllegal = false; - // replace illegal image usage - Map tokens = matchTags(raw); - if (!tokens.isEmpty()) { - for (Map.Entry entry : tokens.entrySet()) { - raw = raw.replace(entry.getKey(), "*"); - hasIllegal = true; - } - } - - if (this.isDefaultFontInUse()) { - // replace illegal codepoint - char[] chars = raw.toCharArray(); - int[] codepoints = CharacterUtils.charsToCodePoints(chars); - int[] newCodepoints = new int[codepoints.length]; - - for (int i = 0; i < codepoints.length; i++) { - int codepoint = codepoints[i]; - if (!isIllegalCodepoint(codepoint)) { - newCodepoints[i] = codepoint; - } else { - newCodepoints[i] = '*'; - hasIllegal = true; - } - } - - if (hasIllegal) { - return IllegalCharacterProcessResult.has(new String(newCodepoints, 0, newCodepoints.length)); - } - } else if (hasIllegal) { - 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/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 6fd9b5a9d..85708dc2a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -20,6 +20,7 @@ import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.font.FontManager; +import net.momirealms.craftengine.core.font.IllegalCharacterProcessResult; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.network.ConnectionState; @@ -1640,21 +1641,21 @@ public class PacketConsumers { public static final TriConsumer RENAME_ITEM = (user, event, packet) -> { try { if (!Config.filterAnvil()) return; + if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_ANVIL)) { + return; + } String message = (String) Reflections.field$ServerboundRenameItemPacket$name.get(packet); if (message != null && !message.isEmpty()) { - FontManager manager = CraftEngine.instance().imageManager(); - if (!manager.isDefaultFontInUse()) return; // check bypass - if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_ANVIL)) { - return; - } - runIfContainsIllegalCharacter(message, manager, (s) -> { + FontManager manager = CraftEngine.instance().imageManager(); + IllegalCharacterProcessResult result = manager.processIllegalCharacters(message); + if (result.has()) { try { - Reflections.field$ServerboundRenameItemPacket$name.set(packet, s); + Reflections.field$ServerboundRenameItemPacket$name.set(packet, result.text()); } catch (ReflectiveOperationException e) { CraftEngine.instance().logger().warn("Failed to replace chat", e); } - }); + } } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ServerboundRenameItemPacket", e); @@ -1665,20 +1666,19 @@ public class PacketConsumers { public static final TriConsumer SIGN_UPDATE = (user, event, packet) -> { try { if (!Config.filterSign()) return; - String[] lines = (String[]) Reflections.field$ServerboundSignUpdatePacket$lines.get(packet); - FontManager manager = CraftEngine.instance().imageManager(); - if (!manager.isDefaultFontInUse()) return; // check bypass if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_SIGN)) { return; } + String[] lines = (String[]) Reflections.field$ServerboundSignUpdatePacket$lines.get(packet); + FontManager manager = CraftEngine.instance().imageManager(); + if (!manager.isDefaultFontInUse()) return; for (int i = 0; i < lines.length; i++) { String line = lines[i]; if (line != null && !line.isEmpty()) { - try { - int lineIndex = i; - runIfContainsIllegalCharacter(line, manager, (s) -> lines[lineIndex] = s); - } catch (Exception ignore) { + IllegalCharacterProcessResult result = manager.processIllegalCharacters(line); + if (result.has()) { + lines[i] = result.text(); } } } @@ -1763,25 +1763,6 @@ public class PacketConsumers { return hasIllegal ? Pair.of(true, new String(newCodepoints, 0, newCodepoints.length)) : Pair.of(false, original); } - private static void runIfContainsIllegalCharacter(String string, FontManager manager, Consumer callback) { - if (string.isEmpty()) return; - int[] codepoints = CharacterUtils.charsToCodePoints(string.toCharArray()); - int[] newCodepoints = new int[codepoints.length]; - boolean hasIllegal = false; - for (int i = 0; i < codepoints.length; i++) { - int codepoint = codepoints[i]; - if (!manager.isIllegalCodepoint(codepoint)) { - newCodepoints[i] = codepoint; - } else { - newCodepoints[i] = '*'; - hasIllegal = true; - } - } - if (hasIllegal) { - callback.accept(new String(newCodepoints, 0, newCodepoints.length)); - } - } - public static final TriConsumer CUSTOM_PAYLOAD = (user, event, packet) -> { try { if (!VersionHelper.isVersionNewerThan1_20_5()) return; 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 a98476665..d2dd94945 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 @@ -91,22 +91,6 @@ public abstract class AbstractFontManager implements FontManager { return tags; } - @Override - public String stripTags(String text) { - if (this.imageTagTrie == null) { - return text; - } - StringBuilder builder = new StringBuilder(); - for (Token token : this.imageTagTrie.tokenize(text)) { - if (token.isMatch()) { - builder.append("*"); - } else { - builder.append(token.getFragment()); - } - } - return builder.toString(); - } - @Override public EmojiTextProcessResult replaceMiniMessageEmoji(@NotNull String miniMessage, Player player, int maxTimes) { if (this.emojiKeywordTrie == null || maxTimes <= 0) { @@ -221,6 +205,43 @@ public abstract class AbstractFontManager implements FontManager { return EmojiComponentProcessResult.success(text); } + @Override + public IllegalCharacterProcessResult processIllegalCharacters(String raw, char replacement) { + boolean hasIllegal = false; + // replace illegal image usage + Map tokens = matchTags(raw); + if (!tokens.isEmpty()) { + for (Map.Entry entry : tokens.entrySet()) { + raw = raw.replace(entry.getKey(), String.valueOf(replacement)); + hasIllegal = true; + } + } + + if (this.isDefaultFontInUse()) { + // replace illegal codepoint + char[] chars = raw.toCharArray(); + int[] codepoints = CharacterUtils.charsToCodePoints(chars); + int[] newCodepoints = new int[codepoints.length]; + + for (int i = 0; i < codepoints.length; i++) { + int codepoint = codepoints[i]; + if (!isIllegalCodepoint(codepoint)) { + newCodepoints[i] = codepoint; + } else { + newCodepoints[i] = replacement; + hasIllegal = true; + } + } + + if (hasIllegal) { + return IllegalCharacterProcessResult.has(new String(newCodepoints, 0, newCodepoints.length)); + } + } else if (hasIllegal) { + return IllegalCharacterProcessResult.has(raw); + } + return IllegalCharacterProcessResult.not(); + } + @Override public ConfigSectionParser[] parsers() { return new ConfigSectionParser[] {this.imageParser, this.emojiParser}; 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 62f4eafe6..8da06423e 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 @@ -25,8 +25,6 @@ public interface FontManager extends Manageable { String BYPASS_COMMAND = "craftengine.filter.bypass.command"; String BYPASS_ANVIL = "craftengine.filter.bypass.anvil"; - String stripTags(String text); - default EmojiComponentProcessResult replaceComponentEmoji(@NotNull Component text, @Nullable Player player) { return replaceComponentEmoji(text, player, Config.maxEmojisPerParse()); } @@ -41,6 +39,12 @@ public interface FontManager extends Manageable { EmojiComponentProcessResult replaceComponentEmoji(@NotNull Component text, @Nullable Player player, @NotNull String raw, int maxTimes); + default IllegalCharacterProcessResult processIllegalCharacters(String raw) { + return processIllegalCharacters(raw, '*'); + } + + IllegalCharacterProcessResult processIllegalCharacters(String raw, char replacement); + ConfigSectionParser[] parsers(); default EmojiTextProcessResult replaceMiniMessageEmoji(@NotNull String miniMessage, @Nullable Player player) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/IllegalCharacterProcessResult.java b/core/src/main/java/net/momirealms/craftengine/core/font/IllegalCharacterProcessResult.java new file mode 100644 index 000000000..f2568de6e --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/font/IllegalCharacterProcessResult.java @@ -0,0 +1,12 @@ +package net.momirealms.craftengine.core.font; + +public record IllegalCharacterProcessResult(boolean has, String text) { + + public static IllegalCharacterProcessResult has(String text) { + return new IllegalCharacterProcessResult(true, text); + } + + public static IllegalCharacterProcessResult not() { + return new IllegalCharacterProcessResult(false, ""); + } +} \ No newline at end of file