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 }