diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitImageManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitImageManager.java new file mode 100644 index 000000000..be5a5ebdf --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitImageManager.java @@ -0,0 +1,94 @@ +package net.momirealms.craftengine.bukkit.font; + +import com.google.gson.JsonElement; +import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent; +import io.papermc.paper.event.player.AsyncChatDecorateEvent; +import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.font.AbstractImageManager; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.CharacterUtils; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.server.ServerCommandEvent; + +import java.lang.reflect.InvocationTargetException; + +public class BukkitImageManager extends AbstractImageManager implements Listener { + private final BukkitCraftEngine plugin; + private final Object serializer; + + public BukkitImageManager(BukkitCraftEngine plugin) { + super(plugin); + this.plugin = plugin; + try { + Object builder = Reflections.method$GsonComponentSerializer$builder.invoke(null); + this.serializer = Reflections.method$GsonComponentSerializer$Builder$build.invoke(builder); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Override + public void delayedInit() { + Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap()); + } + + @Override + public void disable() { + super.disable(); + HandlerList.unregisterAll(this); + } + + @EventHandler + @SuppressWarnings("UnstableApiUsage") + public void onChat(AsyncChatDecorateEvent event) { + if (event.player() == null) return; + this.ProcessChatMessages(event); + } + + @EventHandler + @SuppressWarnings("UnstableApiUsage") + public void onChatCommand(AsyncChatCommandDecorateEvent event) { + if (event.player() == null) return; + this.ProcessChatMessages(event); + } + + @EventHandler + public void onCommand(PlayerCommandPreprocessEvent event) { + event.setMessage(processIllegalString(event.getMessage())); + } + + @SuppressWarnings("UnstableApiUsage") + private void ProcessChatMessages(AsyncChatDecorateEvent event) { + try { + Object originalMessage = Reflections.clazz$AdventureComponent.cast(Reflections.field$AsyncChatDecorateEvent$originalMessage.get(event)); + JsonElement json = (JsonElement) Reflections.method$GsonComponentSerializer$serializeToTree.invoke(serializer, originalMessage); + String jsonMessage = AdventureHelper.jsonElementToStringJson(json); + if (!this.isDefaultFontInUse()) return; + String str = processIllegalString(jsonMessage); + Object component = Reflections.method$ComponentSerializer$deserialize.invoke(serializer, str); + Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + private String processIllegalString(String string) { + char[] chars = string.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 (!this.isIllegalCharacter(codepoint)) { + newCodepoints[i] = codepoint; + } else { + newCodepoints[i] = '*'; + } + } + return new String(newCodepoints, 0, newCodepoints.length); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java index 6104cfcf5..28ab754da 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java @@ -5,6 +5,7 @@ import net.momirealms.craftengine.bukkit.api.event.CraftEngineReloadEvent; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; +import net.momirealms.craftengine.bukkit.font.BukkitImageManager; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.item.behavior.BukkitItemBehaviors; import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager; @@ -138,6 +139,7 @@ public class BukkitCraftEngine extends CraftEngine { super.worldManager = new BukkitWorldManager(this); super.soundManager = new BukkitSoundManager(this); super.vanillaLootManager = new BukkitVanillaLootManager(this); + this.imageManager = new BukkitImageManager(this); super.enable(); // tick task if (VersionHelper.isFolia()) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 01a98be91..e47b3715b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -125,11 +125,11 @@ public class BukkitNetworkManager implements NetworkManager, Listener { registerNMSPacketConsumer(PacketConsumers.MOVE_ENTITY, Reflections.clazz$ClientboundMoveEntityPacket$Pos); registerNMSPacketConsumer(PacketConsumers.PICK_ITEM_FROM_ENTITY, Reflections.clazz$ServerboundPickItemFromEntityPacket); registerNMSPacketConsumer(PacketConsumers.SOUND, Reflections.clazz$ClientboundSoundPacket); - registerNMSPacketConsumer(PacketConsumers.CHAT, Reflections.clazz$ServerboundChatPacket); + // registerNMSPacketConsumer(PacketConsumers.CHAT, Reflections.clazz$ServerboundChatPacket); registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, Reflections.clazz$ServerboundRenameItemPacket); registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket); - registerNMSPacketConsumer(PacketConsumers.COMMAND_SIGNED, Reflections.clazz$ServerboundChatCommandSignedPacket); - registerNMSPacketConsumer(PacketConsumers.COMMAND, Reflections.clazz$ServerboundChatCommandPacket); + // registerNMSPacketConsumer(PacketConsumers.COMMAND_SIGNED, Reflections.clazz$ServerboundChatCommandSignedPacket); + // registerNMSPacketConsumer(PacketConsumers.COMMAND, Reflections.clazz$ServerboundChatCommandPacket); registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket()); registerByteBufPacketConsumer(PacketConsumers.LEVEL_PARTICLE, this.packetIds.clientboundLevelParticlesPacket()); 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 61033587e..73e940c4c 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 @@ -777,6 +777,7 @@ public class PacketConsumers { }; private static void runIfContainsIllegalCharacter(String string, ImageManager manager, Consumer callback) { + //noinspection DuplicatedCode char[] chars = string.toCharArray(); int[] codepoints = CharacterUtils.charsToCodePoints(chars); int[] newCodepoints = new int[codepoints.length]; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index fba5c7c3a..a859275ad 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java @@ -1,12 +1,14 @@ package net.momirealms.craftengine.bukkit.util; import com.google.common.collect.ImmutableList; +import com.google.gson.JsonElement; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.MessageToByteEncoder; +import io.papermc.paper.event.player.AsyncChatDecorateEvent; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.util.ReflectionUtils; import net.momirealms.craftengine.core.util.VersionHelper; @@ -5265,4 +5267,64 @@ public class Reflections { public static final Constructor constructor$ServerboundChatCommandPacket = Optional.ofNullable(clazz$ServerboundChatCommandPacket) .map(it -> ReflectionUtils.getConstructor(it, String.class)) .orElse(null); + + @SuppressWarnings("UnstableApiUsage") + public static final Field field$AsyncChatDecorateEvent$originalMessage = requireNonNull( + ReflectionUtils.getDeclaredField(AsyncChatDecorateEvent.class, "originalMessage") + ); + + public static final Class clazz$ComponentSerializer = requireNonNull( + ReflectionUtils.getClazz( + "net{}kyori{}adventure{}text{}serializer{}ComponentSerializer".replace("{}", ".") + ) + ); + + public static final Class clazz$GsonComponentSerializer = requireNonNull( + ReflectionUtils.getClazz( + "net{}kyori{}adventure{}text{}serializer{}gson{}GsonComponentSerializer".replace("{}", ".") + ) + ); + + public static final Class clazz$GsonComponentSerializer$Builder = requireNonNull( + ReflectionUtils.getClazz( + "net{}kyori{}adventure{}text{}serializer{}gson{}GsonComponentSerializer$Builder".replace("{}", ".") + ) + ); + + public static final Class clazz$AdventureComponent = requireNonNull( + ReflectionUtils.getClazz( + "net{}kyori{}adventure{}text{}Component".replace("{}", ".") + ) + ); + + public static final Method method$GsonComponentSerializer$builder = requireNonNull( + ReflectionUtils.getMethod( + clazz$GsonComponentSerializer, clazz$GsonComponentSerializer$Builder + ) + ); + + public static final Method method$GsonComponentSerializer$Builder$build = requireNonNull( + ReflectionUtils.getMethod( + clazz$GsonComponentSerializer$Builder, clazz$GsonComponentSerializer + ) + ); + + public static final Method method$GsonComponentSerializer$serializeToTree = requireNonNull( + ReflectionUtils.getMethod( + clazz$GsonComponentSerializer, JsonElement.class, clazz$AdventureComponent + ) + ); + + public static final Method method$ComponentSerializer$deserialize = requireNonNull( + ReflectionUtils.getMethod( + clazz$ComponentSerializer, Object.class, Object.class + ) + ); + + @SuppressWarnings("UnstableApiUsage") + public static final Method method$AsyncChatDecorateEvent$result = requireNonNull( + ReflectionUtils.getMethod( + AsyncChatDecorateEvent.class, void.class, clazz$AdventureComponent + ) + ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/ImageManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractImageManager.java similarity index 98% rename from core/src/main/java/net/momirealms/craftengine/core/font/ImageManagerImpl.java rename to core/src/main/java/net/momirealms/craftengine/core/font/AbstractImageManager.java index ace8f47e0..59c46871b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/ImageManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractImageManager.java @@ -12,7 +12,7 @@ import java.nio.file.Path; import java.util.*; import java.util.function.BiFunction; -public class ImageManagerImpl implements ImageManager { +public abstract class AbstractImageManager implements ImageManager { private final CraftEngine plugin; // namespace:font font private final HashMap fonts = new HashMap<>(); @@ -22,7 +22,7 @@ public class ImageManagerImpl implements ImageManager { private OffsetFont offsetFont; - public ImageManagerImpl(CraftEngine plugin) { + public AbstractImageManager(CraftEngine plugin) { this.plugin = plugin; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/ImageManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/ImageManager.java index ac114d578..7c38e8643 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/ImageManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/ImageManager.java @@ -68,4 +68,6 @@ public interface ImageManager extends Reloadable, ConfigSectionParser { default int loadingSequence() { return LoadingSequence.FONT; } + + void delayedInit(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java index 931804201..62626b2c3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.plugin; import net.momirealms.craftengine.core.block.BlockManager; import net.momirealms.craftengine.core.entity.furniture.FurnitureManager; import net.momirealms.craftengine.core.font.ImageManager; -import net.momirealms.craftengine.core.font.ImageManagerImpl; +import net.momirealms.craftengine.core.font.AbstractImageManager; import net.momirealms.craftengine.core.item.ItemManager; import net.momirealms.craftengine.core.item.recipe.RecipeManager; import net.momirealms.craftengine.core.loot.VanillaLootManager; @@ -120,7 +120,6 @@ public abstract class CraftEngine implements Plugin { @Override public void enable() { this.networkManager.enable(); - this.imageManager = new ImageManagerImpl(this); this.templateManager = new TemplateManagerImpl(this); this.itemBrowserManager = new ItemBrowserManagerImpl(this); this.commandManager.registerDefaultFeatures(); @@ -135,6 +134,7 @@ public abstract class CraftEngine implements Plugin { this.worldManager.delayedInit(); this.packManager.delayedInit(); this.furnitureManager.delayedInit(); + this.imageManager.delayedInit(); }); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java index d75919cbc..ef6d177ae 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.util; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +import com.google.gson.JsonElement; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.key.Key; import net.kyori.adventure.sound.Sound; @@ -182,6 +183,24 @@ public class AdventureHelper { return getGson().serialize(component); } + /** + * Converts a JsonElement to a Component. + * @param gson the JsonElement to convert + * @return the resulting Component + */ + public static Component jsonElementToComponent(JsonElement gson) { + return GsonComponentSerializer.gson().deserializeFromTree(gson); + } + + /** + * Converts a JsonElement to a JSON string. + * @param gson the JsonElement to convert + * @return the JSON string representation + */ + public static String jsonElementToStringJson(JsonElement gson) { + return componentToJson(jsonElementToComponent(gson)); + } + /** * Checks if a character is a legacy color code. *