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.net.Socket; 010 import java.net.SocketAddress; 011 import java.net.SocketException; 012 import java.security.PrivateKey; 013 import java.util.ArrayList; 014 import java.util.Collections; 015 import java.util.Iterator; 016 import java.util.List; 017 import java.util.concurrent.atomic.AtomicInteger; 018 import javax.crypto.SecretKey; 019 020 import cpw.mods.fml.common.network.FMLNetworkHandler; 021 022 public class TcpConnection implements INetworkManager 023 { 024 public static AtomicInteger field_74471_a = new AtomicInteger(); 025 public static AtomicInteger field_74469_b = new AtomicInteger(); 026 027 /** The object used for synchronization on the send queue. */ 028 private Object sendQueueLock; 029 030 /** The socket used by this network manager. */ 031 private Socket networkSocket; 032 033 /** The InetSocketAddress of the remote endpoint */ 034 private final SocketAddress remoteSocketAddress; 035 036 /** The input stream connected to the socket. */ 037 private volatile DataInputStream socketInputStream; 038 039 /** The output stream connected to the socket. */ 040 private volatile DataOutputStream socketOutputStream; 041 042 /** Whether the network is currently operational. */ 043 private volatile boolean isRunning; 044 045 /** 046 * Whether this network manager is currently terminating (and should ignore further errors). 047 */ 048 private volatile boolean isTerminating; 049 050 /** 051 * Linked list of packets that have been read and are awaiting processing. 052 */ 053 private List readPackets; 054 055 /** Linked list of packets awaiting sending. */ 056 private List dataPackets; 057 058 /** Linked list of packets with chunk data that are awaiting sending. */ 059 private List chunkDataPackets; 060 061 /** A reference to the NetHandler object. */ 062 private NetHandler theNetHandler; 063 064 /** 065 * Whether this server is currently terminating. If this is a client, this is always false. 066 */ 067 private boolean isServerTerminating; 068 069 /** The thread used for writing. */ 070 private Thread writeThread; 071 072 /** The thread used for reading. */ 073 private Thread readThread; 074 075 /** A String indicating why the network has shutdown. */ 076 private String terminationReason; 077 private Object[] field_74480_w; 078 private int field_74490_x; 079 080 /** 081 * The length in bytes of the packets in both send queues (data and chunkData). 082 */ 083 private int sendQueueByteLength; 084 public static int[] field_74470_c = new int[256]; 085 public static int[] field_74467_d = new int[256]; 086 public int field_74468_e; 087 boolean isInputBeingDecrypted; 088 boolean isOutputEncrypted; 089 private SecretKey sharedKeyForEncryption; 090 private PrivateKey field_74463_A; 091 private int field_74464_B; 092 093 @SideOnly(Side.CLIENT) 094 public TcpConnection(Socket par1Socket, String par2Str, NetHandler par3NetHandler) throws IOException 095 { 096 this(par1Socket, par2Str, par3NetHandler, (PrivateKey)null); 097 } 098 099 public TcpConnection(Socket par1Socket, String par2Str, NetHandler par3NetHandler, PrivateKey par4PrivateKey) throws IOException 100 { 101 this.sendQueueLock = new Object(); 102 this.isRunning = true; 103 this.isTerminating = false; 104 this.readPackets = Collections.synchronizedList(new ArrayList()); 105 this.dataPackets = Collections.synchronizedList(new ArrayList()); 106 this.chunkDataPackets = Collections.synchronizedList(new ArrayList()); 107 this.isServerTerminating = false; 108 this.terminationReason = ""; 109 this.field_74490_x = 0; 110 this.sendQueueByteLength = 0; 111 this.field_74468_e = 0; 112 this.isInputBeingDecrypted = false; 113 this.isOutputEncrypted = false; 114 this.sharedKeyForEncryption = null; 115 this.field_74463_A = null; 116 this.field_74464_B = 50; 117 this.field_74463_A = par4PrivateKey; 118 this.networkSocket = par1Socket; 119 this.remoteSocketAddress = par1Socket.getRemoteSocketAddress(); 120 this.theNetHandler = par3NetHandler; 121 122 try 123 { 124 par1Socket.setSoTimeout(30000); 125 par1Socket.setTrafficClass(24); 126 } 127 catch (SocketException var6) 128 { 129 System.err.println(var6.getMessage()); 130 } 131 132 this.socketInputStream = new DataInputStream(par1Socket.getInputStream()); 133 this.socketOutputStream = new DataOutputStream(new BufferedOutputStream(par1Socket.getOutputStream(), 5120)); 134 this.readThread = new TcpReaderThread(this, par2Str + " read thread"); 135 this.writeThread = new TcpWriterThread(this, par2Str + " write thread"); 136 this.readThread.start(); 137 this.writeThread.start(); 138 } 139 140 @SideOnly(Side.CLIENT) 141 public void closeConnections() 142 { 143 this.wakeThreads(); 144 this.writeThread = null; 145 this.readThread = null; 146 } 147 148 /** 149 * Sets the NetHandler for this NetworkManager. Server-only. 150 */ 151 public void setNetHandler(NetHandler par1NetHandler) 152 { 153 this.theNetHandler = par1NetHandler; 154 } 155 156 /** 157 * Adds the packet to the correct send queue (chunk data packets go to a separate queue). 158 */ 159 public void addToSendQueue(Packet par1Packet) 160 { 161 if (!this.isServerTerminating) 162 { 163 Object var2 = this.sendQueueLock; 164 165 synchronized (this.sendQueueLock) 166 { 167 this.sendQueueByteLength += par1Packet.getPacketSize() + 1; 168 169 if (par1Packet.isChunkDataPacket) 170 { 171 this.chunkDataPackets.add(par1Packet); 172 } 173 else 174 { 175 this.dataPackets.add(par1Packet); 176 } 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 || 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).func_73304_d(); 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.field_74464_B-- <= 0 && (this.field_74468_e == 0 || 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.field_74464_B = 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).func_73303_a(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.isWritePacket() && this.theNetHandler.canProcessPackets()) 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 this.socketInputStream = null; 402 this.socketOutputStream.close(); 403 this.socketOutputStream = null; 404 this.networkSocket.close(); 405 this.networkSocket = null; 406 } 407 catch (Throwable var4) 408 { 409 ; 410 } 411 } 412 } 413 414 /** 415 * Checks timeouts and processes all pending read packets. 416 */ 417 public void processReadPackets() 418 { 419 if (this.sendQueueByteLength > 2097152) 420 { 421 this.networkShutdown("disconnect.overflow", new Object[0]); 422 } 423 424 if (this.readPackets.isEmpty()) 425 { 426 if (this.field_74490_x++ == 1200) 427 { 428 this.networkShutdown("disconnect.timeout", new Object[0]); 429 } 430 } 431 else 432 { 433 this.field_74490_x = 0; 434 } 435 436 int var1 = 1000; 437 438 while (!this.readPackets.isEmpty() && var1-- >= 0) 439 { 440 Packet var2 = (Packet)this.readPackets.remove(0); 441 var2.processPacket(this.theNetHandler); 442 } 443 444 this.wakeThreads(); 445 446 if (this.isTerminating && this.readPackets.isEmpty()) 447 { 448 this.theNetHandler.handleErrorMessage(this.terminationReason, this.field_74480_w); 449 FMLNetworkHandler.onConnectionClosed(this, this.theNetHandler.getPlayer()); 450 } 451 } 452 453 /** 454 * Return the InetSocketAddress of the remote endpoint 455 */ 456 public SocketAddress getSocketAddress() 457 { 458 return this.remoteSocketAddress; 459 } 460 461 /** 462 * Shuts down the server. (Only actually used on the server) 463 */ 464 public void serverShutdown() 465 { 466 if (!this.isServerTerminating) 467 { 468 this.wakeThreads(); 469 this.isServerTerminating = true; 470 this.readThread.interrupt(); 471 (new TcpMonitorThread(this)).start(); 472 } 473 } 474 475 private void decryptInputStream() throws IOException 476 { 477 this.isInputBeingDecrypted = true; 478 this.socketInputStream = new DataInputStream(CryptManager.decryptInputStream(this.sharedKeyForEncryption, this.networkSocket.getInputStream())); 479 } 480 481 /** 482 * flushes the stream and replaces it with an encryptedOutputStream 483 */ 484 private void encryptOuputStream() throws IOException 485 { 486 this.socketOutputStream.flush(); 487 this.isOutputEncrypted = true; 488 this.socketOutputStream = new DataOutputStream(new BufferedOutputStream(CryptManager.encryptOuputStream(this.sharedKeyForEncryption, this.networkSocket.getOutputStream()), 5120)); 489 } 490 491 /** 492 * returns 0 for memoryConnections 493 */ 494 public int packetSize() 495 { 496 return this.chunkDataPackets.size(); 497 } 498 499 public Socket getSocket() 500 { 501 return this.networkSocket; 502 } 503 504 /** 505 * Whether the network is operational. 506 */ 507 static boolean isRunning(TcpConnection par0TcpConnection) 508 { 509 return par0TcpConnection.isRunning; 510 } 511 512 /** 513 * Is the server terminating? Client side aways returns false. 514 */ 515 static boolean isServerTerminating(TcpConnection par0TcpConnection) 516 { 517 return par0TcpConnection.isServerTerminating; 518 } 519 520 /** 521 * Static accessor to readPacket. 522 */ 523 static boolean readNetworkPacket(TcpConnection par0TcpConnection) 524 { 525 return par0TcpConnection.readPacket(); 526 } 527 528 /** 529 * Static accessor to sendPacket. 530 */ 531 static boolean sendNetworkPacket(TcpConnection par0TcpConnection) 532 { 533 return par0TcpConnection.sendPacket(); 534 } 535 536 static DataOutputStream getOutputStream(TcpConnection par0TcpConnection) 537 { 538 return par0TcpConnection.socketOutputStream; 539 } 540 541 /** 542 * Gets whether the Network manager is terminating. 543 */ 544 static boolean isTerminating(TcpConnection par0TcpConnection) 545 { 546 return par0TcpConnection.isTerminating; 547 } 548 549 /** 550 * Sends the network manager an error 551 */ 552 static void sendError(TcpConnection par0TcpConnection, Exception par1Exception) 553 { 554 par0TcpConnection.onNetworkError(par1Exception); 555 } 556 557 /** 558 * Returns the read thread. 559 */ 560 static Thread getReadThread(TcpConnection par0TcpConnection) 561 { 562 return par0TcpConnection.readThread; 563 } 564 565 /** 566 * Returns the write thread. 567 */ 568 static Thread getWriteThread(TcpConnection par0TcpConnection) 569 { 570 return par0TcpConnection.writeThread; 571 } 572 }