    001package net.minecraft.world.chunk;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005import java.util.ArrayList;
    006import java.util.Arrays;
    007import java.util.HashMap;
    008import java.util.Iterator;
    009import java.util.List;
    010import java.util.Map;
    011import java.util.Random;
    012import net.minecraft.block.Block;
    013import net.minecraft.block.BlockContainer;
    014import net.minecraft.block.material.Material;
    015import net.minecraft.command.IEntitySelector;
    016import net.minecraft.entity.Entity;
    017import net.minecraft.tileentity.TileEntity;
    018import net.minecraft.util.AxisAlignedBB;
    019import net.minecraft.util.MathHelper;
    020import net.minecraft.world.ChunkCoordIntPair;
    021import net.minecraft.world.ChunkPosition;
    022import net.minecraft.world.EnumSkyBlock;
    023import net.minecraft.world.World;
    024import net.minecraft.world.biome.BiomeGenBase;
    025import net.minecraft.world.biome.WorldChunkManager;
    026import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
    027
    028import net.minecraftforge.common.MinecraftForge;
    029import net.minecraftforge.event.entity.EntityEvent;
    030import net.minecraftforge.event.world.ChunkEvent;
    031
    032public class Chunk
    033{
    034    /**
    035     * Determines if the chunk is lit or not at a light value greater than 0.
    036     */
    037    public static boolean isLit;
    038
    039    /**
    040     * Used to store block IDs, block MSBs, Sky-light maps, Block-light maps, and metadata. Each entry corresponds to a
    041     * logical segment of 16x16x16 blocks, stacked vertically.
    042     */
    043    private ExtendedBlockStorage[] storageArrays;
    044
    045    /**
    046     * Contains a 16x16 mapping on the X/Z plane of the biome ID to which each colum belongs.
    047     */
    048    private byte[] blockBiomeArray;
    049
    050    /**
    051     * A map, similar to heightMap, that tracks how far down precipitation can fall.
    052     */
    053    public int[] precipitationHeightMap;
    054
    055    /** Which columns need their skylightMaps updated. */
    056    public boolean[] updateSkylightColumns;
    057
    058    /** Whether or not this Chunk is currently loaded into the World */
    059    public boolean isChunkLoaded;
    060
    061    /** Reference to the World object. */
    062    public World worldObj;
    063    public int[] heightMap;
    064
    065    /** The x coordinate of the chunk. */
    066    public final int xPosition;
    067
    068    /** The z coordinate of the chunk. */
    069    public final int zPosition;
    070    private boolean isGapLightingUpdated;
    071
    072    /** A Map of ChunkPositions to TileEntities in this chunk */
    073    public Map chunkTileEntityMap;
    074
    075    /**
    076     * Array of Lists containing the entities in this Chunk. Each List represents a 16 block subchunk.
    077     */
    078    public List[] entityLists;
    079
    080    /** Boolean value indicating if the terrain is populated. */
    081    public boolean isTerrainPopulated;
    082
    083    /**
    084     * Set to true if the chunk has been modified and needs to be updated internally.
    085     */
    086    public boolean isModified;
    087
    088    /**
    089     * Whether this Chunk has any Entities and thus requires saving on every tick
    090     */
    091    public boolean hasEntities;
    092
    093    /** The time according to World.worldTime when this chunk was last saved */
    094    public long lastSaveTime;
    095    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        else if (var4 >= this.entityLists.length) //BugFix: Collision above the world
    1132        {
    1133            var4 = this.entityLists.length - 1;
    1134        }
    1135
    1136        if (var5 >= this.entityLists.length)
    1137        {
    1138            var5 = this.entityLists.length - 1;
    1139        }
    1140        else if (var5 < 0) //BugFix: Collision below the world
    1141        {
    1142            var5 = 0;
    1143        }
    1144
    1145        for (int var6 = var4; var6 <= var5; ++var6)
    1146        {
    1147            List var7 = this.entityLists[var6];
    1148
    1149            for (int var8 = 0; var8 < var7.size(); ++var8)
    1150            {
    1151                Entity var9 = (Entity)var7.get(var8);
    1152
    1153                if (var9 != par1Entity && var9.boundingBox.intersectsWith(par2AxisAlignedBB))
    1154                {
    1155                    par3List.add(var9);
    1156                    Entity[] var10 = var9.getParts();
    1157
    1158                    if (var10 != null)
    1159                    {
    1160                        for (int var11 = 0; var11 < var10.length; ++var11)
    1161                        {
    1162                            var9 = var10[var11];
    1163
    1164                            if (var9 != par1Entity && var9.boundingBox.intersectsWith(par2AxisAlignedBB))
    1165                            {
    1166                                par3List.add(var9);
    1167                            }
    1168                        }
    1169                    }
    1170                }
    1171            }
    1172        }
    1173    }
    1174
    1175    /**
    1176     * Gets all entities that can be assigned to the specified class. Args: entityClass, aabb, listToFill
    1177     */
    1178    public void getEntitiesOfTypeWithinAAAB(Class par1Class, AxisAlignedBB par2AxisAlignedBB, List par3List, IEntitySelector par4IEntitySelector)
    1179    {
    1180        int var5 = MathHelper.floor_double((par2AxisAlignedBB.minY - World.MAX_ENTITY_RADIUS) / 16.0D);
    1181        int var6 = MathHelper.floor_double((par2AxisAlignedBB.maxY + World.MAX_ENTITY_RADIUS) / 16.0D);
    1182
    1183        if (var5 < 0)
    1184        {
    1185            var5 = 0;
    1186        }
    1187        else if (var5 >= this.entityLists.length)
    1188        {
    1189            var5 = this.entityLists.length - 1;
    1190        }
    1191
    1192        if (var6 >= this.entityLists.length)
    1193        {
    1194            var6 = this.entityLists.length - 1;
    1195        }
    1196        else if (var6 < 0)
    1197        {
    1198            var6 = 0;
    1199        }
    1200
    1201        for (int var7 = var5; var7 <= var6; ++var7)
    1202        {
    1203            List var8 = this.entityLists[var7];
    1204
    1205            for (int var9 = 0; var9 < var8.size(); ++var9)
    1206            {
    1207                Entity var10 = (Entity)var8.get(var9);
    1208
    1209                if (par1Class.isAssignableFrom(var10.getClass()) && var10.boundingBox.intersectsWith(par2AxisAlignedBB) && (par4IEntitySelector == null || par4IEntitySelector.isEntityApplicable(var10)))
    1210                {
    1211                    par3List.add(var10);
    1212                }
    1213            }
    1214        }
    1215    }
    1216
    1217    /**
    1218     * Returns true if this Chunk needs to be saved
    1219     */
    1220    public boolean needsSaving(boolean par1)
    1221    {
    1222        if (par1)
    1223        {
    1224            if (this.hasEntities && this.worldObj.getTotalWorldTime() != this.lastSaveTime)
    1225            {
    1226                return true;
    1227            }
    1228        }
    1229        else if (this.hasEntities && this.worldObj.getTotalWorldTime() >= this.lastSaveTime + 600L)
    1230        {
    1231            return true;
    1232        }
    1233
    1234        return this.isModified;
    1235    }
    1236
    1237    public Random getRandomWithSeed(long par1)
    1238    {
    1239        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);
    1240    }
    1241
    1242    public boolean isEmpty()
    1243    {
    1244        return false;
    1245    }
    1246
    1247    public void populateChunk(IChunkProvider par1IChunkProvider, IChunkProvider par2IChunkProvider, int par3, int par4)
    1248    {
    1249        if (!this.isTerrainPopulated && par1IChunkProvider.chunkExists(par3 + 1, par4 + 1) && par1IChunkProvider.chunkExists(par3, par4 + 1) && par1IChunkProvider.chunkExists(par3 + 1, par4))
    1250        {
    1251            par1IChunkProvider.populate(par2IChunkProvider, par3, par4);
    1252        }
    1253
    1254        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))
    1255        {
    1256            par1IChunkProvider.populate(par2IChunkProvider, par3 - 1, par4);
    1257        }
    1258
    1259        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))
    1260        {
    1261            par1IChunkProvider.populate(par2IChunkProvider, par3, par4 - 1);
    1262        }
    1263
    1264        if (par1IChunkProvider.chunkExists(par3 - 1, par4 - 1) && !par1IChunkProvider.provideChunk(par3 - 1, par4 - 1).isTerrainPopulated && par1IChunkProvider.chunkExists(par3, par4 - 1) && par1IChunkProvider.chunkExists(par3 - 1, par4))
    1265        {
    1266            par1IChunkProvider.populate(par2IChunkProvider, par3 - 1, par4 - 1);
    1267        }
    1268    }
    1269
    1270    /**
    1271     * Gets the height to which rain/snow will fall. Calculates it if not already stored.
    1272     */
    1273    public int getPrecipitationHeight(int par1, int par2)
    1274    {
    1275        int var3 = par1 | par2 << 4;
    1276        int var4 = this.precipitationHeightMap[var3];
    1277
    1278        if (var4 == -999)
    1279        {
    1280            int var5 = this.getTopFilledSegment() + 15;
    1281            var4 = -1;
    1282
    1283            while (var5 > 0 && var4 == -1)
    1284            {
    1285                int var6 = this.getBlockID(par1, var5, par2);
    1286                Material var7 = var6 == 0 ? Material.air : Block.blocksList[var6].blockMaterial;
    1287
    1288                if (!var7.blocksMovement() && !var7.isLiquid())
    1289                {
    1290                    --var5;
    1291                }
    1292                else
    1293                {
    1294                    var4 = var5 + 1;
    1295                }
    1296            }
    1297
    1298            this.precipitationHeightMap[var3] = var4;
    1299        }
    1300
    1301        return var4;
    1302    }
    1303
    1304    /**
    1305     * Checks whether skylight needs updated; if it does, calls updateSkylight_do
    1306     */
    1307    public void updateSkylight()
    1308    {
    1309        if (this.isGapLightingUpdated && !this.worldObj.provider.hasNoSky)
    1310        {
    1311            this.updateSkylight_do();
    1312        }
    1313    }
    1314
    1315    /**
    1316     * Gets a ChunkCoordIntPair representing the Chunk's position.
    1317     */
    1318    public ChunkCoordIntPair getChunkCoordIntPair()
    1319    {
    1320        return new ChunkCoordIntPair(this.xPosition, this.zPosition);
    1321    }
    1322
    1323    /**
    1324     * Returns whether the ExtendedBlockStorages containing levels (in blocks) from arg 1 to arg 2 are fully empty
    1325     * (true) or not (false).
    1326     */
    1327    public boolean getAreLevelsEmpty(int par1, int par2)
    1328    {
    1329        if (par1 < 0)
    1330        {
    1331            par1 = 0;
    1332        }
    1333
    1334        if (par2 >= 256)
    1335        {
    1336            par2 = 255;
    1337        }
    1338
    1339        for (int var3 = par1; var3 <= par2; var3 += 16)
    1340        {
    1341            ExtendedBlockStorage var4 = this.storageArrays[var3 >> 4];
    1342
    1343            if (var4 != null && !var4.isEmpty())
    1344            {
    1345                return false;
    1346            }
    1347        }
    1348
    1349        return true;
    1350    }
    1351
    1352    public void setStorageArrays(ExtendedBlockStorage[] par1ArrayOfExtendedBlockStorage)
    1353    {
    1354        this.storageArrays = par1ArrayOfExtendedBlockStorage;
    1355    }
    1356
    1357    @SideOnly(Side.CLIENT)
    1358
    1359    /**
    1360     * Initialise this chunk with new binary data
    1361     */
    1362    public void fillChunk(byte[] par1ArrayOfByte, int par2, int par3, boolean par4)
    1363    {
    1364        Iterator iterator = chunkTileEntityMap.values().iterator();
    1365        while(iterator.hasNext())
    1366        {
    1367            TileEntity tileEntity = (TileEntity)iterator.next();
    1368            tileEntity.updateContainingBlockInfo();
    1369            tileEntity.getBlockMetadata();
    1370            tileEntity.getBlockType();
    1371        }
    1372
    1373        int var5 = 0;
    1374        boolean var6 = !this.worldObj.provider.hasNoSky;
    1375        int var7;
    1376
    1377        for (var7 = 0; var7 < this.storageArrays.length; ++var7)
    1378        {
    1379            if ((par2 & 1 << var7) != 0)
    1380            {
    1381                if (this.storageArrays[var7] == null)
    1382                {
    1383                    this.storageArrays[var7] = new ExtendedBlockStorage(var7 << 4, var6);
    1384                }
    1385
    1386                byte[] var8 = this.storageArrays[var7].getBlockLSBArray();
    1387                System.arraycopy(par1ArrayOfByte, var5, var8, 0, var8.length);
    1388                var5 += var8.length;
    1389            }
    1390            else if (par4 && this.storageArrays[var7] != null)
    1391            {
    1392                this.storageArrays[var7] = null;
    1393            }
    1394        }
    1395
    1396        NibbleArray var9;
    1397
    1398        for (var7 = 0; var7 < this.storageArrays.length; ++var7)
    1399        {
    1400            if ((par2 & 1 << var7) != 0 && this.storageArrays[var7] != null)
    1401            {
    1402                var9 = this.storageArrays[var7].getMetadataArray();
    1403                System.arraycopy(par1ArrayOfByte, var5, var9.data, 0, var9.data.length);
    1404                var5 += var9.data.length;
    1405            }
    1406        }
    1407
    1408        for (var7 = 0; var7 < this.storageArrays.length; ++var7)
    1409        {
    1410            if ((par2 & 1 << var7) != 0 && this.storageArrays[var7] != null)
    1411            {
    1412                var9 = this.storageArrays[var7].getBlocklightArray();
    1413                System.arraycopy(par1ArrayOfByte, var5, var9.data, 0, var9.data.length);
    1414                var5 += var9.data.length;
    1415            }
    1416        }
    1417
    1418        if (var6)
    1419        {
    1420            for (var7 = 0; var7 < this.storageArrays.length; ++var7)
    1421            {
    1422                if ((par2 & 1 << var7) != 0 && this.storageArrays[var7] != null)
    1423                {
    1424                    var9 = this.storageArrays[var7].getSkylightArray();
    1425                    System.arraycopy(par1ArrayOfByte, var5, var9.data, 0, var9.data.length);
    1426                    var5 += var9.data.length;
    1427                }
    1428            }
    1429        }
    1430
    1431        for (var7 = 0; var7 < this.storageArrays.length; ++var7)
    1432        {
    1433            if ((par3 & 1 << var7) != 0)
    1434            {
    1435                if (this.storageArrays[var7] == null)
    1436                {
    1437                    var5 += 2048;
    1438                }
    1439                else
    1440                {
    1441                    var9 = this.storageArrays[var7].getBlockMSBArray();
    1442
    1443                    if (var9 == null)
    1444                    {
    1445                        var9 = this.storageArrays[var7].createBlockMSBArray();
    1446                    }
    1447
    1448                    System.arraycopy(par1ArrayOfByte, var5, var9.data, 0, var9.data.length);
    1449                    var5 += var9.data.length;
    1450                }
    1451            }
    1452            else if (par4 && this.storageArrays[var7] != null && this.storageArrays[var7].getBlockMSBArray() != null)
    1453            {
    1454                this.storageArrays[var7].clearMSBArray();
    1455            }
    1456        }
    1457
    1458        if (par4)
    1459        {
    1460            System.arraycopy(par1ArrayOfByte, var5, this.blockBiomeArray, 0, this.blockBiomeArray.length);
    1461            int var10000 = var5 + this.blockBiomeArray.length;
    1462        }
    1463
    1464        for (var7 = 0; var7 < this.storageArrays.length; ++var7)
    1465        {
    1466            if (this.storageArrays[var7] != null && (par2 & 1 << var7) != 0)
    1467            {
    1468                this.storageArrays[var7].removeInvalidBlocks();
    1469            }
    1470        }
    1471
    1472        this.generateHeightMap();
    1473
    1474        List<TileEntity> invalidList = new ArrayList<TileEntity>();
    1475        iterator = chunkTileEntityMap.values().iterator();
    1476        while (iterator.hasNext())
    1477        {
    1478            TileEntity tileEntity = (TileEntity)iterator.next();
    1479            int x = tileEntity.xCoord & 15;
    1480            int y = tileEntity.yCoord;
    1481            int z = tileEntity.zCoord & 15;
    1482            Block block = tileEntity.getBlockType();
    1483            if (block == null || block.blockID != getBlockID(x, y, z) || tileEntity.getBlockMetadata() != getBlockMetadata(x, y, z))
    1484            {
    1485                invalidList.add(tileEntity);
    1486            }
    1487            tileEntity.updateContainingBlockInfo();
    1488        }
    1489
    1490        for (TileEntity tileEntity : invalidList)
    1491        {
    1492            tileEntity.invalidate();
    1493        }
    1494    }
    1495
    1496    /**
    1497     * This method retrieves the biome at a set of coordinates
    1498     */
    1499    public BiomeGenBase getBiomeGenForWorldCoords(int par1, int par2, WorldChunkManager par3WorldChunkManager)
    1500    {
    1501        int var4 = this.blockBiomeArray[par2 << 4 | par1] & 255;
    1502
    1503        if (var4 == 255)
    1504        {
    1505            BiomeGenBase var5 = par3WorldChunkManager.getBiomeGenAt((this.xPosition << 4) + par1, (this.zPosition << 4) + par2);
    1506            var4 = var5.biomeID;
    1507            this.blockBiomeArray[par2 << 4 | par1] = (byte)(var4 & 255);
    1508        }
    1509
    1510        return BiomeGenBase.biomeList[var4] == null ? BiomeGenBase.plains : BiomeGenBase.biomeList[var4];
    1511    }
    1512
    1513    /**
    1514     * Returns an array containing a 16x16 mapping on the X/Z of block positions in this Chunk to biome IDs.
    1515     */
    1516    public byte[] getBiomeArray()
    1517    {
    1518        return this.blockBiomeArray;
    1519    }
    1520
    1521    /**
    1522     * Accepts a 256-entry array that contains a 16x16 mapping on the X/Z plane of block positions in this Chunk to
    1523     * biome IDs.
    1524     */
    1525    public void setBiomeArray(byte[] par1ArrayOfByte)
    1526    {
    1527        this.blockBiomeArray = par1ArrayOfByte;
    1528    }
    1529
    1530    /**
    1531     * Resets the relight check index to 0 for this Chunk.
    1532     */
    1533    public void resetRelightChecks()
    1534    {
    1535        this.queuedLightChecks = 0;
    1536    }
    1537
    1538    /**
    1539     * Called once-per-chunk-per-tick, and advances the round-robin relight check index per-storage-block by up to 8
    1540     * blocks at a time. In a worst-case scenario, can potentially take up to 1.6 seconds, calculated via
    1541     * (4096/(8*16))/20, to re-check all blocks in a chunk, which could explain both lagging light updates in certain
    1542     * cases as well as Nether relight
    1543     */
    1544    public void enqueueRelightChecks()
    1545    {
    1546        for (int var1 = 0; var1 < 8; ++var1)
    1547        {
    1548            if (this.queuedLightChecks >= 4096)
    1549            {
    1550                return;
    1551            }
    1552
    1553            int var2 = this.queuedLightChecks % 16;
    1554            int var3 = this.queuedLightChecks / 16 % 16;
    1555            int var4 = this.queuedLightChecks / 256;
    1556            ++this.queuedLightChecks;
    1557            int var5 = (this.xPosition << 4) + var3;
    1558            int var6 = (this.zPosition << 4) + var4;
    1559
    1560            for (int var7 = 0; var7 < 16; ++var7)
    1561            {
    1562                int var8 = (var2 << 4) + var7;
    1563
    1564                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)
    1565                {
    1566                    if (Block.lightValue[this.worldObj.getBlockId(var5, var8 - 1, var6)] > 0)
    1567                    {
    1568                        this.worldObj.updateAllLightTypes(var5, var8 - 1, var6);
    1569                    }
    1570
    1571                    if (Block.lightValue[this.worldObj.getBlockId(var5, var8 + 1, var6)] > 0)
    1572                    {
    1573                        this.worldObj.updateAllLightTypes(var5, var8 + 1, var6);
    1574                    }
    1575
    1576                    if (Block.lightValue[this.worldObj.getBlockId(var5 - 1, var8, var6)] > 0)
    1577                    {
    1578                        this.worldObj.updateAllLightTypes(var5 - 1, var8, var6);
    1579                    }
    1580
    1581                    if (Block.lightValue[this.worldObj.getBlockId(var5 + 1, var8, var6)] > 0)
    1582                    {
    1583                        this.worldObj.updateAllLightTypes(var5 + 1, var8, var6);
    1584                    }
    1585
    1586                    if (Block.lightValue[this.worldObj.getBlockId(var5, var8, var6 - 1)] > 0)
    1587                    {
    1588                        this.worldObj.updateAllLightTypes(var5, var8, var6 - 1);
    1589                    }
    1590
    1591                    if (Block.lightValue[this.worldObj.getBlockId(var5, var8, var6 + 1)] > 0)
    1592                    {
    1593                        this.worldObj.updateAllLightTypes(var5, var8, var6 + 1);
    1594                    }
    1595
    1596                    this.worldObj.updateAllLightTypes(var5, var8, var6);
    1597                }
    1598            }
    1599        }
    1600    }
    1601
    1602    /** FORGE: Used to remove only invalid TileEntities */
    1603    public void cleanChunkBlockTileEntity(int x, int y, int z)
    1604    {
    1605        ChunkPosition position = new ChunkPosition(x, y, z);
    1606        if (isChunkLoaded)
    1607        {
    1608            TileEntity entity = (TileEntity)chunkTileEntityMap.get(position);
    1609            if (entity != null && entity.isInvalid())
    1610            {
    1611                chunkTileEntityMap.remove(position);
    1612            }
    1613        }
    1614    }
    1615}