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 67591a0a3..c37cec1c3 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 @@ -127,6 +127,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener { registerNMSPacketConsumer(PacketConsumers.SOUND, Reflections.clazz$ClientboundSoundPacket); registerNMSPacketConsumer(PacketConsumers.CHAT, Reflections.clazz$ServerboundChatPacket); registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, Reflections.clazz$ServerboundRenameItemPacket); + registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket); registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket()); registerByteBufPacketConsumer(PacketConsumers.LEVEL_PARTICLE, this.packetIds.clientboundLevelParticlesPacket()); @@ -267,6 +268,20 @@ public class BukkitNetworkManager implements NetworkManager, Listener { this.packetsConsumer.accept(player.serverPlayer(), packet); } + public void receivePacket(@NotNull NetWorkUser player, Object packet) { + Channel channel = player.nettyChannel(); + List handlerNames = channel.pipeline().names(); + if (handlerNames.contains("decompress")) { + channel.pipeline().context("decompress").fireChannelRead(packet); + } else { + if (handlerNames.contains("decrypt")) { + channel.pipeline().context("decrypt").fireChannelRead(packet); + } else { + channel.pipeline().context("splitter").fireChannelRead(packet); + } + } + } + private void injectServerChannel(Channel serverChannel) { ChannelPipeline pipeline = serverChannel.pipeline(); ChannelHandler connectionHandler = pipeline.get(CONNECTION_HANDLER_NAME); 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 6deef92bc..806bbb19c 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 @@ -3,6 +3,8 @@ package net.momirealms.craftengine.bukkit.plugin.network; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import it.unimi.dsi.fastutil.ints.IntList; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent; import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent; @@ -33,9 +35,8 @@ import org.bukkit.util.RayTraceResult; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Map; -import java.util.Objects; +import java.time.Instant; +import java.util.*; import java.util.function.BiConsumer; public class PacketConsumers { @@ -670,17 +671,19 @@ public class PacketConsumers { if (message != null && !message.isEmpty()) { ImageManager manager = CraftEngine.instance().imageManager(); if (!manager.isDefaultFontInUse()) return; - char[] chars = message.toCharArray(); try { - int[] codepoints = CharacterUtils.charsToCodePoints(chars); - for (int codepoint : codepoints) { - if (manager.isIllegalCharacter(codepoint)) { - event.setCancelled(true); - return; - } - } - } catch (Exception ignore) { - } + String str = replaceIllegalString(message, manager); + if (message.equals(str)) return; + event.setCancelled(true); + Object newPacket = Reflections.constructor$ServerboundChatPacket.newInstance( + str, + Reflections.field$ServerboundChatPacket$timeStamp.get(packet), + Reflections.field$ServerboundChatPacket$salt.get(packet), + Reflections.field$ServerboundChatPacket$signature.get(packet), + Reflections.field$ServerboundChatPacket$lastSeenMessages.get(packet) + ); + user.receivePacket(newPacket); + } catch (Exception ignore) {} } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ServerboundChatPacket", e); @@ -694,25 +697,49 @@ public class PacketConsumers { if (message != null && !message.isEmpty()) { ImageManager manager = CraftEngine.instance().imageManager(); if (!manager.isDefaultFontInUse()) return; - char[] chars = message.toCharArray(); try { - int[] codepoints = CharacterUtils.charsToCodePoints(chars); - int[] newCodepoints = new int[codepoints.length]; - for (int i = 0; i < codepoints.length; i++) { - int codepoint = codepoints[i]; - if (!manager.isIllegalCharacter(codepoint)) { - newCodepoints[i] = codepoint; - } else { - newCodepoints[i] = '*'; - } - } - String str = new String(newCodepoints, 0, newCodepoints.length); + String str = replaceIllegalString(message, manager); Reflections.field$ServerboundRenameItemPacket$name.set(packet, str); - } catch (Exception ignore) { - } + } catch (Exception ignore) {} } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ServerboundRenameItemPacket", e); } }; + + // we handle it on packet level to prevent it from being captured by plugins + public static final TriConsumer SIGN_UPDATE = (user, event, packet) -> { + try { + String[] lines = (String[]) Reflections.field$ServerboundSignUpdatePacket$lines.get(packet); + ImageManager manager = CraftEngine.instance().imageManager(); + for (int i = 0; i < lines.length; i++) { + String line = lines[i]; + if (line != null && !line.isEmpty()) { + if (!manager.isDefaultFontInUse()) return; + try { + String str = replaceIllegalString(line, manager); + if (line.equals(str)) continue; + lines[i] = str; + } catch (Exception ignore){} + } + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundSignUpdatePacket", e); + } + }; + + private static String replaceIllegalString(String string, ImageManager manager) { + 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 (!manager.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/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index dba6359ea..1c292d9e3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -231,6 +231,11 @@ public class BukkitServerPlayer extends Player { this.plugin.networkManager().sendPacket(this, packet, immediately); } + @Override + public void receivePacket(Object packet) { + this.plugin.networkManager().receivePacket(this, packet); + } + @Override public ConnectionState decoderState() { return decoderState; 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 662108a21..71ffe4d60 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 @@ -7,6 +7,7 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.MessageToByteEncoder; +import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.util.ReflectionUtils; import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Location; @@ -26,6 +27,7 @@ import sun.misc.Unsafe; import java.io.BufferedReader; import java.lang.reflect.*; +import java.time.Instant; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; @@ -134,9 +136,9 @@ public class Reflections { ); public static final Constructor constructor$ClientboundSystemChatPacket = requireNonNull( - ReflectionUtils.getConstructor( - clazz$ClientboundSystemChatPacket, clazz$Component, boolean.class - ) + VersionHelper.isVersionNewerThan1_20_4() + ? ReflectionUtils.getConstructor(clazz$ClientboundSystemChatPacket, clazz$Component, boolean.class) + : ReflectionUtils.getConstructor(clazz$ClientboundSystemChatPacket, Component.class, String.class, boolean.class) ); public static final Field field$ClientboundSystemChatPacket$overlay = requireNonNull( @@ -189,6 +191,11 @@ public class Reflections { clazz$ClientboundSystemChatPacket, clazz$Component, 0 ); + public static final Field field$ClientboundSystemChatPacket$adventure$content = + ReflectionUtils.getDeclaredField( + clazz$ClientboundSystemChatPacket, Component.class, 0 + ); + public static final Field field$ClientboundSystemChatPacket$text = ReflectionUtils.getDeclaredField( clazz$ClientboundSystemChatPacket, String.class, 0 @@ -5115,6 +5122,19 @@ public class Reflections { ) ); + public static final Class clazz$MessageSignature = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.chat.MessageSignature") + ) + ); + + public static final Class clazz$LastSeenMessages$Update = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.chat.LastSeenMessages$Update"), + BukkitReflectionUtils.assembleMCClass("network.chat.LastSeenMessages$b") + ) + ); + public static final Class clazz$ServerboundChatPacket = requireNonNull( ReflectionUtils.getClazz( BukkitReflectionUtils.assembleMCClass("network.protocol.game.ServerboundChatPacket"), @@ -5122,12 +5142,42 @@ public class Reflections { ) ); + public static final Constructor constructor$ServerboundChatPacket = requireNonNull( + ReflectionUtils.getConstructor( + clazz$ServerboundChatPacket, String.class, Instant.class, long.class, clazz$MessageSignature, clazz$LastSeenMessages$Update + ) + ); + public static final Field field$ServerboundChatPacket$message = requireNonNull( ReflectionUtils.getDeclaredField( clazz$ServerboundChatPacket, String.class, 0 ) ); + public static final Field field$ServerboundChatPacket$timeStamp = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ServerboundChatPacket, Instant.class, 0 + ) + ); + + public static final Field field$ServerboundChatPacket$salt = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ServerboundChatPacket, long.class, 0 + ) + ); + + public static final Field field$ServerboundChatPacket$signature = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ServerboundChatPacket, clazz$MessageSignature, 0 + ) + ); + + public static final Field field$ServerboundChatPacket$lastSeenMessages = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ServerboundChatPacket, clazz$LastSeenMessages$Update, 0 + ) + ); + public static final Class clazz$ServerboundRenameItemPacket = requireNonNull( ReflectionUtils.getClazz( BukkitReflectionUtils.assembleMCClass("network.protocol.game.ServerboundRenameItemPacket"), @@ -5140,4 +5190,17 @@ public class Reflections { clazz$ServerboundRenameItemPacket, String.class, 0 ) ); + + public static final Class clazz$ServerboundSignUpdatePacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ServerboundSignUpdatePacket"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayInUpdateSign") + ) + ); + + public static final Field field$ServerboundSignUpdatePacket$lines = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ServerboundSignUpdatePacket, String[].class, 0 + ) + ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java index 465c3d88e..3f9c1f21a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java @@ -16,6 +16,8 @@ public interface NetWorkUser { void sendPacket(Object packet, boolean immediately); + void receivePacket(Object packet); + @ApiStatus.Internal ConnectionState decoderState();