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:
parent
24a65d8fa9
commit
5c551fd899
@ -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" );
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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" );
|
||||
|
Loading…
Reference in New Issue
Block a user