package net.minecraft.server; import com.google.common.collect.Queues; import com.google.common.util.concurrent.ThreadFactoryBuilder; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.local.LocalChannel; import io.netty.channel.local.LocalEventLoopGroup; import io.netty.channel.local.LocalServerChannel; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.handler.timeout.TimeoutException; import io.netty.util.AttributeKey; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import java.net.SocketAddress; import java.util.Queue; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.annotation.Nullable; import javax.crypto.SecretKey; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.Validate; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; import org.torch.api.Async; public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> { private static final Logger g = LogManager.getLogger(); public static final Marker a = MarkerManager.getMarker("NETWORK"); public static final Marker b = MarkerManager.getMarker("NETWORK_PACKETS", NetworkManager.a); public static final AttributeKey<EnumProtocol> c = AttributeKey.valueOf("protocol"); public static final LazyInitVar<NioEventLoopGroup> d = new LazyInitVar() { protected NioEventLoopGroup a() { return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build()); } @Override protected Object init() { return this.a(); } }; public static final LazyInitVar<EpollEventLoopGroup> e = new LazyInitVar() { protected EpollEventLoopGroup a() { return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).build()); } @Override protected Object init() { return this.a(); } }; public static final LazyInitVar<LocalEventLoopGroup> f = new LazyInitVar() { protected LocalEventLoopGroup a() { return new LocalEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build()); } @Override protected Object init() { return this.a(); } }; private final EnumProtocolDirection h; // private final Queue<NetworkManager.QueuedPacket> i = Queues.newConcurrentLinkedQueue(); // Paper // private final ReentrantReadWriteLock j = new ReentrantReadWriteLock(); // Paper public Channel channel; // Spigot Start // PAIL public SocketAddress l; public java.util.UUID spoofedUUID; public com.mojang.authlib.properties.Property[] spoofedProfile; public boolean preparing = true; // Spigot End private PacketListener m; private IChatBaseComponent n; private boolean o; private boolean p; public NetworkManager(EnumProtocolDirection enumprotocoldirection) { this.h = enumprotocoldirection; } @Override public void channelActive(ChannelHandlerContext channelhandlercontext) throws Exception { super.channelActive(channelhandlercontext); this.channel = channelhandlercontext.channel(); this.l = this.channel.remoteAddress(); // Spigot Start this.preparing = false; // Spigot End try { this.setProtocol(EnumProtocol.HANDSHAKING); } catch (Throwable throwable) { NetworkManager.g.fatal(throwable); } } public void setProtocol(EnumProtocol enumprotocol) { this.channel.attr(NetworkManager.c).set(enumprotocol); this.channel.config().setAutoRead(true); NetworkManager.g.debug("Enabled auto read"); } @Override public void channelInactive(ChannelHandlerContext channelhandlercontext) throws Exception { this.close(new ChatMessage("disconnect.endOfStream", new Object[0])); } @Override public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) throws Exception { ChatMessage chatmessage; if (throwable instanceof TimeoutException) { chatmessage = new ChatMessage("disconnect.timeout", new Object[0]); } else { chatmessage = new ChatMessage("disconnect.genericReason", new Object[] { "Internal Exception: " + throwable}); } NetworkManager.g.debug(throwable); this.close(chatmessage); if (MinecraftServer.getServer().isDebugging()) throwable.printStackTrace(); // Spigot } protected void a(ChannelHandlerContext channelhandlercontext, Packet<?> packet) throws Exception { if (this.channel.isOpen()) { try { ((Packet) packet).a(this.m); // CraftBukkit - decompile error } catch (CancelledPacketHandleException cancelledpackethandleexception) { ; } } } public void setPacketListener(PacketListener packetlistener) { Validate.notNull(packetlistener, "packetListener", new Object[0]); NetworkManager.g.debug("Set listener of {} to {}", new Object[] { this, packetlistener}); this.m = packetlistener; } @Async public void sendPacket(Packet<?> packet) { if (this.isConnected()) { this.m(); this.a(packet, (GenericFutureListener[]) null); } // Paper start - Remove but force a conflict // else { // this.j.writeLock().lock(); // // try { // this.i.add(new NetworkManager.QueuedPacket(packet, (GenericFutureListener[]) null)); // } finally { // this.j.writeLock().unlock(); // } // } // Paper end } @Async public void sendPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> genericfuturelistener, GenericFutureListener<? extends Future<? super Void>>... agenericfuturelistener) { if (this.isConnected()) { this.m(); this.a(packet, ArrayUtils.add(agenericfuturelistener, 0, genericfuturelistener)); } // Paper start - Remove but force a conflict // else { // this.j.writeLock().lock(); // // try { // this.i.add(new NetworkManager.QueuedPacket(packet, (GenericFutureListener[]) ArrayUtils.add(agenericfuturelistener, 0, genericfuturelistener))); // } finally { // this.j.writeLock().unlock(); // } // } // Paper end } @Async private void a(final Packet<?> packet, @Nullable final GenericFutureListener<? extends Future<? super Void>>[] agenericfuturelistener) { final EnumProtocol enumprotocol = EnumProtocol.a(packet); final EnumProtocol enumprotocol1 = this.channel.attr(NetworkManager.c).get(); if (enumprotocol1 != enumprotocol) { NetworkManager.g.debug("Disabled auto read"); this.channel.config().setAutoRead(false); } if (this.channel.eventLoop().inEventLoop()) { if (enumprotocol != enumprotocol1) { this.setProtocol(enumprotocol); } ChannelFuture channelfuture = this.channel.writeAndFlush(packet); if (agenericfuturelistener != null) { channelfuture.addListeners(agenericfuturelistener); } channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); } else { this.channel.eventLoop().execute(new Runnable() { @Override public void run() { if (enumprotocol != enumprotocol1) { NetworkManager.this.setProtocol(enumprotocol); } ChannelFuture channelfuture = NetworkManager.this.channel.writeAndFlush(packet); if (agenericfuturelistener != null) { channelfuture.addListeners(agenericfuturelistener); } channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); } }); } } private void m() { // Paper start - Remove but force a conflict // if (this.channel != null && this.channel.isOpen()) { // this.j.readLock().lock(); // // try { // while (!this.i.isEmpty()) { // NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.i.poll(); // // this.a(networkmanager_queuedpacket.a, networkmanager_queuedpacket.b); // } // } finally { // this.j.readLock().unlock(); // } // // } // Paper end } public void a() { this.m(); if (this.m instanceof ITickable) { ((ITickable) this.m).F_(); } this.channel.flush(); } public SocketAddress getSocketAddress() { return this.l; } public void close(IChatBaseComponent ichatbasecomponent) { // Spigot Start this.preparing = false; // Spigot End if (this.channel.isOpen()) { this.channel.close(); // We can't wait as this may be called from an event loop. this.n = ichatbasecomponent; } } public boolean isLocal() { return this.channel instanceof LocalChannel || this.channel instanceof LocalServerChannel; } public void a(SecretKey secretkey) { this.o = true; this.channel.pipeline().addBefore("splitter", "decrypt", new PacketDecrypter(MinecraftEncryption.a(2, secretkey))); this.channel.pipeline().addBefore("prepender", "encrypt", new PacketEncrypter(MinecraftEncryption.a(1, secretkey))); } public boolean isConnected() { return this.channel != null && this.channel.isOpen(); } public boolean h() { return this.channel == null; } public PacketListener i() { return this.m; } public IChatBaseComponent j() { return this.n; } public void stopReading() { this.channel.config().setAutoRead(false); } public void setCompressionLevel(int i) { if (i >= 0) { if (this.channel.pipeline().get("decompress") instanceof PacketDecompressor) { ((PacketDecompressor) this.channel.pipeline().get("decompress")).a(i); } else { this.channel.pipeline().addBefore("decoder", "decompress", new PacketDecompressor(i)); } if (this.channel.pipeline().get("compress") instanceof PacketCompressor) { ((PacketCompressor) this.channel.pipeline().get("compress")).a(i); } else { this.channel.pipeline().addBefore("encoder", "compress", new PacketCompressor(i)); } } else { if (this.channel.pipeline().get("decompress") instanceof PacketDecompressor) { this.channel.pipeline().remove("decompress"); } if (this.channel.pipeline().get("compress") instanceof PacketCompressor) { this.channel.pipeline().remove("compress"); } } } public void handleDisconnection() { if (this.channel != null && !this.channel.isOpen()) { if (this.p) { NetworkManager.g.warn("handleDisconnection() called twice"); } else { this.p = true; if (this.j() != null) { this.i().a(this.j()); } else if (this.i() != null) { this.i().a(new ChatComponentText("Disconnected")); } // this.i.clear(); // Free up packet queue. // Paper - remove unneeded packet queue } } } @Override protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet object) throws Exception { // CraftBukkit - fix decompile error this.a(channelhandlercontext, object); } static class QueuedPacket { private final Packet<?> a; private final GenericFutureListener<? extends Future<? super Void>>[] b; public QueuedPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>>... agenericfuturelistener) { this.a = packet; this.b = agenericfuturelistener; } } // Spigot Start public SocketAddress getRawAddress() { return this.channel.remoteAddress(); } // Spigot End }