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