Fix server pings and the infamous closed channel exception
As a result, packet listeners for OUT_SERVER_INFO will be processed on the netty server io thread, so they will have to be thread safe if they aren't already. Fixes #119
Dieser Commit ist enthalten in:
Ursprung
9433ea5e48
Commit
245433b29a
@ -269,7 +269,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
|
|||||||
public static class Server extends ObjectEnum<PacketType> {
|
public static class Server extends ObjectEnum<PacketType> {
|
||||||
private final static Sender SENDER = Sender.SERVER;
|
private final static Sender SENDER = Sender.SERVER;
|
||||||
|
|
||||||
public static final PacketType OUT_SERVER_INFO = new PacketType(PROTOCOL, SENDER, 0x00, 255);
|
public static final PacketType OUT_SERVER_INFO = new PacketType(PROTOCOL, SENDER, 0x00, 255).forceAsync(true);
|
||||||
public static final PacketType OUT_PING = new PacketType(PROTOCOL, SENDER, 0x01, 230);
|
public static final PacketType OUT_PING = new PacketType(PROTOCOL, SENDER, 0x01, 230);
|
||||||
|
|
||||||
private final static Server INSTANCE = new Server();
|
private final static Server INSTANCE = new Server();
|
||||||
@ -523,7 +523,8 @@ public class PacketType implements Serializable, Comparable<PacketType> {
|
|||||||
private final int currentId;
|
private final int currentId;
|
||||||
private final int legacyId;
|
private final int legacyId;
|
||||||
private final MinecraftVersion version;
|
private final MinecraftVersion version;
|
||||||
private final boolean dynamic;
|
private boolean forceAsync;
|
||||||
|
private boolean dynamic;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the current packet/legacy lookup.
|
* Retrieve the current packet/legacy lookup.
|
||||||
@ -679,7 +680,8 @@ public class PacketType implements Serializable, Comparable<PacketType> {
|
|||||||
PacketType type = getLookup().getFromCurrent(protocol, sender, packetId);
|
PacketType type = getLookup().getFromCurrent(protocol, sender, packetId);
|
||||||
|
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
type = new PacketType(protocol, sender, packetId, legacyId, PROTOCOL_VERSION, true);
|
type = new PacketType(protocol, sender, packetId, legacyId, PROTOCOL_VERSION);
|
||||||
|
type.dynamic = true;
|
||||||
|
|
||||||
// Many may be scheduled, but only the first will be executed
|
// Many may be scheduled, but only the first will be executed
|
||||||
scheduleRegister(type, "Dynamic-" + UUID.randomUUID().toString());
|
scheduleRegister(type, "Dynamic-" + UUID.randomUUID().toString());
|
||||||
@ -798,25 +800,11 @@ public class PacketType implements Serializable, Comparable<PacketType> {
|
|||||||
* @param version - the version of the current ID.
|
* @param version - the version of the current ID.
|
||||||
*/
|
*/
|
||||||
public PacketType(Protocol protocol, Sender sender, int currentId, int legacyId, MinecraftVersion version) {
|
public PacketType(Protocol protocol, Sender sender, int currentId, int legacyId, MinecraftVersion version) {
|
||||||
this(protocol, sender, currentId, legacyId, version, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a new packet type.
|
|
||||||
* @param protocol - the current protocol.
|
|
||||||
* @param sender - client or server.
|
|
||||||
* @param currentId - the current packet ID.
|
|
||||||
* @param legacyId - the legacy packet ID.
|
|
||||||
* @param version - the version of the current ID.
|
|
||||||
* @param dynamic - if this type was created dynamically.
|
|
||||||
*/
|
|
||||||
public PacketType(Protocol protocol, Sender sender, int currentId, int legacyId, MinecraftVersion version, boolean dynamic) {
|
|
||||||
this.protocol = Preconditions.checkNotNull(protocol, "protocol cannot be NULL");
|
this.protocol = Preconditions.checkNotNull(protocol, "protocol cannot be NULL");
|
||||||
this.sender = Preconditions.checkNotNull(sender, "sender cannot be NULL");
|
this.sender = Preconditions.checkNotNull(sender, "sender cannot be NULL");
|
||||||
this.currentId = currentId;
|
this.currentId = currentId;
|
||||||
this.legacyId = legacyId;
|
this.legacyId = legacyId;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.dynamic = dynamic;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -920,13 +908,26 @@ public class PacketType implements Serializable, Comparable<PacketType> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not this packet was dynamically created (ie we don't have it registered)
|
* Whether or not this packet was dynamically created (i.e. we don't have it registered)
|
||||||
* @return True if dnyamic, false if not.
|
* @return True if dnyamic, false if not.
|
||||||
*/
|
*/
|
||||||
public boolean isDynamic() {
|
public boolean isDynamic() {
|
||||||
return dynamic;
|
return dynamic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PacketType forceAsync(boolean forceAsync) {
|
||||||
|
this.forceAsync = forceAsync;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not this packet must be processed asynchronously.
|
||||||
|
* @return True if it must be, false if not.
|
||||||
|
*/
|
||||||
|
public boolean forceAsync() {
|
||||||
|
return forceAsync;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hashCode(protocol, sender, currentId, legacyId);
|
return Objects.hashCode(protocol, sender, currentId, legacyId);
|
||||||
|
@ -19,7 +19,6 @@ package com.comphenix.protocol.compat.netty.independent;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerAdapter;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
@ -27,14 +26,12 @@ import io.netty.channel.ChannelPromise;
|
|||||||
import io.netty.channel.socket.SocketChannel;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
import io.netty.handler.codec.MessageToByteEncoder;
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
|
||||||
import io.netty.util.concurrent.GenericFutureListener;
|
import io.netty.util.concurrent.GenericFutureListener;
|
||||||
import io.netty.util.internal.TypeParameterMatcher;
|
import io.netty.util.internal.TypeParameterMatcher;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.nio.channels.ClosedChannelException;
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -283,37 +280,10 @@ public class NettyChannelInjector extends ByteToMessageDecoder implements Channe
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ChannelHandlerAdapter exceptionHandler = new ChannelHandlerAdapter() {
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
|
||||||
if (channelListener.isDebug()) {
|
|
||||||
// People were complaining about this on the forums, figure I might as well figure out the cause
|
|
||||||
System.out.println("------------ ProtocolLib Debug ------------");
|
|
||||||
System.out.println("Caught an exception in " + playerName + "\'s channel pipeline.");
|
|
||||||
System.out.println("Context: " + ctx);
|
|
||||||
System.out.println("The exception was: " + cause);
|
|
||||||
System.out.println("Stack trace:");
|
|
||||||
cause.printStackTrace(System.out);
|
|
||||||
System.out.println("Please create an issue on GitHub with the above message.");
|
|
||||||
System.out.println("https://github.com/dmulloy2/ProtocolLib/issues");
|
|
||||||
System.out.println("-------------------------------------------");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cause instanceof ClosedChannelException) {
|
|
||||||
// This is what the DefaultChannelPipeline does
|
|
||||||
ReferenceCountUtil.release(cause);
|
|
||||||
} else {
|
|
||||||
// We only care about closed channel exceptions, pass everything else along
|
|
||||||
super.exceptionCaught(ctx, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Insert our handlers - note that we effectively replace the vanilla encoder/decoder
|
// Insert our handlers - note that we effectively replace the vanilla encoder/decoder
|
||||||
originalChannel.pipeline().addBefore("decoder", "protocol_lib_decoder", this);
|
originalChannel.pipeline().addBefore("decoder", "protocol_lib_decoder", this);
|
||||||
originalChannel.pipeline().addBefore("protocol_lib_decoder", "protocol_lib_finish", finishHandler);
|
originalChannel.pipeline().addBefore("protocol_lib_decoder", "protocol_lib_finish", finishHandler);
|
||||||
originalChannel.pipeline().addAfter("encoder", "protocol_lib_encoder", protocolEncoder);
|
originalChannel.pipeline().addAfter("encoder", "protocol_lib_encoder", protocolEncoder);
|
||||||
originalChannel.pipeline().addLast("protocol_lib_exception_handler", exceptionHandler);
|
|
||||||
|
|
||||||
// Intercept all write methods
|
// Intercept all write methods
|
||||||
channelField.setValue(new NettyChannelProxy(originalChannel, MinecraftReflection.getPacketClass()) {
|
channelField.setValue(new NettyChannelProxy(originalChannel, MinecraftReflection.getPacketClass()) {
|
||||||
@ -840,7 +810,7 @@ public class NettyChannelInjector extends ByteToMessageDecoder implements Channe
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
String[] handlers = new String[] {
|
String[] handlers = new String[] {
|
||||||
"protocol_lib_decoder", "protocol_lib_finish", "protocol_lib_encoder", "protocol_lib_exception_handler"
|
"protocol_lib_decoder", "protocol_lib_finish", "protocol_lib_encoder"
|
||||||
};
|
};
|
||||||
|
|
||||||
for (String handler : handlers) {
|
for (String handler : handlers) {
|
||||||
|
@ -356,7 +356,7 @@ public class NettyProtocolInjector implements ProtocolInjector {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addPacketHandler(PacketType type, Set<ListenerOptions> options) {
|
public void addPacketHandler(PacketType type, Set<ListenerOptions> options) {
|
||||||
if (options != null && !options.contains(ListenerOptions.ASYNC))
|
if (options != null && !type.forceAsync() && !options.contains(ListenerOptions.ASYNC))
|
||||||
mainThreadFilters.addType(type);
|
mainThreadFilters.addType(type);
|
||||||
super.addPacketHandler(type, options);
|
super.addPacketHandler(type, options);
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ package com.comphenix.protocol.compat.netty.shaded;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.nio.channels.ClosedChannelException;
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -32,7 +31,6 @@ import java.util.concurrent.ConcurrentMap;
|
|||||||
import net.minecraft.util.io.netty.buffer.ByteBuf;
|
import net.minecraft.util.io.netty.buffer.ByteBuf;
|
||||||
import net.minecraft.util.io.netty.channel.Channel;
|
import net.minecraft.util.io.netty.channel.Channel;
|
||||||
import net.minecraft.util.io.netty.channel.ChannelHandler;
|
import net.minecraft.util.io.netty.channel.ChannelHandler;
|
||||||
import net.minecraft.util.io.netty.channel.ChannelHandlerAdapter;
|
|
||||||
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
|
import net.minecraft.util.io.netty.channel.ChannelHandlerContext;
|
||||||
import net.minecraft.util.io.netty.channel.ChannelInboundHandlerAdapter;
|
import net.minecraft.util.io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
import net.minecraft.util.io.netty.channel.ChannelPipeline;
|
import net.minecraft.util.io.netty.channel.ChannelPipeline;
|
||||||
@ -40,7 +38,6 @@ import net.minecraft.util.io.netty.channel.ChannelPromise;
|
|||||||
import net.minecraft.util.io.netty.channel.socket.SocketChannel;
|
import net.minecraft.util.io.netty.channel.socket.SocketChannel;
|
||||||
import net.minecraft.util.io.netty.handler.codec.ByteToMessageDecoder;
|
import net.minecraft.util.io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
import net.minecraft.util.io.netty.handler.codec.MessageToByteEncoder;
|
import net.minecraft.util.io.netty.handler.codec.MessageToByteEncoder;
|
||||||
import net.minecraft.util.io.netty.util.ReferenceCountUtil;
|
|
||||||
import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener;
|
import net.minecraft.util.io.netty.util.concurrent.GenericFutureListener;
|
||||||
import net.minecraft.util.io.netty.util.internal.TypeParameterMatcher;
|
import net.minecraft.util.io.netty.util.internal.TypeParameterMatcher;
|
||||||
import net.sf.cglib.proxy.Factory;
|
import net.sf.cglib.proxy.Factory;
|
||||||
@ -282,37 +279,10 @@ public class ShadedChannelInjector extends ByteToMessageDecoder implements Chann
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ChannelHandlerAdapter exceptionHandler = new ChannelHandlerAdapter() {
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
|
||||||
if (channelListener.isDebug()) {
|
|
||||||
// People were complaining about this on the forums, figure I might as well figure out the cause
|
|
||||||
System.out.println("------------ ProtocolLib Debug ------------");
|
|
||||||
System.out.println("Caught an exception in " + playerName + "\'s channel pipeline.");
|
|
||||||
System.out.println("Context: " + ctx);
|
|
||||||
System.out.println("The exception was: " + cause);
|
|
||||||
System.out.println("Stack trace:");
|
|
||||||
cause.printStackTrace(System.out);
|
|
||||||
System.out.println("Please create an issue on GitHub with the above message.");
|
|
||||||
System.out.println("https://github.com/dmulloy2/ProtocolLib/issues");
|
|
||||||
System.out.println("-------------------------------------------");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cause instanceof ClosedChannelException) {
|
|
||||||
// This is what the DefaultChannelPipeline does
|
|
||||||
ReferenceCountUtil.release(cause);
|
|
||||||
} else {
|
|
||||||
// We only care about closed channel exceptions, pass everything else along
|
|
||||||
super.exceptionCaught(ctx, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Insert our handlers - note that we effectively replace the vanilla encoder/decoder
|
// Insert our handlers - note that we effectively replace the vanilla encoder/decoder
|
||||||
originalChannel.pipeline().addBefore("decoder", "protocol_lib_decoder", this);
|
originalChannel.pipeline().addBefore("decoder", "protocol_lib_decoder", this);
|
||||||
originalChannel.pipeline().addBefore("protocol_lib_decoder", "protocol_lib_finish", finishHandler);
|
originalChannel.pipeline().addBefore("protocol_lib_decoder", "protocol_lib_finish", finishHandler);
|
||||||
originalChannel.pipeline().addAfter("encoder", "protocol_lib_encoder", protocolEncoder);
|
originalChannel.pipeline().addAfter("encoder", "protocol_lib_encoder", protocolEncoder);
|
||||||
originalChannel.pipeline().addLast("protocol_lib_exception_handler", exceptionHandler);
|
|
||||||
|
|
||||||
// Intercept all write methods
|
// Intercept all write methods
|
||||||
channelField.setValue(new ShadedChannelProxy(originalChannel, MinecraftReflection.getPacketClass()) {
|
channelField.setValue(new ShadedChannelProxy(originalChannel, MinecraftReflection.getPacketClass()) {
|
||||||
@ -839,7 +809,7 @@ public class ShadedChannelInjector extends ByteToMessageDecoder implements Chann
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
String[] handlers = new String[] {
|
String[] handlers = new String[] {
|
||||||
"protocol_lib_decoder", "protocol_lib_finish", "protocol_lib_encoder", "protocol_lib_exception_handler"
|
"protocol_lib_decoder", "protocol_lib_finish", "protocol_lib_encoder"
|
||||||
};
|
};
|
||||||
|
|
||||||
for (String handler : handlers) {
|
for (String handler : handlers) {
|
||||||
|
@ -356,7 +356,7 @@ public class ShadedProtocolInjector implements ProtocolInjector {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addPacketHandler(PacketType type, Set<ListenerOptions> options) {
|
public void addPacketHandler(PacketType type, Set<ListenerOptions> options) {
|
||||||
if (options != null && !options.contains(ListenerOptions.ASYNC))
|
if (options != null && !type.forceAsync() && !options.contains(ListenerOptions.ASYNC))
|
||||||
mainThreadFilters.addType(type);
|
mainThreadFilters.addType(type);
|
||||||
super.addPacketHandler(type, options);
|
super.addPacketHandler(type, options);
|
||||||
}
|
}
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren