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[] getCompressedChunkData()
283    {
284        return this.compressedChunkData;
285    }
286}