Added GroupedPacketHandler to prevent rewriting the same packet multiple times

This commit is contained in:
themode 2020-11-20 05:37:13 +01:00
parent 3455c77eb7
commit 4060f8d290
7 changed files with 108 additions and 36 deletions

View File

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

View File

@ -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<FramedPacket> {
@Override
protected void encode(ChannelHandlerContext ctx, FramedPacket msg, ByteBuf out) {
out.writeBytes(msg.body.retainedSlice());
}
}

View File

@ -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<ByteBuf> {
@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

View File

@ -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<ByteBuf> {
@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

View File

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

View File

@ -134,7 +134,7 @@ public class NettyPlayerConnection extends PlayerConnection {
@Override
public void disconnect() {
channel.close();
this.channel.close();
}
@NotNull

View File

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