    001package net.minecraft.world.chunk;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005import java.util.ArrayList;
    006import java.util.Arrays;
    007import java.util.HashMap;
    008import java.util.Iterator;
    009import java.util.List;
    010import java.util.Map;
    011import java.util.Random;
    012import net.minecraft.block.Block;
    013import net.minecraft.block.ITileEntityProvider;
    014import net.minecraft.block.material.Material;
    015import net.minecraft.command.IEntitySelector;
    016import net.minecraft.entity.Entity;
    017import net.minecraft.tileentity.TileEntity;
    018import net.minecraft.util.AxisAlignedBB;
    019import net.minecraft.util.MathHelper;
    020import net.minecraft.world.ChunkCoordIntPair;
    021import net.minecraft.world.ChunkPosition;
    022import net.minecraft.world.EnumSkyBlock;
    023import net.minecraft.world.World;
    024import net.minecraft.world.biome.BiomeGenBase;
    025import net.minecraft.world.biome.WorldChunkManager;
    026import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
    027
    028import net.minecraftforge.common.MinecraftForge;
    029import net.minecraftforge.event.entity.EntityEvent;
    030import net.minecraftforge.event.world.ChunkEvent;
    031
    032public class Chunk
    033{
    034    /**
    035     * Determines if the chunk is lit or not at a light value greater than 0.
    036     */
    037    public static boolean isLit;
    038
    039    /**
    040     * Used to store block IDs, block MSBs, Sky-light maps, Block-light maps, and metadata. Each entry corresponds to a
    041     * logical segment of 16x16x16 blocks, stacked vertically.
    042     */
    043    private ExtendedBlockStorage[] storageArrays;
    044
    045    /**
    046     * Contains a 16x16 mapping on the X/Z plane of the biome ID to which each colum belongs.
    047     */
    048    private byte[] blockBiomeArray;
    049
    050    /**
    051     * A map, similar to heightMap, that tracks how far down precipitation can fall.
    052     */
    053    public int[] precipitationHeightMap;
    054
    055    /** Which columns need their skylightMaps updated. */
    056    public boolean[] updateSkylightColumns;
    057
    058    /** Whether or not this Chunk is currently loaded into the World */
    059    public boolean isChunkLoaded;
    060
    061    /** Reference to the World object. */
    062    public World worldObj;
    063    public int[] heightMap;
    064
    065    /** The x coordinate of the chunk. */
    066    public final int xPosition;
    067
    068    /** The z coordinate of the chunk. */
    069    public final int zPosition;
    070    private boolean isGapLightingUpdated;
    071
    072    /** A Map of ChunkPositions to TileEntities in this chunk */
    073    public Map chunkTileEntityMap;
    074
    075    /**
    076     * Array of Lists containing the entities in this Chunk. Each List represents a 16 block subchunk.
    077     */
    078    public List[] entityLists;
    079
    080    /** Boolean value indicating if the terrain is populated. */
    081    public boolean isTerrainPopulated;
    082
    083    /**
    084     * Set to true if the chunk has been modified and needs to be updated internally.
    085     */
    086    public boolean isModified;
    087
    088    /**
    089     * Whether this Chunk has any Entities and thus requires saving on every tick
    090     */
    091    public boolean hasEntities;
    092
    093    /** The time according to World.worldTime when this chunk was last saved */
    094    public long lastSaveTime;
    095
    096    /**
    097     * Updates to this chunk will not be sent to clients if this is false. This field is set to true the first time the
    098     * chunk is sent to a client, and never set to false.
    099     */
    100    public boolean sendUpdates;
    101    public int field_82912_p;
    102
    103    /**
    104     * Contains the current round-robin relight check index, and is implied as the relight check location as well.
    105     */
    106    private int queuedLightChecks;
    107    boolean field_76653_p;
    108
    109    public Chunk(World par1World, int par2, int par3)
    110    {
    111        this.storageArrays = new ExtendedBlockStorage[16];
    112        this.blockBiomeArray = new byte[256];
    113        this.precipitationHeightMap = new int[256];
    114        this.updateSkylightColumns = new boolean[256];
    115        this.isGapLightingUpdated = false;
    116        this.chunkTileEntityMap = new HashMap();
    117        this.isTerrainPopulated = false;
    118        this.isModified = false;
    119        this.hasEntities = false;
    120        this.lastSaveTime = 0L;
    121        this.sendUpdates = false;
    122        this.field_82912_p = 0;
    123        this.queuedLightChecks = 4096;
    124        this.field_76653_p = false;
    125        this.entityLists = new List[16];
    126        this.worldObj = par1World;
    127        this.xPosition = par2;
    128        this.zPosition = par3;
    129        this.heightMap = new int[256];
    130
    131        for (int k = 0; k < this.entityLists.length; ++k)
    132        {
    133            this.entityLists[k] = new ArrayList();
    134        }
    135
    136        Arrays.fill(this.precipitationHeightMap, -999);
    137        Arrays.fill(this.blockBiomeArray, (byte) - 1);
    138    }
    139
    140    public Chunk(World par1World, byte[] par2ArrayOfByte, int par3, int par4)
    141    {
    142        this(par1World, par3, par4);
    143        int k = par2ArrayOfByte.length / 256;
    144
    145        for (int l = 0; l < 16; ++l)
    146        {
    147            for (int i1 = 0; i1 < 16; ++i1)
    148            {
    149                for (int j1 = 0; j1 < k; ++j1)
    150                {
    151                    /* FORGE: The following change, a cast from unsigned byte to int,
    152                     * fixes a vanilla bug when generating new chunks that contain a block ID > 127 */
    153                    int b0 = par2ArrayOfByte[l << 11 | i1 << 7 | j1] & 0xFF;
    154
    155                    if (b0 != 0)
    156                    {
    157                        int k1 = j1 >> 4;
    158
    159                        if (this.storageArrays[k1] == null)
    160                        {
    161                            this.storageArrays[k1] = new ExtendedBlockStorage(k1 << 4, !par1World.provider.hasNoSky);
    162                        }
    163
    164                        this.storageArrays[k1].setExtBlockID(l, j1 & 15, i1, b0);
    165                    }
    166                }
    167            }
    168        }
    169    }
    170
    171    /**
    172     * Metadata sensitive Chunk constructor for use in new ChunkProviders that
    173     * use metadata sensitive blocks during generation.
    174     *
    175     * @param world The world this chunk belongs to
    176     * @param ids A ByteArray containing all the BlockID's to set this chunk to
    177     * @param metadata A ByteArray containing all the metadata to set this chunk to
    178     * @param chunkX The chunk's X position
    179     * @param chunkZ The Chunk's Z position
    180     */
    181    public Chunk(World world, byte[] ids, byte[] metadata, int chunkX, int chunkZ)
    182    {
    183        this(world, chunkX, chunkZ);
    184        int k = ids.length / 256;
    185
    186        for (int x = 0; x < 16; ++x)
    187        {
    188            for (int z = 0; z < 16; ++z)
    189            {
    190                for (int y = 0; y < k; ++y)
    191                {
    192                    int idx = x << 11 | z << 7 | y;
    193                   int id = ids[idx] & 0xFF;
    194                    int meta = metadata[idx];
    195
    196                    if (id != 0)
    197                    {
    198                        int l = y >> 4;
    199
    200                        if (this.storageArrays[l] == null)
    201                        {
    202                            this.storageArrays[l] = new ExtendedBlockStorage(l << 4, !world.provider.hasNoSky);
    203                        }
    204
    205                        this.storageArrays[l].setExtBlockID(x, y & 15, z, id);
    206                        this.storageArrays[l].setExtBlockMetadata(x, y & 15, z, meta);
    207                    }
    208                }
    209            }
    210        }
    211    }
    212
    213    /**
    214     * A Chunk Constructor which handles shorts to allow block ids > 256 (full 4096 range)
    215     * Meta data sensitive
    216     * NOTE: The x,y,z order of the array is different from the native Chunk constructor to allow for generation > y127
    217     * NOTE: This is possibly more efficient than the standard constructor due to less memory skipping
    218     *
    219     * @param world The world this chunk belongs to
    220     * @param ids A ShortArray containing all the BlockID's to set this chunk to (x is low order, z is mid, y is high)
    221     * @param metadata A ByteArray containing all the metadata to set this chunk to
    222     * @param chunkX The chunk's X position
    223     * @param chunkZ The Chunk's Z position
    224     */
    225    public Chunk(World world, short[] ids, byte[] metadata, int chunkX, int chunkZ)
    226    {
    227        this(world, chunkX, chunkZ);
    228        int max = ids.length / 256;
    229
    230        for (int y = 0; y < max; ++y)
    231        {
    232            for (int z = 0; z < 16; ++z)
    233            {
    234                for (int x = 0; x < 16; ++x)
    235                {
    236                    int idx = y << 8 | z << 4 | x;
    237                    int id = ids[idx] & 0xFFFFFF;
    238                    int meta = metadata[idx];
    239
    240                    if (id != 0) {
    241                        int storageBlock = y >> 4;
    242
    243                        if (this.storageArrays[storageBlock] == null) {
    244                                this.storageArrays[storageBlock] = new ExtendedBlockStorage(storageBlock << 4, !world.provider.hasNoSky);
    245                        }
    246
    247                        this.storageArrays[storageBlock].setExtBlockID(x, y & 15, z, id);
    248                        this.storageArrays[storageBlock].setExtBlockMetadata(x, y & 15, z, meta);
    249                    }
    250                }
    251            }
    252        }
    253    }
    254
    255    /**
    256     * Checks whether the chunk is at the X/Z location specified
    257     */
    258    public boolean isAtLocation(int par1, int par2)
    259    {
    260        return par1 == this.xPosition && par2 == this.zPosition;
    261    }
    262
    263    /**
    264     * Returns the value in the height map at this x, z coordinate in the chunk
    265     */
    266    public int getHeightValue(int par1, int par2)
    267    {
    268        return this.heightMap[par2 << 4 | par1];
    269    }
    270
    271    /**
    272     * Returns the topmost ExtendedBlockStorage instance for this Chunk that actually contains a block.
    273     */
    274    public int getTopFilledSegment()
    275    {
    276        for (int i = this.storageArrays.length - 1; i >= 0; --i)
    277        {
    278            if (this.storageArrays[i] != null)
    279            {
    280                return this.storageArrays[i].getYLocation();
    281            }
    282        }
    283
    284        return 0;
    285    }
    286
    287    /**
    288     * Returns the ExtendedBlockStorage array for this Chunk.
    289     */
    290    public ExtendedBlockStorage[] getBlockStorageArray()
    291    {
    292        return this.storageArrays;
    293    }
    294
    295    @SideOnly(Side.CLIENT)
    296
    297    /**
    298     * Generates the height map for a chunk from scratch
    299     */
    300    public void generateHeightMap()
    301    {
    302        int i = this.getTopFilledSegment();
    303
    304        for (int j = 0; j < 16; ++j)
    305        {
    306            int k = 0;
    307
    308            while (k < 16)
    309            {
    310                this.precipitationHeightMap[j + (k << 4)] = -999;
    311                int l = i + 16 - 1;
    312
    313                while (true)
    314                {
    315                    if (l > 0)
    316                    {
    317                        int i1 = this.getBlockID(j, l - 1, k);
    318
    319                        if (getBlockLightOpacity(j, l - 1, k) == 0)
    320                        {
    321                            --l;
    322                            continue;
    323                        }
    324
    325                        this.heightMap[k << 4 | j] = l;
    326                    }
    327
    328                    ++k;
    329                    break;
    330                }
    331            }
    332        }
    333
    334        this.isModified = true;
    335    }
    336
    337    /**
    338     * Generates the initial skylight map for the chunk upon generation or load.
    339     */
    340    public void generateSkylightMap()
    341    {
    342        int i = this.getTopFilledSegment();
    343        this.field_82912_p = Integer.MAX_VALUE;
    344        int j;
    345        int k;
    346
    347        for (j = 0; j < 16; ++j)
    348        {
    349            k = 0;
    350
    351            while (k < 16)
    352            {
    353                this.precipitationHeightMap[j + (k << 4)] = -999;
    354                int l = i + 16 - 1;
    355
    356                while (true)
    357                {
    358                    if (l > 0)
    359                    {
    360                        if (this.getBlockLightOpacity(j, l - 1, k) == 0)
    361                        {
    362                            --l;
    363                            continue;
    364                        }
    365
    366                        this.heightMap[k << 4 | j] = l;
    367
    368                        if (l < this.field_82912_p)
    369                        {
    370                            this.field_82912_p = l;
    371                        }
    372                    }
    373
    374                    if (!this.worldObj.provider.hasNoSky)
    375                    {
    376                        l = 15;
    377                        int i1 = i + 16 - 1;
    378
    379                        do
    380                        {
    381                            l -= this.getBlockLightOpacity(j, i1, k);
    382
    383                            if (l > 0)
    384                            {
    385                                ExtendedBlockStorage extendedblockstorage = this.storageArrays[i1 >> 4];
    386
    387                                if (extendedblockstorage != null)
    388                                {
    389                                    extendedblockstorage.setExtSkylightValue(j, i1 & 15, k, l);
    390                                    this.worldObj.markBlockForRenderUpdate((this.xPosition << 4) + j, i1, (this.zPosition << 4) + k);
    391                                }
    392                            }
    393
    394                            --i1;
    395                        }
    396                        while (i1 > 0 && l > 0);
    397                    }
    398
    399                    ++k;
    400                    break;
    401                }
    402            }
    403        }
    404
    405        this.isModified = true;
    406
    407        for (j = 0; j < 16; ++j)
    408        {
    409            for (k = 0; k < 16; ++k)
    410            {
    411                this.propagateSkylightOcclusion(j, k);
    412            }
    413        }
    414    }
    415
    416    /**
    417     * Propagates a given sky-visible block's light value downward and upward to neighboring blocks as necessary.
    418     */
    419    private void propagateSkylightOcclusion(int par1, int par2)
    420    {
    421        this.updateSkylightColumns[par1 + par2 * 16] = true;
    422        this.isGapLightingUpdated = true;
    423    }
    424
    425    /**
    426     * Runs delayed skylight updates.
    427     */
    428    private void updateSkylight_do()
    429    {
    430        this.worldObj.theProfiler.startSection("recheckGaps");
    431
    432        if (this.worldObj.doChunksNearChunkExist(this.xPosition * 16 + 8, 0, this.zPosition * 16 + 8, 16))
    433        {
    434            for (int i = 0; i < 16; ++i)
    435            {
    436                for (int j = 0; j < 16; ++j)
    437                {
    438                    if (this.updateSkylightColumns[i + j * 16])
    439                    {
    440                        this.updateSkylightColumns[i + j * 16] = false;
    441                        int k = this.getHeightValue(i, j);
    442                        int l = this.xPosition * 16 + i;
    443                        int i1 = this.zPosition * 16 + j;
    444                        int j1 = this.worldObj.func_82734_g(l - 1, i1);
    445                        int k1 = this.worldObj.func_82734_g(l + 1, i1);
    446                        int l1 = this.worldObj.func_82734_g(l, i1 - 1);
    447                        int i2 = this.worldObj.func_82734_g(l, i1 + 1);
    448
    449                        if (k1 < j1)
    450                        {
    451                            j1 = k1;
    452                        }
    453
    454                        if (l1 < j1)
    455                        {
    456                            j1 = l1;
    457                        }
    458
    459                        if (i2 < j1)
    460                        {
    461                            j1 = i2;
    462                        }
    463
    464                        this.checkSkylightNeighborHeight(l, i1, j1);
    465                        this.checkSkylightNeighborHeight(l - 1, i1, k);
    466                        this.checkSkylightNeighborHeight(l + 1, i1, k);
    467                        this.checkSkylightNeighborHeight(l, i1 - 1, k);
    468                        this.checkSkylightNeighborHeight(l, i1 + 1, k);
    469                    }
    470                }
    471            }
    472
    473            this.isGapLightingUpdated = false;
    474        }
    475
    476        this.worldObj.theProfiler.endSection();
    477    }
    478
    479    /**
    480     * Checks the height of a block next to a sky-visible block and schedules a lighting update as necessary.
    481     */
    482    private void checkSkylightNeighborHeight(int par1, int par2, int par3)
    483    {
    484        int l = this.worldObj.getHeightValue(par1, par2);
    485
    486        if (l > par3)
    487        {
    488            this.updateSkylightNeighborHeight(par1, par2, par3, l + 1);
    489        }
    490        else if (l < par3)
    491        {
    492            this.updateSkylightNeighborHeight(par1, par2, l, par3 + 1);
    493        }
    494    }
    495
    496    private void updateSkylightNeighborHeight(int par1, int par2, int par3, int par4)
    497    {
    498        if (par4 > par3 && this.worldObj.doChunksNearChunkExist(par1, 0, par2, 16))
    499        {
    500            for (int i1 = par3; i1 < par4; ++i1)
    501            {
    502                this.worldObj.updateLightByType(EnumSkyBlock.Sky, par1, i1, par2);
    503            }
    504
    505            this.isModified = true;
    506        }
    507    }
    508
    509    /**
    510     * Initiates the recalculation of both the block-light and sky-light for a given block inside a chunk.
    511     */
    512    private void relightBlock(int par1, int par2, int par3)
    513    {
    514        int l = this.heightMap[par3 << 4 | par1] & 255;
    515        int i1 = l;
    516
    517        if (par2 > l)
    518        {
    519            i1 = par2;
    520        }
    521
    522        while (i1 > 0 && this.getBlockLightOpacity(par1, i1 - 1, par3) == 0)
    523        {
    524            --i1;
    525        }
    526
    527        if (i1 != l)
    528        {
    529            this.worldObj.markBlocksDirtyVertical(par1 + this.xPosition * 16, par3 + this.zPosition * 16, i1, l);
    530            this.heightMap[par3 << 4 | par1] = i1;
    531            int j1 = this.xPosition * 16 + par1;
    532            int k1 = this.zPosition * 16 + par3;
    533            int l1;
    534            int i2;
    535
    536            if (!this.worldObj.provider.hasNoSky)
    537            {
    538                ExtendedBlockStorage extendedblockstorage;
    539
    540                if (i1 < l)
    541                {
    542                    for (l1 = i1; l1 < l; ++l1)
    543                    {
    544                        extendedblockstorage = this.storageArrays[l1 >> 4];
    545
    546                        if (extendedblockstorage != null)
    547                        {
    548                            extendedblockstorage.setExtSkylightValue(par1, l1 & 15, par3, 15);
    549                            this.worldObj.markBlockForRenderUpdate((this.xPosition << 4) + par1, l1, (this.zPosition << 4) + par3);
    550                        }
    551                    }
    552                }
    553                else
    554                {
    555                    for (l1 = l; l1 < i1; ++l1)
    556                    {
    557                        extendedblockstorage = this.storageArrays[l1 >> 4];
    558
    559                        if (extendedblockstorage != null)
    560                        {
    561                            extendedblockstorage.setExtSkylightValue(par1, l1 & 15, par3, 0);
    562                            this.worldObj.markBlockForRenderUpdate((this.xPosition << 4) + par1, l1, (this.zPosition << 4) + par3);
    563                        }
    564                    }
    565                }
    566
    567                l1 = 15;
    568
    569                while (i1 > 0 && l1 > 0)
    570                {
    571                    --i1;
    572                    i2 = this.getBlockLightOpacity(par1, i1, par3);
    573
    574                    if (i2 == 0)
    575                    {
    576                        i2 = 1;
    577                    }
    578
    579                    l1 -= i2;
    580
    581                    if (l1 < 0)
    582                    {
    583                        l1 = 0;
    584                    }
    585
    586                    ExtendedBlockStorage extendedblockstorage1 = this.storageArrays[i1 >> 4];
    587
    588                    if (extendedblockstorage1 != null)
    589                    {
    590                        extendedblockstorage1.setExtSkylightValue(par1, i1 & 15, par3, l1);
    591                    }
    592                }
    593            }
    594
    595            l1 = this.heightMap[par3 << 4 | par1];
    596            i2 = l;
    597            int j2 = l1;
    598
    599            if (l1 < l)
    600            {
    601                i2 = l1;
    602                j2 = l;
    603            }
    604
    605            if (l1 < this.field_82912_p)
    606            {
    607                this.field_82912_p = l1;
    608            }
    609
    610            if (!this.worldObj.provider.hasNoSky)
    611            {
    612                this.updateSkylightNeighborHeight(j1 - 1, k1, i2, j2);
    613                this.updateSkylightNeighborHeight(j1 + 1, k1, i2, j2);
    614                this.updateSkylightNeighborHeight(j1, k1 - 1, i2, j2);
    615                this.updateSkylightNeighborHeight(j1, k1 + 1, i2, j2);
    616                this.updateSkylightNeighborHeight(j1, k1, i2, j2);
    617            }
    618
    619            this.isModified = true;
    620        }
    621    }
    622
    623    public int getBlockLightOpacity(int par1, int par2, int par3)
    624    {
    625        int x = (xPosition << 4) + par1;
    626        int z = (zPosition << 4) + par3;
    627        Block block = Block.blocksList[getBlockID(par1, par2, par3)];
    628        return (block == null ? 0 : block.getLightOpacity(worldObj, x, par2, z));
    629    }
    630
    631    /**
    632     * Return the ID of a block in the chunk.
    633     */
    634    public int getBlockID(int par1, int par2, int par3)
    635    {
    636        if (par2 >> 4 >= this.storageArrays.length || par2 >> 4 < 0)
    637        {
    638            return 0;
    639        }
    640        else
    641        {
    642            ExtendedBlockStorage extendedblockstorage = this.storageArrays[par2 >> 4];
    643            return extendedblockstorage != null ? extendedblockstorage.getExtBlockID(par1, par2 & 15, par3) : 0;
    644        }
    645    }
    646
    647    /**
    648     * Return the metadata corresponding to the given coordinates inside a chunk.
    649     */
    650    public int getBlockMetadata(int par1, int par2, int par3)
    651    {
    652        if (par2 >> 4 >= this.storageArrays.length || par2 >> 4 < 0)
    653        {
    654            return 0;
    655        }
    656        else
    657        {
    658            ExtendedBlockStorage extendedblockstorage = this.storageArrays[par2 >> 4];
    659            return extendedblockstorage != null ? extendedblockstorage.getExtBlockMetadata(par1, par2 & 15, par3) : 0;
    660        }
    661    }
    662
    663    /**
    664     * Sets a blockID of a position within a chunk with metadata. Args: x, y, z, blockID, metadata
    665     */
    666    public boolean setBlockIDWithMetadata(int par1, int par2, int par3, int par4, int par5)
    667    {
    668        int j1 = par3 << 4 | par1;
    669
    670        if (par2 >= this.precipitationHeightMap[j1] - 1)
    671        {
    672            this.precipitationHeightMap[j1] = -999;
    673        }
    674
    675        int k1 = this.heightMap[j1];
    676        int l1 = this.getBlockID(par1, par2, par3);
    677        int i2 = this.getBlockMetadata(par1, par2, par3);
    678
    679        if (l1 == par4 && i2 == par5)
    680        {
    681            return false;
    682        }
    683        else
    684        {
    685            if (par2 >> 4 >= storageArrays.length || par2 >> 4 < 0)
    686            {
    687                return false;
    688            }
    689
    690            ExtendedBlockStorage extendedblockstorage = this.storageArrays[par2 >> 4];
    691            boolean flag = false;
    692
    693            if (extendedblockstorage == null)
    694            {
    695                if (par4 == 0)
    696                {
    697                    return false;
    698                }
    699
    700                extendedblockstorage = this.storageArrays[par2 >> 4] = new ExtendedBlockStorage(par2 >> 4 << 4, !this.worldObj.provider.hasNoSky);
    701                flag = par2 >= k1;
    702            }
    703
    704            int j2 = this.xPosition * 16 + par1;
    705            int k2 = this.zPosition * 16 + par3;
    706
    707            if (l1 != 0 && !this.worldObj.isRemote)
    708            {
    709                Block.blocksList[l1].onSetBlockIDWithMetaData(this.worldObj, j2, par2, k2, i2);
    710            }
    711
    712            extendedblockstorage.setExtBlockID(par1, par2 & 15, par3, par4);
    713
    714            if (l1 != 0)
    715            {
    716                if (!this.worldObj.isRemote)
    717                {
    718                    Block.blocksList[l1].breakBlock(this.worldObj, j2, par2, k2, l1, i2);
    719                }
    720                else if (Block.blocksList[l1] != null && Block.blocksList[l1].hasTileEntity(i2))
    721                {
    722                    TileEntity te = worldObj.getBlockTileEntity(j2, par2, k2);
    723                    if (te != null && te.shouldRefresh(l1, par4, i2, par5, worldObj, j2, par2, k2))
    724                    {
    725                        this.worldObj.removeBlockTileEntity(j2, par2, k2);
    726                    }
    727                }
    728            }
    729
    730            if (extendedblockstorage.getExtBlockID(par1, par2 & 15, par3) != par4)
    731            {
    732                return false;
    733            }
    734            else
    735            {
    736                extendedblockstorage.setExtBlockMetadata(par1, par2 & 15, par3, par5);
    737
    738                if (flag)
    739                {
    740                    this.generateSkylightMap();
    741                }
    742                else
    743                {
    744                    if (getBlockLightOpacity(par1, par2, par3) > 0)
    745                    {
    746                        if (par2 >= k1)
    747                        {
    748                            this.relightBlock(par1, par2 + 1, par3);
    749                        }
    750                    }
    751                    else if (par2 == k1 - 1)
    752                    {
    753                        this.relightBlock(par1, par2, par3);
    754                    }
    755
    756                    this.propagateSkylightOcclusion(par1, par3);
    757                }
    758
    759                TileEntity tileentity;
    760
    761                if (par4 != 0)
    762                {
    763                    if (!this.worldObj.isRemote)
    764                    {
    765                        Block.blocksList[par4].onBlockAdded(this.worldObj, j2, par2, k2);
    766                    }
    767
    768                    if (Block.blocksList[par4] != null && Block.blocksList[par4].hasTileEntity(par5))
    769                    {
    770                        tileentity = this.getChunkBlockTileEntity(par1, par2, par3);
    771
    772                        if (tileentity == null)
    773                        {
    774                            tileentity = Block.blocksList[par4].createTileEntity(this.worldObj, par5);
    775                            this.worldObj.setBlockTileEntity(j2, par2, k2, tileentity);
    776                        }
    777
    778                        if (tileentity != null)
    779                        {
    780                            tileentity.updateContainingBlockInfo();
    781                            tileentity.blockMetadata = par5;
    782                        }
    783                    }
    784                }
    785
    786                this.isModified = true;
    787                return true;
    788            }
    789        }
    790    }
    791
    792    /**
    793     * Set the metadata of a block in the chunk
    794     */
    795    public boolean setBlockMetadata(int par1, int par2, int par3, int par4)
    796    {
    797        ExtendedBlockStorage extendedblockstorage = (par2 >> 4 >= storageArrays.length || par2 >> 4 < 0 ? null : storageArrays[par2 >> 4]);
    798
    799        if (extendedblockstorage == null)
    800        {
    801            return false;
    802        }
    803        else
    804        {
    805            int i1 = extendedblockstorage.getExtBlockMetadata(par1, par2 & 15, par3);
    806
    807            if (i1 == par4)
    808            {
    809                return false;
    810            }
    811            else
    812            {
    813                this.isModified = true;
    814                extendedblockstorage.setExtBlockMetadata(par1, par2 & 15, par3, par4);
    815                int j1 = extendedblockstorage.getExtBlockID(par1, par2 & 15, par3);
    816
    817                if (j1 > 0 && Block.blocksList[j1] != null && Block.blocksList[j1].hasTileEntity(par4))
    818                {
    819                    TileEntity tileentity = this.getChunkBlockTileEntity(par1, par2, par3);
    820
    821                    if (tileentity != null)
    822                    {
    823                        tileentity.updateContainingBlockInfo();
    824                        tileentity.blockMetadata = par4;
    825                    }
    826                }
    827
    828                return true;
    829            }
    830        }
    831    }
    832
    833    /**
    834     * Gets the amount of light saved in this block (doesn't adjust for daylight)
    835     */
    836    public int getSavedLightValue(EnumSkyBlock par1EnumSkyBlock, int par2, int par3, int par4)
    837    {
    838        ExtendedBlockStorage extendedblockstorage = (par3 >> 4 >= storageArrays.length || par3 >> 4 < 0 ? null : storageArrays[par3 >> 4]);
    839        return extendedblockstorage == null ? (this.canBlockSeeTheSky(par2, par3, par4) ? par1EnumSkyBlock.defaultLightValue : 0) : (par1EnumSkyBlock == EnumSkyBlock.Sky ? (this.worldObj.provider.hasNoSky ? 0 : extendedblockstorage.getExtSkylightValue(par2, par3 & 15, par4)) : (par1EnumSkyBlock == EnumSkyBlock.Block ? extendedblockstorage.getExtBlocklightValue(par2, par3 & 15, par4) : par1EnumSkyBlock.defaultLightValue));
    840    }
    841
    842    /**
    843     * Sets the light value at the coordinate. If enumskyblock is set to sky it sets it in the skylightmap and if its a
    844     * block then into the blocklightmap. Args enumSkyBlock, x, y, z, lightValue
    845     */
    846    public void setLightValue(EnumSkyBlock par1EnumSkyBlock, int par2, int par3, int par4, int par5)
    847    {
    848        if (par3 >> 4 >= storageArrays.length || par3 >> 4 < 0)
    849        {
    850            return;
    851        }
    852
    853        ExtendedBlockStorage extendedblockstorage = this.storageArrays[par3 >> 4];
    854
    855        if (extendedblockstorage == null)
    856        {
    857            extendedblockstorage = this.storageArrays[par3 >> 4] = new ExtendedBlockStorage(par3 >> 4 << 4, !this.worldObj.provider.hasNoSky);
    858            this.generateSkylightMap();
    859        }
    860
    861        this.isModified = true;
    862
    863        if (par1EnumSkyBlock == EnumSkyBlock.Sky)
    864        {
    865            if (!this.worldObj.provider.hasNoSky)
    866            {
    867                extendedblockstorage.setExtSkylightValue(par2, par3 & 15, par4, par5);
    868            }
    869        }
    870        else if (par1EnumSkyBlock == EnumSkyBlock.Block)
    871        {
    872            extendedblockstorage.setExtBlocklightValue(par2, par3 & 15, par4, par5);
    873        }
    874    }
    875
    876    /**
    877     * Gets the amount of light on a block taking into account sunlight
    878     */
    879    public int getBlockLightValue(int par1, int par2, int par3, int par4)
    880    {
    881        ExtendedBlockStorage extendedblockstorage = (par2 >> 4 >= storageArrays.length || par2 >> 4 < 0 ? null : storageArrays[par2 >> 4]);
    882
    883        if (extendedblockstorage == null)
    884        {
    885            return !this.worldObj.provider.hasNoSky && par4 < EnumSkyBlock.Sky.defaultLightValue ? EnumSkyBlock.Sky.defaultLightValue - par4 : 0;
    886        }
    887        else
    888        {
    889            int i1 = this.worldObj.provider.hasNoSky ? 0 : extendedblockstorage.getExtSkylightValue(par1, par2 & 15, par3);
    890
    891            if (i1 > 0)
    892            {
    893                isLit = true;
    894            }
    895
    896            i1 -= par4;
    897            int j1 = extendedblockstorage.getExtBlocklightValue(par1, par2 & 15, par3);
    898
    899            if (j1 > i1)
    900            {
    901                i1 = j1;
    902            }
    903
    904            return i1;
    905        }
    906    }
    907
    908    /**
    909     * Adds an entity to the chunk. Args: entity
    910     */
    911    public void addEntity(Entity par1Entity)
    912    {
    913        this.hasEntities = true;
    914        int i = MathHelper.floor_double(par1Entity.posX / 16.0D);
    915        int j = MathHelper.floor_double(par1Entity.posZ / 16.0D);
    916
    917        if (i != this.xPosition || j != this.zPosition)
    918        {
    919            this.worldObj.func_98180_V().func_98232_c("Wrong location! " + par1Entity);
    920            Thread.dumpStack();
    921        }
    922
    923        int k = MathHelper.floor_double(par1Entity.posY / 16.0D);
    924
    925        if (k < 0)
    926        {
    927            k = 0;
    928        }
    929
    930        if (k >= this.entityLists.length)
    931        {
    932            k = this.entityLists.length - 1;
    933        }
    934        MinecraftForge.EVENT_BUS.post(new EntityEvent.EnteringChunk(par1Entity, this.xPosition, this.zPosition, par1Entity.chunkCoordX, par1Entity.chunkCoordZ));
    935        par1Entity.addedToChunk = true;
    936        par1Entity.chunkCoordX = this.xPosition;
    937        par1Entity.chunkCoordY = k;
    938        par1Entity.chunkCoordZ = this.zPosition;
    939        this.entityLists[k].add(par1Entity);
    940    }
    941
    942    /**
    943     * removes entity using its y chunk coordinate as its index
    944     */
    945    public void removeEntity(Entity par1Entity)
    946    {
    947        this.removeEntityAtIndex(par1Entity, par1Entity.chunkCoordY);
    948    }
    949
    950    /**
    951     * Removes entity at the specified index from the entity array.
    952     */
    953    public void removeEntityAtIndex(Entity par1Entity, int par2)
    954    {
    955        if (par2 < 0)
    956        {
    957            par2 = 0;
    958        }
    959
    960        if (par2 >= this.entityLists.length)
    961        {
    962            par2 = this.entityLists.length - 1;
    963        }
    964
    965        this.entityLists[par2].remove(par1Entity);
    966    }
    967
    968    /**
    969     * Returns whether is not a block above this one blocking sight to the sky (done via checking against the heightmap)
    970     */
    971    public boolean canBlockSeeTheSky(int par1, int par2, int par3)
    972    {
    973        return par2 >= this.heightMap[par3 << 4 | par1];
    974    }
    975
    976    /**
    977     * Gets the TileEntity for a given block in this chunk
    978     */
    979    public TileEntity getChunkBlockTileEntity(int par1, int par2, int par3)
    980    {
    981        ChunkPosition chunkposition = new ChunkPosition(par1, par2, par3);
    982        TileEntity tileentity = (TileEntity)this.chunkTileEntityMap.get(chunkposition);
    983
    984        if (tileentity != null && tileentity.isInvalid())
    985        {
    986            chunkTileEntityMap.remove(chunkposition);
    987            tileentity = null;
    988        }
    989
    990        if (tileentity == null)
    991        {
    992            int l = this.getBlockID(par1, par2, par3);
    993            int meta = this.getBlockMetadata(par1, par2, par3);
    994
    995            if (l <= 0 || !Block.blocksList[l].hasTileEntity(meta))
    996            {
    997                return null;
    998            }
    999
    1000            if (tileentity == null)
    1001            {
    1002                tileentity = Block.blocksList[l].createTileEntity(this.worldObj, meta);
    1003                this.worldObj.setBlockTileEntity(this.xPosition * 16 + par1, par2, this.zPosition * 16 + par3, tileentity);
    1004            }
    1005
    1006            tileentity = (TileEntity)this.chunkTileEntityMap.get(chunkposition);
    1007        }
    1008
    1009        return tileentity;
    1010    }
    1011
    1012    /**
    1013     * Adds a TileEntity to a chunk
    1014     */
    1015    public void addTileEntity(TileEntity par1TileEntity)
    1016    {
    1017        int i = par1TileEntity.xCoord - this.xPosition * 16;
    1018        int j = par1TileEntity.yCoord;
    1019        int k = par1TileEntity.zCoord - this.zPosition * 16;
    1020        this.setChunkBlockTileEntity(i, j, k, par1TileEntity);
    1021
    1022        if (this.isChunkLoaded)
    1023        {
    1024            this.worldObj.addTileEntity(par1TileEntity);
    1025        }
    1026    }
    1027
    1028    /**
    1029     * Sets the TileEntity for a given block in this chunk
    1030     */
    1031    public void setChunkBlockTileEntity(int par1, int par2, int par3, TileEntity par4TileEntity)
    1032    {
    1033        ChunkPosition chunkposition = new ChunkPosition(par1, par2, par3);
    1034        par4TileEntity.setWorldObj(this.worldObj);
    1035        par4TileEntity.xCoord = this.xPosition * 16 + par1;
    1036        par4TileEntity.yCoord = par2;
    1037        par4TileEntity.zCoord = this.zPosition * 16 + par3;
    1038
    1039        Block block = Block.blocksList[getBlockID(par1, par2, par3)];
    1040        if (block != null && block.hasTileEntity(getBlockMetadata(par1, par2, par3)))
    1041        {
    1042            if (this.chunkTileEntityMap.containsKey(chunkposition))
    1043            {
    1044                ((TileEntity)this.chunkTileEntityMap.get(chunkposition)).invalidate();
    1045            }
    1046
    1047            par4TileEntity.validate();
    1048            this.chunkTileEntityMap.put(chunkposition, par4TileEntity);
    1049        }
    1050    }
    1051
    1052    /**
    1053     * Removes the TileEntity for a given block in this chunk
    1054     */
    1055    public void removeChunkBlockTileEntity(int par1, int par2, int par3)
    1056    {
    1057        ChunkPosition chunkposition = new ChunkPosition(par1, par2, par3);
    1058
    1059        if (this.isChunkLoaded)
    1060        {
    1061            TileEntity tileentity = (TileEntity)this.chunkTileEntityMap.remove(chunkposition);
    1062
    1063            if (tileentity != null)
    1064            {
    1065                tileentity.invalidate();
    1066            }
    1067        }
    1068    }
    1069
    1070    /**
    1071     * Called when this Chunk is loaded by the ChunkProvider
    1072     */
    1073    public void onChunkLoad()
    1074    {
    1075        this.isChunkLoaded = true;
    1076        this.worldObj.addTileEntity(this.chunkTileEntityMap.values());
    1077
    1078        for (int i = 0; i < this.entityLists.length; ++i)
    1079        {
    1080            this.worldObj.addLoadedEntities(this.entityLists[i]);
    1081        }
    1082        MinecraftForge.EVENT_BUS.post(new ChunkEvent.Load(this));
    1083    }
    1084
    1085    /**
    1086     * Called when this Chunk is unloaded by the ChunkProvider
    1087     */
    1088    public void onChunkUnload()
    1089    {
    1090        this.isChunkLoaded = false;
    1091        Iterator iterator = this.chunkTileEntityMap.values().iterator();
    1092
    1093        while (iterator.hasNext())
    1094        {
    1095            TileEntity tileentity = (TileEntity)iterator.next();
    1096            this.worldObj.markTileEntityForDespawn(tileentity);
    1097        }
    1098
    1099        for (int i = 0; i < this.entityLists.length; ++i)
    1100        {
    1101            this.worldObj.unloadEntities(this.entityLists[i]);
    1102        }
    1103        MinecraftForge.EVENT_BUS.post(new ChunkEvent.Unload(this));
    1104    }
    1105
    1106    /**
    1107     * Sets the isModified flag for this Chunk
    1108     */
    1109    public void setChunkModified()
    1110    {
    1111        this.isModified = true;
    1112    }
    1113
    1114    /**
    1115     * Fills the given list of all entities that intersect within the given bounding box that aren't the passed entity
    1116     * Args: entity, aabb, listToFill
    1117     */
    1118    public void getEntitiesWithinAABBForEntity(Entity par1Entity, AxisAlignedBB par2AxisAlignedBB, List par3List, IEntitySelector par4IEntitySelector)
    1119    {
    1120        int i = MathHelper.floor_double((par2AxisAlignedBB.minY - World.MAX_ENTITY_RADIUS) / 16.0D);
    1121        int j = MathHelper.floor_double((par2AxisAlignedBB.maxY + World.MAX_ENTITY_RADIUS) / 16.0D);
    1122
    1123        if (i < 0)
    1124        {
    1125            i = 0;
    1126            j = Math.max(i, j);
    1127        }
    1128
    1129        if (j >= this.entityLists.length)
    1130        {
    1131            j = this.entityLists.length - 1;
    1132            i = Math.min(i, j);
    1133        }
    1134
    1135        for (int k = i; k <= j; ++k)
    1136        {
    1137            List list1 = this.entityLists[k];
    1138
    1139            for (int l = 0; l < list1.size(); ++l)
    1140            {
    1141                Entity entity1 = (Entity)list1.get(l);
    1142
    1143                if (entity1 != par1Entity && entity1.boundingBox.intersectsWith(par2AxisAlignedBB) && (par4IEntitySelector == null || par4IEntitySelector.isEntityApplicable(entity1)))
    1144                {
    1145                    par3List.add(entity1);
    1146                    Entity[] aentity = entity1.getParts();
    1147
    1148                    if (aentity != null)
    1149                    {
    1150                        for (int i1 = 0; i1 < aentity.length; ++i1)
    1151                        {
    1152                            entity1 = aentity[i1];
    1153
    1154                            if (entity1 != par1Entity && entity1.boundingBox.intersectsWith(par2AxisAlignedBB) && (par4IEntitySelector == null || par4IEntitySelector.isEntityApplicable(entity1)))
    1155                            {
    1156                                par3List.add(entity1);
    1157                            }
    1158                        }
    1159                    }
    1160                }
    1161            }
    1162        }
    1163    }
    1164
    1165    /**
    1166     * Gets all entities that can be assigned to the specified class. Args: entityClass, aabb, listToFill
    1167     */
    1168    public void getEntitiesOfTypeWithinAAAB(Class par1Class, AxisAlignedBB par2AxisAlignedBB, List par3List, IEntitySelector par4IEntitySelector)
    1169    {
    1170        int i = MathHelper.floor_double((par2AxisAlignedBB.minY - World.MAX_ENTITY_RADIUS) / 16.0D);
    1171        int j = MathHelper.floor_double((par2AxisAlignedBB.maxY + World.MAX_ENTITY_RADIUS) / 16.0D);
    1172
    1173        if (i < 0)
    1174        {
    1175            i = 0;
    1176        }
    1177        else if (i >= this.entityLists.length)
    1178        {
    1179            i = this.entityLists.length - 1;
    1180        }
    1181
    1182        if (j >= this.entityLists.length)
    1183        {
    1184            j = this.entityLists.length - 1;
    1185        }
    1186        else if (j < 0)
    1187        {
    1188            j = 0;
    1189        }
    1190
    1191        for (int k = i; k <= j; ++k)
    1192        {
    1193            List list1 = this.entityLists[k];
    1194
    1195            for (int l = 0; l < list1.size(); ++l)
    1196            {
    1197                Entity entity = (Entity)list1.get(l);
    1198
    1199                if (par1Class.isAssignableFrom(entity.getClass()) && entity.boundingBox.intersectsWith(par2AxisAlignedBB) && (par4IEntitySelector == null || par4IEntitySelector.isEntityApplicable(entity)))
    1200                {
    1201                    par3List.add(entity);
    1202                }
    1203            }
    1204        }
    1205    }
    1206
    1207    /**
    1208     * Returns true if this Chunk needs to be saved
    1209     */
    1210    public boolean needsSaving(boolean par1)
    1211    {
    1212        if (par1)
    1213        {
    1214            if (this.hasEntities && this.worldObj.getTotalWorldTime() != this.lastSaveTime || this.isModified)
    1215            {
    1216                return true;
    1217            }
    1218        }
    1219        else if (this.hasEntities && this.worldObj.getTotalWorldTime() >= this.lastSaveTime + 600L)
    1220        {
    1221            return true;
    1222        }
    1223
    1224        return this.isModified;
    1225    }
    1226
    1227    public Random getRandomWithSeed(long par1)
    1228    {
    1229        return new Random(this.worldObj.getSeed() + (long)(this.xPosition * this.xPosition * 4987142) + (long)(this.xPosition * 5947611) + (long)(this.zPosition * this.zPosition) * 4392871L + (long)(this.zPosition * 389711) ^ par1);
    1230    }
    1231
    1232    public boolean isEmpty()
    1233    {
    1234        return false;
    1235    }
    1236
    1237    public void populateChunk(IChunkProvider par1IChunkProvider, IChunkProvider par2IChunkProvider, int par3, int par4)
    1238    {
    1239        if (!this.isTerrainPopulated && par1IChunkProvider.chunkExists(par3 + 1, par4 + 1) && par1IChunkProvider.chunkExists(par3, par4 + 1) && par1IChunkProvider.chunkExists(par3 + 1, par4))
    1240        {
    1241            par1IChunkProvider.populate(par2IChunkProvider, par3, par4);
    1242        }
    1243
    1244        if (par1IChunkProvider.chunkExists(par3 - 1, par4) && !par1IChunkProvider.provideChunk(par3 - 1, par4).isTerrainPopulated && par1IChunkProvider.chunkExists(par3 - 1, par4 + 1) && par1IChunkProvider.chunkExists(par3, par4 + 1) && par1IChunkProvider.chunkExists(par3 - 1, par4 + 1))
    1245        {
    1246            par1IChunkProvider.populate(par2IChunkProvider, par3 - 1, par4);
    1247        }
    1248
    1249        if (par1IChunkProvider.chunkExists(par3, par4 - 1) && !par1IChunkProvider.provideChunk(par3, par4 - 1).isTerrainPopulated && par1IChunkProvider.chunkExists(par3 + 1, par4 - 1) && par1IChunkProvider.chunkExists(par3 + 1, par4 - 1) && par1IChunkProvider.chunkExists(par3 + 1, par4))
    1250        {
    1251            par1IChunkProvider.populate(par2IChunkProvider, par3, par4 - 1);
    1252        }
    1253
    1254        if (par1IChunkProvider.chunkExists(par3 - 1, par4 - 1) && !par1IChunkProvider.provideChunk(par3 - 1, par4 - 1).isTerrainPopulated && par1IChunkProvider.chunkExists(par3, par4 - 1) && par1IChunkProvider.chunkExists(par3 - 1, par4))
    1255        {
    1256            par1IChunkProvider.populate(par2IChunkProvider, par3 - 1, par4 - 1);
    1257        }
    1258    }
    1259
    1260    /**
    1261     * Gets the height to which rain/snow will fall. Calculates it if not already stored.
    1262     */
    1263    public int getPrecipitationHeight(int par1, int par2)
    1264    {
    1265        int k = par1 | par2 << 4;
    1266        int l = this.precipitationHeightMap[k];
    1267
    1268        if (l == -999)
    1269        {
    1270            int i1 = this.getTopFilledSegment() + 15;
    1271            l = -1;
    1272
    1273            while (i1 > 0 && l == -1)
    1274            {
    1275                int j1 = this.getBlockID(par1, i1, par2);
    1276                Material material = j1 == 0 ? Material.air : Block.blocksList[j1].blockMaterial;
    1277
    1278                if (!material.blocksMovement() && !material.isLiquid())
    1279                {
    1280                    --i1;
    1281                }
    1282                else
    1283                {
    1284                    l = i1 + 1;
    1285                }
    1286            }
    1287
    1288            this.precipitationHeightMap[k] = l;
    1289        }
    1290
    1291        return l;
    1292    }
    1293
    1294    /**
    1295     * Checks whether skylight needs updated; if it does, calls updateSkylight_do
    1296     */
    1297    public void updateSkylight()
    1298    {
    1299        if (this.isGapLightingUpdated && !this.worldObj.provider.hasNoSky)
    1300        {
    1301            this.updateSkylight_do();
    1302        }
    1303    }
    1304
    1305    /**
    1306     * Gets a ChunkCoordIntPair representing the Chunk's position.
    1307     */
    1308    public ChunkCoordIntPair getChunkCoordIntPair()
    1309    {
    1310        return new ChunkCoordIntPair(this.xPosition, this.zPosition);
    1311    }
    1312
    1313    /**
    1314     * Returns whether the ExtendedBlockStorages containing levels (in blocks) from arg 1 to arg 2 are fully empty
    1315     * (true) or not (false).
    1316     */
    1317    public boolean getAreLevelsEmpty(int par1, int par2)
    1318    {
    1319        if (par1 < 0)
    1320        {
    1321            par1 = 0;
    1322        }
    1323
    1324        if (par2 >= 256)
    1325        {
    1326            par2 = 255;
    1327        }
    1328
    1329        for (int k = par1; k <= par2; k += 16)
    1330        {
    1331            ExtendedBlockStorage extendedblockstorage = this.storageArrays[k >> 4];
    1332
    1333            if (extendedblockstorage != null && !extendedblockstorage.isEmpty())
    1334            {
    1335                return false;
    1336            }
    1337        }
    1338
    1339        return true;
    1340    }
    1341
    1342    public void setStorageArrays(ExtendedBlockStorage[] par1ArrayOfExtendedBlockStorage)
    1343    {
    1344        this.storageArrays = par1ArrayOfExtendedBlockStorage;
    1345    }
    1346
    1347    @SideOnly(Side.CLIENT)
    1348
    1349    /**
    1350     * Initialise this chunk with new binary data
    1351     */
    1352    public void fillChunk(byte[] par1ArrayOfByte, int par2, int par3, boolean par4)
    1353    {
    1354        Iterator iterator = chunkTileEntityMap.values().iterator();
    1355        while(iterator.hasNext())
    1356        {
    1357            TileEntity tileEntity = (TileEntity)iterator.next();
    1358            tileEntity.updateContainingBlockInfo();
    1359            tileEntity.getBlockMetadata();
    1360            tileEntity.getBlockType();
    1361        }
    1362
    1363        int k = 0;
    1364        boolean flag1 = !this.worldObj.provider.hasNoSky;
    1365        int l;
    1366
    1367        for (l = 0; l < this.storageArrays.length; ++l)
    1368        {
    1369            if ((par2 & 1 << l) != 0)
    1370            {
    1371                if (this.storageArrays[l] == null)
    1372                {
    1373                    this.storageArrays[l] = new ExtendedBlockStorage(l << 4, flag1);
    1374                }
    1375
    1376                byte[] abyte1 = this.storageArrays[l].getBlockLSBArray();
    1377                System.arraycopy(par1ArrayOfByte, k, abyte1, 0, abyte1.length);
    1378                k += abyte1.length;
    1379            }
    1380            else if (par4 && this.storageArrays[l] != null)
    1381            {
    1382                this.storageArrays[l] = null;
    1383            }
    1384        }
    1385
    1386        NibbleArray nibblearray;
    1387
    1388        for (l = 0; l < this.storageArrays.length; ++l)
    1389        {
    1390            if ((par2 & 1 << l) != 0 && this.storageArrays[l] != null)
    1391            {
    1392                nibblearray = this.storageArrays[l].getMetadataArray();
    1393                System.arraycopy(par1ArrayOfByte, k, nibblearray.data, 0, nibblearray.data.length);
    1394                k += nibblearray.data.length;
    1395            }
    1396        }
    1397
    1398        for (l = 0; l < this.storageArrays.length; ++l)
    1399        {
    1400            if ((par2 & 1 << l) != 0 && this.storageArrays[l] != null)
    1401            {
    1402                nibblearray = this.storageArrays[l].getBlocklightArray();
    1403                System.arraycopy(par1ArrayOfByte, k, nibblearray.data, 0, nibblearray.data.length);
    1404                k += nibblearray.data.length;
    1405            }
    1406        }
    1407
    1408        if (flag1)
    1409        {
    1410            for (l = 0; l < this.storageArrays.length; ++l)
    1411            {
    1412                if ((par2 & 1 << l) != 0 && this.storageArrays[l] != null)
    1413                {
    1414                    nibblearray = this.storageArrays[l].getSkylightArray();
    1415                    System.arraycopy(par1ArrayOfByte, k, nibblearray.data, 0, nibblearray.data.length);
    1416                    k += nibblearray.data.length;
    1417                }
    1418            }
    1419        }
    1420
    1421        for (l = 0; l < this.storageArrays.length; ++l)
    1422        {
    1423            if ((par3 & 1 << l) != 0)
    1424            {
    1425                if (this.storageArrays[l] == null)
    1426                {
    1427                    k += 2048;
    1428                }
    1429                else
    1430                {
    1431                    nibblearray = this.storageArrays[l].getBlockMSBArray();
    1432
    1433                    if (nibblearray == null)
    1434                    {
    1435                        nibblearray = this.storageArrays[l].createBlockMSBArray();
    1436                    }
    1437
    1438                    System.arraycopy(par1ArrayOfByte, k, nibblearray.data, 0, nibblearray.data.length);
    1439                    k += nibblearray.data.length;
    1440                }
    1441            }
    1442            else if (par4 && this.storageArrays[l] != null && this.storageArrays[l].getBlockMSBArray() != null)
    1443            {
    1444                this.storageArrays[l].clearMSBArray();
    1445            }
    1446        }
    1447
    1448        if (par4)
    1449        {
    1450            System.arraycopy(par1ArrayOfByte, k, this.blockBiomeArray, 0, this.blockBiomeArray.length);
    1451            int i1 = k + this.blockBiomeArray.length;
    1452        }
    1453
    1454        for (l = 0; l < this.storageArrays.length; ++l)
    1455        {
    1456            if (this.storageArrays[l] != null && (par2 & 1 << l) != 0)
    1457            {
    1458                this.storageArrays[l].removeInvalidBlocks();
    1459            }
    1460        }
    1461
    1462        this.generateHeightMap();
    1463
    1464        List<TileEntity> invalidList = new ArrayList<TileEntity>();
    1465        iterator = chunkTileEntityMap.values().iterator();
    1466        while (iterator.hasNext())
    1467        {
    1468            TileEntity tileEntity = (TileEntity)iterator.next();
    1469            int x = tileEntity.xCoord & 15;
    1470            int y = tileEntity.yCoord;
    1471            int z = tileEntity.zCoord & 15;
    1472            Block block = tileEntity.getBlockType();
    1473            if (block == null || block.blockID != getBlockID(x, y, z) || tileEntity.getBlockMetadata() != getBlockMetadata(x, y, z))
    1474            {
    1475                invalidList.add(tileEntity);
    1476            }
    1477            tileEntity.updateContainingBlockInfo();
    1478        }
    1479
    1480        for (TileEntity tileEntity : invalidList)
    1481        {
    1482            tileEntity.invalidate();
    1483        }
    1484    }
    1485
    1486    /**
    1487     * This method retrieves the biome at a set of coordinates
    1488     */
    1489    public BiomeGenBase getBiomeGenForWorldCoords(int par1, int par2, WorldChunkManager par3WorldChunkManager)
    1490    {
    1491        int k = this.blockBiomeArray[par2 << 4 | par1] & 255;
    1492
    1493        if (k == 255)
    1494        {
    1495            BiomeGenBase biomegenbase = par3WorldChunkManager.getBiomeGenAt((this.xPosition << 4) + par1, (this.zPosition << 4) + par2);
    1496            k = biomegenbase.biomeID;
    1497            this.blockBiomeArray[par2 << 4 | par1] = (byte)(k & 255);
    1498        }
    1499
    1500        return BiomeGenBase.biomeList[k] == null ? BiomeGenBase.plains : BiomeGenBase.biomeList[k];
    1501    }
    1502
    1503    /**
    1504     * Returns an array containing a 16x16 mapping on the X/Z of block positions in this Chunk to biome IDs.
    1505     */
    1506    public byte[] getBiomeArray()
    1507    {
    1508        return this.blockBiomeArray;
    1509    }
    1510
    1511    /**
    1512     * Accepts a 256-entry array that contains a 16x16 mapping on the X/Z plane of block positions in this Chunk to
    1513     * biome IDs.
    1514     */
    1515    public void setBiomeArray(byte[] par1ArrayOfByte)
    1516    {
    1517        this.blockBiomeArray = par1ArrayOfByte;
    1518    }
    1519
    1520    /**
    1521     * Resets the relight check index to 0 for this Chunk.
    1522     */
    1523    public void resetRelightChecks()
    1524    {
    1525        this.queuedLightChecks = 0;
    1526    }
    1527
    1528    /**
    1529     * Called once-per-chunk-per-tick, and advances the round-robin relight check index per-storage-block by up to 8
    1530     * blocks at a time. In a worst-case scenario, can potentially take up to 1.6 seconds, calculated via
    1531     * (4096/(8*16))/20, to re-check all blocks in a chunk, which could explain both lagging light updates in certain
    1532     * cases as well as Nether relight
    1533     */
    1534    public void enqueueRelightChecks()
    1535    {
    1536        for (int i = 0; i < 8; ++i)
    1537        {
    1538            if (this.queuedLightChecks >= 4096)
    1539            {
    1540                return;
    1541            }
    1542
    1543            int j = this.queuedLightChecks % 16;
    1544            int k = this.queuedLightChecks / 16 % 16;
    1545            int l = this.queuedLightChecks / 256;
    1546            ++this.queuedLightChecks;
    1547            int i1 = (this.xPosition << 4) + k;
    1548            int j1 = (this.zPosition << 4) + l;
    1549
    1550            for (int k1 = 0; k1 < 16; ++k1)
    1551            {
    1552                int l1 = (j << 4) + k1;
    1553
    1554                if (this.storageArrays[j] == null && (k1 == 0 || k1 == 15 || k == 0 || k == 15 || l == 0 || l == 15) || this.storageArrays[j] != null && this.storageArrays[j].getExtBlockID(k, k1, l) == 0)
    1555                {
    1556                    if (Block.lightValue[this.worldObj.getBlockId(i1, l1 - 1, j1)] > 0)
    1557                    {
    1558                        this.worldObj.updateAllLightTypes(i1, l1 - 1, j1);
    1559                    }
    1560
    1561                    if (Block.lightValue[this.worldObj.getBlockId(i1, l1 + 1, j1)] > 0)
    1562                    {
    1563                        this.worldObj.updateAllLightTypes(i1, l1 + 1, j1);
    1564                    }
    1565
    1566                    if (Block.lightValue[this.worldObj.getBlockId(i1 - 1, l1, j1)] > 0)
    1567                    {
    1568                        this.worldObj.updateAllLightTypes(i1 - 1, l1, j1);
    1569                    }
    1570
    1571                    if (Block.lightValue[this.worldObj.getBlockId(i1 + 1, l1, j1)] > 0)
    1572                    {
    1573                        this.worldObj.updateAllLightTypes(i1 + 1, l1, j1);
    1574                    }
    1575
    1576                    if (Block.lightValue[this.worldObj.getBlockId(i1, l1, j1 - 1)] > 0)
    1577                    {
    1578                        this.worldObj.updateAllLightTypes(i1, l1, j1 - 1);
    1579                    }
    1580
    1581                    if (Block.lightValue[this.worldObj.getBlockId(i1, l1, j1 + 1)] > 0)
    1582                    {
    1583                        this.worldObj.updateAllLightTypes(i1, l1, j1 + 1);
    1584                    }
    1585
    1586                    this.worldObj.updateAllLightTypes(i1, l1, j1);
    1587                }
    1588            }
    1589        }
    1590    }
    1591
    1592    /** FORGE: Used to remove only invalid TileEntities */
    1593    public void cleanChunkBlockTileEntity(int x, int y, int z)
    1594    {
    1595        ChunkPosition position = new ChunkPosition(x, y, z);
    1596        if (isChunkLoaded)
    1597        {
    1598            TileEntity entity = (TileEntity)chunkTileEntityMap.get(position);
    1599            if (entity != null && entity.isInvalid())
    1600            {
    1601                chunkTileEntityMap.remove(position);
    1602            }
    1603        }
    1604    }
    1605}