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