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 }