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