mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-25 18:09:27 +00:00
Merge branch 'Xiao-MoMi:dev' into dev
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
templates:
|
||||
default:emoji/basic:
|
||||
content: "<hover:show_text:'<i18n:emoji.tip>'><arg:emoji></hover>"
|
||||
content: "<hover:show_text:'<i18n:emoji.tip>'><white><arg:emoji></white></hover>"
|
||||
default:emoji/addition_info:
|
||||
content: "<hover:show_text:'<i18n:emoji.tip>'>{text}<arg:emoji></hover>"
|
||||
content: "<hover:show_text:'<i18n:emoji.tip>'>{text}<white><arg:emoji></white></hover>"
|
||||
|
||||
emoji:
|
||||
default:emoji_location:
|
||||
|
||||
@@ -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<ItemStack> 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<String> 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<String, Component> tokens = matchTags(raw);
|
||||
if (!tokens.isEmpty()) {
|
||||
for (Map.Entry<String, Component> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<NetWorkUser, NMSPacketEvent, Object> 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<NetWorkUser, NMSPacketEvent, Object> 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<String> 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<NetWorkUser, NMSPacketEvent, Object> CUSTOM_PAYLOAD = (user, event, packet) -> {
|
||||
try {
|
||||
if (!VersionHelper.isVersionNewerThan1_20_5()) return;
|
||||
|
||||
@@ -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<String, String> 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<String, Emoji> emojis = new HashMap<>();
|
||||
Map<String, Component> 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<String, Emoji> 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<String, Component> 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<String, Component> tokens = matchTags(raw);
|
||||
if (!tokens.isEmpty()) {
|
||||
for (Map.Entry<String, Component> 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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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, "");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user