Improve Quality of Channel Close Code

* Don't double disconnect due to client exceptions
* Add generic delayed close method
* Properly format imports in changed files
This commit is contained in:
md_5 2016-10-19 21:08:31 +11:00
parent 24a65d8fa9
commit 5c551fd899
3 changed files with 68 additions and 52 deletions

View File

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

View File

@ -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,15 +538,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Override
public void disconnect(final BaseComponent... reason)
{
if ( !disconnecting || !ch.isClosed() )
{
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()
ch.delayedClose( new Runnable()
{
@Override
@ -552,10 +548,8 @@ public class InitialHandler extends PacketHandler implements PendingConnection
{
unsafe().sendPacket( new Kick( ComponentSerializer.toString( reason ) ) );
}
ch.close();
}
}, 500, TimeUnit.MILLISECONDS );
}
} );
}
@Override

View File

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