package net.minecraft.network;
import com.google.common.collect.Queues;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import cpw.mods.fml.common.network.internal.FMLProxyPacket;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalServerChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.TimeoutException;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GenericFutureListener;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.util.Queue;
import javax.crypto.SecretKey;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.CryptManager;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.MessageDeserializer;
import net.minecraft.util.MessageDeserializer2;
import net.minecraft.util.MessageSerializer;
import net.minecraft.util.MessageSerializer2;
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 Marker logMarkerStat = MarkerManager.getMarker("NETWORK_STAT", logMarkerNetwork);
public static final AttributeKey attrKeyConnectionState = new AttributeKey("protocol");
public static final AttributeKey attrKeyReceivable = new AttributeKey("receivable_packets");
public static final AttributeKey attrKeySendable = new AttributeKey("sendable_packets");
public static final NioEventLoopGroup eventLoops = new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build());
public static final NetworkStatistics STATISTICS = new NetworkStatistics();
/** Whether this NetworkManager deals with the client or server side of the connection */
private final boolean isClientSide;
/** The queue for received, unprioritized packets that will be processed at the earliest opportunity */
private final Queue receivedPacketsQueue = Queues.newConcurrentLinkedQueue();
/** 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;
/** The current connection state, being one of: HANDSHAKING, PLAY, STATUS, LOGIN */
private EnumConnectionState connectionState;
/** A String indicating why the network has shutdown. */
private IChatComponent terminationReason;
private boolean isEncrypted;
private static final String __OBFID = "CL_00001240";
public NetworkManager(boolean isClient)
{
this.isClientSide = isClient;
}
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();
this.setConnectionState(EnumConnectionState.HANDSHAKING);
}
/**
* Sets the new connection state and registers which packets this channel may send and receive
*/
public void setConnectionState(EnumConnectionState newState)
{
this.connectionState = (EnumConnectionState)this.channel.attr(attrKeyConnectionState).getAndSet(newState);
this.channel.attr(attrKeyReceivable).set(newState.func_150757_a(this.isClientSide));
this.channel.attr(attrKeySendable).set(newState.func_150754_b(this.isClientSide));
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_)
{
ChatComponentTranslation chatcomponenttranslation;
if (p_exceptionCaught_2_ instanceof TimeoutException)
{
chatcomponenttranslation = new ChatComponentTranslation("disconnect.timeout", new Object[0]);
}
else
{
chatcomponenttranslation = new ChatComponentTranslation("disconnect.genericReason", new Object[] {"Internal Exception: " + p_exceptionCaught_2_});
}
this.closeChannel(chatcomponenttranslation);
}
protected void channelRead0(ChannelHandlerContext p_channelRead0_1_, Packet p_channelRead0_2_)
{
if (this.channel.isOpen())
{
if (p_channelRead0_2_.hasPriority())
{
p_channelRead0_2_.processPacket(this.packetListener);
}
else
{
this.receivedPacketsQueue.add(p_channelRead0_2_);
}
}
}
/**
* 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;
}
/**
* Will flush the outbound queue and dispatch the supplied Packet if the channel is ready, otherwise it adds the
* packet to the outbound queue and registers the GenericFutureListener to fire after transmission
*/
public void scheduleOutboundPacket(Packet inPacket, GenericFutureListener ... futureListeners)
{
if (this.channel != null && this.channel.isOpen())
{
this.flushOutboundQueue();
this.dispatchPacket(inPacket, futureListeners);
}
else
{
this.outboundPacketsQueue.add(new NetworkManager.InboundHandlerTuplePacketListener(inPacket, futureListeners));
}
}
/**
* 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 enumconnectionstate = EnumConnectionState.getFromPacket(inPacket);
final EnumConnectionState enumconnectionstate1 = (EnumConnectionState)this.channel.attr(attrKeyConnectionState).get();
if (enumconnectionstate1 != enumconnectionstate && !( inPacket instanceof FMLProxyPacket))
{
logger.debug("Disabled auto read");
this.channel.config().setAutoRead(false);
}
if (this.channel.eventLoop().inEventLoop())
{
if (enumconnectionstate != enumconnectionstate1 && !( inPacket instanceof FMLProxyPacket))
{
this.setConnectionState(enumconnectionstate);
}
this.channel.writeAndFlush(inPacket).addListeners(futureListeners).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
}
else
{
this.channel.eventLoop().execute(new Runnable()
{
private static final String __OBFID = "CL_00001241";
public void run()
{
if (enumconnectionstate != enumconnectionstate1 && !( inPacket instanceof FMLProxyPacket))
{
NetworkManager.this.setConnectionState(enumconnectionstate);
}
NetworkManager.this.channel.writeAndFlush(inPacket).addListeners(futureListeners).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 inboundhandlertuplepacketlistener = (NetworkManager.InboundHandlerTuplePacketListener)this.outboundPacketsQueue.poll();
this.dispatchPacket(inboundhandlertuplepacketlistener.packet, inboundhandlertuplepacketlistener.futureListeners);
}
}
}
/**
* Checks timeouts and processes all packets received
*/
public void processReceivedPackets()
{
this.flushOutboundQueue();
EnumConnectionState enumconnectionstate = (EnumConnectionState)this.channel.attr(attrKeyConnectionState).get();
if (this.connectionState != enumconnectionstate)
{
if (this.connectionState != null)
{
this.packetListener.onConnectionStateTransition(this.connectionState, enumconnectionstate);
}
this.connectionState = enumconnectionstate;
}
if (this.packetListener != null)
{
for (int i = 1000; !this.receivedPacketsQueue.isEmpty() && i >= 0; --i)
{
Packet packet = (Packet)this.receivedPacketsQueue.poll();
packet.processPacket(this.packetListener);
}
this.packetListener.onNetworkTick();
}
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();
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;
}
/**
* Prepares a clientside NetworkManager: establishes a connection to the address and port supplied and configures
* the channel pipeline. Returns the newly created instance.
*/
@SideOnly(Side.CLIENT)
public static NetworkManager provideLanClient(InetAddress p_150726_0_, int p_150726_1_)
{
final NetworkManager networkmanager = new NetworkManager(true);
((Bootstrap)((Bootstrap)((Bootstrap)(new Bootstrap()).group(eventLoops)).handler(new ChannelInitializer()
{
private static final String __OBFID = "CL_00001242";
protected void initChannel(Channel p_initChannel_1_)
{
try
{
p_initChannel_1_.config().setOption(ChannelOption.IP_TOS, Integer.valueOf(24));
}
catch (ChannelException channelexception1)
{
;
}
try
{
p_initChannel_1_.config().setOption(ChannelOption.TCP_NODELAY, Boolean.valueOf(false));
}
catch (ChannelException channelexception)
{
;
}
p_initChannel_1_.pipeline().addLast("timeout", new ReadTimeoutHandler(20)).addLast("splitter", new MessageDeserializer2()).addLast("decoder", new MessageDeserializer(NetworkManager.STATISTICS)).addLast("prepender", new MessageSerializer2()).addLast("encoder", new MessageSerializer(NetworkManager.STATISTICS)).addLast("packet_handler", networkmanager);
}
})).channel(NioSocketChannel.class)).connect(p_150726_0_, p_150726_1_).syncUninterruptibly();
return networkmanager;
}
/**
* Prepares a clientside NetworkManager: establishes a connection to the socket supplied and configures the channel
* pipeline. Returns the newly created instance.
*/
@SideOnly(Side.CLIENT)
public static NetworkManager provideLocalClient(SocketAddress p_150722_0_)
{
final NetworkManager networkmanager = new NetworkManager(true);
((Bootstrap)((Bootstrap)((Bootstrap)(new Bootstrap()).group(eventLoops)).handler(new ChannelInitializer()
{
private static final String __OBFID = "CL_00001243";
protected void initChannel(Channel p_initChannel_1_)
{
p_initChannel_1_.pipeline().addLast("packet_handler", networkmanager);
}
})).channel(LocalChannel.class)).connect(p_150722_0_).syncUninterruptibly();
return networkmanager;
}
/**
* Adds an encoder+decoder to the channel pipeline. The parameter is the secret key used for encrypted communication
*/
public void enableEncryption(SecretKey key)
{
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)));
this.isEncrypted = true;
}
/**
* Returns true if this NetworkManager has an active channel, false otherwise
*/
public boolean isChannelOpen()
{
return this.channel != null && this.channel.isOpen();
}
/**
* 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);
}
protected void channelRead0(ChannelHandlerContext p_channelRead0_1_, Object p_channelRead0_2_)
{
this.channelRead0(p_channelRead0_1_, (Packet)p_channelRead0_2_);
}
public Channel channel()
{
/** The active channel */
return channel;
}
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;
}
}
}