001 package net.minecraft.network.packet;
002
003 import cpw.mods.fml.relauncher.Side;
004 import cpw.mods.fml.relauncher.SideOnly;
005 import java.io.DataInputStream;
006 import java.io.DataOutputStream;
007 import java.io.IOException;
008 import java.util.concurrent.Semaphore;
009 import java.util.zip.DataFormatException;
010 import java.util.zip.Deflater;
011 import java.util.zip.Inflater;
012 import net.minecraft.world.chunk.Chunk;
013 import net.minecraft.world.chunk.NibbleArray;
014 import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
015
016 public 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 var4 = getMapChunkData(par1Chunk, par2, par3);
065 this.yChMax = var4.chunkHasAddSectionFlag;
066 this.yChMin = var4.chunkExistFlag;
067 this.compressedChunkData = var4.compressedData;
068 this.deflateGate = new Semaphore(1);
069 }
070
071 private void deflate()
072 {
073 Deflater var5 = new Deflater(-1);
074 try
075 {
076 var5.setInput(compressedChunkData, 0, compressedChunkData.length);
077 var5.finish();
078 byte[] deflated = new byte[compressedChunkData.length];
079 this.tempLength = var5.deflate(deflated);
080 this.chunkData = deflated;
081 }
082 finally
083 {
084 var5.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 var2 = 0;
107 int var3;
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 (var3 = 0; var3 < 16; ++var3)
111 {
112 var2 += this.yChMin >> var3 & 1;
113 msb += this.yChMax >> var3 & 1;
114 }
115
116 var3 = 12288 * var2;
117 var3 += 2048 * msb;
118
119 if (this.includeInitialize)
120 {
121 var3 += 256;
122 }
123
124 this.compressedChunkData = new byte[var3];
125 Inflater var4 = new Inflater();
126 var4.setInput(temp, 0, this.tempLength);
127
128 try
129 {
130 var4.inflate(this.compressedChunkData);
131 }
132 catch (DataFormatException var9)
133 {
134 throw new IOException("Bad compressed data format");
135 }
136 finally
137 {
138 var4.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 var3 = 0;
185 ExtendedBlockStorage[] var4 = par0Chunk.getBlockStorageArray();
186 int var5 = 0;
187 Packet51MapChunkData var6 = new Packet51MapChunkData();
188 byte[] var7 = temp;
189
190 if (par1)
191 {
192 par0Chunk.deferRender = true;
193 }
194
195 int var8;
196
197 for (var8 = 0; var8 < var4.length; ++var8)
198 {
199 if (var4[var8] != null && (!par1 || !var4[var8].isEmpty()) && (par2 & 1 << var8) != 0)
200 {
201 var6.chunkExistFlag |= 1 << var8;
202
203 if (var4[var8].getBlockMSBArray() != null)
204 {
205 var6.chunkHasAddSectionFlag |= 1 << var8;
206 ++var5;
207 }
208 }
209 }
210
211 for (var8 = 0; var8 < var4.length; ++var8)
212 {
213 if (var4[var8] != null && (!par1 || !var4[var8].isEmpty()) && (par2 & 1 << var8) != 0)
214 {
215 byte[] var9 = var4[var8].getBlockLSBArray();
216 System.arraycopy(var9, 0, var7, var3, var9.length);
217 var3 += var9.length;
218 }
219 }
220
221 NibbleArray var10;
222
223 for (var8 = 0; var8 < var4.length; ++var8)
224 {
225 if (var4[var8] != null && (!par1 || !var4[var8].isEmpty()) && (par2 & 1 << var8) != 0)
226 {
227 var10 = var4[var8].getMetadataArray();
228 System.arraycopy(var10.data, 0, var7, var3, var10.data.length);
229 var3 += var10.data.length;
230 }
231 }
232
233 for (var8 = 0; var8 < var4.length; ++var8)
234 {
235 if (var4[var8] != null && (!par1 || !var4[var8].isEmpty()) && (par2 & 1 << var8) != 0)
236 {
237 var10 = var4[var8].getBlocklightArray();
238 System.arraycopy(var10.data, 0, var7, var3, var10.data.length);
239 var3 += var10.data.length;
240 }
241 }
242
243 if (!par0Chunk.worldObj.provider.hasNoSky)
244 {
245 for (var8 = 0; var8 < var4.length; ++var8)
246 {
247 if (var4[var8] != null && (!par1 || !var4[var8].isEmpty()) && (par2 & 1 << var8) != 0)
248 {
249 var10 = var4[var8].getSkylightArray();
250 System.arraycopy(var10.data, 0, var7, var3, var10.data.length);
251 var3 += var10.data.length;
252 }
253 }
254 }
255
256 if (var5 > 0)
257 {
258 for (var8 = 0; var8 < var4.length; ++var8)
259 {
260 if (var4[var8] != null && (!par1 || !var4[var8].isEmpty()) && var4[var8].getBlockMSBArray() != null && (par2 & 1 << var8) != 0)
261 {
262 var10 = var4[var8].getBlockMSBArray();
263 System.arraycopy(var10.data, 0, var7, var3, var10.data.length);
264 var3 += var10.data.length;
265 }
266 }
267 }
268
269 if (par1)
270 {
271 byte[] var11 = par0Chunk.getBiomeArray();
272 System.arraycopy(var11, 0, var7, var3, var11.length);
273 var3 += var11.length;
274 }
275
276 var6.compressedData = new byte[var3];
277 System.arraycopy(var7, 0, var6.compressedData, 0, var3);
278 return var6;
279 }
280
281 @SideOnly(Side.CLIENT)
282 public byte[] func_73593_d()
283 {
284 return this.compressedChunkData;
285 }
286 }