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