diff --git a/src/main/java/net/minestom/server/network/netty/NettyServer.java b/src/main/java/net/minestom/server/network/netty/NettyServer.java index db7ce3b88..93bad8b0c 100644 --- a/src/main/java/net/minestom/server/network/netty/NettyServer.java +++ b/src/main/java/net/minestom/server/network/netty/NettyServer.java @@ -16,10 +16,7 @@ import io.netty.handler.traffic.GlobalChannelTrafficShapingHandler; import io.netty.handler.traffic.TrafficCounter; import net.minestom.server.network.PacketProcessor; import net.minestom.server.network.netty.channel.ClientChannel; -import net.minestom.server.network.netty.codec.LegacyPingHandler; -import net.minestom.server.network.netty.codec.PacketDecoder; -import net.minestom.server.network.netty.codec.PacketEncoder; -import net.minestom.server.network.netty.codec.PacketFramer; +import net.minestom.server.network.netty.codec.*; import org.jetbrains.annotations.NotNull; import java.net.InetSocketAddress; @@ -37,6 +34,7 @@ public final class NettyServer { public static final String ENCRYPT_HANDLER_NAME = "encrypt"; // Write public static final String DECRYPT_HANDLER_NAME = "decrypt"; // Read + public static final String GROUPED_PACKET_HANDLER_NAME = "grouped-packet"; // Write public static final String FRAMER_HANDLER_NAME = "framer"; // Read/write public static final String COMPRESSOR_HANDLER_NAME = "compressor"; // Read/write @@ -110,6 +108,9 @@ public final class NettyServer { // Removed from the pipeline later in LegacyPingHandler if unnecessary (>1.6) pipeline.addLast(LEGACY_PING_HANDLER_NAME, new LegacyPingHandler()); + // Used to bypass all the previous handlers by directly sending a framed buffer + pipeline.addLast(GROUPED_PACKET_HANDLER_NAME, new GroupedPacketHandler()); + // Adds packetLength at start | Reads framed bytebuf pipeline.addLast(FRAMER_HANDLER_NAME, new PacketFramer(packetProcessor)); diff --git a/src/main/java/net/minestom/server/network/netty/codec/GroupedPacketHandler.java b/src/main/java/net/minestom/server/network/netty/codec/GroupedPacketHandler.java new file mode 100644 index 000000000..ba23a268b --- /dev/null +++ b/src/main/java/net/minestom/server/network/netty/codec/GroupedPacketHandler.java @@ -0,0 +1,14 @@ +package net.minestom.server.network.netty.codec; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import net.minestom.server.network.netty.packet.FramedPacket; + +public class GroupedPacketHandler extends MessageToByteEncoder { + + @Override + protected void encode(ChannelHandlerContext ctx, FramedPacket msg, ByteBuf out) { + out.writeBytes(msg.body.retainedSlice()); + } +} diff --git a/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java b/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java index eae4f8313..79f09af67 100644 --- a/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java +++ b/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java @@ -21,6 +21,7 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageCodec; import io.netty.handler.codec.DecoderException; +import net.minestom.server.utils.PacketUtils; import net.minestom.server.utils.Utils; import java.util.List; @@ -42,24 +43,7 @@ public class PacketCompressor extends ByteToMessageCodec { @Override protected void encode(ChannelHandlerContext ctx, ByteBuf from, ByteBuf to) { - final int packetLength = from.readableBytes(); - - if (packetLength < this.threshold) { - Utils.writeVarIntBuf(to, 0); - to.writeBytes(from); - } else { - Utils.writeVarIntBuf(to, packetLength); - - deflater.setInput(from.nioBuffer()); - deflater.finish(); - - while (!deflater.finished()) { - final int length = deflater.deflate(buffer); - to.writeBytes(buffer, 0, length); - } - - deflater.reset(); - } + PacketUtils.compressBuffer(deflater, buffer, from, to); } @Override diff --git a/src/main/java/net/minestom/server/network/netty/codec/PacketFramer.java b/src/main/java/net/minestom/server/network/netty/codec/PacketFramer.java index 62af408d0..a342a73e4 100644 --- a/src/main/java/net/minestom/server/network/netty/codec/PacketFramer.java +++ b/src/main/java/net/minestom/server/network/netty/codec/PacketFramer.java @@ -7,6 +7,7 @@ import io.netty.handler.codec.CorruptedFrameException; import net.minestom.server.MinecraftServer; import net.minestom.server.network.PacketProcessor; import net.minestom.server.network.player.PlayerConnection; +import net.minestom.server.utils.PacketUtils; import net.minestom.server.utils.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,17 +26,7 @@ public class PacketFramer extends ByteToMessageCodec { @Override protected void encode(ChannelHandlerContext ctx, ByteBuf from, ByteBuf to) { - final int packetSize = from.readableBytes(); - final int headerSize = Utils.getVarIntSize(packetSize); - - if (headerSize > 3) { - throw new IllegalStateException("Unable to fit " + headerSize + " into 3"); - } - - to.ensureWritable(packetSize + headerSize); - - Utils.writeVarIntBuf(to, packetSize); - to.writeBytes(from, from.readerIndex(), packetSize); + PacketUtils.frameBuffer(from, to); } @Override diff --git a/src/main/java/net/minestom/server/network/netty/packet/FramedPacket.java b/src/main/java/net/minestom/server/network/netty/packet/FramedPacket.java new file mode 100644 index 000000000..4466539b7 --- /dev/null +++ b/src/main/java/net/minestom/server/network/netty/packet/FramedPacket.java @@ -0,0 +1,18 @@ +package net.minestom.server.network.netty.packet; + +import io.netty.buffer.ByteBuf; +import org.jetbrains.annotations.NotNull; + +/** + * Represents a packet which is already framed. + * Can be used if you want to send the exact same buffer to multiple clients without processing it more than once. + */ +public class FramedPacket { + + public final ByteBuf body; + + public FramedPacket(@NotNull ByteBuf body) { + this.body = body; + } + +} diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index 9a2192037..7365329a4 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -134,7 +134,7 @@ public class NettyPlayerConnection extends PlayerConnection { @Override public void disconnect() { - channel.close(); + this.channel.close(); } @NotNull diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index 52772b503..d8d99d82e 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -5,6 +5,7 @@ import io.netty.buffer.Unpooled; import net.minestom.server.MinecraftServer; import net.minestom.server.entity.Player; import net.minestom.server.listener.manager.PacketListenerManager; +import net.minestom.server.network.netty.packet.FramedPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.network.player.PlayerConnection; @@ -12,6 +13,7 @@ import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; import java.util.Collection; +import java.util.zip.Deflater; /** * Utils class for packets. Including writing a {@link ServerPacket} into a {@link ByteBuf} @@ -21,6 +23,9 @@ public final class PacketUtils { private static final PacketListenerManager PACKET_LISTENER_MANAGER = MinecraftServer.getPacketListenerManager(); + private static Deflater deflater = new Deflater(); + private static byte[] buffer = new byte[8192]; + private PacketUtils() { } @@ -36,13 +41,14 @@ public final class PacketUtils { public static void sendGroupedPacket(@NotNull Collection players, @NotNull ServerPacket packet) { final boolean success = PACKET_LISTENER_MANAGER.processServerPacket(packet, players); if (success) { - final ByteBuf buffer = writePacket(packet); + final ByteBuf finalBuffer = createFramedPacket(packet); + final FramedPacket framedPacket = new FramedPacket(finalBuffer); for (Player player : players) { final PlayerConnection playerConnection = player.getPlayerConnection(); if (playerConnection instanceof NettyPlayerConnection) { final NettyPlayerConnection nettyPlayerConnection = (NettyPlayerConnection) playerConnection; - nettyPlayerConnection.getChannel().write(buffer.retainedSlice()); + nettyPlayerConnection.getChannel().write(framedPacket); } else { playerConnection.sendPacket(packet); } @@ -108,4 +114,62 @@ public final class PacketUtils { return writer.getBuffer(); } + public static void frameBuffer(@NotNull ByteBuf from, @NotNull ByteBuf to) { + final int packetSize = from.readableBytes(); + final int headerSize = Utils.getVarIntSize(packetSize); + + if (headerSize > 3) { + throw new IllegalStateException("Unable to fit " + headerSize + " into 3"); + } + + to.ensureWritable(packetSize + headerSize); + + Utils.writeVarIntBuf(to, packetSize); + to.writeBytes(from, from.readerIndex(), packetSize); + } + + public static void compressBuffer(@NotNull Deflater deflater, @NotNull byte[] buffer, @NotNull ByteBuf from, @NotNull ByteBuf to) { + final int packetLength = from.readableBytes(); + + if (packetLength < MinecraftServer.getCompressionThreshold()) { + Utils.writeVarIntBuf(to, 0); + to.writeBytes(from); + } else { + Utils.writeVarIntBuf(to, packetLength); + + deflater.setInput(from.nioBuffer()); + deflater.finish(); + + while (!deflater.finished()) { + final int length = deflater.deflate(buffer); + to.writeBytes(buffer, 0, length); + } + + deflater.reset(); + } + } + + private static ByteBuf createFramedPacket(@NotNull ServerPacket serverPacket) { + ByteBuf packetBuf = writePacket(serverPacket); + + if (MinecraftServer.getCompressionThreshold() > 0) { + + ByteBuf compressedBuf = Unpooled.buffer(); + ByteBuf framedBuf = Unpooled.buffer(); + synchronized (deflater) { + compressBuffer(deflater, buffer, packetBuf, compressedBuf); + } + + frameBuffer(compressedBuf, framedBuf); + + return framedBuf; + } else { + ByteBuf framedBuf = Unpooled.buffer(); + frameBuffer(packetBuf, framedBuf); + + return framedBuf; + } + + } + }