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 NetworkManager 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 public void setNetHandler(NetHandler par1NetHandler) 149 { 150 this.theNetHandler = par1NetHandler; 151 } 152 153 /** 154 * Adds the packet to the correct send queue (chunk data packets go to a separate queue). 155 */ 156 public void addToSendQueue(Packet par1Packet) 157 { 158 if (!this.isServerTerminating) 159 { 160 Object var2 = this.sendQueueLock; 161 162 synchronized (this.sendQueueLock) 163 { 164 this.sendQueueByteLength += par1Packet.getPacketSize() + 1; 165 166 if (par1Packet.isChunkDataPacket) 167 { 168 this.chunkDataPackets.add(par1Packet); 169 } 170 else 171 { 172 this.dataPackets.add(par1Packet); 173 } 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 || 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 || 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()); 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 this.socketInputStream = null; 399 this.socketOutputStream.close(); 400 this.socketOutputStream = null; 401 this.networkSocket.close(); 402 this.networkSocket = null; 403 } 404 catch (Throwable var4) 405 { 406 ; 407 } 408 } 409 } 410 411 /** 412 * Checks timeouts and processes all pending read packets. 413 */ 414 public void processReadPackets() 415 { 416 if (this.sendQueueByteLength > 2097152) 417 { 418 this.networkShutdown("disconnect.overflow", new Object[0]); 419 } 420 421 if (this.readPackets.isEmpty()) 422 { 423 if (this.field_74490_x++ == 1200) 424 { 425 this.networkShutdown("disconnect.timeout", new Object[0]); 426 } 427 } 428 else 429 { 430 this.field_74490_x = 0; 431 } 432 433 int var1 = 1000; 434 435 while (!this.readPackets.isEmpty() && var1-- >= 0) 436 { 437 Packet var2 = (Packet)this.readPackets.remove(0); 438 var2.processPacket(this.theNetHandler); 439 } 440 441 this.wakeThreads(); 442 443 if (this.isTerminating && this.readPackets.isEmpty()) 444 { 445 this.theNetHandler.handleErrorMessage(this.terminationReason, this.field_74480_w); 446 FMLNetworkHandler.onConnectionClosed(this); 447 } 448 } 449 450 /** 451 * Return the InetSocketAddress of the remote endpoint 452 */ 453 public SocketAddress getSocketAddress() 454 { 455 return this.remoteSocketAddress; 456 } 457 458 /** 459 * Shuts down the server. (Only actually used on the server) 460 */ 461 public void serverShutdown() 462 { 463 if (!this.isServerTerminating) 464 { 465 this.wakeThreads(); 466 this.isServerTerminating = true; 467 this.readThread.interrupt(); 468 (new TcpMonitorThread(this)).start(); 469 } 470 } 471 472 private void decryptInputStream() throws IOException 473 { 474 this.isInputBeingDecrypted = true; 475 this.socketInputStream = new DataInputStream(CryptManager.decryptInputStream(this.sharedKeyForEncryption, this.networkSocket.getInputStream())); 476 } 477 478 /** 479 * flushes the stream and replaces it with an encryptedOutputStream 480 */ 481 private void encryptOuputStream() throws IOException 482 { 483 this.socketOutputStream.flush(); 484 this.isOutputEncrypted = true; 485 this.socketOutputStream = new DataOutputStream(new BufferedOutputStream(CryptManager.encryptOuputStream(this.sharedKeyForEncryption, this.networkSocket.getOutputStream()), 5120)); 486 } 487 488 /** 489 * returns 0 for memoryConnections 490 */ 491 public int packetSize() 492 { 493 return this.chunkDataPackets.size(); 494 } 495 496 public Socket getSocket() 497 { 498 return this.networkSocket; 499 } 500 501 /** 502 * Whether the network is operational. 503 */ 504 static boolean isRunning(TcpConnection par0TcpConnection) 505 { 506 return par0TcpConnection.isRunning; 507 } 508 509 /** 510 * Is the server terminating? Client side aways returns false. 511 */ 512 static boolean isServerTerminating(TcpConnection par0TcpConnection) 513 { 514 return par0TcpConnection.isServerTerminating; 515 } 516 517 /** 518 * Static accessor to readPacket. 519 */ 520 static boolean readNetworkPacket(TcpConnection par0TcpConnection) 521 { 522 return par0TcpConnection.readPacket(); 523 } 524 525 /** 526 * Static accessor to sendPacket. 527 */ 528 static boolean sendNetworkPacket(TcpConnection par0TcpConnection) 529 { 530 return par0TcpConnection.sendPacket(); 531 } 532 533 static DataOutputStream getOutputStream(TcpConnection par0TcpConnection) 534 { 535 return par0TcpConnection.socketOutputStream; 536 } 537 538 /** 539 * Gets whether the Network manager is terminating. 540 */ 541 static boolean isTerminating(TcpConnection par0TcpConnection) 542 { 543 return par0TcpConnection.isTerminating; 544 } 545 546 /** 547 * Sends the network manager an error 548 */ 549 static void sendError(TcpConnection par0TcpConnection, Exception par1Exception) 550 { 551 par0TcpConnection.onNetworkError(par1Exception); 552 } 553 554 /** 555 * Returns the read thread. 556 */ 557 static Thread getReadThread(TcpConnection par0TcpConnection) 558 { 559 return par0TcpConnection.readThread; 560 } 561 562 /** 563 * Returns the write thread. 564 */ 565 static Thread getWriteThread(TcpConnection par0TcpConnection) 566 { 567 return par0TcpConnection.writeThread; 568 } 569 }