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