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