diff --git a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java
index 1f330bd218..1141c51616 100644
--- a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java
+++ b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java
@@ -16,6 +16,7 @@
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.api.scheduler.TaskScheduler;
+import net.md_5.bungee.protocol.channel.BungeeChannelInitializer;
public abstract class ProxyServer
{
@@ -311,4 +312,56 @@ public static void setInstance(ProxyServer instance)
*/
public abstract Title createTitle();
+ /**
+ * Get the unsafe methods of this class.
+ *
+ * @return the unsafe method interface
+ */
+ public abstract Unsafe unsafe();
+
+ public interface Unsafe
+ {
+ /**
+ * Gets the frontend channel initializer
+ *
+ * @return the frontend channel initializer
+ */
+ BungeeChannelInitializer getFrontendChannelInitializer();
+
+ /**
+ * Gets the backend channel initializer
+ *
+ * @return the backend channel initializer
+ */
+ BungeeChannelInitializer getBackendChannelInitializer();
+
+ /**
+ * Gets the server info channel initializer
+ *
+ * @return the server info channel initializer
+ */
+ BungeeChannelInitializer getServerInfoChannelInitializer();
+
+ /**
+ * Set the frontend channel initializer of this proxy
+ *
+ * @param channelInitializer the frontend channelInitializer to set
+ */
+ void setFrontendChannelInitializer(BungeeChannelInitializer channelInitializer);
+
+ /**
+ * Set the backend channel initializer of this proxy
+ *
+ * @param channelInitializer the backend channelInitializer to set
+ */
+ void setBackendChannelInitializer(BungeeChannelInitializer channelInitializer);
+
+ /**
+ * Set the server info channel initializer of this proxy
+ *
+ * @param channelInitializer the server info channelInitializer to set
+ */
+ void setServerInfoChannelInitializer(BungeeChannelInitializer channelInitializer);
+ }
+
}
diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/channel/BungeeChannelInitializer.java b/protocol/src/main/java/net/md_5/bungee/protocol/channel/BungeeChannelInitializer.java
new file mode 100644
index 0000000000..64de20607e
--- /dev/null
+++ b/protocol/src/main/java/net/md_5/bungee/protocol/channel/BungeeChannelInitializer.java
@@ -0,0 +1,49 @@
+package net.md_5.bungee.protocol.channel;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelInitializer;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+/**
+ * This class hold a netty channel initializer that calls the given {@link ChannelAcceptor}
+ * Use {@link BungeeChannelInitializer#create(ChannelAcceptor)} to create a new instance
+ *
+ * Please note that this API is unsafe and doesn't provide any guarantees about the stability of the
+ * channel pipeline. Use at your own risk.
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public abstract class BungeeChannelInitializer
+{
+ public abstract ChannelAcceptor getChannelAcceptor();
+ public abstract ChannelInitializer getChannelInitializer();
+
+ /**
+ * Creates a new instance of BungeeChannelInitializer
+ *
+ * @param acceptor the {@link ChannelAcceptor} that will accept the channel and initializer the pipeline
+ * @return {@link BungeeChannelInitializer} containing a cached {@link ChannelInitializer} that will call the acceptor
+ */
+ public static BungeeChannelInitializer create(ChannelAcceptor acceptor)
+ {
+ return new BungeeChannelInitializer()
+ {
+ @Getter
+ private final ChannelAcceptor channelAcceptor = acceptor;
+
+ @Getter // cache the ChannelInitializer
+ private final ChannelInitializer channelInitializer = new ChannelInitializer()
+ {
+ @Override
+ protected void initChannel(Channel channel) throws Exception
+ {
+ if ( !getChannelAcceptor().accept( channel ) )
+ {
+ channel.close();
+ }
+ }
+ };
+ };
+ }
+}
diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/channel/ChannelAcceptor.java b/protocol/src/main/java/net/md_5/bungee/protocol/channel/ChannelAcceptor.java
new file mode 100644
index 0000000000..b67ce23414
--- /dev/null
+++ b/protocol/src/main/java/net/md_5/bungee/protocol/channel/ChannelAcceptor.java
@@ -0,0 +1,15 @@
+package net.md_5.bungee.protocol.channel;
+
+import io.netty.channel.Channel;
+
+@FunctionalInterface
+public interface ChannelAcceptor
+{
+ /**
+ * Inside this method the pipeline should be initialized.
+ *
+ * @param channel the channel to be accepted and initialized
+ * @return if the channel was accepted
+ */
+ boolean accept(Channel channel);
+}
diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java
index e7f3f09af9..17af51455e 100644
--- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java
+++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java
@@ -95,6 +95,7 @@
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.ProtocolConstants;
+import net.md_5.bungee.protocol.channel.BungeeChannelInitializer;
import net.md_5.bungee.protocol.packet.PluginMessage;
import net.md_5.bungee.query.RemoteQuery;
import net.md_5.bungee.scheduler.BungeeScheduler;
@@ -188,6 +189,21 @@ public static BungeeCord getInstance()
return (BungeeCord) ProxyServer.getInstance();
}
+ private final Unsafe unsafe = new Unsafe()
+ {
+ @Getter
+ @Setter
+ private BungeeChannelInitializer frontendChannelInitializer;
+
+ @Getter
+ @Setter
+ private BungeeChannelInitializer backendChannelInitializer;
+
+ @Getter
+ @Setter
+ private BungeeChannelInitializer serverInfoChannelInitializer;
+ };
+
@SuppressFBWarnings("DM_DEFAULT_ENCODING")
public BungeeCord() throws IOException
{
@@ -360,7 +376,7 @@ public void operationComplete(ChannelFuture future) throws Exception
.channel( PipelineUtils.getServerChannel( info.getSocketAddress() ) )
.option( ChannelOption.SO_REUSEADDR, true ) // TODO: Move this elsewhere!
.childAttr( PipelineUtils.LISTENER, info )
- .childHandler( PipelineUtils.SERVER_CHILD )
+ .childHandler( ProxyServer.getInstance().unsafe().getFrontendChannelInitializer().getChannelInitializer() )
.group( eventLoops )
.localAddress( info.getSocketAddress() )
.bind().addListener( listener );
@@ -831,4 +847,10 @@ public Title createTitle()
{
return new BungeeTitle();
}
+
+ @Override
+ public Unsafe unsafe()
+ {
+ return unsafe;
+ }
}
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 671cf96ff2..7700f1d6ca 100644
--- a/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java
+++ b/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java
@@ -186,7 +186,7 @@ public void operationComplete(ChannelFuture future) throws Exception
new Bootstrap()
.channel( PipelineUtils.getChannel( socketAddress ) )
.group( BungeeCord.getInstance().eventLoops )
- .handler( PipelineUtils.BASE_SERVERSIDE )
+ .handler( ProxyServer.getInstance().unsafe().getServerInfoChannelInitializer().getChannelInitializer() )
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, BungeeCord.getInstance().getConfig().getRemotePingTimeout() )
.remoteAddress( socketAddress )
.connect()
diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java
index b58a01c6dd..8f52c50eb7 100644
--- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java
+++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java
@@ -104,6 +104,7 @@ public void exception(Throwable t) throws Exception
@Override
public void connected(ChannelWrapper channel) throws Exception
{
+ channel.setVersion( user.getPendingConnection().getVersion() );
this.ch = channel;
this.handshakeHandler = new ForgeServerHandler( user, ch, target );
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 8f121aa952..a76f46f0f7 100644
--- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java
+++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java
@@ -3,10 +3,8 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import io.netty.bootstrap.Bootstrap;
-import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
-import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.util.internal.PlatformDependent;
import java.net.InetSocketAddress;
@@ -50,8 +48,6 @@
import net.md_5.bungee.netty.HandlerBoss;
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.protocol.DefinedPacket;
-import net.md_5.bungee.protocol.MinecraftDecoder;
-import net.md_5.bungee.protocol.MinecraftEncoder;
import net.md_5.bungee.protocol.PacketWrapper;
import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.ProtocolConstants;
@@ -362,17 +358,6 @@ private void connect0(final ServerConnectRequest request)
pendingConnects.add( target );
- ChannelInitializer initializer = new ChannelInitializer()
- {
- @Override
- protected void initChannel(Channel ch) throws Exception
- {
- 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 ) );
- }
- };
ChannelFutureListener listener = new ChannelFutureListener()
{
@Override
@@ -401,13 +386,16 @@ public void operationComplete(ChannelFuture future) throws Exception
{
sendMessage( bungee.getTranslation( "fallback_kick", connectionFailMessage( future.cause() ) ) );
}
+ } else
+ {
+ future.channel().pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) );
}
}
};
Bootstrap b = new Bootstrap()
.channel( PipelineUtils.getChannel( target.getAddress() ) )
.group( ch.getHandle().eventLoop() )
- .handler( initializer )
+ .handler( ProxyServer.getInstance().unsafe().getBackendChannelInitializer().getChannelInitializer() )
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, request.getConnectTimeout() )
.remoteAddress( target.getAddress() );
// Windows is bugged, multi homed users will just have to live with random connecting IPs
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 2446f48919..cc1d1110e5 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
@@ -4,7 +4,6 @@
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
-import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
@@ -51,33 +50,33 @@
import net.md_5.bungee.protocol.Varint21FrameDecoder;
import net.md_5.bungee.protocol.Varint21LengthFieldExtraBufPrepender;
import net.md_5.bungee.protocol.Varint21LengthFieldPrepender;
+import net.md_5.bungee.protocol.channel.BungeeChannelInitializer;
+import net.md_5.bungee.protocol.channel.ChannelAcceptor;
public class PipelineUtils
{
public static final AttributeKey LISTENER = AttributeKey.valueOf( "ListerInfo" );
- public static final ChannelInitializer SERVER_CHILD = new ChannelInitializer()
+
+ private static void setChannelInitializerHolders()
{
- @Override
- protected void initChannel(Channel ch) throws Exception
+ ProxyServer.getInstance().unsafe().setFrontendChannelInitializer( BungeeChannelInitializer.create( ch ->
{
SocketAddress remoteAddress = ( ch.remoteAddress() == null ) ? ch.parent().localAddress() : ch.remoteAddress();
if ( BungeeCord.getInstance().getConnectionThrottle() != null && BungeeCord.getInstance().getConnectionThrottle().throttle( remoteAddress ) )
{
- ch.close();
- return;
+ return false;
}
ListenerInfo listener = ch.attr( LISTENER ).get();
if ( BungeeCord.getInstance().getPluginManager().callEvent( new ClientConnectEvent( remoteAddress, listener ) ).isCancelled() )
{
- ch.close();
- return;
+ return false;
}
- BASE.initChannel( ch );
+ BASE.accept( ch );
ch.pipeline().addBefore( FRAME_DECODER, LEGACY_DECODER, new LegacyDecoder() );
ch.pipeline().addAfter( FRAME_DECODER, PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion() ) );
ch.pipeline().addAfter( FRAME_PREPENDER, PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion() ) );
@@ -88,10 +87,22 @@ protected void initChannel(Channel ch) throws Exception
{
ch.pipeline().addFirst( new HAProxyMessageDecoder() );
}
- }
- };
- public static final Base BASE = new Base( false );
- public static final Base BASE_SERVERSIDE = new Base( true );
+ return true;
+ } ) );
+
+ ProxyServer.getInstance().unsafe().setBackendChannelInitializer( BungeeChannelInitializer.create( ch ->
+ {
+ PipelineUtils.BASE_SERVERSIDE.accept( ch );
+ ch.pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, false, ProxyServer.getInstance().getProtocolVersion() ) );
+ ch.pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, false, ProxyServer.getInstance().getProtocolVersion() ) );
+ return true;
+ } ) );
+
+ ProxyServer.getInstance().unsafe().setServerInfoChannelInitializer( BungeeChannelInitializer.create( BASE_SERVERSIDE ) );
+ }
+
+ private static final ChannelAcceptor BASE = new Base( false );
+ private static final ChannelAcceptor BASE_SERVERSIDE = new Base( true );
private static final KickStringWriter legacyKicker = new KickStringWriter();
private static final Varint21LengthFieldExtraBufPrepender serverFramePrepender = new Varint21LengthFieldExtraBufPrepender();
public static final String TIMEOUT_HANDLER = "timeout";
@@ -137,6 +148,8 @@ protected void initChannel(Channel ch) throws Exception
}
}
}
+
+ setChannelInitializerHolders();
}
public static EventLoopGroup newEventLoopGroup(int threads, ThreadFactory factory)
@@ -179,13 +192,13 @@ public static Class extends DatagramChannel> getDatagramChannel()
@NoArgsConstructor // for backwards compatibility
@AllArgsConstructor
- public static final class Base extends ChannelInitializer
+ public static final class Base implements ChannelAcceptor
{
private boolean toServer = false;
@Override
- public void initChannel(Channel ch) throws Exception
+ public boolean accept(Channel ch)
{
try
{
@@ -204,6 +217,7 @@ public void initChannel(Channel ch) throws Exception
ch.pipeline().addLast( FRAME_PREPENDER, ( toServer ) ? serverFramePrepender : new Varint21LengthFieldPrepender() );
ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() );
+ return true;
}
}
}