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 }