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            List[] var1 = this.entityLists;
1021            int var2 = var1.length;
1022    
1023            for (int var3 = 0; var3 < var2; ++var3)
1024            {
1025                List var4 = var1[var3];
1026                this.worldObj.addLoadedEntities(var4);
1027            }
1028            MinecraftForge.EVENT_BUS.post(new ChunkEvent.Load(this));
1029        }
1030    
1031        /**
1032         * Called when this Chunk is unloaded by the ChunkProvider
1033         */
1034        public void onChunkUnload()
1035        {
1036            this.isChunkLoaded = false;
1037            Iterator var1 = this.chunkTileEntityMap.values().iterator();
1038    
1039            while (var1.hasNext())
1040            {
1041                TileEntity var2 = (TileEntity)var1.next();
1042                this.worldObj.markTileEntityForDespawn(var2);
1043            }
1044    
1045            List[] var5 = this.entityLists;
1046            int var6 = var5.length;
1047    
1048            for (int var3 = 0; var3 < var6; ++var3)
1049            {
1050                List var4 = var5[var3];
1051                this.worldObj.unloadEntities(var4);
1052            }
1053            MinecraftForge.EVENT_BUS.post(new ChunkEvent.Unload(this));
1054        }
1055    
1056        /**
1057         * Sets the isModified flag for this Chunk
1058         */
1059        public void setChunkModified()
1060        {
1061            this.isModified = true;
1062        }
1063    
1064        /**
1065         * Fills the given list of all entities that intersect within the given bounding box that aren't the passed entity
1066         * Args: entity, aabb, listToFill
1067         */
1068        public void getEntitiesWithinAABBForEntity(Entity par1Entity, AxisAlignedBB par2AxisAlignedBB, List par3List)
1069        {
1070            int var4 = MathHelper.floor_double((par2AxisAlignedBB.minY - World.MAX_ENTITY_RADIUS) / 16.0D);
1071            int var5 = MathHelper.floor_double((par2AxisAlignedBB.maxY + World.MAX_ENTITY_RADIUS) / 16.0D);
1072    
1073            if (var4 < 0)
1074            {
1075                var4 = 0;
1076            }
1077    
1078            if (var5 >= this.entityLists.length)
1079            {
1080                var5 = this.entityLists.length - 1;
1081            }
1082    
1083            for (int var6 = var4; var6 <= var5; ++var6)
1084            {
1085                List var7 = this.entityLists[var6];
1086                Iterator var8 = var7.iterator();
1087    
1088                while (var8.hasNext())
1089                {
1090                    Entity var9 = (Entity)var8.next();
1091    
1092                    if (var9 != par1Entity && var9.boundingBox.intersectsWith(par2AxisAlignedBB))
1093                    {
1094                        par3List.add(var9);
1095                        Entity[] var10 = var9.getParts();
1096    
1097                        if (var10 != null)
1098                        {
1099                            for (int var11 = 0; var11 < var10.length; ++var11)
1100                            {
1101                                var9 = var10[var11];
1102    
1103                                if (var9 != par1Entity && var9.boundingBox.intersectsWith(par2AxisAlignedBB))
1104                                {
1105                                    par3List.add(var9);
1106                                }
1107                            }
1108                        }
1109                    }
1110                }
1111            }
1112        }
1113    
1114        /**
1115         * Gets all entities that can be assigned to the specified class. Args: entityClass, aabb, listToFill
1116         */
1117        public void getEntitiesOfTypeWithinAAAB(Class par1Class, AxisAlignedBB par2AxisAlignedBB, List par3List, IEntitySelector par4IEntitySelector)
1118        {
1119            int var5 = MathHelper.floor_double((par2AxisAlignedBB.minY - World.MAX_ENTITY_RADIUS) / 16.0D);
1120            int var6 = MathHelper.floor_double((par2AxisAlignedBB.maxY + World.MAX_ENTITY_RADIUS) / 16.0D);
1121    
1122            if (var5 < 0)
1123            {
1124                var5 = 0;
1125            }
1126            else if (var5 >= this.entityLists.length)
1127            {
1128                var5 = this.entityLists.length - 1;
1129            }
1130    
1131            if (var6 >= this.entityLists.length)
1132            {
1133                var6 = this.entityLists.length - 1;
1134            }
1135            else if (var6 < 0)
1136            {
1137                var6 = 0;
1138            }
1139    
1140            for (int var7 = var5; var7 <= var6; ++var7)
1141            {
1142                List var8 = this.entityLists[var7];
1143                Iterator var9 = var8.iterator();
1144    
1145                while (var9.hasNext())
1146                {
1147                    Entity var10 = (Entity)var9.next();
1148    
1149                    if (par1Class.isAssignableFrom(var10.getClass()) && var10.boundingBox.intersectsWith(par2AxisAlignedBB) && (par4IEntitySelector == null || par4IEntitySelector.isEntityApplicable(var10)))
1150                    {
1151                        par3List.add(var10);
1152                    }
1153                }
1154            }
1155        }
1156    
1157        /**
1158         * Returns true if this Chunk needs to be saved
1159         */
1160        public boolean needsSaving(boolean par1)
1161        {
1162            if (par1)
1163            {
1164                if (this.hasEntities && this.worldObj.getTotalWorldTime() != this.lastSaveTime)
1165                {
1166                    return true;
1167                }
1168            }
1169            else if (this.hasEntities && this.worldObj.getTotalWorldTime() >= this.lastSaveTime + 600L)
1170            {
1171                return true;
1172            }
1173    
1174            return this.isModified;
1175        }
1176    
1177        public Random getRandomWithSeed(long par1)
1178        {
1179            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);
1180        }
1181    
1182        public boolean isEmpty()
1183        {
1184            return false;
1185        }
1186    
1187        public void populateChunk(IChunkProvider par1IChunkProvider, IChunkProvider par2IChunkProvider, int par3, int par4)
1188        {
1189            if (!this.isTerrainPopulated && par1IChunkProvider.chunkExists(par3 + 1, par4 + 1) && par1IChunkProvider.chunkExists(par3, par4 + 1) && par1IChunkProvider.chunkExists(par3 + 1, par4))
1190            {
1191                par1IChunkProvider.populate(par2IChunkProvider, par3, par4);
1192            }
1193    
1194            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))
1195            {
1196                par1IChunkProvider.populate(par2IChunkProvider, par3 - 1, par4);
1197            }
1198    
1199            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))
1200            {
1201                par1IChunkProvider.populate(par2IChunkProvider, par3, par4 - 1);
1202            }
1203    
1204            if (par1IChunkProvider.chunkExists(par3 - 1, par4 - 1) && !par1IChunkProvider.provideChunk(par3 - 1, par4 - 1).isTerrainPopulated && par1IChunkProvider.chunkExists(par3, par4 - 1) && par1IChunkProvider.chunkExists(par3 - 1, par4))
1205            {
1206                par1IChunkProvider.populate(par2IChunkProvider, par3 - 1, par4 - 1);
1207            }
1208        }
1209    
1210        /**
1211         * Gets the height to which rain/snow will fall. Calculates it if not already stored.
1212         */
1213        public int getPrecipitationHeight(int par1, int par2)
1214        {
1215            int var3 = par1 | par2 << 4;
1216            int var4 = this.precipitationHeightMap[var3];
1217    
1218            if (var4 == -999)
1219            {
1220                int var5 = this.getTopFilledSegment() + 15;
1221                var4 = -1;
1222    
1223                while (var5 > 0 && var4 == -1)
1224                {
1225                    int var6 = this.getBlockID(par1, var5, par2);
1226                    Material var7 = var6 == 0 ? Material.air : Block.blocksList[var6].blockMaterial;
1227    
1228                    if (!var7.blocksMovement() && !var7.isLiquid())
1229                    {
1230                        --var5;
1231                    }
1232                    else
1233                    {
1234                        var4 = var5 + 1;
1235                    }
1236                }
1237    
1238                this.precipitationHeightMap[var3] = var4;
1239            }
1240    
1241            return var4;
1242        }
1243    
1244        /**
1245         * Checks whether skylight needs updated; if it does, calls updateSkylight_do
1246         */
1247        public void updateSkylight()
1248        {
1249            if (this.isGapLightingUpdated && !this.worldObj.provider.hasNoSky)
1250            {
1251                this.updateSkylight_do();
1252            }
1253        }
1254    
1255        /**
1256         * Gets a ChunkCoordIntPair representing the Chunk's position.
1257         */
1258        public ChunkCoordIntPair getChunkCoordIntPair()
1259        {
1260            return new ChunkCoordIntPair(this.xPosition, this.zPosition);
1261        }
1262    
1263        /**
1264         * Returns whether the ExtendedBlockStorages containing levels (in blocks) from arg 1 to arg 2 are fully empty
1265         * (true) or not (false).
1266         */
1267        public boolean getAreLevelsEmpty(int par1, int par2)
1268        {
1269            if (par1 < 0)
1270            {
1271                par1 = 0;
1272            }
1273    
1274            if (par2 >= 256)
1275            {
1276                par2 = 255;
1277            }
1278    
1279            for (int var3 = par1; var3 <= par2; var3 += 16)
1280            {
1281                ExtendedBlockStorage var4 = this.storageArrays[var3 >> 4];
1282    
1283                if (var4 != null && !var4.isEmpty())
1284                {
1285                    return false;
1286                }
1287            }
1288    
1289            return true;
1290        }
1291    
1292        public void setStorageArrays(ExtendedBlockStorage[] par1ArrayOfExtendedBlockStorage)
1293        {
1294            this.storageArrays = par1ArrayOfExtendedBlockStorage;
1295        }
1296    
1297        @SideOnly(Side.CLIENT)
1298    
1299        /**
1300         * Initialise this chunk with new binary data
1301         */
1302        public void fillChunk(byte[] par1ArrayOfByte, int par2, int par3, boolean par4)
1303        {
1304            Iterator iterator = chunkTileEntityMap.values().iterator();
1305            while(iterator.hasNext())
1306            {
1307                TileEntity tileEntity = (TileEntity)iterator.next();
1308                tileEntity.updateContainingBlockInfo();
1309                tileEntity.getBlockMetadata();
1310                tileEntity.getBlockType();
1311            }
1312    
1313            int var5 = 0;
1314            int var6;
1315    
1316            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
1317            {
1318                if ((par2 & 1 << var6) != 0)
1319                {
1320                    if (this.storageArrays[var6] == null)
1321                    {
1322                        this.storageArrays[var6] = new ExtendedBlockStorage(var6 << 4);
1323                    }
1324    
1325                    byte[] var7 = this.storageArrays[var6].getBlockLSBArray();
1326                    System.arraycopy(par1ArrayOfByte, var5, var7, 0, var7.length);
1327                    var5 += var7.length;
1328                }
1329                else if (par4 && this.storageArrays[var6] != null)
1330                {
1331                    this.storageArrays[var6] = null;
1332                }
1333            }
1334    
1335            NibbleArray var8;
1336    
1337            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
1338            {
1339                if ((par2 & 1 << var6) != 0 && this.storageArrays[var6] != null)
1340                {
1341                    var8 = this.storageArrays[var6].getMetadataArray();
1342                    System.arraycopy(par1ArrayOfByte, var5, var8.data, 0, var8.data.length);
1343                    var5 += var8.data.length;
1344                }
1345            }
1346    
1347            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
1348            {
1349                if ((par2 & 1 << var6) != 0 && this.storageArrays[var6] != null)
1350                {
1351                    var8 = this.storageArrays[var6].getBlocklightArray();
1352                    System.arraycopy(par1ArrayOfByte, var5, var8.data, 0, var8.data.length);
1353                    var5 += var8.data.length;
1354                }
1355            }
1356    
1357            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
1358            {
1359                if ((par2 & 1 << var6) != 0 && this.storageArrays[var6] != null)
1360                {
1361                    var8 = this.storageArrays[var6].getSkylightArray();
1362                    System.arraycopy(par1ArrayOfByte, var5, var8.data, 0, var8.data.length);
1363                    var5 += var8.data.length;
1364                }
1365            }
1366    
1367            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
1368            {
1369                if ((par3 & 1 << var6) != 0)
1370                {
1371                    if (this.storageArrays[var6] == null)
1372                    {
1373                        var5 += 2048;
1374                    }
1375                    else
1376                    {
1377                        var8 = this.storageArrays[var6].getBlockMSBArray();
1378    
1379                        if (var8 == null)
1380                        {
1381                            var8 = this.storageArrays[var6].createBlockMSBArray();
1382                        }
1383    
1384                        System.arraycopy(par1ArrayOfByte, var5, var8.data, 0, var8.data.length);
1385                        var5 += var8.data.length;
1386                    }
1387                }
1388                else if (par4 && this.storageArrays[var6] != null && this.storageArrays[var6].getBlockMSBArray() != null)
1389                {
1390                    this.storageArrays[var6].clearMSBArray();
1391                }
1392            }
1393    
1394            if (par4)
1395            {
1396                System.arraycopy(par1ArrayOfByte, var5, this.blockBiomeArray, 0, this.blockBiomeArray.length);
1397                int var10000 = var5 + this.blockBiomeArray.length;
1398            }
1399    
1400            for (var6 = 0; var6 < this.storageArrays.length; ++var6)
1401            {
1402                if (this.storageArrays[var6] != null && (par2 & 1 << var6) != 0)
1403                {
1404                    this.storageArrays[var6].removeInvalidBlocks();
1405                }
1406            }
1407    
1408            this.generateHeightMap();
1409    
1410            List<TileEntity> invalidList = new ArrayList<TileEntity>();
1411            iterator = chunkTileEntityMap.values().iterator();
1412            while (iterator.hasNext())
1413            {
1414                TileEntity tileEntity = (TileEntity)iterator.next();
1415                int x = tileEntity.xCoord & 15;
1416                int y = tileEntity.yCoord;
1417                int z = tileEntity.zCoord & 15;
1418                Block block = tileEntity.getBlockType();
1419                if (block == null || block.blockID != getBlockID(x, y, z) || tileEntity.getBlockMetadata() != getBlockMetadata(x, y, z))
1420                {
1421                    invalidList.add(tileEntity);
1422                }
1423                tileEntity.updateContainingBlockInfo();
1424            }
1425    
1426            for (TileEntity tileEntity : invalidList)
1427            {
1428                tileEntity.invalidate();
1429            }
1430        }
1431    
1432        /**
1433         * This method retrieves the biome at a set of coordinates
1434         */
1435        public BiomeGenBase getBiomeGenForWorldCoords(int par1, int par2, WorldChunkManager par3WorldChunkManager)
1436        {
1437            int var4 = this.blockBiomeArray[par2 << 4 | par1] & 255;
1438    
1439            if (var4 == 255)
1440            {
1441                BiomeGenBase var5 = par3WorldChunkManager.getBiomeGenAt((this.xPosition << 4) + par1, (this.zPosition << 4) + par2);
1442                var4 = var5.biomeID;
1443                this.blockBiomeArray[par2 << 4 | par1] = (byte)(var4 & 255);
1444            }
1445    
1446            return BiomeGenBase.biomeList[var4] == null ? BiomeGenBase.plains : BiomeGenBase.biomeList[var4];
1447        }
1448    
1449        /**
1450         * Returns an array containing a 16x16 mapping on the X/Z of block positions in this Chunk to biome IDs.
1451         */
1452        public byte[] getBiomeArray()
1453        {
1454            return this.blockBiomeArray;
1455        }
1456    
1457        /**
1458         * Accepts a 256-entry array that contains a 16x16 mapping on the X/Z plane of block positions in this Chunk to
1459         * biome IDs.
1460         */
1461        public void setBiomeArray(byte[] par1ArrayOfByte)
1462        {
1463            this.blockBiomeArray = par1ArrayOfByte;
1464        }
1465    
1466        /**
1467         * Resets the relight check index to 0 for this Chunk.
1468         */
1469        public void resetRelightChecks()
1470        {
1471            this.queuedLightChecks = 0;
1472        }
1473    
1474        /**
1475         * Called once-per-chunk-per-tick, and advances the round-robin relight check index per-storage-block by up to 8
1476         * blocks at a time. In a worst-case scenario, can potentially take up to 1.6 seconds, calculated via
1477         * (4096/(8*16))/20, to re-check all blocks in a chunk, which could explain both lagging light updates in certain
1478         * cases as well as Nether relight
1479         */
1480        public void enqueueRelightChecks()
1481        {
1482            for (int var1 = 0; var1 < 8; ++var1)
1483            {
1484                if (this.queuedLightChecks >= 4096)
1485                {
1486                    return;
1487                }
1488    
1489                int var2 = this.queuedLightChecks % 16;
1490                int var3 = this.queuedLightChecks / 16 % 16;
1491                int var4 = this.queuedLightChecks / 256;
1492                ++this.queuedLightChecks;
1493                int var5 = (this.xPosition << 4) + var3;
1494                int var6 = (this.zPosition << 4) + var4;
1495    
1496                for (int var7 = 0; var7 < 16; ++var7)
1497                {
1498                    int var8 = (var2 << 4) + var7;
1499    
1500                    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)
1501                    {
1502                        if (Block.lightValue[this.worldObj.getBlockId(var5, var8 - 1, var6)] > 0)
1503                        {
1504                            this.worldObj.updateAllLightTypes(var5, var8 - 1, var6);
1505                        }
1506    
1507                        if (Block.lightValue[this.worldObj.getBlockId(var5, var8 + 1, var6)] > 0)
1508                        {
1509                            this.worldObj.updateAllLightTypes(var5, var8 + 1, var6);
1510                        }
1511    
1512                        if (Block.lightValue[this.worldObj.getBlockId(var5 - 1, var8, var6)] > 0)
1513                        {
1514                            this.worldObj.updateAllLightTypes(var5 - 1, var8, var6);
1515                        }
1516    
1517                        if (Block.lightValue[this.worldObj.getBlockId(var5 + 1, var8, var6)] > 0)
1518                        {
1519                            this.worldObj.updateAllLightTypes(var5 + 1, var8, var6);
1520                        }
1521    
1522                        if (Block.lightValue[this.worldObj.getBlockId(var5, var8, var6 - 1)] > 0)
1523                        {
1524                            this.worldObj.updateAllLightTypes(var5, var8, var6 - 1);
1525                        }
1526    
1527                        if (Block.lightValue[this.worldObj.getBlockId(var5, var8, var6 + 1)] > 0)
1528                        {
1529                            this.worldObj.updateAllLightTypes(var5, var8, var6 + 1);
1530                        }
1531    
1532                        this.worldObj.updateAllLightTypes(var5, var8, var6);
1533                    }
1534                }
1535            }
1536        }
1537    
1538        /** FORGE: Used to remove only invalid TileEntities */
1539        public void cleanChunkBlockTileEntity(int x, int y, int z)
1540        {
1541            ChunkPosition position = new ChunkPosition(x, y, z);
1542            if (isChunkLoaded)
1543            {
1544                TileEntity entity = (TileEntity)chunkTileEntityMap.get(position);
1545                if (entity != null && entity.isInvalid())
1546                {
1547                    chunkTileEntityMap.remove(position);
1548                }
1549            }
1550        }
1551    }