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
096 /**
097 * Delay for sending pending chunk data packets (as opposed to pending non-chunk data packets)
098 */
099 private int chunkDataPacketsDelay;
100
101 @SideOnly(Side.CLIENT)
102 public TcpConnection(Socket par1Socket, String par2Str, NetHandler par3NetHandler) throws IOException
103 {
104 this(par1Socket, par2Str, par3NetHandler, (PrivateKey)null);
105 }
106
107 public TcpConnection(Socket par1Socket, String par2Str, NetHandler par3NetHandler, PrivateKey par4PrivateKey) throws IOException
108 {
109 this.sendQueueLock = new Object();
110 this.isRunning = true;
111 this.isTerminating = false;
112 this.readPackets = Collections.synchronizedList(new ArrayList());
113 this.dataPackets = Collections.synchronizedList(new ArrayList());
114 this.chunkDataPackets = Collections.synchronizedList(new ArrayList());
115 this.isServerTerminating = false;
116 this.terminationReason = "";
117 this.field_74490_x = 0;
118 this.sendQueueByteLength = 0;
119 this.field_74468_e = 0;
120 this.isInputBeingDecrypted = false;
121 this.isOutputEncrypted = false;
122 this.sharedKeyForEncryption = null;
123 this.field_74463_A = null;
124 this.chunkDataPacketsDelay = 50;
125 this.field_74463_A = par4PrivateKey;
126 this.networkSocket = par1Socket;
127 this.remoteSocketAddress = par1Socket.getRemoteSocketAddress();
128 this.theNetHandler = par3NetHandler;
129
130 try
131 {
132 par1Socket.setSoTimeout(30000);
133 par1Socket.setTrafficClass(24);
134 }
135 catch (SocketException var6)
136 {
137 System.err.println(var6.getMessage());
138 }
139
140 this.socketInputStream = new DataInputStream(par1Socket.getInputStream());
141 this.socketOutputStream = new DataOutputStream(new BufferedOutputStream(par1Socket.getOutputStream(), 5120));
142 this.readThread = new TcpReaderThread(this, par2Str + " read thread");
143 this.writeThread = new TcpWriterThread(this, par2Str + " write thread");
144 this.readThread.start();
145 this.writeThread.start();
146 }
147
148 @SideOnly(Side.CLIENT)
149 public void closeConnections()
150 {
151 this.wakeThreads();
152 this.writeThread = null;
153 this.readThread = null;
154 }
155
156 /**
157 * Sets the NetHandler for this NetworkManager. Server-only.
158 */
159 public void setNetHandler(NetHandler par1NetHandler)
160 {
161 this.theNetHandler = par1NetHandler;
162 }
163
164 /**
165 * Adds the packet to the correct send queue (chunk data packets go to a separate queue).
166 */
167 public void addToSendQueue(Packet par1Packet)
168 {
169 if (!this.isServerTerminating)
170 {
171 Object var2 = this.sendQueueLock;
172
173 synchronized (this.sendQueueLock)
174 {
175 this.sendQueueByteLength += par1Packet.getPacketSize() + 1;
176 this.dataPackets.add(par1Packet);
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 || !this.dataPackets.isEmpty() && 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).getSharedKey();
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.chunkDataPacketsDelay-- <= 0 && (this.field_74468_e == 0 || !this.chunkDataPackets.isEmpty() && 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.chunkDataPacketsDelay = 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).getSharedKey(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.canProcessAsync() && this.theNetHandler.canProcessPacketsAsync())
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 }
402 catch (Throwable var6)
403 {
404 ;
405 }
406
407 try
408 {
409 this.socketOutputStream.close();
410 }
411 catch (Throwable var5)
412 {
413 ;
414 }
415
416 try
417 {
418 this.networkSocket.close();
419 }
420 catch (Throwable var4)
421 {
422 ;
423 }
424
425 this.socketInputStream = null;
426 this.socketOutputStream = null;
427 this.networkSocket = null;
428 }
429 }
430
431 /**
432 * Checks timeouts and processes all pending read packets.
433 */
434 public void processReadPackets()
435 {
436 if (this.sendQueueByteLength > 2097152)
437 {
438 this.networkShutdown("disconnect.overflow", new Object[0]);
439 }
440
441 if (this.readPackets.isEmpty())
442 {
443 if (this.field_74490_x++ == 1200)
444 {
445 this.networkShutdown("disconnect.timeout", new Object[0]);
446 }
447 }
448 else
449 {
450 this.field_74490_x = 0;
451 }
452
453 int var1 = 1000;
454
455 while (!this.readPackets.isEmpty() && var1-- >= 0)
456 {
457 Packet var2 = (Packet)this.readPackets.remove(0);
458 var2.processPacket(this.theNetHandler);
459 }
460
461 this.wakeThreads();
462
463 if (this.isTerminating && this.readPackets.isEmpty())
464 {
465 this.theNetHandler.handleErrorMessage(this.terminationReason, this.field_74480_w);
466 FMLNetworkHandler.onConnectionClosed(this, this.theNetHandler.getPlayer());
467 }
468 }
469
470 /**
471 * Return the InetSocketAddress of the remote endpoint
472 */
473 public SocketAddress getSocketAddress()
474 {
475 return this.remoteSocketAddress;
476 }
477
478 /**
479 * Shuts down the server. (Only actually used on the server)
480 */
481 public void serverShutdown()
482 {
483 if (!this.isServerTerminating)
484 {
485 this.wakeThreads();
486 this.isServerTerminating = true;
487 this.readThread.interrupt();
488 (new TcpMonitorThread(this)).start();
489 }
490 }
491
492 private void decryptInputStream() throws IOException
493 {
494 this.isInputBeingDecrypted = true;
495 InputStream var1 = this.networkSocket.getInputStream();
496 this.socketInputStream = new DataInputStream(CryptManager.decryptInputStream(this.sharedKeyForEncryption, var1));
497 }
498
499 /**
500 * flushes the stream and replaces it with an encryptedOutputStream
501 */
502 private void encryptOuputStream() throws IOException
503 {
504 this.socketOutputStream.flush();
505 this.isOutputEncrypted = true;
506 BufferedOutputStream var1 = new BufferedOutputStream(CryptManager.encryptOuputStream(this.sharedKeyForEncryption, this.networkSocket.getOutputStream()), 5120);
507 this.socketOutputStream = new DataOutputStream(var1);
508 }
509
510 /**
511 * returns 0 for memoryConnections
512 */
513 public int packetSize()
514 {
515 return this.chunkDataPackets.size();
516 }
517
518 public Socket getSocket()
519 {
520 return this.networkSocket;
521 }
522
523 /**
524 * Whether the network is operational.
525 */
526 static boolean isRunning(TcpConnection par0TcpConnection)
527 {
528 return par0TcpConnection.isRunning;
529 }
530
531 /**
532 * Is the server terminating? Client side aways returns false.
533 */
534 static boolean isServerTerminating(TcpConnection par0TcpConnection)
535 {
536 return par0TcpConnection.isServerTerminating;
537 }
538
539 /**
540 * Static accessor to readPacket.
541 */
542 static boolean readNetworkPacket(TcpConnection par0TcpConnection)
543 {
544 return par0TcpConnection.readPacket();
545 }
546
547 /**
548 * Static accessor to sendPacket.
549 */
550 static boolean sendNetworkPacket(TcpConnection par0TcpConnection)
551 {
552 return par0TcpConnection.sendPacket();
553 }
554
555 static DataOutputStream getOutputStream(TcpConnection par0TcpConnection)
556 {
557 return par0TcpConnection.socketOutputStream;
558 }
559
560 /**
561 * Gets whether the Network manager is terminating.
562 */
563 static boolean isTerminating(TcpConnection par0TcpConnection)
564 {
565 return par0TcpConnection.isTerminating;
566 }
567
568 /**
569 * Sends the network manager an error
570 */
571 static void sendError(TcpConnection par0TcpConnection, Exception par1Exception)
572 {
573 par0TcpConnection.onNetworkError(par1Exception);
574 }
575
576 /**
577 * Returns the read thread.
578 */
579 static Thread getReadThread(TcpConnection par0TcpConnection)
580 {
581 return par0TcpConnection.readThread;
582 }
583
584 /**
585 * Returns the write thread.
586 */
587 static Thread getWriteThread(TcpConnection par0TcpConnection)
588 {
589 return par0TcpConnection.writeThread;
590 }
591 }