package net.minecraft.network;
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.local.LocalChannel;
import io.netty.channel.local.LocalEventLoopGroup;
import io.netty.channel.local.LocalServerChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GenericFutureListener;
import java.net.SocketAddress;
import java.util.Queue;
import javax.crypto.SecretKey;
import net.minecraft.server.gui.IUpdatePlayerListBox;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.CryptManager;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.LazyLoadBase;
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;
public class NetworkManager extends SimpleChannelInboundHandler
{
private static final Logger logger = LogManager.getLogger();
public static final Marker logMarkerNetwork = MarkerManager.getMarker("NETWORK");
public static final Marker logMarkerPackets = MarkerManager.getMarker("NETWORK_PACKETS", logMarkerNetwork);
public static final AttributeKey attrKeyConnectionState = AttributeKey.valueOf("protocol");
public static final LazyLoadBase CLIENT_NIO_EVENTLOOP = new LazyLoadBase()
{
private static final String __OBFID = "CL_00001241";
protected NioEventLoopGroup genericLoad()
{
return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build());
}
protected Object load()
{
return this.genericLoad();
}
};
public static final LazyLoadBase CLIENT_LOCAL_EVENTLOOP = new LazyLoadBase()
{
private static final String __OBFID = "CL_00001242";
protected LocalEventLoopGroup genericLoad()
{
return new LocalEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build());
}
protected Object load()
{
return this.genericLoad();
}
};
private final EnumPacketDirection direction;
/** The queue for packets that require transmission */
private final Queue outboundPacketsQueue = Queues.newConcurrentLinkedQueue();
/** The active channel */
private Channel channel;
/** The address of the remote party */
private SocketAddress socketAddress;
/** The INetHandler instance responsible for processing received packets */
private INetHandler packetListener;
/** A String indicating why the network has shutdown. */
private IChatComponent terminationReason;
private boolean isEncrypted;
private boolean disconnected;
private static final String __OBFID = "CL_00001240";
public NetworkManager(EnumPacketDirection packetDirection)
{
this.direction = packetDirection;
}
public void channelActive(ChannelHandlerContext p_channelActive_1_) throws Exception
{
super.channelActive(p_channelActive_1_);
this.channel = p_channelActive_1_.channel();
this.socketAddress = this.channel.remoteAddress();
try
{
this.setConnectionState(EnumConnectionState.HANDSHAKING);
}
catch (Throwable var3)
{
logger.fatal(var3);
}
}
/**
* Sets the new connection state and registers which packets this channel may send and receive
*/
public void setConnectionState(EnumConnectionState newState)
{
this.channel.attr(attrKeyConnectionState).set(newState);
this.channel.config().setAutoRead(true);
logger.debug("Enabled auto read");
}
public void channelInactive(ChannelHandlerContext p_channelInactive_1_)
{
this.closeChannel(new ChatComponentTranslation("disconnect.endOfStream", new Object[0]));
}
public void exceptionCaught(ChannelHandlerContext p_exceptionCaught_1_, Throwable p_exceptionCaught_2_)
{
logger.debug("Disconnecting " + this.getRemoteAddress(), p_exceptionCaught_2_);
this.closeChannel(new ChatComponentTranslation("disconnect.genericReason", new Object[] {"Internal Exception: " + p_exceptionCaught_2_}));
}
protected void channelRead0(ChannelHandlerContext p_channelRead0_1_, Packet p_channelRead0_2_)
{
if (this.channel.isOpen())
{
try
{
p_channelRead0_2_.processPacket(this.packetListener);
}
catch (ThreadQuickExitException var4)
{
;
}
}
}
/**
* Sets the NetHandler for this NetworkManager, no checks are made if this handler is suitable for the particular
* connection state (protocol)
*/
public void setNetHandler(INetHandler handler)
{
Validate.notNull(handler, "packetListener", new Object[0]);
logger.debug("Set listener of {} to {}", new Object[] {this, handler});
this.packetListener = handler;
}
public void sendPacket(Packet packetIn)
{
if (this.channel != null && this.channel.isOpen())
{
this.flushOutboundQueue();
this.dispatchPacket(packetIn, (GenericFutureListener[])null);
}
else
{
this.outboundPacketsQueue.add(new NetworkManager.InboundHandlerTuplePacketListener(packetIn, (GenericFutureListener[])null));
}
}
public void sendPacket(Packet packetIn, GenericFutureListener listener, GenericFutureListener ... listeners)
{
if (this.channel != null && this.channel.isOpen())
{
this.flushOutboundQueue();
this.dispatchPacket(packetIn, (GenericFutureListener[])ArrayUtils.add(listeners, 0, listener));
}
else
{
this.outboundPacketsQueue.add(new NetworkManager.InboundHandlerTuplePacketListener(packetIn, (GenericFutureListener[])ArrayUtils.add(listeners, 0, listener)));
}
}
/**
* Will commit the packet to the channel. If the current thread 'owns' the channel it will write and flush the
* packet, otherwise it will add a task for the channel eventloop thread to do that.
*/
private void dispatchPacket(final Packet inPacket, final GenericFutureListener[] futureListeners)
{
final EnumConnectionState var3 = EnumConnectionState.getFromPacket(inPacket);
final EnumConnectionState var4 = (EnumConnectionState)this.channel.attr(attrKeyConnectionState).get();
if (var4 != var3)
{
logger.debug("Disabled auto read");
this.channel.config().setAutoRead(false);
}
if (this.channel.eventLoop().inEventLoop())
{
if (var3 != var4)
{
this.setConnectionState(var3);
}
ChannelFuture var5 = this.channel.writeAndFlush(inPacket);
if (futureListeners != null)
{
var5.addListeners(futureListeners);
}
var5.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
}
else
{
this.channel.eventLoop().execute(new Runnable()
{
private static final String __OBFID = "CL_00001243";
public void run()
{
if (var3 != var4)
{
NetworkManager.this.setConnectionState(var3);
}
ChannelFuture var1 = NetworkManager.this.channel.writeAndFlush(inPacket);
if (futureListeners != null)
{
var1.addListeners(futureListeners);
}
var1.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
}
});
}
}
/**
* Will iterate through the outboundPacketQueue and dispatch all Packets
*/
private void flushOutboundQueue()
{
if (this.channel != null && this.channel.isOpen())
{
while (!this.outboundPacketsQueue.isEmpty())
{
NetworkManager.InboundHandlerTuplePacketListener var1 = (NetworkManager.InboundHandlerTuplePacketListener)this.outboundPacketsQueue.poll();
this.dispatchPacket(var1.packet, var1.futureListeners);
}
}
}
/**
* Checks timeouts and processes all packets received
*/
public void processReceivedPackets()
{
this.flushOutboundQueue();
if (this.packetListener instanceof IUpdatePlayerListBox)
{
((IUpdatePlayerListBox)this.packetListener).update();
}
this.channel.flush();
}
/**
* Returns the socket address of the remote side. Server-only.
*/
public SocketAddress getRemoteAddress()
{
return this.socketAddress;
}
/**
* Closes the channel, the parameter can be used for an exit message (not certain how it gets sent)
*/
public void closeChannel(IChatComponent message)
{
if (this.channel.isOpen())
{
this.channel.close().awaitUninterruptibly();
this.terminationReason = message;
}
}
/**
* True if this NetworkManager uses a memory connection (single player game). False may imply both an active TCP
* connection or simply no active connection at all
*/
public boolean isLocalChannel()
{
return this.channel instanceof LocalChannel || this.channel instanceof LocalServerChannel;
}
/**
* Adds an encoder+decoder to the channel pipeline. The parameter is the secret key used for encrypted communication
*/
public void enableEncryption(SecretKey key)
{
this.isEncrypted = true;
this.channel.pipeline().addBefore("splitter", "decrypt", new NettyEncryptingDecoder(CryptManager.func_151229_a(2, key)));
this.channel.pipeline().addBefore("prepender", "encrypt", new NettyEncryptingEncoder(CryptManager.func_151229_a(1, key)));
}
/**
* Returns true if this NetworkManager has an active channel, false otherwise
*/
public boolean isChannelOpen()
{
return this.channel != null && this.channel.isOpen();
}
public boolean hasNoChannel()
{
return this.channel == null;
}
/**
* Gets the current handler for processing packets
*/
public INetHandler getNetHandler()
{
return this.packetListener;
}
/**
* If this channel is closed, returns the exit message, null otherwise.
*/
public IChatComponent getExitMessage()
{
return this.terminationReason;
}
/**
* Switches the channel to manual reading modus
*/
public void disableAutoRead()
{
this.channel.config().setAutoRead(false);
}
public void setCompressionTreshold(int treshold)
{
if (treshold >= 0)
{
if (this.channel.pipeline().get("decompress") instanceof NettyCompressionDecoder)
{
((NettyCompressionDecoder)this.channel.pipeline().get("decompress")).setCompressionTreshold(treshold);
}
else
{
this.channel.pipeline().addBefore("decoder", "decompress", new NettyCompressionDecoder(treshold));
}
if (this.channel.pipeline().get("compress") instanceof NettyCompressionEncoder)
{
((NettyCompressionEncoder)this.channel.pipeline().get("decompress")).setCompressionTreshold(treshold);
}
else
{
this.channel.pipeline().addBefore("encoder", "compress", new NettyCompressionEncoder(treshold));
}
}
else
{
if (this.channel.pipeline().get("decompress") instanceof NettyCompressionDecoder)
{
this.channel.pipeline().remove("decompress");
}
if (this.channel.pipeline().get("compress") instanceof NettyCompressionEncoder)
{
this.channel.pipeline().remove("compress");
}
}
}
public void checkDisconnected()
{
if (!this.hasNoChannel() && !this.isChannelOpen() && !this.disconnected)
{
this.disconnected = true;
if (this.getExitMessage() != null)
{
this.getNetHandler().onDisconnect(this.getExitMessage());
}
else if (this.getNetHandler() != null)
{
this.getNetHandler().onDisconnect(new ChatComponentText("Disconnected"));
}
}
}
protected void channelRead0(ChannelHandlerContext p_channelRead0_1_, Object p_channelRead0_2_)
{
this.channelRead0(p_channelRead0_1_, (Packet)p_channelRead0_2_);
}
static class InboundHandlerTuplePacketListener
{
private final Packet packet;
private final GenericFutureListener[] futureListeners;
private static final String __OBFID = "CL_00001244";
public InboundHandlerTuplePacketListener(Packet inPacket, GenericFutureListener ... inFutureListeners)
{
this.packet = inPacket;
this.futureListeners = inFutureListeners;
}
}
}