Instead of storing packets about to be passed on as a byte array, store them as a Netty buffer, which is likely to be pooled, direct and manually memory managed leading to increased performance and less GC strain. In order to ensure no resources are leaked, we free them at the end of each handle cycle if they have not been passed to a channel for writing. In initial profiles this now causes encryption to be one of the most intensive parts of BungeeCord, however in depth profiling snapshots may provide further routes for optimization.
This commit is contained in:
parent
738ed99d54
commit
99f361ca77
@ -1,5 +1,7 @@
|
|||||||
package net.md_5.bungee;
|
package net.md_5.bungee;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to rewrite integers within packets.
|
* Class to rewrite integers within packets.
|
||||||
*/
|
*/
|
||||||
@ -117,20 +119,20 @@ public class EntityMap
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void rewrite(byte[] packet, int oldId, int newId)
|
public static void rewrite(ByteBuf packet, int oldId, int newId)
|
||||||
{
|
{
|
||||||
int packetId = packet[0] & 0xFF;
|
int packetId = packet.getUnsignedByte( 0 );
|
||||||
if ( packetId == 0x1D )
|
if ( packetId == 0x1D )
|
||||||
{ // bulk entity
|
{ // bulk entity
|
||||||
for ( int pos = 2; pos < packet.length; pos += 4 )
|
for ( int pos = 2; pos < packet.readableBytes(); pos += 4 )
|
||||||
{
|
{
|
||||||
int readId = readInt( packet, pos );
|
int readId = packet.getInt( pos );
|
||||||
if ( readId == oldId )
|
if ( readId == oldId )
|
||||||
{
|
{
|
||||||
setInt( packet, pos, newId );
|
packet.setInt( pos, newId );
|
||||||
} else if ( readId == newId )
|
} else if ( readId == newId )
|
||||||
{
|
{
|
||||||
setInt( packet, pos, oldId );
|
packet.setInt( pos, oldId );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -140,41 +142,28 @@ public class EntityMap
|
|||||||
{
|
{
|
||||||
for ( int pos : idArray )
|
for ( int pos : idArray )
|
||||||
{
|
{
|
||||||
int readId = readInt( packet, pos );
|
int readId = packet.getInt( pos );
|
||||||
if ( readId == oldId )
|
if ( readId == oldId )
|
||||||
{
|
{
|
||||||
setInt( packet, pos, newId );
|
packet.setInt( pos, newId );
|
||||||
} else if ( readId == newId )
|
} else if ( readId == newId )
|
||||||
{
|
{
|
||||||
setInt( packet, pos, oldId );
|
packet.setInt( pos, oldId );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( packetId == 0x17 )
|
if ( packetId == 0x17 )
|
||||||
{
|
{
|
||||||
int type = packet[5] & 0xFF;
|
int type = packet.getUnsignedByte( 5 );
|
||||||
if ( type == 60 || type == 90 )
|
if ( type == 60 || type == 90 )
|
||||||
{
|
{
|
||||||
int index20 = readInt( packet, 20 );
|
int index20 = packet.getInt( 20 );
|
||||||
if ( packet.length > 24 && index20 == oldId )
|
if ( packet.readableBytes() > 24 && index20 == oldId )
|
||||||
{
|
{
|
||||||
setInt( packet, 20, newId );
|
packet.setInt( 20, newId );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setInt(byte[] buf, int pos, int i)
|
|
||||||
{
|
|
||||||
buf[pos] = (byte) ( i >> 24 );
|
|
||||||
buf[pos + 1] = (byte) ( i >> 16 );
|
|
||||||
buf[pos + 2] = (byte) ( i >> 8 );
|
|
||||||
buf[pos + 3] = (byte) i;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int readInt(byte[] buf, int pos)
|
|
||||||
{
|
|
||||||
return ( ( ( buf[pos] & 0xFF ) << 24 ) | ( ( buf[pos + 1] & 0xFF ) << 16 ) | ( ( buf[pos + 2] & 0xFF ) << 8 ) | buf[pos + 3] & 0xFF );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import net.md_5.bungee.api.tab.TabListHandler;
|
|||||||
import net.md_5.bungee.connection.InitialHandler;
|
import net.md_5.bungee.connection.InitialHandler;
|
||||||
import net.md_5.bungee.netty.ChannelWrapper;
|
import net.md_5.bungee.netty.ChannelWrapper;
|
||||||
import net.md_5.bungee.netty.HandlerBoss;
|
import net.md_5.bungee.netty.HandlerBoss;
|
||||||
|
import net.md_5.bungee.netty.PacketWrapper;
|
||||||
import net.md_5.bungee.netty.PipelineUtils;
|
import net.md_5.bungee.netty.PipelineUtils;
|
||||||
import net.md_5.bungee.protocol.packet.DefinedPacket;
|
import net.md_5.bungee.protocol.packet.DefinedPacket;
|
||||||
import net.md_5.bungee.protocol.packet.Packet3Chat;
|
import net.md_5.bungee.protocol.packet.Packet3Chat;
|
||||||
@ -126,9 +127,9 @@ public final class UserConnection implements ProxiedPlayer
|
|||||||
this.tabList = tabList;
|
this.tabList = tabList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPacket(byte[] b)
|
public void sendPacket(PacketWrapper packet)
|
||||||
{
|
{
|
||||||
ch.write( b );
|
ch.write( packet );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
@ -21,6 +21,7 @@ import net.md_5.bungee.api.score.Scoreboard;
|
|||||||
import net.md_5.bungee.api.score.Team;
|
import net.md_5.bungee.api.score.Team;
|
||||||
import net.md_5.bungee.netty.ChannelWrapper;
|
import net.md_5.bungee.netty.ChannelWrapper;
|
||||||
import net.md_5.bungee.netty.PacketHandler;
|
import net.md_5.bungee.netty.PacketHandler;
|
||||||
|
import net.md_5.bungee.netty.PacketWrapper;
|
||||||
import net.md_5.bungee.protocol.packet.Packet0KeepAlive;
|
import net.md_5.bungee.protocol.packet.Packet0KeepAlive;
|
||||||
import net.md_5.bungee.protocol.packet.PacketC9PlayerListItem;
|
import net.md_5.bungee.protocol.packet.PacketC9PlayerListItem;
|
||||||
import net.md_5.bungee.protocol.packet.PacketCEScoreboardObjective;
|
import net.md_5.bungee.protocol.packet.PacketCEScoreboardObjective;
|
||||||
@ -70,12 +71,12 @@ public class DownstreamBridge extends PacketHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(byte[] buf) throws Exception
|
public void handle(PacketWrapper packet) throws Exception
|
||||||
{
|
{
|
||||||
if ( !server.isObsolete() )
|
if ( !server.isObsolete() )
|
||||||
{
|
{
|
||||||
EntityMap.rewrite( buf, con.getServerEntityId(), con.getClientEntityId() );
|
EntityMap.rewrite( packet.buf, con.getServerEntityId(), con.getClientEntityId() );
|
||||||
con.sendPacket( buf );
|
con.sendPacket( packet );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
|||||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||||
import net.md_5.bungee.netty.ChannelWrapper;
|
import net.md_5.bungee.netty.ChannelWrapper;
|
||||||
import net.md_5.bungee.netty.PacketHandler;
|
import net.md_5.bungee.netty.PacketHandler;
|
||||||
|
import net.md_5.bungee.netty.PacketWrapper;
|
||||||
import net.md_5.bungee.protocol.packet.Packet0KeepAlive;
|
import net.md_5.bungee.protocol.packet.Packet0KeepAlive;
|
||||||
import net.md_5.bungee.protocol.packet.Packet3Chat;
|
import net.md_5.bungee.protocol.packet.Packet3Chat;
|
||||||
import net.md_5.bungee.protocol.packet.PacketCCSettings;
|
import net.md_5.bungee.protocol.packet.PacketCCSettings;
|
||||||
@ -53,12 +54,12 @@ public class UpstreamBridge extends PacketHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(byte[] buf) throws Exception
|
public void handle(PacketWrapper packet) throws Exception
|
||||||
{
|
{
|
||||||
EntityMap.rewrite( buf, con.getClientEntityId(), con.getServerEntityId() );
|
EntityMap.rewrite( packet.buf, con.getClientEntityId(), con.getServerEntityId() );
|
||||||
if ( con.getServer() != null )
|
if ( con.getServer() != null )
|
||||||
{
|
{
|
||||||
con.getServer().getCh().write( buf );
|
con.getServer().getCh().write( packet );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,8 +21,15 @@ public class ChannelWrapper
|
|||||||
public synchronized void write(Object packet)
|
public synchronized void write(Object packet)
|
||||||
{
|
{
|
||||||
if ( !closed )
|
if ( !closed )
|
||||||
|
{
|
||||||
|
if ( packet instanceof PacketWrapper )
|
||||||
|
{
|
||||||
|
( (PacketWrapper) packet ).setReleased( true );
|
||||||
|
ch.write( ( (PacketWrapper) packet ).buf );
|
||||||
|
} else
|
||||||
{
|
{
|
||||||
ch.write( packet );
|
ch.write( packet );
|
||||||
|
}
|
||||||
ch.flush();
|
ch.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,23 +62,27 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter
|
|||||||
{
|
{
|
||||||
if ( handler != null )
|
if ( handler != null )
|
||||||
{
|
{
|
||||||
if ( msg instanceof PacketWrapper )
|
PacketWrapper packet = (PacketWrapper) msg;
|
||||||
{
|
|
||||||
boolean sendPacket = true;
|
boolean sendPacket = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
( (PacketWrapper) msg ).packet.handle( handler );
|
if ( packet.packet != null )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
packet.packet.handle( handler );
|
||||||
} catch ( CancelSendSignal ex )
|
} catch ( CancelSendSignal ex )
|
||||||
{
|
{
|
||||||
sendPacket = false;
|
sendPacket = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if ( sendPacket )
|
if ( sendPacket )
|
||||||
{
|
{
|
||||||
handler.handle( ( (PacketWrapper) msg ).buf );
|
handler.handle( packet );
|
||||||
}
|
}
|
||||||
} else
|
} finally
|
||||||
{
|
{
|
||||||
handler.handle( (byte[]) msg );
|
packet.trySingleRelease();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,24 +40,11 @@ public class PacketDecoder extends ReplayingDecoder<Void>
|
|||||||
// If we got this far, it means we have formed a packet, so lets grab the end index
|
// If we got this far, it means we have formed a packet, so lets grab the end index
|
||||||
int endIndex = in.readerIndex();
|
int endIndex = in.readerIndex();
|
||||||
// Allocate a buffer big enough for all bytes we have read
|
// Allocate a buffer big enough for all bytes we have read
|
||||||
byte[] buf = new byte[ endIndex - startIndex ];
|
ByteBuf buf = in.copy( startIndex, endIndex - startIndex );
|
||||||
// Go back to start index
|
|
||||||
in.readerIndex( startIndex );
|
|
||||||
// Drain all the bytes into our buffer
|
|
||||||
in.readBytes( buf, 0, buf.length );
|
|
||||||
// Jump back to the end of this packet
|
|
||||||
in.readerIndex( endIndex );
|
|
||||||
// Checkpoint our state incase we don't have enough data for another packet
|
// Checkpoint our state incase we don't have enough data for another packet
|
||||||
checkpoint();
|
checkpoint();
|
||||||
|
|
||||||
// Store our decoded message
|
// Store our decoded message
|
||||||
if ( packet != null )
|
|
||||||
{
|
|
||||||
out.add( new PacketWrapper( packet, buf ) );
|
out.add( new PacketWrapper( packet, buf ) );
|
||||||
} else
|
|
||||||
{
|
|
||||||
out.add( buf );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ public abstract class PacketHandler extends net.md_5.bungee.protocol.packet.Abst
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handle(byte[] buf) throws Exception
|
public void handle(PacketWrapper packet) throws Exception
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,25 @@
|
|||||||
package net.md_5.bungee.netty;
|
package net.md_5.bungee.netty;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
import net.md_5.bungee.protocol.packet.DefinedPacket;
|
import net.md_5.bungee.protocol.packet.DefinedPacket;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class PacketWrapper
|
public class PacketWrapper
|
||||||
{
|
{
|
||||||
|
|
||||||
DefinedPacket packet;
|
public final DefinedPacket packet;
|
||||||
byte[] buf;
|
public final ByteBuf buf;
|
||||||
|
@Setter
|
||||||
|
private boolean released;
|
||||||
|
|
||||||
public PacketWrapper(DefinedPacket packet, byte[] buf)
|
public void trySingleRelease()
|
||||||
{
|
{
|
||||||
this.packet = packet;
|
if ( !released )
|
||||||
this.buf = buf;
|
{
|
||||||
|
buf.release();
|
||||||
|
released = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,11 +50,9 @@ public class PipelineUtils
|
|||||||
};
|
};
|
||||||
public static final Base BASE = new Base();
|
public static final Base BASE = new Base();
|
||||||
private static final DefinedPacketEncoder packetEncoder = new DefinedPacketEncoder();
|
private static final DefinedPacketEncoder packetEncoder = new DefinedPacketEncoder();
|
||||||
private static final ByteArrayEncoder arrayEncoder = new ByteArrayEncoder();
|
|
||||||
public static String TIMEOUT_HANDLER = "timeout";
|
public static String TIMEOUT_HANDLER = "timeout";
|
||||||
public static String PACKET_DECODE_HANDLER = "packet-decoder";
|
public static String PACKET_DECODE_HANDLER = "packet-decoder";
|
||||||
public static String PACKET_ENCODE_HANDLER = "packet-encoder";
|
public static String PACKET_ENCODE_HANDLER = "packet-encoder";
|
||||||
public static String ARRAY_ENCODE_HANDLER = "array-encoder";
|
|
||||||
public static String BOSS_HANDLER = "inbound-boss";
|
public static String BOSS_HANDLER = "inbound-boss";
|
||||||
public static String ENCRYPT_HANDLER = "encrypt";
|
public static String ENCRYPT_HANDLER = "encrypt";
|
||||||
public static String DECRYPT_HANDLER = "decrypt";
|
public static String DECRYPT_HANDLER = "decrypt";
|
||||||
@ -76,7 +74,6 @@ public class PipelineUtils
|
|||||||
ch.pipeline().addLast( TIMEOUT_HANDLER, new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) );
|
ch.pipeline().addLast( TIMEOUT_HANDLER, new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) );
|
||||||
ch.pipeline().addLast( PACKET_DECODE_HANDLER, new PacketDecoder( Vanilla.getInstance() ) );
|
ch.pipeline().addLast( PACKET_DECODE_HANDLER, new PacketDecoder( Vanilla.getInstance() ) );
|
||||||
ch.pipeline().addLast( PACKET_ENCODE_HANDLER, packetEncoder );
|
ch.pipeline().addLast( PACKET_ENCODE_HANDLER, packetEncoder );
|
||||||
ch.pipeline().addLast( ARRAY_ENCODE_HANDLER, arrayEncoder );
|
|
||||||
ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() );
|
ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user