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    }