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 }