001package net.minecraft.network.packet; 002 003import cpw.mods.fml.relauncher.Side; 004import cpw.mods.fml.relauncher.SideOnly; 005import java.io.DataInputStream; 006import java.io.DataOutputStream; 007import java.io.IOException; 008import java.util.concurrent.Semaphore; 009import java.util.zip.DataFormatException; 010import java.util.zip.Deflater; 011import java.util.zip.Inflater; 012import net.minecraft.world.chunk.Chunk; 013import net.minecraft.world.chunk.NibbleArray; 014import net.minecraft.world.chunk.storage.ExtendedBlockStorage; 015 016public class Packet51MapChunk extends Packet 017{ 018 /** The x-position of the transmitted chunk, in chunk coordinates. */ 019 public int xCh; 020 021 /** The z-position of the transmitted chunk, in chunk coordinates. */ 022 public int zCh; 023 024 /** 025 * The y-position of the lowest chunk Section in the transmitted chunk, in chunk coordinates. 026 */ 027 public int yChMin; 028 029 /** 030 * The y-position of the highest chunk Section in the transmitted chunk, in chunk coordinates. 031 */ 032 public int yChMax; 033 034 /** The transmitted chunk data, decompressed. */ 035 private byte[] chunkData; 036 037 /** The compressed chunk data */ 038 private byte[] compressedChunkData; 039 040 /** 041 * Whether to initialize the Chunk before applying the effect of the Packet51MapChunk. 042 */ 043 public boolean includeInitialize; 044 045 /** The length of the compressed chunk data byte array. */ 046 private int tempLength; 047 048 /** A temporary storage for the compressed chunk data byte array. */ 049 private static byte[] temp = new byte[196864]; 050 051 private Semaphore deflateGate; 052 053 public Packet51MapChunk() 054 { 055 this.isChunkDataPacket = true; 056 } 057 058 public Packet51MapChunk(Chunk par1Chunk, boolean par2, int par3) 059 { 060 this.isChunkDataPacket = true; 061 this.xCh = par1Chunk.xPosition; 062 this.zCh = par1Chunk.zPosition; 063 this.includeInitialize = par2; 064 Packet51MapChunkData packet51mapchunkdata = getMapChunkData(par1Chunk, par2, par3); 065 this.yChMax = packet51mapchunkdata.chunkHasAddSectionFlag; 066 this.yChMin = packet51mapchunkdata.chunkExistFlag; 067 this.compressedChunkData = packet51mapchunkdata.compressedData; 068 this.deflateGate = new Semaphore(1); 069 } 070 071 private void deflate() 072 { 073 Deflater deflater = new Deflater(-1); 074 try 075 { 076 deflater.setInput(compressedChunkData, 0, compressedChunkData.length); 077 deflater.finish(); 078 byte[] deflated = new byte[compressedChunkData.length]; 079 this.tempLength = deflater.deflate(deflated); 080 this.chunkData = deflated; 081 } 082 finally 083 { 084 deflater.end(); 085 } 086 } 087 088 /** 089 * Abstract. Reads the raw packet data from the data stream. 090 */ 091 public void readPacketData(DataInputStream par1DataInputStream) throws IOException 092 { 093 this.xCh = par1DataInputStream.readInt(); 094 this.zCh = par1DataInputStream.readInt(); 095 this.includeInitialize = par1DataInputStream.readBoolean(); 096 this.yChMin = par1DataInputStream.readShort(); 097 this.yChMax = par1DataInputStream.readShort(); 098 this.tempLength = par1DataInputStream.readInt(); 099 100 if (temp.length < this.tempLength) 101 { 102 temp = new byte[this.tempLength]; 103 } 104 105 par1DataInputStream.readFully(temp, 0, this.tempLength); 106 int i = 0; 107 int j; 108 int msb = 0; //BugFix: MC does not read the MSB array from the packet properly, causing issues for servers that use blocks > 256 109 110 for (j = 0; j < 16; ++j) 111 { 112 i += this.yChMin >> j & 1; 113 msb += this.yChMax >> j & 1; 114 } 115 116 j = 12288 * i; 117 j += 2048 * msb; 118 119 if (this.includeInitialize) 120 { 121 j += 256; 122 } 123 124 this.compressedChunkData = new byte[j]; 125 Inflater inflater = new Inflater(); 126 inflater.setInput(temp, 0, this.tempLength); 127 128 try 129 { 130 inflater.inflate(this.compressedChunkData); 131 } 132 catch (DataFormatException dataformatexception) 133 { 134 throw new IOException("Bad compressed data format"); 135 } 136 finally 137 { 138 inflater.end(); 139 } 140 } 141 142 /** 143 * Abstract. Writes the raw packet data to the data stream. 144 */ 145 public void writePacketData(DataOutputStream par1DataOutputStream) throws IOException 146 { 147 if (chunkData == null) 148 { 149 deflateGate.acquireUninterruptibly(); 150 if (chunkData == null) 151 { 152 deflate(); 153 } 154 deflateGate.release(); 155 } 156 157 par1DataOutputStream.writeInt(this.xCh); 158 par1DataOutputStream.writeInt(this.zCh); 159 par1DataOutputStream.writeBoolean(this.includeInitialize); 160 par1DataOutputStream.writeShort((short)(this.yChMin & 65535)); 161 par1DataOutputStream.writeShort((short)(this.yChMax & 65535)); 162 par1DataOutputStream.writeInt(this.tempLength); 163 par1DataOutputStream.write(this.chunkData, 0, this.tempLength); 164 } 165 166 /** 167 * Passes this Packet on to the NetHandler for processing. 168 */ 169 public void processPacket(NetHandler par1NetHandler) 170 { 171 par1NetHandler.handleMapChunk(this); 172 } 173 174 /** 175 * Abstract. Return the size of the packet (not counting the header). 176 */ 177 public int getPacketSize() 178 { 179 return 17 + this.tempLength; 180 } 181 182 public static Packet51MapChunkData getMapChunkData(Chunk par0Chunk, boolean par1, int par2) 183 { 184 int j = 0; 185 ExtendedBlockStorage[] aextendedblockstorage = par0Chunk.getBlockStorageArray(); 186 int k = 0; 187 Packet51MapChunkData packet51mapchunkdata = new Packet51MapChunkData(); 188 byte[] abyte = temp; 189 190 if (par1) 191 { 192 par0Chunk.sendUpdates = true; 193 } 194 195 int l; 196 197 for (l = 0; l < aextendedblockstorage.length; ++l) 198 { 199 if (aextendedblockstorage[l] != null && (!par1 || !aextendedblockstorage[l].isEmpty()) && (par2 & 1 << l) != 0) 200 { 201 packet51mapchunkdata.chunkExistFlag |= 1 << l; 202 203 if (aextendedblockstorage[l].getBlockMSBArray() != null) 204 { 205 packet51mapchunkdata.chunkHasAddSectionFlag |= 1 << l; 206 ++k; 207 } 208 } 209 } 210 211 for (l = 0; l < aextendedblockstorage.length; ++l) 212 { 213 if (aextendedblockstorage[l] != null && (!par1 || !aextendedblockstorage[l].isEmpty()) && (par2 & 1 << l) != 0) 214 { 215 byte[] abyte1 = aextendedblockstorage[l].getBlockLSBArray(); 216 System.arraycopy(abyte1, 0, abyte, j, abyte1.length); 217 j += abyte1.length; 218 } 219 } 220 221 NibbleArray nibblearray; 222 223 for (l = 0; l < aextendedblockstorage.length; ++l) 224 { 225 if (aextendedblockstorage[l] != null && (!par1 || !aextendedblockstorage[l].isEmpty()) && (par2 & 1 << l) != 0) 226 { 227 nibblearray = aextendedblockstorage[l].getMetadataArray(); 228 System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length); 229 j += nibblearray.data.length; 230 } 231 } 232 233 for (l = 0; l < aextendedblockstorage.length; ++l) 234 { 235 if (aextendedblockstorage[l] != null && (!par1 || !aextendedblockstorage[l].isEmpty()) && (par2 & 1 << l) != 0) 236 { 237 nibblearray = aextendedblockstorage[l].getBlocklightArray(); 238 System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length); 239 j += nibblearray.data.length; 240 } 241 } 242 243 if (!par0Chunk.worldObj.provider.hasNoSky) 244 { 245 for (l = 0; l < aextendedblockstorage.length; ++l) 246 { 247 if (aextendedblockstorage[l] != null && (!par1 || !aextendedblockstorage[l].isEmpty()) && (par2 & 1 << l) != 0) 248 { 249 nibblearray = aextendedblockstorage[l].getSkylightArray(); 250 System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length); 251 j += nibblearray.data.length; 252 } 253 } 254 } 255 256 if (k > 0) 257 { 258 for (l = 0; l < aextendedblockstorage.length; ++l) 259 { 260 if (aextendedblockstorage[l] != null && (!par1 || !aextendedblockstorage[l].isEmpty()) && aextendedblockstorage[l].getBlockMSBArray() != null && (par2 & 1 << l) != 0) 261 { 262 nibblearray = aextendedblockstorage[l].getBlockMSBArray(); 263 System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length); 264 j += nibblearray.data.length; 265 } 266 } 267 } 268 269 if (par1) 270 { 271 byte[] abyte2 = par0Chunk.getBiomeArray(); 272 System.arraycopy(abyte2, 0, abyte, j, abyte2.length); 273 j += abyte2.length; 274 } 275 276 packet51mapchunkdata.compressedData = new byte[j]; 277 System.arraycopy(abyte, 0, packet51mapchunkdata.compressedData, 0, j); 278 return packet51mapchunkdata; 279 } 280 281 @SideOnly(Side.CLIENT) 282 public byte[] func_73593_d() 283 { 284 return this.compressedChunkData; 285 } 286}