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