From 2ef5e7004b298c058f22b8e1d29494934ed3bdde Mon Sep 17 00:00:00 2001 From: Janmm14 Date: Sat, 25 Mar 2023 11:00:13 +1100 Subject: [PATCH] #3451: Improve length field prepending on bungee -> server connection Use alternative implementation of Varint21LengthFieldPrepender on bungee -> server connection for improved speed - it uses separate buffer to prepend the length to avoid copying large data buffer. Not applied bungee -> client because encrypting 1-5 bytes of length separately through expensive jni call could make it not worth (not measured). --- .../Varint21LengthFieldExtraBufPrepender.java | 25 +++++++++++++++++++ .../Varint21LengthFieldPrepender.java | 5 +++- .../net/md_5/bungee/BungeeServerInfo.java | 2 +- .../java/net/md_5/bungee/UserConnection.java | 2 +- .../net/md_5/bungee/netty/PipelineUtils.java | 15 +++++++++-- 5 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldExtraBufPrepender.java diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldExtraBufPrepender.java b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldExtraBufPrepender.java new file mode 100644 index 000000000..f5ba162aa --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldExtraBufPrepender.java @@ -0,0 +1,25 @@ +package net.md_5.bungee.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageEncoder; +import java.util.List; + +/** + * Prepend length of the message as a Varint21 using an extra buffer for the length, avoiding copying packet data + */ +@ChannelHandler.Sharable +public class Varint21LengthFieldExtraBufPrepender extends MessageToMessageEncoder +{ + + @Override + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception + { + int bodyLen = msg.readableBytes(); + ByteBuf lenBuf = ctx.alloc().ioBuffer( Varint21LengthFieldPrepender.varintSize( bodyLen ) ); + DefinedPacket.writeVarInt( bodyLen, lenBuf ); + out.add( lenBuf ); + out.add( msg.retain() ); + } +} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java index d4c3df44a..9c924660d 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java @@ -5,6 +5,9 @@ import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; +/** + * Prepend length of the message as a Varint21 by writing length and data to a new buffer + */ @ChannelHandler.Sharable public class Varint21LengthFieldPrepender extends MessageToByteEncoder { @@ -20,7 +23,7 @@ public class Varint21LengthFieldPrepender extends MessageToByteEncoder out.writeBytes( msg ); } - private static int varintSize(int paramInt) + static int varintSize(int paramInt) { if ( ( paramInt & 0xFFFFFF80 ) == 0 ) { diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java b/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java index 8fecbd73c..659482575 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java @@ -182,7 +182,7 @@ public class BungeeServerInfo implements ServerInfo new Bootstrap() .channel( PipelineUtils.getChannel( socketAddress ) ) .group( BungeeCord.getInstance().eventLoops ) - .handler( PipelineUtils.BASE ) + .handler( PipelineUtils.BASE_SERVERSIDE ) .option( ChannelOption.CONNECT_TIMEOUT_MILLIS, BungeeCord.getInstance().getConfig().getRemotePingTimeout() ) .remoteAddress( socketAddress ) .connect() diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java index 274292d51..21d9dba08 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -320,7 +320,7 @@ public final class UserConnection implements ProxiedPlayer @Override protected void initChannel(Channel ch) throws Exception { - PipelineUtils.BASE.initChannel( ch ); + PipelineUtils.BASE_SERVERSIDE.initChannel( ch ); ch.pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) ); ch.pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) ); ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) ); diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java index 565633468..5b9c35d1b 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java @@ -30,6 +30,8 @@ import java.net.SocketAddress; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.logging.Level; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; import net.md_5.bungee.BungeeCord; import net.md_5.bungee.Util; import net.md_5.bungee.api.ProxyServer; @@ -42,6 +44,7 @@ import net.md_5.bungee.protocol.MinecraftDecoder; import net.md_5.bungee.protocol.MinecraftEncoder; import net.md_5.bungee.protocol.Protocol; import net.md_5.bungee.protocol.Varint21FrameDecoder; +import net.md_5.bungee.protocol.Varint21LengthFieldExtraBufPrepender; import net.md_5.bungee.protocol.Varint21LengthFieldPrepender; public class PipelineUtils @@ -82,9 +85,11 @@ public class PipelineUtils } } }; - public static final Base BASE = new Base(); + public static final Base BASE = new Base( false ); + public static final Base BASE_SERVERSIDE = new Base( true ); private static final KickStringWriter legacyKicker = new KickStringWriter(); private static final Varint21LengthFieldPrepender framePrepender = new Varint21LengthFieldPrepender(); + private static final Varint21LengthFieldExtraBufPrepender serverFramePrepender = new Varint21LengthFieldExtraBufPrepender(); public static final String TIMEOUT_HANDLER = "timeout"; public static final String PACKET_DECODER = "packet-decoder"; public static final String PACKET_ENCODER = "packet-encoder"; @@ -152,9 +157,13 @@ public class PipelineUtils private static final int HIGH_MARK = Integer.getInteger( "net.md_5.bungee.high_mark", 2 << 20 ); // 2 mb private static final WriteBufferWaterMark MARK = new WriteBufferWaterMark( LOW_MARK, HIGH_MARK ); + @NoArgsConstructor // for backwards compatibility + @AllArgsConstructor public static final class Base extends ChannelInitializer { + private boolean toServer = false; + @Override public void initChannel(Channel ch) throws Exception { @@ -170,7 +179,9 @@ public class PipelineUtils ch.pipeline().addLast( FRAME_DECODER, new Varint21FrameDecoder() ); ch.pipeline().addLast( TIMEOUT_HANDLER, new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) ); - ch.pipeline().addLast( FRAME_PREPENDER, framePrepender ); + // No encryption bungee -> server, therefore use extra buffer to avoid copying everything for length prepending + // Not used bungee -> client as header would need to be encrypted separately through expensive JNI call + ch.pipeline().addLast( FRAME_PREPENDER, ( toServer ) ? serverFramePrepender : framePrepender ); ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() ); }