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 a5010f3e..0e621c2e 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -11,17 +11,14 @@ import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.util.internal.PlatformDependent; import java.net.InetSocketAddress; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; -import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Queue; import java.util.UUID; -import java.util.concurrent.TimeUnit; import java.util.logging.Level; import lombok.Getter; import lombok.NonNull; @@ -59,8 +56,6 @@ import net.md_5.bungee.protocol.packet.Kick; import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter; import net.md_5.bungee.protocol.packet.PluginMessage; import net.md_5.bungee.protocol.packet.SetCompression; -import net.md_5.bungee.tab.Global; -import net.md_5.bungee.tab.GlobalPing; import net.md_5.bungee.tab.ServerUnique; import net.md_5.bungee.tab.TabList; import net.md_5.bungee.util.CaseInsensitiveSet; @@ -364,31 +359,26 @@ public final class UserConnection implements ProxiedPlayer public void disconnect0(final BaseComponent... reason) { - if ( !ch.isClosed() ) + if ( !ch.isClosing() ) { bungee.getLogger().log( Level.INFO, "[{0}] disconnected with: {1}", new Object[] { getName(), BaseComponent.toLegacyText( reason ) } ); - // Why do we have to delay this you might ask? Well the simple reason is MOJANG. - // Despite many a bug report posted, ever since the 1.7 protocol rewrite, the client STILL has a race condition upon switching protocols. - // As such, despite the protocol switch packets already having been sent, there is the possibility of a client side exception - // To help combat this we will wait half a second before actually sending the disconnected packet so that whoever is on the other - // end has a somewhat better chance of receiving the proper packet. - ch.getHandle().eventLoop().schedule( new Runnable() + ch.delayedClose( new Runnable() { @Override public void run() { unsafe().sendPacket( new Kick( ComponentSerializer.toString( reason ) ) ); - ch.close(); } - }, 500, TimeUnit.MILLISECONDS ); + } ); if ( server != null ) { + server.setObsolete( true ); server.disconnect( "Quitting" ); } } diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java index 068374a0..bdcbdb7a 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -2,19 +2,24 @@ package net.md_5.bungee.connection; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; +import com.google.gson.Gson; import java.math.BigInteger; import java.net.InetSocketAddress; import java.net.URLEncoder; import java.security.MessageDigest; import java.util.List; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.crypto.SecretKey; -import com.google.gson.Gson; -import java.util.concurrent.TimeUnit; import lombok.Getter; import lombok.RequiredArgsConstructor; -import net.md_5.bungee.*; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.BungeeServerInfo; +import net.md_5.bungee.EncryptionUtil; +import net.md_5.bungee.UserConnection; +import net.md_5.bungee.Util; +import net.md_5.bungee.api.AbstractReconnectHandler; import net.md_5.bungee.api.Callback; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.Favicon; @@ -23,37 +28,37 @@ import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.api.connection.Connection.Unsafe; import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.LoginEvent; +import net.md_5.bungee.api.event.PlayerHandshakeEvent; import net.md_5.bungee.api.event.PostLoginEvent; +import net.md_5.bungee.api.event.PreLoginEvent; import net.md_5.bungee.api.event.ProxyPingEvent; import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.http.HttpClient; -import net.md_5.bungee.netty.HandlerBoss; +import net.md_5.bungee.jni.cipher.BungeeCipher; import net.md_5.bungee.netty.ChannelWrapper; +import net.md_5.bungee.netty.HandlerBoss; import net.md_5.bungee.netty.PacketHandler; import net.md_5.bungee.netty.PipelineUtils; import net.md_5.bungee.netty.cipher.CipherDecoder; import net.md_5.bungee.netty.cipher.CipherEncoder; import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.PacketWrapper; -import net.md_5.bungee.protocol.ProtocolConstants; -import net.md_5.bungee.protocol.packet.Handshake; -import net.md_5.bungee.protocol.packet.PluginMessage; -import net.md_5.bungee.protocol.packet.EncryptionResponse; -import net.md_5.bungee.protocol.packet.EncryptionRequest; -import net.md_5.bungee.protocol.packet.Kick; -import net.md_5.bungee.api.AbstractReconnectHandler; -import net.md_5.bungee.api.event.PlayerHandshakeEvent; -import net.md_5.bungee.api.event.PreLoginEvent; -import net.md_5.bungee.jni.cipher.BungeeCipher; import net.md_5.bungee.protocol.Protocol; +import net.md_5.bungee.protocol.ProtocolConstants; +import net.md_5.bungee.protocol.packet.EncryptionRequest; +import net.md_5.bungee.protocol.packet.EncryptionResponse; +import net.md_5.bungee.protocol.packet.Handshake; +import net.md_5.bungee.protocol.packet.Kick; import net.md_5.bungee.protocol.packet.LegacyHandshake; import net.md_5.bungee.protocol.packet.LegacyPing; import net.md_5.bungee.protocol.packet.LoginRequest; import net.md_5.bungee.protocol.packet.LoginSuccess; import net.md_5.bungee.protocol.packet.PingPacket; +import net.md_5.bungee.protocol.packet.PluginMessage; import net.md_5.bungee.protocol.packet.StatusRequest; import net.md_5.bungee.protocol.packet.StatusResponse; import net.md_5.bungee.util.BoundedArrayList; @@ -96,12 +101,11 @@ public class InitialHandler extends PacketHandler implements PendingConnection private boolean legacy; @Getter private String extraDataInHandshake = ""; - private boolean disconnecting; @Override public boolean shouldHandle(PacketWrapper packet) throws Exception { - return !disconnecting; + return !ch.isClosing(); } private enum State @@ -534,28 +538,18 @@ public class InitialHandler extends PacketHandler implements PendingConnection @Override public void disconnect(final BaseComponent... reason) { - if ( !disconnecting || !ch.isClosed() ) + ch.delayedClose( new Runnable() { - disconnecting = true; - // Why do we have to delay this you might ask? Well the simple reason is MOJANG. - // Despite many a bug report posted, ever since the 1.7 protocol rewrite, the client STILL has a race condition upon switching protocols. - // As such, despite the protocol switch packets already having been sent, there is the possibility of a client side exception - // To help combat this we will wait half a second before actually sending the disconnected packet so that whoever is on the other - // end has a somewhat better chance of receiving the proper packet. - ch.getHandle().eventLoop().schedule( new Runnable() - { - @Override - public void run() + @Override + public void run() + { + if ( thisState != State.STATUS && thisState != State.PING ) { - if ( thisState != State.STATUS && thisState != State.PING ) - { - unsafe().sendPacket( new Kick( ComponentSerializer.toString( reason ) ) ); - } - ch.close(); + unsafe().sendPacket( new Kick( ComponentSerializer.toString( reason ) ) ); } - }, 500, TimeUnit.MILLISECONDS ); - } + } + } ); } @Override diff --git a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java index 06d19c3c..0b0dd736 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java @@ -1,15 +1,16 @@ package net.md_5.bungee.netty; -import net.md_5.bungee.compress.PacketCompressor; -import net.md_5.bungee.compress.PacketDecompressor; -import net.md_5.bungee.protocol.PacketWrapper; import com.google.common.base.Preconditions; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; +import java.util.concurrent.TimeUnit; import lombok.Getter; +import net.md_5.bungee.compress.PacketCompressor; +import net.md_5.bungee.compress.PacketDecompressor; 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; public class ChannelWrapper @@ -18,6 +19,8 @@ public class ChannelWrapper private final Channel ch; @Getter private volatile boolean closed; + @Getter + private volatile boolean closing; public ChannelWrapper(ChannelHandlerContext ctx) { @@ -56,12 +59,41 @@ public class ChannelWrapper { if ( !closed ) { - closed = true; + closed = closing = true; ch.flush(); ch.close(); } } + public void delayedClose(final Runnable runnable) + { + Preconditions.checkArgument( runnable != null, "runnable" ); + + if ( !closing ) + { + closing = true; + + // Minecraft client can take some time to switch protocols. + // Sending the wrong disconnect packet whilst a protocol switch is in progress will crash it. + // Delay 500ms to ensure that the protocol switch (if any) has definitely taken place. + ch.eventLoop().schedule( new Runnable() + { + + @Override + public void run() + { + try + { + runnable.run(); + } finally + { + ChannelWrapper.this.close(); + } + } + }, 500, TimeUnit.MILLISECONDS ); + } + } + public void addBefore(String baseName, String name, ChannelHandler handler) { Preconditions.checkState( ch.eventLoop().inEventLoop(), "cannot add handler outside of event loop" );