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 (Block.lightOpacity[var5] == 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            return Block.lightOpacity[this.getBlockID(par1, par2, par3)];
551        }
552    
553        /**
554         * Return the ID of a block in the chunk.
555         */
556        public int getBlockID(int par1, int par2, int par3)
557        {
558            if (par2 >> 4 >= this.storageArrays.length || par2 >> 4 < 0)
559            {
560                return 0;
561            }
562            else
563            {
564                ExtendedBlockStorage var4 = this.storageArrays[par2 >> 4];
565                return var4 != null ? var4.getExtBlockID(par1, par2 & 15, par3) : 0;
566            }
567        }
568    
569        /**
570         * Return the metadata corresponding to the given coordinates inside a chunk.
571         */
572        public int getBlockMetadata(int par1, int par2, int par3)
573        {
574            if (par2 >> 4 >= this.storageArrays.length || par2 >> 4 < 0)
575            {
576                return 0;
577            }
578            else
579            {
580                ExtendedBlockStorage var4 = this.storageArrays[par2 >> 4];
581                return var4 != null ? var4.getExtBlockMetadata(par1, par2 & 15, par3) : 0;
582            }
583        }
584    
585        /**
586         * Sets a blockID for a position in the chunk. Args: x, y, z, blockID
587         */
588        public boolean setBlockID(int par1, int par2, int par3, int par4)
589        {
590            return this.setBlockIDWithMetadata(par1, par2, par3, par4, 0);
591        }
592    
593        /**
594         * Sets a blockID of a position within a chunk with metadata. Args: x, y, z, blockID, metadata
595         */
596        public boolean setBlockIDWithMetadata(int par1, int par2, int par3, int par4, int par5)
597        {
598            int var6 = par3 << 4 | par1;
599    
600            if (par2 >= this.precipitationHeightMap[var6] - 1)
601            {
602                this.precipitationHeightMap[var6] = -999;
603            }
604    
605            int var7 = this.heightMap[var6];
606            int var8 = this.getBlockID(par1, par2, par3);
607            int var9 = this.getBlockMetadata(par1, par2, par3);
608    
609            if (var8 == par4 && var9 == par5)
610            {
611                return false;
612            }
613            else
614            {
615                if (par2 >> 4 >= storageArrays.length || par2 >> 4 < 0)
616                {
617                    return false;
618                }
619    
620                ExtendedBlockStorage var10 = this.storageArrays[par2 >> 4];
621                boolean var11 = false;
622    
623                if (var10 == null)
624                {
625                    if (par4 == 0)
626                    {
627                        return false;
628                    }
629    
630                    var10 = this.storageArrays[par2 >> 4] = new ExtendedBlockStorage(par2 >> 4 << 4);
631                    var11 = par2 >= var7;
632                }
633    
634                int var12 = this.xPosition * 16 + par1;
635                int var13 = this.zPosition * 16 + par3;
636    
637                if (var8 != 0 && !this.worldObj.isRemote)
638                {
639                    Block.blocksList[var8].onSetBlockIDWithMetaData(this.worldObj, var12, par2, var13, var9);
640                }
641    
642                var10.setExtBlockID(par1, par2 & 15, par3, par4);
643    
644                if (var8 != 0)
645                {
646                    if (!this.worldObj.isRemote)
647                    {
648                        Block.blocksList[var8].breakBlock(this.worldObj, var12, par2, var13, var8, var9);
649                    }
650                    else if (Block.blocksList[var8] != null && Block.blocksList[var8].hasTileEntity(var9))
651                    {
652                        this.worldObj.removeBlockTileEntity(var12, par2, var13);
653                    }
654                }
655    
656                if (var10.getExtBlockID(par1, par2 & 15, par3) != par4)
657                {
658                    return false;
659                }
660                else
661                {
662                    var10.setExtBlockMetadata(par1, par2 & 15, par3, par5);
663    
664                    if (var11)
665                    {
666                        this.generateSkylightMap();
667                    }
668                    else
669                    {
670                        if (Block.lightOpacity[par4 & 4095] > 0)
671                        {
672                            if (par2 >= var7)
673                            {
674                                this.relightBlock(par1, par2 + 1, par3);
675                            }
676                        }
677                        else if (par2 == var7 - 1)
678                        {
679                            this.relightBlock(par1, par2, par3);
680                        }
681    
682                        this.propagateSkylightOcclusion(par1, par3);
683                    }
684    
685                    TileEntity var14;
686    
687                    if (par4 != 0)
688                    {
689                        if (!this.worldObj.isRemote)
690                        {
691                            Block.blocksList[par4].onBlockAdded(this.worldObj, var12, par2, var13);
692                        }
693    
694                        if (Block.blocksList[par4] != null && Block.blocksList[par4].hasTileEntity(par5))
695                        {
696                            var14 = this.getChunkBlockTileEntity(par1, par2, par3);
697    
698                            if (var14 == null)
699                            {
700                                var14 = Block.blocksList[par4].createTileEntity(this.worldObj, par5);
701                                this.worldObj.setBlockTileEntity(var12, par2, var13, var14);
702                            }
703    
704                            if (var14 != null)
705                            {
706                                var14.updateContainingBlockInfo();
707                                var14.blockMetadata = par5;
708                            }
709                        }
710                    }
711    
712                    this.isModified = true;
713                    return true;
714                }
715            }
716        }
717    
718        /**
719         * Set the metadata of a block in the chunk
720         */
721        public boolean setBlockMetadata(int par1, int par2, int par3, int par4)
722        {
723            ExtendedBlockStorage var5 = (par2 >> 4 >= storageArrays.length || par2 >> 4 < 0 ? null : storageArrays[par2 >> 4]);
724    
725            if (var5 == null)
726            {
727                return false;
728            }
729            else
730            {
731                int var6 = var5.getExtBlockMetadata(par1, par2 & 15, par3);
732    
733                if (var6 == par4)
734                {
735                    return false;
736                }
737                else
738                {
739                    this.isModified = true;
740                    var5.setExtBlockMetadata(par1, par2 & 15, par3, par4);
741                    int var7 = var5.getExtBlockID(par1, par2 & 15, par3);
742    
743                    if (var7 > 0 && Block.blocksList[var7] != null && Block.blocksList[var7].hasTileEntity(par4))
744                    {
745                        TileEntity var8 = this.getChunkBlockTileEntity(par1, par2, par3);
746    
747                        if (var8 != null)
748                        {
749                            var8.updateContainingBlockInfo();
750                            var8.blockMetadata = par4;
751                        }
752                    }
753    
754                    return true;
755                }
756            }
757        }
758    
759        /**
760         * Gets the amount of light saved in this block (doesn't adjust for daylight)
761         */
762        public int getSavedLightValue(EnumSkyBlock par1EnumSkyBlock, int par2, int par3, int par4)
763        {
764            ExtendedBlockStorage var5 = (par3 >> 4 >= storageArrays.length || par3 >> 4 < 0 ? null : storageArrays[par3 >> 4]);
765            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));
766        }
767    
768        /**
769         * Sets the light value at the coordinate. If enumskyblock is set to sky it sets it in the skylightmap and if its a
770         * block then into the blocklightmap. Args enumSkyBlock, x, y, z, lightValue
771         */
772        public void setLightValue(EnumSkyBlock par1EnumSkyBlock, int par2, int par3, int par4, int par5)
773        {
774            if (par3 >> 4 >= storageArrays.length || par3 >> 4 < 0)
775            {
776                return;
777            }
778    
779            ExtendedBlockStorage var6 = this.storageArrays[par3 >> 4];
780    
781            if (var6 == null)
782            {
783                var6 = this.storageArrays[par3 >> 4] = new ExtendedBlockStorage(par3 >> 4 << 4);
784                this.generateSkylightMap();
785            }
786    
787            this.isModified = true;
788    
789            if (par1EnumSkyBlock == EnumSkyBlock.Sky)
790            {
791                if (!this.worldObj.provider.hasNoSky)
792                {
793                    var6.setExtSkylightValue(par2, par3 & 15, par4, par5);
794                }
795            }
796            else if (par1EnumSkyBlock == EnumSkyBlock.Block)
797            {
798                var6.setExtBlocklightValue(par2, par3 & 15, par4, par5);
799            }
800        }
801    
802        /**
803         * Gets the amount of light on a block taking into account sunlight
804         */
805        public int getBlockLightValue(int par1, int par2, int par3, int par4)
806        {
807            ExtendedBlockStorage var5 = (par2 >> 4 >= storageArrays.length || par2 >> 4 < 0 ? null : storageArrays[par2 >> 4]);
808    
809            if (var5 == null)
810            {
811                return !this.worldObj.provider.hasNoSky && par4 < EnumSkyBlock.Sky.defaultLightValue ? EnumSkyBlock.Sky.defaultLightValue - par4 : 0;
812            }
813            else
814            {
815                int var6 = this.worldObj.provider.hasNoSky ? 0 : var5.getExtSkylightValue(par1, par2 & 15, par3);
816    
817                if (var6 > 0)
818                {
819                    isLit = true;
820                }
821    
822                var6 -= par4;
823                int var7 = var5.getExtBlocklightValue(par1, par2 & 15, par3);
824    
825                if (var7 > var6)
826                {
827                    var6 = var7;
828                }
829    
830                return var6;
831            }
832        }
833    
834        /**
835         * Adds an entity to the chunk. Args: entity
836         */
837        public void addEntity(Entity par1Entity)
838        {
839            this.hasEntities = true;
840            int var2 = MathHelper.floor_double(par1Entity.posX / 16.0D);
841            int var3 = MathHelper.floor_double(par1Entity.posZ / 16.0D);
842    
843            if (var2 != this.xPosition || var3 != this.zPosition)
844            {
845                System.out.println("Wrong location! " + par1Entity);
846                Thread.dumpStack();
847            }
848    
849            int var4 = MathHelper.floor_double(par1Entity.posY / 16.0D);
850    
851            if (var4 < 0)
852            {
853                var4 = 0;
854            }
855    
856            if (var4 >= this.entityLists.length)
857            {
858                var4 = this.entityLists.length - 1;
859            }
860            MinecraftForge.EVENT_BUS.post(new EntityEvent.EnteringChunk(par1Entity, this.xPosition, this.zPosition, par1Entity.chunkCoordX, par1Entity.chunkCoordZ));
861            par1Entity.addedToChunk = true;
862            par1Entity.chunkCoordX = this.xPosition;
863            par1Entity.chunkCoordY = var4;
864            par1Entity.chunkCoordZ = this.zPosition;
865            this.entityLists[var4].add(par1Entity);
866        }
867    
868        /**
869         * removes entity using its y chunk coordinate as its index
870         */
871        public void removeEntity(Entity par1Entity)
872        {
873            this.removeEntityAtIndex(par1Entity, par1Entity.chunkCoordY);
874        }
875    
876        /**
877         * Removes entity at the specified index from the entity array.
878         */
879        public void removeEntityAtIndex(Entity par1Entity, int par2)
880        {
881            if (par2 < 0)
882            {
883                par2 = 0;
884            }
885    
886            if (par2 >= this.entityLists.length)
887            {
888                par2 = this.entityLists.length - 1;
889            }
890    
891            this.entityLists[par2].remove(par1Entity);
892        }
893    
894        /**
895         * Returns whether is not a block above this one blocking sight to the sky (done via checking against the heightmap)
896         */
897        public boolean canBlockSeeTheSky(int par1, int par2, int par3)
898        {
899            return par2 >= this.heightMap[par3 << 4 | par1];
900        }
901    
902        /**
903         * Gets the TileEntity for a given block in this chunk
904         */
905        public TileEntity getChunkBlockTileEntity(int par1, int par2, int par3)
906        {
907            ChunkPosition var4 = new ChunkPosition(par1, par2, par3);
908            TileEntity var5 = (TileEntity)this.chunkTileEntityMap.get(var4);
909    
910            if (var5 != null && var5.isInvalid())
911            {
912                chunkTileEntityMap.remove(var4);
913                var5 = null;
914            }
915    
916            if (var5 == null)
917            {
918                int var6 = this.getBlockID(par1, par2, par3);
919    
920                int meta = this.getBlockMetadata(par1, par2, par3);
921    
922                if (var6 <= 0 || !Block.blocksList[var6].hasTileEntity(meta))
923                {
924                    return null;
925                }
926    
927                if (var5 == null)
928                {
929                    var5 = Block.blocksList[var6].createTileEntity(this.worldObj, meta);
930                    this.worldObj.setBlockTileEntity(this.xPosition * 16 + par1, par2, this.zPosition * 16 + par3, var5);
931                }
932    
933                var5 = (TileEntity)this.chunkTileEntityMap.get(var4);
934            }
935    
936            return var5;
937        }
938    
939        /**
940         * Adds a TileEntity to a chunk
941         */
942        public void addTileEntity(TileEntity par1TileEntity)
943        {
944            int var2 = par1TileEntity.xCoord - this.xPosition * 16;
945            int var3 = par1TileEntity.yCoord;
946            int var4 = par1TileEntity.zCoord - this.zPosition * 16;
947            this.setChunkBlockTileEntity(var2, var3, var4, par1TileEntity);
948    
949            if (this.isChunkLoaded)
950            {
951                this.worldObj.addTileEntity(par1TileEntity);
952            }
953        }
954    
955        /**
956         * Sets the TileEntity for a given block in this chunk
957         */
958        public void setChunkBlockTileEntity(int par1, int par2, int par3, TileEntity par4TileEntity)
959        {
960            ChunkPosition var5 = new ChunkPosition(par1, par2, par3);
961            par4TileEntity.setWorldObj(this.worldObj);
962            par4TileEntity.xCoord = this.xPosition * 16 + par1;
963            par4TileEntity.yCoord = par2;
964            par4TileEntity.zCoord = this.zPosition * 16 + par3;
965    
966            Block block = Block.blocksList[getBlockID(par1, par2, par3)];
967            if (block != null && block.hasTileEntity(getBlockMetadata(par1, par2, par3)))
968            {
969                TileEntity old = (TileEntity)chunkTileEntityMap.get(var5);
970                if (old != null)
971                {
972                    old.invalidate();
973                }
974                par4TileEntity.validate();
975                this.chunkTileEntityMap.put(var5, par4TileEntity);
976            }
977        }
978    
979        /**
980         * Removes the TileEntity for a given block in this chunk
981         */
982        public void removeChunkBlockTileEntity(int par1, int par2, int par3)
983        {
984            ChunkPosition var4 = new ChunkPosition(par1, par2, par3);
985    
986            if (this.isChunkLoaded)
987            {
988                TileEntity var5 = (TileEntity)this.chunkTileEntityMap.remove(var4);
989    
990                if (var5 != null)
991                {
992                    var5.invalidate();
993                }
994            }
995        }
996    
997        /**
998         * Called when this Chunk is loaded by the ChunkProvider
999         */
1000        public void onChunkLoad()
1001        {
1002            this.isChunkLoaded = true;
1003            this.worldObj.addTileEntity(this.chunkTileEntityMap.values());
1004            List[] var1 = this.entityLists;
1005            int var2 = var1.length;
1006    
1007            for (int var3 = 0; var3 < var2; ++var3)
1008            {
1009                List var4 = var1[var3];
1010                this.worldObj.addLoadedEntities(var4);
1011            }
1012            MinecraftForge.EVENT_BUS.post(new ChunkEvent.Load(this));
1013        }
1014    
1015        /**
1016         * Called when this Chunk is unloaded by the ChunkProvider
1017         */
1018        public void onChunkUnload()
1019        {
1020            this.isChunkLoaded = false;
1021            Iterator var1 = this.chunkTileEntityMap.values().iterator();
1022    
1023            while (var1.hasNext())
1024            {
1025                TileEntity var2 = (TileEntity)var1.next();
1026                this.worldObj.markTileEntityForDespawn(var2);
1027            }
1028    
1029            List[] var5 = this.entityLists;
1030            int var6 = var5.length;
1031    
1032            for (int var3 = 0; var3 < var6; ++var3)
1033            {
1034                List var4 = var5[var3];
1035                this.worldObj.unloadEntities(var4);
1036            }
1037            MinecraftForge.EVENT_BUS.post(new ChunkEvent.Unload(this));
1038        }
1039    
1040        /**
1041         * Sets the isModified flag for this Chunk
1042         */
1043        public void setChunkModified()
1044        {
1045            this.isModified = true;
1046        }
1047    
1048        /**
1049         * Fills the given list of all entities that intersect within the given bounding box that aren't the passed entity
1050         * Args: entity, aabb, listToFill
1051         */
1052        public void getEntitiesWithinAABBForEntity(Entity par1Entity, AxisAlignedBB par2AxisAlignedBB, List par3List)
1053        {
1054            int var4 = MathHelper.floor_double((par2AxisAlignedBB.minY - World.MAX_ENTITY_RADIUS) / 16.0D);
1055            int var5 = MathHelper.floor_double((par2AxisAlignedBB.maxY + World.MAX_ENTITY_RADIUS) / 16.0D);
1056    
1057            if (var4 < 0)
1058            {
1059                var4 = 0;
1060            }
1061    
1062            if (var5 >= this.entityLists.length)
1063            {
1064                var5 = this.entityLists.length - 1;
1065            }
1066    
1067            for (int var6 = var4; var6 <= var5; ++var6)
1068            {
1069                List var7 = this.entityLists[var6];
1070                Iterator var8 = var7.iterator();
1071    
1072                while (var8.hasNext())
1073                {
1074                    Entity var9 = (Entity)var8.next();
1075    
1076                    if (var9 != par1Entity && var9.boundingBox.intersectsWith(par2AxisAlignedBB))
1077                    {
1078                        par3List.add(var9);
1079                        Entity[] var10 = var9.getParts();
1080    
1081                        if (var10 != null)
1082                        {
1083                            for (int var11 = 0; var11 < var10.length; ++var11)
1084                            {
1085                                var9 = var10[var11];
1086    
1087                                if (var9 != par1Entity && var9.boundingBox.intersectsWith(par2AxisAlignedBB))
1088                                {
1089                                    par3List.add(var9);
1090                                }
1091                            }
1092                        }
1093                    }
1094                }
1095            }
1096        }
1097    
1098        /**
1099         * Gets all entities that can be assigned to the specified class. Args: entityClass, aabb, listToFill
1100         */
1101        public void getEntitiesOfTypeWithinAAAB(Class par1Class, AxisAlignedBB par2AxisAlignedBB, List par3List)
1102        {
1103            int var4 = MathHelper.floor_double((par2AxisAlignedBB.minY - World.MAX_ENTITY_RADIUS) / 16.0D);
1104            int var5 = MathHelper.floor_double((par2AxisAlignedBB.maxY + World.MAX_ENTITY_RADIUS) / 16.0D);
1105    
1106            if (var4 < 0)
1107            {
1108                var4 = 0;
1109            }
1110            else if (var4 >= this.entityLists.length)
1111            {
1112                var4 = this.entityLists.length - 1;
1113            }
1114    
1115            if (var5 >= this.entityLists.length)
1116            {
1117                var5 = this.entityLists.length - 1;
1118            }
1119            else if (var5 < 0)
1120            {
1121                var5 = 0;
1122            }
1123    
1124            for (int var6 = var4; var6 <= var5; ++var6)
1125            {
1126                List var7 = this.entityLists[var6];
1127                Iterator var8 = var7.iterator();
1128    
1129                while (var8.hasNext())
1130                {
1131                    Entity var9 = (Entity)var8.next();
1132    
1133                    if (par1Class.isAssignableFrom(var9.getClass()) && var9.boundingBox.intersectsWith(par2AxisAlignedBB))
1134                    {
1135                        par3List.add(var9);
1136                    }
1137                }
1138            }
1139        }
1140    
1141        /**
1142         * Returns true if this Chunk needs to be saved
1143         */
1144        public boolean needsSaving(boolean par1)
1145        {
1146            if (par1)
1147            {
1148                if (this.hasEntities && this.worldObj.getWorldTime() != this.lastSaveTime)
1149                {
1150                    return true;
1151                }
1152            }
1153            else if (this.hasEntities && this.worldObj.getWorldTime() >= this.lastSaveTime + 600L)
1154            {
1155                return true;
1156            }
1157    
1158            return this.isModified;
1159        }
1160    
1161        public Random getRandomWithSeed(long par1)
1162        {
1163            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);
1164        }
1165    
1166        public boolean isEmpty()
1167        {
1168            return false;
1169        }
1170    
1171        public void populateChunk(IChunkProvider par1IChunkProvider, IChunkProvider par2IChunkProvider, int par3, int par4)
1172        {
1173            if (!this.isTerrainPopulated && par1IChunkProvider.chunkExists(par3 + 1, par4 + 1) && par1IChunkProvider.chunkExists(par3, par4 + 1) && par1IChunkProvider.chunkExists(par3 + 1, par4))
1174            {
1175                par1IChunkProvider.populate(par2IChunkProvider, par3, par4);
1176            }
1177    
1178            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))
1179            {
1180                par1IChunkProvider.populate(par2IChunkProvider, par3 - 1, par4);
1181            }
1182    
1183            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))
1184            {
1185                par1IChunkProvider.populate(par2IChunkProvider, par3, par4 - 1);
1186            }
1187    
1188            if (par1IChunkProvider.chunkExists(par3 - 1, par4 - 1) && !par1IChunkProvider.provideChunk(par3 - 1, par4 - 1).isTerrainPopulated && par1IChunkProvider.chunkExists(par3, par4 - 1) && par1IChunkProvider.chunkExists(par3 - 1, par4))
1189            {
1190                par1IChunkProvider.populate(par2IChunkProvider, par3 - 1, par4 - 1);
1191            }
1192        }
1193    
1194        /**
1195         * Gets the height to which rain/snow will fall. Calculates it if not already stored.
1196         */
1197        public int getPrecipitationHeight(int par1, int par2)
1198        {
1199            int var3 = par1 | par2 << 4;
1200            int var4 = this.precipitationHeightMap[var3];
1201    
1202            if (var4 == -999)
1203            {
1204                int var5 = this.getTopFilledSegment() + 15;
1205                var4 = -1;
1206    
1207                while (var5 > 0 && var4 == -1)
1208                {
1209                    int var6 = this.getBlockID(par1, var5, par2);
1210                    Material var7 = var6 == 0 ? Material.air : Block.blocksList[var6].blockMaterial;
1211    
1212                    if (!var7.blocksMovement() && !var7.isLiquid())
1213                    {
1214                        --var5;
1215                    }
1216                    else
1217                    {
1218                        var4 = var5 + 1;
1219                    }
1220                }
1221    
1222                this.precipitationHeightMap[var3] = var4;
1223            }
1224    
1225            return var4;
1226        }
1227    
1228        /**
1229         * Checks whether skylight needs updated; if it does, calls updateSkylight_do
1230         */
1231        public void updateSkylight()
1232        {
1233            if (this.isGapLightingUpdated && !this.worldObj.provider.hasNoSky)
1234            {
1235                this.updateSkylight_do();
1236            }
1237        }
1238    
1239        /**
1240         * Gets a ChunkCoordIntPair representing the Chunk's position.
1241         */
1242        public ChunkCoordIntPair getChunkCoordIntPair()
1243        {
1244            return new ChunkCoordIntPair(this.xPosition, this.zPosition);
1245        }
1246    
1247        /**
1248         * Returns whether the ExtendedBlockStorages containing levels (in blocks) from arg 1 to arg 2 are fully empty
1249         * (true) or not (false).
1250         */
1251        public boolean getAreLevelsEmpty(int par1, int par2)
1252        {
1253            if (par1 < 0)
1254            {
1255                par1 = 0;
1256            }
1257    
1258            if (par2 >= 256)
1259            {
1260                par2 = 255;
1261            }
1262    
1263            for (int var3 = par1; var3 <= par2; var3 += 16)
1264            {
1265                ExtendedBlockStorage var4 = this.storageArrays[var3 >> 4];
1266    
1267                if (var4 != null && !var4.isEmpty())
1268                {
1269                    return false;
1270                }
1271            }
1272    
1273            return true;
1274        }
1275    
1276        public void setStorageArrays(ExtendedBlockStorage[] par1ArrayOfExtendedBlockStorage)
1277        {
1278            this.storageArrays = par1ArrayOfExtendedBlockStorage;
1279        }
1280    
1281        @SideOnly(Side.CLIENT)
1282    
1283        /**
1284         * Initialise this chunk with new binary data
1285         */
1286        public void fillChunk(byte[] par1ArrayOfByte, int par2, int par3, boolean par4)
1287        {
1288            Iterator iterator = chunkTileEntityMap.values().iterator();
1289            while(iterator.hasNext())
1290            {
1291                TileEntity tileEntity = (TileEntity)iterator.next();
1292                tileEntity.updateContainingBlockInfo();
1293                tileEntity.getBlockMetadata();
1294                tileEntity.getBlockType();
1295            }
1296    
1297            int var5 = 0;
1298            int var6;
1299    
1300            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
1301            {
1302                if ((par2 & 1 << var6) != 0)
1303                {
1304                    if (this.storageArrays[var6] == null)
1305                    {
1306                        this.storageArrays[var6] = new ExtendedBlockStorage(var6 << 4);
1307                    }
1308    
1309                    byte[] var7 = this.storageArrays[var6].getBlockLSBArray();
1310                    System.arraycopy(par1ArrayOfByte, var5, var7, 0, var7.length);
1311                    var5 += var7.length;
1312                }
1313                else if (par4 && this.storageArrays[var6] != null)
1314                {
1315                    this.storageArrays[var6] = null;
1316                }
1317            }
1318    
1319            NibbleArray var8;
1320    
1321            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
1322            {
1323                if ((par2 & 1 << var6) != 0 && this.storageArrays[var6] != null)
1324                {
1325                    var8 = this.storageArrays[var6].getMetadataArray();
1326                    System.arraycopy(par1ArrayOfByte, var5, var8.data, 0, var8.data.length);
1327                    var5 += var8.data.length;
1328                }
1329            }
1330    
1331            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
1332            {
1333                if ((par2 & 1 << var6) != 0 && this.storageArrays[var6] != null)
1334                {
1335                    var8 = this.storageArrays[var6].getBlocklightArray();
1336                    System.arraycopy(par1ArrayOfByte, var5, var8.data, 0, var8.data.length);
1337                    var5 += var8.data.length;
1338                }
1339            }
1340    
1341            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
1342            {
1343                if ((par2 & 1 << var6) != 0 && this.storageArrays[var6] != null)
1344                {
1345                    var8 = this.storageArrays[var6].getSkylightArray();
1346                    System.arraycopy(par1ArrayOfByte, var5, var8.data, 0, var8.data.length);
1347                    var5 += var8.data.length;
1348                }
1349            }
1350    
1351            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
1352            {
1353                if ((par3 & 1 << var6) != 0)
1354                {
1355                    if (this.storageArrays[var6] == null)
1356                    {
1357                        var5 += 2048;
1358                    }
1359                    else
1360                    {
1361                        var8 = this.storageArrays[var6].getBlockMSBArray();
1362    
1363                        if (var8 == null)
1364                        {
1365                            var8 = this.storageArrays[var6].createBlockMSBArray();
1366                        }
1367    
1368                        System.arraycopy(par1ArrayOfByte, var5, var8.data, 0, var8.data.length);
1369                        var5 += var8.data.length;
1370                    }
1371                }
1372                else if (par4 && this.storageArrays[var6] != null && this.storageArrays[var6].getBlockMSBArray() != null)
1373                {
1374                    this.storageArrays[var6].clearMSBArray();
1375                }
1376            }
1377    
1378            if (par4)
1379            {
1380                System.arraycopy(par1ArrayOfByte, var5, this.blockBiomeArray, 0, this.blockBiomeArray.length);
1381                int var10000 = var5 + this.blockBiomeArray.length;
1382            }
1383    
1384            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
1385            {
1386                if (this.storageArrays[var6] != null && (par2 & 1 << var6) != 0)
1387                {
1388                    this.storageArrays[var6].removeInvalidBlocks();
1389                }
1390            }
1391    
1392            this.generateHeightMap();
1393    
1394            List<TileEntity> invalidList = new ArrayList<TileEntity>();
1395            iterator = chunkTileEntityMap.values().iterator();
1396            while (iterator.hasNext())
1397            {
1398                TileEntity tileEntity = (TileEntity)iterator.next();
1399                int x = tileEntity.xCoord & 15;
1400                int y = tileEntity.yCoord;
1401                int z = tileEntity.zCoord & 15;
1402                Block block = tileEntity.getBlockType();
1403                if (block == null || block.blockID != getBlockID(x, y, z) || tileEntity.getBlockMetadata() != getBlockMetadata(x, y, z))
1404                {
1405                    invalidList.add(tileEntity);
1406                }
1407                tileEntity.updateContainingBlockInfo();
1408            }
1409    
1410            for (TileEntity tileEntity : invalidList)
1411            {
1412                tileEntity.invalidate();
1413            }
1414        }
1415    
1416        /**
1417         * This method retrieves the biome at a set of coordinates
1418         */
1419        public BiomeGenBase getBiomeGenForWorldCoords(int par1, int par2, WorldChunkManager par3WorldChunkManager)
1420        {
1421            int var4 = this.blockBiomeArray[par2 << 4 | par1] & 255;
1422    
1423            if (var4 == 255)
1424            {
1425                BiomeGenBase var5 = par3WorldChunkManager.getBiomeGenAt((this.xPosition << 4) + par1, (this.zPosition << 4) + par2);
1426                var4 = var5.biomeID;
1427                this.blockBiomeArray[par2 << 4 | par1] = (byte)(var4 & 255);
1428            }
1429    
1430            return BiomeGenBase.biomeList[var4] == null ? BiomeGenBase.plains : BiomeGenBase.biomeList[var4];
1431        }
1432    
1433        /**
1434         * Returns an array containing a 16x16 mapping on the X/Z of block positions in this Chunk to biome IDs.
1435         */
1436        public byte[] getBiomeArray()
1437        {
1438            return this.blockBiomeArray;
1439        }
1440    
1441        /**
1442         * Accepts a 256-entry array that contains a 16x16 mapping on the X/Z plane of block positions in this Chunk to
1443         * biome IDs.
1444         */
1445        public void setBiomeArray(byte[] par1ArrayOfByte)
1446        {
1447            this.blockBiomeArray = par1ArrayOfByte;
1448        }
1449    
1450        /**
1451         * Resets the relight check index to 0 for this Chunk.
1452         */
1453        public void resetRelightChecks()
1454        {
1455            this.queuedLightChecks = 0;
1456        }
1457    
1458        /**
1459         * Called once-per-chunk-per-tick, and advances the round-robin relight check index per-storage-block by up to 8
1460         * blocks at a time. In a worst-case scenario, can potentially take up to 1.6 seconds, calculated via
1461         * (4096/(8*16))/20, to re-check all blocks in a chunk, which could explain both lagging light updates in certain
1462         * cases as well as Nether relight
1463         */
1464        public void enqueueRelightChecks()
1465        {
1466            for (int var1 = 0; var1 < 8; ++var1)
1467            {
1468                if (this.queuedLightChecks >= 4096)
1469                {
1470                    return;
1471                }
1472    
1473                int var2 = this.queuedLightChecks % 16;
1474                int var3 = this.queuedLightChecks / 16 % 16;
1475                int var4 = this.queuedLightChecks / 256;
1476                ++this.queuedLightChecks;
1477                int var5 = (this.xPosition << 4) + var3;
1478                int var6 = (this.zPosition << 4) + var4;
1479    
1480                for (int var7 = 0; var7 < 16; ++var7)
1481                {
1482                    int var8 = (var2 << 4) + var7;
1483    
1484                    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)
1485                    {
1486                        if (Block.lightValue[this.worldObj.getBlockId(var5, var8 - 1, var6)] > 0)
1487                        {
1488                            this.worldObj.updateAllLightTypes(var5, var8 - 1, var6);
1489                        }
1490    
1491                        if (Block.lightValue[this.worldObj.getBlockId(var5, var8 + 1, var6)] > 0)
1492                        {
1493                            this.worldObj.updateAllLightTypes(var5, var8 + 1, var6);
1494                        }
1495    
1496                        if (Block.lightValue[this.worldObj.getBlockId(var5 - 1, var8, var6)] > 0)
1497                        {
1498                            this.worldObj.updateAllLightTypes(var5 - 1, var8, var6);
1499                        }
1500    
1501                        if (Block.lightValue[this.worldObj.getBlockId(var5 + 1, var8, var6)] > 0)
1502                        {
1503                            this.worldObj.updateAllLightTypes(var5 + 1, var8, var6);
1504                        }
1505    
1506                        if (Block.lightValue[this.worldObj.getBlockId(var5, var8, var6 - 1)] > 0)
1507                        {
1508                            this.worldObj.updateAllLightTypes(var5, var8, var6 - 1);
1509                        }
1510    
1511                        if (Block.lightValue[this.worldObj.getBlockId(var5, var8, var6 + 1)] > 0)
1512                        {
1513                            this.worldObj.updateAllLightTypes(var5, var8, var6 + 1);
1514                        }
1515    
1516                        this.worldObj.updateAllLightTypes(var5, var8, var6);
1517                    }
1518                }
1519            }
1520        }
1521    
1522        /** FORGE: Used to remove only invalid TileEntities */
1523        public void cleanChunkBlockTileEntity(int x, int y, int z)
1524        {
1525            ChunkPosition position = new ChunkPosition(x, y, z);
1526            if (isChunkLoaded)
1527            {
1528                TileEntity entity = (TileEntity)chunkTileEntityMap.get(position);
1529                if (entity != null && entity.isInvalid())
1530                {
1531                    chunkTileEntityMap.remove(position);
1532                }
1533            }
1534        }
1535    }