    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}