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