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