9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-27 02:49:15 +00:00

Merge branch 'Xiao-MoMi:dev' into dev

This commit is contained in:
jhqwqmc
2025-04-11 23:06:16 +08:00
committed by GitHub
8 changed files with 236 additions and 201 deletions

View File

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

View File

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