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 }