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