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