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 }