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: 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 0311ef73f..866bff3f0 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,18 +10,12 @@ 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.Emoji; -import net.momirealms.craftengine.core.font.EmojiParameters; -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; 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 +30,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; @@ -112,7 +107,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()); } } } @@ -142,9 +137,8 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { } if (renameText == null || renameText.isEmpty()) return; - 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())); @@ -160,7 +154,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()))); @@ -190,7 +184,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 { @@ -212,118 +206,23 @@ 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()); + Object component = ComponentUtils.jsonToPaperAdventure(result.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); } } 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) { throw new RuntimeException(e); } } - - 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 - 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); - } - } - - 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/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 dc3cd1e69..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 @@ -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; @@ -88,30 +92,20 @@ public abstract class AbstractFontManager implements FontManager { } @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 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 +113,133 @@ 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 + 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 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..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 @@ -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; @@ -21,13 +25,39 @@ 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()); + } + + 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); + + default IllegalCharacterProcessResult processIllegalCharacters(String raw) { + return processIllegalCharacters(raw, '*'); + } + + IllegalCharacterProcessResult processIllegalCharacters(String raw, char replacement); 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(); 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