    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
    1021            for (int var1 = 0; var1 < this.entityLists.length; ++var1)
    1022            {
    1023                this.worldObj.addLoadedEntities(this.entityLists[var1]);
    1024            }
    1025            MinecraftForge.EVENT_BUS.post(new ChunkEvent.Load(this));
    1026        }
    1027
    1028        /**
    1029         * Called when this Chunk is unloaded by the ChunkProvider
    1030         */
    1031        public void onChunkUnload()
    1032        {
    1033            this.isChunkLoaded = false;
    1034            Iterator var1 = this.chunkTileEntityMap.values().iterator();
    1035
    1036            while (var1.hasNext())
    1037            {
    1038                TileEntity var2 = (TileEntity)var1.next();
    1039                this.worldObj.markTileEntityForDespawn(var2);
    1040            }
    1041
    1042            for (int var3 = 0; var3 < this.entityLists.length; ++var3)
    1043            {
    1044                this.worldObj.unloadEntities(this.entityLists[var3]);
    1045            }
    1046            MinecraftForge.EVENT_BUS.post(new ChunkEvent.Unload(this));
    1047        }
    1048
    1049        /**
    1050         * Sets the isModified flag for this Chunk
    1051         */
    1052        public void setChunkModified()
    1053        {
    1054            this.isModified = true;
    1055        }
    1056
    1057        /**
    1058         * Fills the given list of all entities that intersect within the given bounding box that aren't the passed entity
    1059         * Args: entity, aabb, listToFill
    1060         */
    1061        public void getEntitiesWithinAABBForEntity(Entity par1Entity, AxisAlignedBB par2AxisAlignedBB, List par3List)
    1062        {
    1063            int var4 = MathHelper.floor_double((par2AxisAlignedBB.minY - World.MAX_ENTITY_RADIUS) / 16.0D);
    1064            int var5 = MathHelper.floor_double((par2AxisAlignedBB.maxY + World.MAX_ENTITY_RADIUS) / 16.0D);
    1065
    1066            if (var4 < 0)
    1067            {
    1068                var4 = 0;
    1069            }
    1070
    1071            if (var5 >= this.entityLists.length)
    1072            {
    1073                var5 = this.entityLists.length - 1;
    1074            }
    1075
    1076            for (int var6 = var4; var6 <= var5; ++var6)
    1077            {
    1078                List var7 = this.entityLists[var6];
    1079
    1080                for (int var8 = 0; var8 < var7.size(); ++var8)
    1081                {
    1082                    Entity var9 = (Entity)var7.get(var8);
    1083
    1084                    if (var9 != par1Entity && var9.boundingBox.intersectsWith(par2AxisAlignedBB))
    1085                    {
    1086                        par3List.add(var9);
    1087                        Entity[] var10 = var9.getParts();
    1088
    1089                        if (var10 != null)
    1090                        {
    1091                            for (int var11 = 0; var11 < var10.length; ++var11)
    1092                            {
    1093                                var9 = var10[var11];
    1094
    1095                                if (var9 != par1Entity && var9.boundingBox.intersectsWith(par2AxisAlignedBB))
    1096                                {
    1097                                    par3List.add(var9);
    1098                                }
    1099                            }
    1100                        }
    1101                    }
    1102                }
    1103            }
    1104        }
    1105
    1106        /**
    1107         * Gets all entities that can be assigned to the specified class. Args: entityClass, aabb, listToFill
    1108         */
    1109        public void getEntitiesOfTypeWithinAAAB(Class par1Class, AxisAlignedBB par2AxisAlignedBB, List par3List, IEntitySelector par4IEntitySelector)
    1110        {
    1111            int var5 = MathHelper.floor_double((par2AxisAlignedBB.minY - World.MAX_ENTITY_RADIUS) / 16.0D);
    1112            int var6 = MathHelper.floor_double((par2AxisAlignedBB.maxY + World.MAX_ENTITY_RADIUS) / 16.0D);
    1113
    1114            if (var5 < 0)
    1115            {
    1116                var5 = 0;
    1117            }
    1118            else if (var5 >= this.entityLists.length)
    1119            {
    1120                var5 = this.entityLists.length - 1;
    1121            }
    1122
    1123            if (var6 >= this.entityLists.length)
    1124            {
    1125                var6 = this.entityLists.length - 1;
    1126            }
    1127            else if (var6 < 0)
    1128            {
    1129                var6 = 0;
    1130            }
    1131
    1132            for (int var7 = var5; var7 <= var6; ++var7)
    1133            {
    1134                List var8 = this.entityLists[var7];
    1135
    1136                for (int var9 = 0; var9 < var8.size(); ++var9)
    1137                {
    1138                    Entity var10 = (Entity)var8.get(var9);
    1139
    1140                    if (par1Class.isAssignableFrom(var10.getClass()) && var10.boundingBox.intersectsWith(par2AxisAlignedBB) && (par4IEntitySelector == null || par4IEntitySelector.isEntityApplicable(var10)))
    1141                    {
    1142                        par3List.add(var10);
    1143                    }
    1144                }
    1145            }
    1146        }
    1147
    1148        /**
    1149         * Returns true if this Chunk needs to be saved
    1150         */
    1151        public boolean needsSaving(boolean par1)
    1152        {
    1153            if (par1)
    1154            {
    1155                if (this.hasEntities && this.worldObj.getTotalWorldTime() != this.lastSaveTime)
    1156                {
    1157                    return true;
    1158                }
    1159            }
    1160            else if (this.hasEntities && this.worldObj.getTotalWorldTime() >= this.lastSaveTime + 600L)
    1161            {
    1162                return true;
    1163            }
    1164
    1165            return this.isModified;
    1166        }
    1167
    1168        public Random getRandomWithSeed(long par1)
    1169        {
    1170            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);
    1171        }
    1172
    1173        public boolean isEmpty()
    1174        {
    1175            return false;
    1176        }
    1177
    1178        public void populateChunk(IChunkProvider par1IChunkProvider, IChunkProvider par2IChunkProvider, int par3, int par4)
    1179        {
    1180            if (!this.isTerrainPopulated && par1IChunkProvider.chunkExists(par3 + 1, par4 + 1) && par1IChunkProvider.chunkExists(par3, par4 + 1) && par1IChunkProvider.chunkExists(par3 + 1, par4))
    1181            {
    1182                par1IChunkProvider.populate(par2IChunkProvider, par3, par4);
    1183            }
    1184
    1185            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))
    1186            {
    1187                par1IChunkProvider.populate(par2IChunkProvider, par3 - 1, par4);
    1188            }
    1189
    1190            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))
    1191            {
    1192                par1IChunkProvider.populate(par2IChunkProvider, par3, par4 - 1);
    1193            }
    1194
    1195            if (par1IChunkProvider.chunkExists(par3 - 1, par4 - 1) && !par1IChunkProvider.provideChunk(par3 - 1, par4 - 1).isTerrainPopulated && par1IChunkProvider.chunkExists(par3, par4 - 1) && par1IChunkProvider.chunkExists(par3 - 1, par4))
    1196            {
    1197                par1IChunkProvider.populate(par2IChunkProvider, par3 - 1, par4 - 1);
    1198            }
    1199        }
    1200
    1201        /**
    1202         * Gets the height to which rain/snow will fall. Calculates it if not already stored.
    1203         */
    1204        public int getPrecipitationHeight(int par1, int par2)
    1205        {
    1206            int var3 = par1 | par2 << 4;
    1207            int var4 = this.precipitationHeightMap[var3];
    1208
    1209            if (var4 == -999)
    1210            {
    1211                int var5 = this.getTopFilledSegment() + 15;
    1212                var4 = -1;
    1213
    1214                while (var5 > 0 && var4 == -1)
    1215                {
    1216                    int var6 = this.getBlockID(par1, var5, par2);
    1217                    Material var7 = var6 == 0 ? Material.air : Block.blocksList[var6].blockMaterial;
    1218
    1219                    if (!var7.blocksMovement() && !var7.isLiquid())
    1220                    {
    1221                        --var5;
    1222                    }
    1223                    else
    1224                    {
    1225                        var4 = var5 + 1;
    1226                    }
    1227                }
    1228
    1229                this.precipitationHeightMap[var3] = var4;
    1230            }
    1231
    1232            return var4;
    1233        }
    1234
    1235        /**
    1236         * Checks whether skylight needs updated; if it does, calls updateSkylight_do
    1237         */
    1238        public void updateSkylight()
    1239        {
    1240            if (this.isGapLightingUpdated && !this.worldObj.provider.hasNoSky)
    1241            {
    1242                this.updateSkylight_do();
    1243            }
    1244        }
    1245
    1246        /**
    1247         * Gets a ChunkCoordIntPair representing the Chunk's position.
    1248         */
    1249        public ChunkCoordIntPair getChunkCoordIntPair()
    1250        {
    1251            return new ChunkCoordIntPair(this.xPosition, this.zPosition);
    1252        }
    1253
    1254        /**
    1255         * Returns whether the ExtendedBlockStorages containing levels (in blocks) from arg 1 to arg 2 are fully empty
    1256         * (true) or not (false).
    1257         */
    1258        public boolean getAreLevelsEmpty(int par1, int par2)
    1259        {
    1260            if (par1 < 0)
    1261            {
    1262                par1 = 0;
    1263            }
    1264
    1265            if (par2 >= 256)
    1266            {
    1267                par2 = 255;
    1268            }
    1269
    1270            for (int var3 = par1; var3 <= par2; var3 += 16)
    1271            {
    1272                ExtendedBlockStorage var4 = this.storageArrays[var3 >> 4];
    1273
    1274                if (var4 != null && !var4.isEmpty())
    1275                {
    1276                    return false;
    1277                }
    1278            }
    1279
    1280            return true;
    1281        }
    1282
    1283        public void setStorageArrays(ExtendedBlockStorage[] par1ArrayOfExtendedBlockStorage)
    1284        {
    1285            this.storageArrays = par1ArrayOfExtendedBlockStorage;
    1286        }
    1287
    1288        @SideOnly(Side.CLIENT)
    1289
    1290        /**
    1291         * Initialise this chunk with new binary data
    1292         */
    1293        public void fillChunk(byte[] par1ArrayOfByte, int par2, int par3, boolean par4)
    1294        {
    1295            Iterator iterator = chunkTileEntityMap.values().iterator();
    1296            while(iterator.hasNext())
    1297            {
    1298                TileEntity tileEntity = (TileEntity)iterator.next();
    1299                tileEntity.updateContainingBlockInfo();
    1300                tileEntity.getBlockMetadata();
    1301                tileEntity.getBlockType();
    1302            }
    1303
    1304            int var5 = 0;
    1305            int var6;
    1306
    1307            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
    1308            {
    1309                if ((par2 & 1 << var6) != 0)
    1310                {
    1311                    if (this.storageArrays[var6] == null)
    1312                    {
    1313                        this.storageArrays[var6] = new ExtendedBlockStorage(var6 << 4);
    1314                    }
    1315
    1316                    byte[] var7 = this.storageArrays[var6].getBlockLSBArray();
    1317                    System.arraycopy(par1ArrayOfByte, var5, var7, 0, var7.length);
    1318                    var5 += var7.length;
    1319                }
    1320                else if (par4 && this.storageArrays[var6] != null)
    1321                {
    1322                    this.storageArrays[var6] = null;
    1323                }
    1324            }
    1325
    1326            NibbleArray var8;
    1327
    1328            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
    1329            {
    1330                if ((par2 & 1 << var6) != 0 && this.storageArrays[var6] != null)
    1331                {
    1332                    var8 = this.storageArrays[var6].getMetadataArray();
    1333                    System.arraycopy(par1ArrayOfByte, var5, var8.data, 0, var8.data.length);
    1334                    var5 += var8.data.length;
    1335                }
    1336            }
    1337
    1338            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
    1339            {
    1340                if ((par2 & 1 << var6) != 0 && this.storageArrays[var6] != null)
    1341                {
    1342                    var8 = this.storageArrays[var6].getBlocklightArray();
    1343                    System.arraycopy(par1ArrayOfByte, var5, var8.data, 0, var8.data.length);
    1344                    var5 += var8.data.length;
    1345                }
    1346            }
    1347
    1348            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
    1349            {
    1350                if ((par2 & 1 << var6) != 0 && this.storageArrays[var6] != null)
    1351                {
    1352                    var8 = this.storageArrays[var6].getSkylightArray();
    1353                    System.arraycopy(par1ArrayOfByte, var5, var8.data, 0, var8.data.length);
    1354                    var5 += var8.data.length;
    1355                }
    1356            }
    1357
    1358            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
    1359            {
    1360                if ((par3 & 1 << var6) != 0)
    1361                {
    1362                    if (this.storageArrays[var6] == null)
    1363                    {
    1364                        var5 += 2048;
    1365                    }
    1366                    else
    1367                    {
    1368                        var8 = this.storageArrays[var6].getBlockMSBArray();
    1369
    1370                        if (var8 == null)
    1371                        {
    1372                            var8 = this.storageArrays[var6].createBlockMSBArray();
    1373                        }
    1374
    1375                        System.arraycopy(par1ArrayOfByte, var5, var8.data, 0, var8.data.length);
    1376                        var5 += var8.data.length;
    1377                    }
    1378                }
    1379                else if (par4 && this.storageArrays[var6] != null && this.storageArrays[var6].getBlockMSBArray() != null)
    1380                {
    1381                    this.storageArrays[var6].clearMSBArray();
    1382                }
    1383            }
    1384
    1385            if (par4)
    1386            {
    1387                System.arraycopy(par1ArrayOfByte, var5, this.blockBiomeArray, 0, this.blockBiomeArray.length);
    1388                int var10000 = var5 + this.blockBiomeArray.length;
    1389            }
    1390
    1391            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
    1392            {
    1393                if (this.storageArrays[var6] != null && (par2 & 1 << var6) != 0)
    1394                {
    1395                    this.storageArrays[var6].removeInvalidBlocks();
    1396                }
    1397            }
    1398
    1399            this.generateHeightMap();
    1400
    1401            List<TileEntity> invalidList = new ArrayList<TileEntity>();
    1402            iterator = chunkTileEntityMap.values().iterator();
    1403            while (iterator.hasNext())
    1404            {
    1405                TileEntity tileEntity = (TileEntity)iterator.next();
    1406                int x = tileEntity.xCoord & 15;
    1407                int y = tileEntity.yCoord;
    1408                int z = tileEntity.zCoord & 15;
    1409                Block block = tileEntity.getBlockType();
    1410                if (block == null || block.blockID != getBlockID(x, y, z) || tileEntity.getBlockMetadata() != getBlockMetadata(x, y, z))
    1411                {
    1412                    invalidList.add(tileEntity);
    1413                }
    1414                tileEntity.updateContainingBlockInfo();
    1415            }
    1416
    1417            for (TileEntity tileEntity : invalidList)
    1418            {
    1419                tileEntity.invalidate();
    1420            }
    1421        }
    1422
    1423        /**
    1424         * This method retrieves the biome at a set of coordinates
    1425         */
    1426        public BiomeGenBase getBiomeGenForWorldCoords(int par1, int par2, WorldChunkManager par3WorldChunkManager)
    1427        {
    1428            int var4 = this.blockBiomeArray[par2 << 4 | par1] & 255;
    1429
    1430            if (var4 == 255)
    1431            {
    1432                BiomeGenBase var5 = par3WorldChunkManager.getBiomeGenAt((this.xPosition << 4) + par1, (this.zPosition << 4) + par2);
    1433                var4 = var5.biomeID;
    1434                this.blockBiomeArray[par2 << 4 | par1] = (byte)(var4 & 255);
    1435            }
    1436
    1437            return BiomeGenBase.biomeList[var4] == null ? BiomeGenBase.plains : BiomeGenBase.biomeList[var4];
    1438        }
    1439
    1440        /**
    1441         * Returns an array containing a 16x16 mapping on the X/Z of block positions in this Chunk to biome IDs.
    1442         */
    1443        public byte[] getBiomeArray()
    1444        {
    1445            return this.blockBiomeArray;
    1446        }
    1447
    1448        /**
    1449         * Accepts a 256-entry array that contains a 16x16 mapping on the X/Z plane of block positions in this Chunk to
    1450         * biome IDs.
    1451         */
    1452        public void setBiomeArray(byte[] par1ArrayOfByte)
    1453        {
    1454            this.blockBiomeArray = par1ArrayOfByte;
    1455        }
    1456
    1457        /**
    1458         * Resets the relight check index to 0 for this Chunk.
    1459         */
    1460        public void resetRelightChecks()
    1461        {
    1462            this.queuedLightChecks = 0;
    1463        }
    1464
    1465        /**
    1466         * Called once-per-chunk-per-tick, and advances the round-robin relight check index per-storage-block by up to 8
    1467         * blocks at a time. In a worst-case scenario, can potentially take up to 1.6 seconds, calculated via
    1468         * (4096/(8*16))/20, to re-check all blocks in a chunk, which could explain both lagging light updates in certain
    1469         * cases as well as Nether relight
    1470         */
    1471        public void enqueueRelightChecks()
    1472        {
    1473            for (int var1 = 0; var1 < 8; ++var1)
    1474            {
    1475                if (this.queuedLightChecks >= 4096)
    1476                {
    1477                    return;
    1478                }
    1479
    1480                int var2 = this.queuedLightChecks % 16;
    1481                int var3 = this.queuedLightChecks / 16 % 16;
    1482                int var4 = this.queuedLightChecks / 256;
    1483                ++this.queuedLightChecks;
    1484                int var5 = (this.xPosition << 4) + var3;
    1485                int var6 = (this.zPosition << 4) + var4;
    1486
    1487                for (int var7 = 0; var7 < 16; ++var7)
    1488                {
    1489                    int var8 = (var2 << 4) + var7;
    1490
    1491                    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)
    1492                    {
    1493                        if (Block.lightValue[this.worldObj.getBlockId(var5, var8 - 1, var6)] > 0)
    1494                        {
    1495                            this.worldObj.updateAllLightTypes(var5, var8 - 1, var6);
    1496                        }
    1497
    1498                        if (Block.lightValue[this.worldObj.getBlockId(var5, var8 + 1, var6)] > 0)
    1499                        {
    1500                            this.worldObj.updateAllLightTypes(var5, var8 + 1, var6);
    1501                        }
    1502
    1503                        if (Block.lightValue[this.worldObj.getBlockId(var5 - 1, var8, var6)] > 0)
    1504                        {
    1505                            this.worldObj.updateAllLightTypes(var5 - 1, var8, var6);
    1506                        }
    1507
    1508                        if (Block.lightValue[this.worldObj.getBlockId(var5 + 1, var8, var6)] > 0)
    1509                        {
    1510                            this.worldObj.updateAllLightTypes(var5 + 1, var8, var6);
    1511                        }
    1512
    1513                        if (Block.lightValue[this.worldObj.getBlockId(var5, var8, var6 - 1)] > 0)
    1514                        {
    1515                            this.worldObj.updateAllLightTypes(var5, var8, var6 - 1);
    1516                        }
    1517
    1518                        if (Block.lightValue[this.worldObj.getBlockId(var5, var8, var6 + 1)] > 0)
    1519                        {
    1520                            this.worldObj.updateAllLightTypes(var5, var8, var6 + 1);
    1521                        }
    1522
    1523                        this.worldObj.updateAllLightTypes(var5, var8, var6);
    1524                    }
    1525                }
    1526            }
    1527        }
    1528
    1529        /** FORGE: Used to remove only invalid TileEntities */
    1530        public void cleanChunkBlockTileEntity(int x, int y, int z)
    1531        {
    1532            ChunkPosition position = new ChunkPosition(x, y, z);
    1533            if (isChunkLoaded)
    1534            {
    1535                TileEntity entity = (TileEntity)chunkTileEntityMap.get(position);
    1536                if (entity != null && entity.isInvalid())
    1537                {
    1538                    chunkTileEntityMap.remove(position);
    1539                }
    1540            }
    1541        }
    1542    }