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