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