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