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 }