blob: ce68358353c6bf91db8c636e034fd26f5015ab42 [file] [log] [blame] [raw]
package protocolsupport.injector.network;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.logging.log4j.Logger;
import org.spigotmc.SpigotConfig;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.minecraft.server.v1_9_R1.ChatComponentText;
import net.minecraft.server.v1_9_R1.LazyInitVar;
import net.minecraft.server.v1_9_R1.MinecraftServer;
import net.minecraft.server.v1_9_R1.NetworkManager;
import net.minecraft.server.v1_9_R1.PacketPlayOutKickDisconnect;
import net.minecraft.server.v1_9_R1.ServerConnection;
import protocolsupport.utils.ReflectionUtils;
public class NonBlockingServerConnection extends ServerConnection {
//the same names as in ServerConnection in case of plugins that do not search ServerConnection or class hierarchy
private static Logger e;
private List<ChannelFuture> g;
private List<NetworkManager> h;
//synthetic methods from ServerConnection with signatures that plugins may have actually used
static List<NetworkManager> access$0(ServerConnection connection) {
return ((NonBlockingServerConnection) connection).h;
}
static List<NetworkManager> access$0(NonBlockingServerConnection connection) {
return connection.h;
}
@SuppressWarnings("deprecation")
static MinecraftServer access$1(ServerConnection connection) {
return MinecraftServer.getServer();
}
@SuppressWarnings("deprecation")
static MinecraftServer access$1(NonBlockingServerConnection connection) {
return MinecraftServer.getServer();
}
//constructor that is used when initalizing server connection in case there wasn't one before
@SuppressWarnings({ "unchecked", "deprecation" })
public NonBlockingServerConnection() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
super(MinecraftServer.getServer());
e = (Logger) ReflectionUtils.setAccessible(ServerConnection.class.getDeclaredField("e")).get(null);
g = (List<ChannelFuture>) ReflectionUtils.setAccessible(ServerConnection.class.getDeclaredField("g")).get(this);
h = new ConcurrentLinkedQueueFakeListImpl<NetworkManager>();
ReflectionUtils.setAccessible(ServerConnection.class.getDeclaredField("h")).set(this, h);
}
@SuppressWarnings("deprecation")
public static void inject() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
ReflectionUtils.setAccessible(MinecraftServer.class.getDeclaredField("p")).set(MinecraftServer.getServer(), new NonBlockingServerConnection());
}
@SuppressWarnings("deprecation")
@Override
public void a(InetAddress inetaddress, int port) throws IOException {
synchronized (this.g) {
Class<? extends ServerSocketChannel> oclass;
LazyInitVar<? extends EventLoopGroup> lazyinitvar;
if (Epoll.isAvailable() && MinecraftServer.getServer().ae()) {
oclass = EpollServerSocketChannel.class;
lazyinitvar = ServerConnection.b;
e.info("Using epoll channel type");
} else {
oclass = NioServerSocketChannel.class;
lazyinitvar = ServerConnection.a;
e.info("Using default channel type");
}
this.g.add(
new ServerBootstrap()
.channel(oclass)
.childHandler(new ServerConnectionChannel(h))
.group(lazyinitvar.c())
.localAddress(inetaddress, port)
.bind().syncUninterruptibly()
);
}
}
//We use CLQ, so we no longer need to synchronize, so netty channel initializer no longer wastes time waiting for network manager list to unlock
@SuppressWarnings("unchecked")
@Override
public void c() {
if (SpigotConfig.playerShuffle > 0 && MinecraftServer.currentTick % SpigotConfig.playerShuffle == 0) {
Collections.shuffle(this.h);
}
final Iterator<NetworkManager> iterator = this.h.iterator();
while (iterator.hasNext()) {
final NetworkManager networkmanager = iterator.next();
if (!networkmanager.h()) {
if (!networkmanager.isConnected()) {
if (networkmanager.preparing) {
continue;
}
iterator.remove();
networkmanager.handleDisconnection();
} else {
try {
networkmanager.a();
} catch (Exception exception) {
e.warn("Failed to handle packet for " + networkmanager.getSocketAddress(), exception);
final ChatComponentText chatcomponenttext = new ChatComponentText("Internal server error");
networkmanager.sendPacket(new PacketPlayOutKickDisconnect(chatcomponenttext), new GenericFutureListener<Future<? super Void>>() {
@Override
public void operationComplete(Future<? super Void> future) throws Exception {
networkmanager.close(chatcomponenttext);
}
});
networkmanager.stopReading();
}
}
}
}
}
//CLQ that implements List, but no list methods are actually supported
public static class ConcurrentLinkedQueueFakeListImpl<E> extends ConcurrentLinkedQueue<E> implements List<E> {
private static final long serialVersionUID = -8466302736959675653L;
@Override
public boolean addAll(int index, Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
@Override
public E get(int index) {
throw new UnsupportedOperationException();
}
@Override
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
@Override
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
@Override
public E remove(int index) {
throw new UnsupportedOperationException();
}
@Override
public int indexOf(Object o) {
throw new UnsupportedOperationException();
}
@Override
public int lastIndexOf(Object o) {
throw new UnsupportedOperationException();
}
@Override
public ListIterator<E> listIterator() {
throw new UnsupportedOperationException();
}
@Override
public ListIterator<E> listIterator(int index) {
throw new UnsupportedOperationException();
}
@Override
public List<E> subList(int fromIndex, int toIndex) {
throw new UnsupportedOperationException();
}
}
}