    001    package net.minecraft.network;
    002
    003    import cpw.mods.fml.common.network.FMLNetworkHandler;
    004    import cpw.mods.fml.relauncher.Side;
    005    import cpw.mods.fml.relauncher.SideOnly;
    006    import java.io.BufferedOutputStream;
    007    import java.io.DataInputStream;
    008    import java.io.DataOutputStream;
    009    import java.io.IOException;
    010    import java.io.InputStream;
    011    import java.net.Socket;
    012    import java.net.SocketAddress;
    013    import java.net.SocketException;
    014    import java.security.PrivateKey;
    015    import java.util.ArrayList;
    016    import java.util.Collections;
    017    import java.util.Iterator;
    018    import java.util.List;
    019    import java.util.concurrent.atomic.AtomicInteger;
    020    import javax.crypto.SecretKey;
    021    import net.minecraft.network.packet.NetHandler;
    022    import net.minecraft.network.packet.Packet;
    023    import net.minecraft.network.packet.Packet252SharedKey;
    024    import net.minecraft.util.CryptManager;
    025
    026    public class TcpConnection implements INetworkManager
    027    {
    028        public static AtomicInteger field_74471_a = new AtomicInteger();
    029        public static AtomicInteger field_74469_b = new AtomicInteger();
    030
    031        /** The object used for synchronization on the send queue. */
    032        private Object sendQueueLock;
    033
    034        /** The socket used by this network manager. */
    035        private Socket networkSocket;
    036
    037        /** The InetSocketAddress of the remote endpoint */
    038        private final SocketAddress remoteSocketAddress;
    039
    040        /** The input stream connected to the socket. */
    041        private volatile DataInputStream socketInputStream;
    042
    043        /** The output stream connected to the socket. */
    044        private volatile DataOutputStream socketOutputStream;
    045
    046        /** Whether the network is currently operational. */
    047        private volatile boolean isRunning;
    048
    049        /**
    050         * Whether this network manager is currently terminating (and should ignore further errors).
    051         */
    052        private volatile boolean isTerminating;
    053
    054        /**
    055         * Linked list of packets that have been read and are awaiting processing.
    056         */
    057        private List readPackets;
    058
    059        /** Linked list of packets awaiting sending. */
    060        private List dataPackets;
    061
    062        /** Linked list of packets with chunk data that are awaiting sending. */
    063        private List chunkDataPackets;
    064
    065        /** A reference to the NetHandler object. */
    066        private NetHandler theNetHandler;
    067
    068        /**
    069         * Whether this server is currently terminating. If this is a client, this is always false.
    070         */
    071        private boolean isServerTerminating;
    072
    073        /** The thread used for writing. */
    074        private Thread writeThread;
    075
    076        /** The thread used for reading. */
    077        private Thread readThread;
    078
    079        /** A String indicating why the network has shutdown. */
    080        private String terminationReason;
    081        private Object[] field_74480_w;
    082        private int field_74490_x;
    083
    084        /**
    085         * The length in bytes of the packets in both send queues (data and chunkData).
    086         */
    087        private int sendQueueByteLength;
    088        public static int[] field_74470_c = new int[256];
    089        public static int[] field_74467_d = new int[256];
    090        public int field_74468_e;
    091        boolean isInputBeingDecrypted;
    092        boolean isOutputEncrypted;
    093        private SecretKey sharedKeyForEncryption;
    094        private PrivateKey field_74463_A;
    095        private int field_74464_B;
    096
    097        @SideOnly(Side.CLIENT)
    098        public TcpConnection(Socket par1Socket, String par2Str, NetHandler par3NetHandler) throws IOException
    099        {
    100            this(par1Socket, par2Str, par3NetHandler, (PrivateKey)null);
    101        }
    102
    103        public TcpConnection(Socket par1Socket, String par2Str, NetHandler par3NetHandler, PrivateKey par4PrivateKey) throws IOException
    104        {
    105            this.sendQueueLock = new Object();
    106            this.isRunning = true;
    107            this.isTerminating = false;
    108            this.readPackets = Collections.synchronizedList(new ArrayList());
    109            this.dataPackets = Collections.synchronizedList(new ArrayList());
    110            this.chunkDataPackets = Collections.synchronizedList(new ArrayList());
    111            this.isServerTerminating = false;
    112            this.terminationReason = "";
    113            this.field_74490_x = 0;
    114            this.sendQueueByteLength = 0;
    115            this.field_74468_e = 0;
    116            this.isInputBeingDecrypted = false;
    117            this.isOutputEncrypted = false;
    118            this.sharedKeyForEncryption = null;
    119            this.field_74463_A = null;
    120            this.field_74464_B = 50;
    121            this.field_74463_A = par4PrivateKey;
    122            this.networkSocket = par1Socket;
    123            this.remoteSocketAddress = par1Socket.getRemoteSocketAddress();
    124            this.theNetHandler = par3NetHandler;
    125
    126            try
    127            {
    128                par1Socket.setSoTimeout(30000);
    129                par1Socket.setTrafficClass(24);
    130            }
    131            catch (SocketException var6)
    132            {
    133                System.err.println(var6.getMessage());
    134            }
    135
    136            this.socketInputStream = new DataInputStream(par1Socket.getInputStream());
    137            this.socketOutputStream = new DataOutputStream(new BufferedOutputStream(par1Socket.getOutputStream(), 5120));
    138            this.readThread = new TcpReaderThread(this, par2Str + " read thread");
    139            this.writeThread = new TcpWriterThread(this, par2Str + " write thread");
    140            this.readThread.start();
    141            this.writeThread.start();
    142        }
    143
    144        @SideOnly(Side.CLIENT)
    145        public void closeConnections()
    146        {
    147            this.wakeThreads();
    148            this.writeThread = null;
    149            this.readThread = null;
    150        }
    151
    152        /**
    153         * Sets the NetHandler for this NetworkManager. Server-only.
    154         */
    155        public void setNetHandler(NetHandler par1NetHandler)
    156        {
    157            this.theNetHandler = par1NetHandler;
    158        }
    159
    160        /**
    161         * Adds the packet to the correct send queue (chunk data packets go to a separate queue).
    162         */
    163        public void addToSendQueue(Packet par1Packet)
    164        {
    165            if (!this.isServerTerminating)
    166            {
    167                Object var2 = this.sendQueueLock;
    168
    169                synchronized (this.sendQueueLock)
    170                {
    171                    this.sendQueueByteLength += par1Packet.getPacketSize() + 1;
    172                    this.dataPackets.add(par1Packet);
    173                }
    174            }
    175        }
    176
    177        /**
    178         * Sends a data packet if there is one to send, or sends a chunk data packet if there is one and the counter is up,
    179         * or does nothing.
    180         */
    181        private boolean sendPacket()
    182        {
    183            boolean var1 = false;
    184
    185            try
    186            {
    187                Packet var2;
    188                int var10001;
    189                int[] var10000;
    190
    191                if (this.field_74468_e == 0 || !this.dataPackets.isEmpty() && System.currentTimeMillis() - ((Packet)this.dataPackets.get(0)).creationTimeMillis >= (long)this.field_74468_e)
    192                {
    193                    var2 = this.func_74460_a(false);
    194
    195                    if (var2 != null)
    196                    {
    197                        Packet.writePacket(var2, this.socketOutputStream);
    198
    199                        if (var2 instanceof Packet252SharedKey && !this.isOutputEncrypted)
    200                        {
    201                            if (!this.theNetHandler.isServerHandler())
    202                            {
    203                                this.sharedKeyForEncryption = ((Packet252SharedKey)var2).func_73304_d();
    204                            }
    205
    206                            this.encryptOuputStream();
    207                        }
    208
    209                        var10000 = field_74467_d;
    210                        var10001 = var2.getPacketId();
    211                        var10000[var10001] += var2.getPacketSize() + 1;
    212                        var1 = true;
    213                    }
    214                }
    215
    216                if (this.field_74464_B-- <= 0 && (this.field_74468_e == 0 || !this.chunkDataPackets.isEmpty() && System.currentTimeMillis() - ((Packet)this.chunkDataPackets.get(0)).creationTimeMillis >= (long)this.field_74468_e))
    217                {
    218                    var2 = this.func_74460_a(true);
    219
    220                    if (var2 != null)
    221                    {
    222                        Packet.writePacket(var2, this.socketOutputStream);
    223                        var10000 = field_74467_d;
    224                        var10001 = var2.getPacketId();
    225                        var10000[var10001] += var2.getPacketSize() + 1;
    226                        this.field_74464_B = 0;
    227                        var1 = true;
    228                    }
    229                }
    230
    231                return var1;
    232            }
    233            catch (Exception var3)
    234            {
    235                if (!this.isTerminating)
    236                {
    237                    this.onNetworkError(var3);
    238                }
    239
    240                return false;
    241            }
    242        }
    243
    244        private Packet func_74460_a(boolean par1)
    245        {
    246            Packet var2 = null;
    247            List var3 = par1 ? this.chunkDataPackets : this.dataPackets;
    248            Object var4 = this.sendQueueLock;
    249
    250            synchronized (this.sendQueueLock)
    251            {
    252                while (!var3.isEmpty() && var2 == null)
    253                {
    254                    var2 = (Packet)var3.remove(0);
    255                    this.sendQueueByteLength -= var2.getPacketSize() + 1;
    256
    257                    if (this.func_74454_a(var2, par1))
    258                    {
    259                        var2 = null;
    260                    }
    261                }
    262
    263                return var2;
    264            }
    265        }
    266
    267        private boolean func_74454_a(Packet par1Packet, boolean par2)
    268        {
    269            if (!par1Packet.isRealPacket())
    270            {
    271                return false;
    272            }
    273            else
    274            {
    275                List var3 = par2 ? this.chunkDataPackets : this.dataPackets;
    276                Iterator var4 = var3.iterator();
    277                Packet var5;
    278
    279                do
    280                {
    281                    if (!var4.hasNext())
    282                    {
    283                        return false;
    284                    }
    285
    286                    var5 = (Packet)var4.next();
    287                }
    288                while (var5.getPacketId() != par1Packet.getPacketId());
    289
    290                return par1Packet.containsSameEntityIDAs(var5);
    291            }
    292        }
    293
    294        /**
    295         * Wakes reader and writer threads
    296         */
    297        public void wakeThreads()
    298        {
    299            if (this.readThread != null)
    300            {
    301                this.readThread.interrupt();
    302            }
    303
    304            if (this.writeThread != null)
    305            {
    306                this.writeThread.interrupt();
    307            }
    308        }
    309
    310        /**
    311         * Reads a single packet from the input stream and adds it to the read queue. If no packet is read, it shuts down
    312         * the network.
    313         */
    314        private boolean readPacket()
    315        {
    316            boolean var1 = false;
    317
    318            try
    319            {
    320                Packet var2 = Packet.readPacket(this.socketInputStream, this.theNetHandler.isServerHandler(), this.networkSocket);
    321
    322                if (var2 != null)
    323                {
    324                    if (var2 instanceof Packet252SharedKey && !this.isInputBeingDecrypted)
    325                    {
    326                        if (this.theNetHandler.isServerHandler())
    327                        {
    328                            this.sharedKeyForEncryption = ((Packet252SharedKey)var2).func_73303_a(this.field_74463_A);
    329                        }
    330
    331                        this.decryptInputStream();
    332                    }
    333
    334                    int[] var10000 = field_74470_c;
    335                    int var10001 = var2.getPacketId();
    336                    var10000[var10001] += var2.getPacketSize() + 1;
    337
    338                    if (!this.isServerTerminating)
    339                    {
    340                        if (var2.isWritePacket() && this.theNetHandler.canProcessPackets())
    341                        {
    342                            this.field_74490_x = 0;
    343                            var2.processPacket(this.theNetHandler);
    344                        }
    345                        else
    346                        {
    347                            this.readPackets.add(var2);
    348                        }
    349                    }
    350
    351                    var1 = true;
    352                }
    353                else
    354                {
    355                    this.networkShutdown("disconnect.endOfStream", new Object[0]);
    356                }
    357
    358                return var1;
    359            }
    360            catch (Exception var3)
    361            {
    362                if (!this.isTerminating)
    363                {
    364                    this.onNetworkError(var3);
    365                }
    366
    367                return false;
    368            }
    369        }
    370
    371        /**
    372         * Used to report network errors and causes a network shutdown.
    373         */
    374        private void onNetworkError(Exception par1Exception)
    375        {
    376            par1Exception.printStackTrace();
    377            this.networkShutdown("disconnect.genericReason", new Object[] {"Internal exception: " + par1Exception.toString()});
    378        }
    379
    380        /**
    381         * Shuts down the network with the specified reason. Closes all streams and sockets, spawns NetworkMasterThread to
    382         * stop reading and writing threads.
    383         */
    384        public void networkShutdown(String par1Str, Object ... par2ArrayOfObj)
    385        {
    386            if (this.isRunning)
    387            {
    388                this.isTerminating = true;
    389                this.terminationReason = par1Str;
    390                this.field_74480_w = par2ArrayOfObj;
    391                this.isRunning = false;
    392                (new TcpMasterThread(this)).start();
    393
    394                try
    395                {
    396                    this.socketInputStream.close();
    397                }
    398                catch (Throwable var6)
    399                {
    400                    ;
    401                }
    402
    403                try
    404                {
    405                    this.socketOutputStream.close();
    406                }
    407                catch (Throwable var5)
    408                {
    409                    ;
    410                }
    411
    412                try
    413                {
    414                    this.networkSocket.close();
    415                }
    416                catch (Throwable var4)
    417                {
    418                    ;
    419                }
    420
    421                this.socketInputStream = null;
    422                this.socketOutputStream = null;
    423                this.networkSocket = null;
    424            }
    425        }
    426
    427        /**
    428         * Checks timeouts and processes all pending read packets.
    429         */
    430        public void processReadPackets()
    431        {
    432            if (this.sendQueueByteLength > 2097152)
    433            {
    434                this.networkShutdown("disconnect.overflow", new Object[0]);
    435            }
    436
    437            if (this.readPackets.isEmpty())
    438            {
    439                if (this.field_74490_x++ == 1200)
    440                {
    441                    this.networkShutdown("disconnect.timeout", new Object[0]);
    442                }
    443            }
    444            else
    445            {
    446                this.field_74490_x = 0;
    447            }
    448
    449            int var1 = 1000;
    450
    451            while (!this.readPackets.isEmpty() && var1-- >= 0)
    452            {
    453                Packet var2 = (Packet)this.readPackets.remove(0);
    454                var2.processPacket(this.theNetHandler);
    455            }
    456
    457            this.wakeThreads();
    458
    459            if (this.isTerminating && this.readPackets.isEmpty())
    460            {
    461                this.theNetHandler.handleErrorMessage(this.terminationReason, this.field_74480_w);
    462                FMLNetworkHandler.onConnectionClosed(this, this.theNetHandler.getPlayer());
    463            }
    464        }
    465
    466        /**
    467         * Return the InetSocketAddress of the remote endpoint
    468         */
    469        public SocketAddress getSocketAddress()
    470        {
    471            return this.remoteSocketAddress;
    472        }
    473
    474        /**
    475         * Shuts down the server. (Only actually used on the server)
    476         */
    477        public void serverShutdown()
    478        {
    479            if (!this.isServerTerminating)
    480            {
    481                this.wakeThreads();
    482                this.isServerTerminating = true;
    483                this.readThread.interrupt();
    484                (new TcpMonitorThread(this)).start();
    485            }
    486        }
    487
    488        private void decryptInputStream() throws IOException
    489        {
    490            this.isInputBeingDecrypted = true;
    491            InputStream var1 = this.networkSocket.getInputStream();
    492            this.socketInputStream = new DataInputStream(CryptManager.decryptInputStream(this.sharedKeyForEncryption, var1));
    493        }
    494
    495        /**
    496         * flushes the stream and replaces it with an encryptedOutputStream
    497         */
    498        private void encryptOuputStream() throws IOException
    499        {
    500            this.socketOutputStream.flush();
    501            this.isOutputEncrypted = true;
    502            BufferedOutputStream var1 = new BufferedOutputStream(CryptManager.encryptOuputStream(this.sharedKeyForEncryption, this.networkSocket.getOutputStream()), 5120);
    503            this.socketOutputStream = new DataOutputStream(var1);
    504        }
    505
    506        /**
    507         * returns 0 for memoryConnections
    508         */
    509        public int packetSize()
    510        {
    511            return this.chunkDataPackets.size();
    512        }
    513
    514        public Socket getSocket()
    515        {
    516            return this.networkSocket;
    517        }
    518
    519        /**
    520         * Whether the network is operational.
    521         */
    522        static boolean isRunning(TcpConnection par0TcpConnection)
    523        {
    524            return par0TcpConnection.isRunning;
    525        }
    526
    527        /**
    528         * Is the server terminating? Client side aways returns false.
    529         */
    530        static boolean isServerTerminating(TcpConnection par0TcpConnection)
    531        {
    532            return par0TcpConnection.isServerTerminating;
    533        }
    534
    535        /**
    536         * Static accessor to readPacket.
    537         */
    538        static boolean readNetworkPacket(TcpConnection par0TcpConnection)
    539        {
    540            return par0TcpConnection.readPacket();
    541        }
    542
    543        /**
    544         * Static accessor to sendPacket.
    545         */
    546        static boolean sendNetworkPacket(TcpConnection par0TcpConnection)
    547        {
    548            return par0TcpConnection.sendPacket();
    549        }
    550
    551        static DataOutputStream getOutputStream(TcpConnection par0TcpConnection)
    552        {
    553            return par0TcpConnection.socketOutputStream;
    554        }
    555
    556        /**
    557         * Gets whether the Network manager is terminating.
    558         */
    559        static boolean isTerminating(TcpConnection par0TcpConnection)
    560        {
    561            return par0TcpConnection.isTerminating;
    562        }
    563
    564        /**
    565         * Sends the network manager an error
    566         */
    567        static void sendError(TcpConnection par0TcpConnection, Exception par1Exception)
    568        {
    569            par0TcpConnection.onNetworkError(par1Exception);
    570        }
    571
    572        /**
    573         * Returns the read thread.
    574         */
    575        static Thread getReadThread(TcpConnection par0TcpConnection)
    576        {
    577            return par0TcpConnection.readThread;
    578        }
    579
    580        /**
    581         * Returns the write thread.
    582         */
    583        static Thread getWriteThread(TcpConnection par0TcpConnection)
    584        {
    585            return par0TcpConnection.writeThread;
    586        }
    587    }