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 || par2 >> 4 < 0) 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 || par2 >> 4 < 0) 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 if (par2 >> 4 >= storageArrays.length || par2 >> 4 < 0) 688 { 689 return false; 690 } 691 692 ExtendedBlockStorage extendedblockstorage = this.storageArrays[par2 >> 4]; 693 boolean flag = false; 694 695 if (extendedblockstorage == null) 696 { 697 if (par4 == 0) 698 { 699 return false; 700 } 701 702 extendedblockstorage = this.storageArrays[par2 >> 4] = new ExtendedBlockStorage(par2 >> 4 << 4, !this.worldObj.provider.hasNoSky); 703 flag = par2 >= k1; 704 } 705 706 int j2 = this.xPosition * 16 + par1; 707 int k2 = this.zPosition * 16 + par3; 708 709 if (l1 != 0 && !this.worldObj.isRemote) 710 { 711 Block.blocksList[l1].onSetBlockIDWithMetaData(this.worldObj, j2, par2, k2, i2); 712 } 713 714 extendedblockstorage.setExtBlockID(par1, par2 & 15, par3, par4); 715 716 if (l1 != 0) 717 { 718 if (!this.worldObj.isRemote) 719 { 720 Block.blocksList[l1].breakBlock(this.worldObj, j2, par2, k2, l1, i2); 721 } 722 else if (Block.blocksList[l1] != null && Block.blocksList[l1].hasTileEntity(i2)) 723 { 724 TileEntity te = worldObj.getBlockTileEntity(j2, par2, k2); 725 if (te != null && te.shouldRefresh(l1, par4, i2, par5, worldObj, j2, par2, k2)) 726 { 727 this.worldObj.removeBlockTileEntity(j2, par2, k2); 728 } 729 } 730 } 731 732 if (extendedblockstorage.getExtBlockID(par1, par2 & 15, par3) != par4) 733 { 734 return false; 735 } 736 else 737 { 738 extendedblockstorage.setExtBlockMetadata(par1, par2 & 15, par3, par5); 739 740 if (flag) 741 { 742 this.generateSkylightMap(); 743 } 744 else 745 { 746 if (getBlockLightOpacity(par1, par2, par3) > 0) 747 { 748 if (par2 >= k1) 749 { 750 this.relightBlock(par1, par2 + 1, par3); 751 } 752 } 753 else if (par2 == k1 - 1) 754 { 755 this.relightBlock(par1, par2, par3); 756 } 757 758 this.propagateSkylightOcclusion(par1, par3); 759 } 760 761 TileEntity tileentity; 762 763 if (par4 != 0) 764 { 765 if (!this.worldObj.isRemote) 766 { 767 Block.blocksList[par4].onBlockAdded(this.worldObj, j2, par2, k2); 768 } 769 770 if (Block.blocksList[par4] != null && Block.blocksList[par4].hasTileEntity(par5)) 771 { 772 tileentity = this.getChunkBlockTileEntity(par1, par2, par3); 773 774 if (tileentity == null) 775 { 776 tileentity = Block.blocksList[par4].createTileEntity(this.worldObj, par5); 777 this.worldObj.setBlockTileEntity(j2, par2, k2, tileentity); 778 } 779 780 if (tileentity != null) 781 { 782 tileentity.updateContainingBlockInfo(); 783 tileentity.blockMetadata = par5; 784 } 785 } 786 } 787 788 this.isModified = true; 789 return true; 790 } 791 } 792 } 793 794 /** 795 * Set the metadata of a block in the chunk 796 */ 797 public boolean setBlockMetadata(int par1, int par2, int par3, int par4) 798 { 799 ExtendedBlockStorage extendedblockstorage = (par2 >> 4 >= storageArrays.length || par2 >> 4 < 0 ? null : storageArrays[par2 >> 4]); 800 801 if (extendedblockstorage == null) 802 { 803 return false; 804 } 805 else 806 { 807 int i1 = extendedblockstorage.getExtBlockMetadata(par1, par2 & 15, par3); 808 809 if (i1 == par4) 810 { 811 return false; 812 } 813 else 814 { 815 this.isModified = true; 816 extendedblockstorage.setExtBlockMetadata(par1, par2 & 15, par3, par4); 817 int j1 = extendedblockstorage.getExtBlockID(par1, par2 & 15, par3); 818 819 if (j1 > 0 && Block.blocksList[j1] != null && Block.blocksList[j1].hasTileEntity(par4)) 820 { 821 TileEntity tileentity = this.getChunkBlockTileEntity(par1, par2, par3); 822 823 if (tileentity != null) 824 { 825 tileentity.updateContainingBlockInfo(); 826 tileentity.blockMetadata = par4; 827 } 828 } 829 830 return true; 831 } 832 } 833 } 834 835 /** 836 * Gets the amount of light saved in this block (doesn't adjust for daylight) 837 */ 838 public int getSavedLightValue(EnumSkyBlock par1EnumSkyBlock, int par2, int par3, int par4) 839 { 840 ExtendedBlockStorage extendedblockstorage = (par3 >> 4 >= storageArrays.length || par3 >> 4 < 0 ? null : storageArrays[par3 >> 4]); 841 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)); 842 } 843 844 /** 845 * Sets the light value at the coordinate. If enumskyblock is set to sky it sets it in the skylightmap and if its a 846 * block then into the blocklightmap. Args enumSkyBlock, x, y, z, lightValue 847 */ 848 public void setLightValue(EnumSkyBlock par1EnumSkyBlock, int par2, int par3, int par4, int par5) 849 { 850 if (par3 >> 4 >= storageArrays.length || par3 >> 4 < 0) 851 { 852 return; 853 } 854 855 ExtendedBlockStorage extendedblockstorage = this.storageArrays[par3 >> 4]; 856 857 if (extendedblockstorage == null) 858 { 859 extendedblockstorage = this.storageArrays[par3 >> 4] = new ExtendedBlockStorage(par3 >> 4 << 4, !this.worldObj.provider.hasNoSky); 860 this.generateSkylightMap(); 861 } 862 863 this.isModified = true; 864 865 if (par1EnumSkyBlock == EnumSkyBlock.Sky) 866 { 867 if (!this.worldObj.provider.hasNoSky) 868 { 869 extendedblockstorage.setExtSkylightValue(par2, par3 & 15, par4, par5); 870 } 871 } 872 else if (par1EnumSkyBlock == EnumSkyBlock.Block) 873 { 874 extendedblockstorage.setExtBlocklightValue(par2, par3 & 15, par4, par5); 875 } 876 } 877 878 /** 879 * Gets the amount of light on a block taking into account sunlight 880 */ 881 public int getBlockLightValue(int par1, int par2, int par3, int par4) 882 { 883 ExtendedBlockStorage extendedblockstorage = (par2 >> 4 >= storageArrays.length || par2 >> 4 < 0 ? null : storageArrays[par2 >> 4]); 884 885 if (extendedblockstorage == null) 886 { 887 return !this.worldObj.provider.hasNoSky && par4 < EnumSkyBlock.Sky.defaultLightValue ? EnumSkyBlock.Sky.defaultLightValue - par4 : 0; 888 } 889 else 890 { 891 int i1 = this.worldObj.provider.hasNoSky ? 0 : extendedblockstorage.getExtSkylightValue(par1, par2 & 15, par3); 892 893 if (i1 > 0) 894 { 895 isLit = true; 896 } 897 898 i1 -= par4; 899 int j1 = extendedblockstorage.getExtBlocklightValue(par1, par2 & 15, par3); 900 901 if (j1 > i1) 902 { 903 i1 = j1; 904 } 905 906 return i1; 907 } 908 } 909 910 /** 911 * Adds an entity to the chunk. Args: entity 912 */ 913 public void addEntity(Entity par1Entity) 914 { 915 this.hasEntities = true; 916 int i = MathHelper.floor_double(par1Entity.posX / 16.0D); 917 int j = MathHelper.floor_double(par1Entity.posZ / 16.0D); 918 919 if (i != this.xPosition || j != this.zPosition) 920 { 921 this.worldObj.getWorldLogAgent().func_98232_c("Wrong location! " + par1Entity); 922 Thread.dumpStack(); 923 } 924 925 int k = MathHelper.floor_double(par1Entity.posY / 16.0D); 926 927 if (k < 0) 928 { 929 k = 0; 930 } 931 932 if (k >= this.entityLists.length) 933 { 934 k = this.entityLists.length - 1; 935 } 936 MinecraftForge.EVENT_BUS.post(new EntityEvent.EnteringChunk(par1Entity, this.xPosition, this.zPosition, par1Entity.chunkCoordX, par1Entity.chunkCoordZ)); 937 par1Entity.addedToChunk = true; 938 par1Entity.chunkCoordX = this.xPosition; 939 par1Entity.chunkCoordY = k; 940 par1Entity.chunkCoordZ = this.zPosition; 941 this.entityLists[k].add(par1Entity); 942 } 943 944 /** 945 * removes entity using its y chunk coordinate as its index 946 */ 947 public void removeEntity(Entity par1Entity) 948 { 949 this.removeEntityAtIndex(par1Entity, par1Entity.chunkCoordY); 950 } 951 952 /** 953 * Removes entity at the specified index from the entity array. 954 */ 955 public void removeEntityAtIndex(Entity par1Entity, int par2) 956 { 957 if (par2 < 0) 958 { 959 par2 = 0; 960 } 961 962 if (par2 >= this.entityLists.length) 963 { 964 par2 = this.entityLists.length - 1; 965 } 966 967 this.entityLists[par2].remove(par1Entity); 968 } 969 970 /** 971 * Returns whether is not a block above this one blocking sight to the sky (done via checking against the heightmap) 972 */ 973 public boolean canBlockSeeTheSky(int par1, int par2, int par3) 974 { 975 return par2 >= this.heightMap[par3 << 4 | par1]; 976 } 977 978 /** 979 * Gets the TileEntity for a given block in this chunk 980 */ 981 public TileEntity getChunkBlockTileEntity(int par1, int par2, int par3) 982 { 983 ChunkPosition chunkposition = new ChunkPosition(par1, par2, par3); 984 TileEntity tileentity = (TileEntity)this.chunkTileEntityMap.get(chunkposition); 985 986 if (tileentity != null && tileentity.isInvalid()) 987 { 988 chunkTileEntityMap.remove(chunkposition); 989 tileentity = null; 990 } 991 992 if (tileentity == null) 993 { 994 int l = this.getBlockID(par1, par2, par3); 995 int meta = this.getBlockMetadata(par1, par2, par3); 996 997 if (l <= 0 || !Block.blocksList[l].hasTileEntity(meta)) 998 { 999 return null; 1000 } 1001 1002 if (tileentity == null) 1003 { 1004 tileentity = Block.blocksList[l].createTileEntity(this.worldObj, meta); 1005 this.worldObj.setBlockTileEntity(this.xPosition * 16 + par1, par2, this.zPosition * 16 + par3, tileentity); 1006 } 1007 1008 tileentity = (TileEntity)this.chunkTileEntityMap.get(chunkposition); 1009 } 1010 1011 return tileentity; 1012 } 1013 1014 /** 1015 * Adds a TileEntity to a chunk 1016 */ 1017 public void addTileEntity(TileEntity par1TileEntity) 1018 { 1019 int i = par1TileEntity.xCoord - this.xPosition * 16; 1020 int j = par1TileEntity.yCoord; 1021 int k = par1TileEntity.zCoord - this.zPosition * 16; 1022 this.setChunkBlockTileEntity(i, j, k, par1TileEntity); 1023 1024 if (this.isChunkLoaded) 1025 { 1026 this.worldObj.addTileEntity(par1TileEntity); 1027 } 1028 } 1029 1030 /** 1031 * Sets the TileEntity for a given block in this chunk 1032 */ 1033 public void setChunkBlockTileEntity(int par1, int par2, int par3, TileEntity par4TileEntity) 1034 { 1035 ChunkPosition chunkposition = new ChunkPosition(par1, par2, par3); 1036 par4TileEntity.setWorldObj(this.worldObj); 1037 par4TileEntity.xCoord = this.xPosition * 16 + par1; 1038 par4TileEntity.yCoord = par2; 1039 par4TileEntity.zCoord = this.zPosition * 16 + par3; 1040 1041 Block block = Block.blocksList[getBlockID(par1, par2, par3)]; 1042 if (block != null && block.hasTileEntity(getBlockMetadata(par1, par2, par3))) 1043 { 1044 if (this.chunkTileEntityMap.containsKey(chunkposition)) 1045 { 1046 ((TileEntity)this.chunkTileEntityMap.get(chunkposition)).invalidate(); 1047 } 1048 1049 par4TileEntity.validate(); 1050 this.chunkTileEntityMap.put(chunkposition, par4TileEntity); 1051 } 1052 } 1053 1054 /** 1055 * Removes the TileEntity for a given block in this chunk 1056 */ 1057 public void removeChunkBlockTileEntity(int par1, int par2, int par3) 1058 { 1059 ChunkPosition chunkposition = new ChunkPosition(par1, par2, par3); 1060 1061 if (this.isChunkLoaded) 1062 { 1063 TileEntity tileentity = (TileEntity)this.chunkTileEntityMap.remove(chunkposition); 1064 1065 if (tileentity != null) 1066 { 1067 tileentity.invalidate(); 1068 } 1069 } 1070 } 1071 1072 /** 1073 * Called when this Chunk is loaded by the ChunkProvider 1074 */ 1075 public void onChunkLoad() 1076 { 1077 this.isChunkLoaded = true; 1078 this.worldObj.addTileEntity(this.chunkTileEntityMap.values()); 1079 1080 for (int i = 0; i < this.entityLists.length; ++i) 1081 { 1082 this.worldObj.addLoadedEntities(this.entityLists[i]); 1083 } 1084 MinecraftForge.EVENT_BUS.post(new ChunkEvent.Load(this)); 1085 } 1086 1087 /** 1088 * Called when this Chunk is unloaded by the ChunkProvider 1089 */ 1090 public void onChunkUnload() 1091 { 1092 this.isChunkLoaded = false; 1093 Iterator iterator = this.chunkTileEntityMap.values().iterator(); 1094 1095 while (iterator.hasNext()) 1096 { 1097 TileEntity tileentity = (TileEntity)iterator.next(); 1098 this.worldObj.markTileEntityForDespawn(tileentity); 1099 } 1100 1101 for (int i = 0; i < this.entityLists.length; ++i) 1102 { 1103 this.worldObj.unloadEntities(this.entityLists[i]); 1104 } 1105 MinecraftForge.EVENT_BUS.post(new ChunkEvent.Unload(this)); 1106 } 1107 1108 /** 1109 * Sets the isModified flag for this Chunk 1110 */ 1111 public void setChunkModified() 1112 { 1113 this.isModified = true; 1114 } 1115 1116 /** 1117 * Fills the given list of all entities that intersect within the given bounding box that aren't the passed entity 1118 * Args: entity, aabb, listToFill 1119 */ 1120 public void getEntitiesWithinAABBForEntity(Entity par1Entity, AxisAlignedBB par2AxisAlignedBB, List par3List, IEntitySelector par4IEntitySelector) 1121 { 1122 int i = MathHelper.floor_double((par2AxisAlignedBB.minY - World.MAX_ENTITY_RADIUS) / 16.0D); 1123 int j = MathHelper.floor_double((par2AxisAlignedBB.maxY + World.MAX_ENTITY_RADIUS) / 16.0D); 1124 1125 if (i < 0) 1126 { 1127 i = 0; 1128 j = Math.max(i, j); 1129 } 1130 1131 if (j >= this.entityLists.length) 1132 { 1133 j = this.entityLists.length - 1; 1134 i = Math.min(i, j); 1135 } 1136 1137 for (int k = i; k <= j; ++k) 1138 { 1139 List list1 = this.entityLists[k]; 1140 1141 for (int l = 0; l < list1.size(); ++l) 1142 { 1143 Entity entity1 = (Entity)list1.get(l); 1144 1145 if (entity1 != par1Entity && entity1.boundingBox.intersectsWith(par2AxisAlignedBB) && (par4IEntitySelector == null || par4IEntitySelector.isEntityApplicable(entity1))) 1146 { 1147 par3List.add(entity1); 1148 Entity[] aentity = entity1.getParts(); 1149 1150 if (aentity != null) 1151 { 1152 for (int i1 = 0; i1 < aentity.length; ++i1) 1153 { 1154 entity1 = aentity[i1]; 1155 1156 if (entity1 != par1Entity && entity1.boundingBox.intersectsWith(par2AxisAlignedBB) && (par4IEntitySelector == null || par4IEntitySelector.isEntityApplicable(entity1))) 1157 { 1158 par3List.add(entity1); 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 i = MathHelper.floor_double((par2AxisAlignedBB.minY - World.MAX_ENTITY_RADIUS) / 16.0D); 1173 int j = MathHelper.floor_double((par2AxisAlignedBB.maxY + World.MAX_ENTITY_RADIUS) / 16.0D); 1174 1175 if (i < 0) 1176 { 1177 i = 0; 1178 } 1179 else if (i >= this.entityLists.length) 1180 { 1181 i = this.entityLists.length - 1; 1182 } 1183 1184 if (j >= this.entityLists.length) 1185 { 1186 j = this.entityLists.length - 1; 1187 } 1188 else if (j < 0) 1189 { 1190 j = 0; 1191 } 1192 1193 for (int k = i; k <= j; ++k) 1194 { 1195 List list1 = this.entityLists[k]; 1196 1197 for (int l = 0; l < list1.size(); ++l) 1198 { 1199 Entity entity = (Entity)list1.get(l); 1200 1201 if (par1Class.isAssignableFrom(entity.getClass()) && entity.boundingBox.intersectsWith(par2AxisAlignedBB) && (par4IEntitySelector == null || par4IEntitySelector.isEntityApplicable(entity))) 1202 { 1203 par3List.add(entity); 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 || this.isModified) 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 k = par1 | par2 << 4; 1268 int l = this.precipitationHeightMap[k]; 1269 1270 if (l == -999) 1271 { 1272 int i1 = this.getTopFilledSegment() + 15; 1273 l = -1; 1274 1275 while (i1 > 0 && l == -1) 1276 { 1277 int j1 = this.getBlockID(par1, i1, par2); 1278 Material material = j1 == 0 ? Material.air : Block.blocksList[j1].blockMaterial; 1279 1280 if (!material.blocksMovement() && !material.isLiquid()) 1281 { 1282 --i1; 1283 } 1284 else 1285 { 1286 l = i1 + 1; 1287 } 1288 } 1289 1290 this.precipitationHeightMap[k] = l; 1291 } 1292 1293 return l; 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 k = par1; k <= par2; k += 16) 1332 { 1333 ExtendedBlockStorage extendedblockstorage = this.storageArrays[k >> 4]; 1334 1335 if (extendedblockstorage != null && !extendedblockstorage.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 k = 0; 1366 boolean flag1 = !this.worldObj.provider.hasNoSky; 1367 int l; 1368 1369 for (l = 0; l < this.storageArrays.length; ++l) 1370 { 1371 if ((par2 & 1 << l) != 0) 1372 { 1373 if (this.storageArrays[l] == null) 1374 { 1375 this.storageArrays[l] = new ExtendedBlockStorage(l << 4, flag1); 1376 } 1377 1378 byte[] abyte1 = this.storageArrays[l].getBlockLSBArray(); 1379 System.arraycopy(par1ArrayOfByte, k, abyte1, 0, abyte1.length); 1380 k += abyte1.length; 1381 } 1382 else if (par4 && this.storageArrays[l] != null) 1383 { 1384 this.storageArrays[l] = null; 1385 } 1386 } 1387 1388 NibbleArray nibblearray; 1389 1390 for (l = 0; l < this.storageArrays.length; ++l) 1391 { 1392 if ((par2 & 1 << l) != 0 && this.storageArrays[l] != null) 1393 { 1394 nibblearray = this.storageArrays[l].getMetadataArray(); 1395 System.arraycopy(par1ArrayOfByte, k, nibblearray.data, 0, nibblearray.data.length); 1396 k += nibblearray.data.length; 1397 } 1398 } 1399 1400 for (l = 0; l < this.storageArrays.length; ++l) 1401 { 1402 if ((par2 & 1 << l) != 0 && this.storageArrays[l] != null) 1403 { 1404 nibblearray = this.storageArrays[l].getBlocklightArray(); 1405 System.arraycopy(par1ArrayOfByte, k, nibblearray.data, 0, nibblearray.data.length); 1406 k += nibblearray.data.length; 1407 } 1408 } 1409 1410 if (flag1) 1411 { 1412 for (l = 0; l < this.storageArrays.length; ++l) 1413 { 1414 if ((par2 & 1 << l) != 0 && this.storageArrays[l] != null) 1415 { 1416 nibblearray = this.storageArrays[l].getSkylightArray(); 1417 System.arraycopy(par1ArrayOfByte, k, nibblearray.data, 0, nibblearray.data.length); 1418 k += nibblearray.data.length; 1419 } 1420 } 1421 } 1422 1423 for (l = 0; l < this.storageArrays.length; ++l) 1424 { 1425 if ((par3 & 1 << l) != 0) 1426 { 1427 if (this.storageArrays[l] == null) 1428 { 1429 k += 2048; 1430 } 1431 else 1432 { 1433 nibblearray = this.storageArrays[l].getBlockMSBArray(); 1434 1435 if (nibblearray == null) 1436 { 1437 nibblearray = this.storageArrays[l].createBlockMSBArray(); 1438 } 1439 1440 System.arraycopy(par1ArrayOfByte, k, nibblearray.data, 0, nibblearray.data.length); 1441 k += nibblearray.data.length; 1442 } 1443 } 1444 else if (par4 && this.storageArrays[l] != null && this.storageArrays[l].getBlockMSBArray() != null) 1445 { 1446 this.storageArrays[l].clearMSBArray(); 1447 } 1448 } 1449 1450 if (par4) 1451 { 1452 System.arraycopy(par1ArrayOfByte, k, this.blockBiomeArray, 0, this.blockBiomeArray.length); 1453 int i1 = k + this.blockBiomeArray.length; 1454 } 1455 1456 for (l = 0; l < this.storageArrays.length; ++l) 1457 { 1458 if (this.storageArrays[l] != null && (par2 & 1 << l) != 0) 1459 { 1460 this.storageArrays[l].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 k = this.blockBiomeArray[par2 << 4 | par1] & 255; 1494 1495 if (k == 255) 1496 { 1497 BiomeGenBase biomegenbase = par3WorldChunkManager.getBiomeGenAt((this.xPosition << 4) + par1, (this.zPosition << 4) + par2); 1498 k = biomegenbase.biomeID; 1499 this.blockBiomeArray[par2 << 4 | par1] = (byte)(k & 255); 1500 } 1501 1502 return BiomeGenBase.biomeList[k] == null ? BiomeGenBase.plains : BiomeGenBase.biomeList[k]; 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 i = 0; i < 8; ++i) 1539 { 1540 if (this.queuedLightChecks >= 4096) 1541 { 1542 return; 1543 } 1544 1545 int j = this.queuedLightChecks % 16; 1546 int k = this.queuedLightChecks / 16 % 16; 1547 int l = this.queuedLightChecks / 256; 1548 ++this.queuedLightChecks; 1549 int i1 = (this.xPosition << 4) + k; 1550 int j1 = (this.zPosition << 4) + l; 1551 1552 for (int k1 = 0; k1 < 16; ++k1) 1553 { 1554 int l1 = (j << 4) + k1; 1555 1556 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) 1557 { 1558 if (Block.lightValue[this.worldObj.getBlockId(i1, l1 - 1, j1)] > 0) 1559 { 1560 this.worldObj.updateAllLightTypes(i1, l1 - 1, j1); 1561 } 1562 1563 if (Block.lightValue[this.worldObj.getBlockId(i1, l1 + 1, j1)] > 0) 1564 { 1565 this.worldObj.updateAllLightTypes(i1, l1 + 1, j1); 1566 } 1567 1568 if (Block.lightValue[this.worldObj.getBlockId(i1 - 1, l1, j1)] > 0) 1569 { 1570 this.worldObj.updateAllLightTypes(i1 - 1, l1, j1); 1571 } 1572 1573 if (Block.lightValue[this.worldObj.getBlockId(i1 + 1, l1, j1)] > 0) 1574 { 1575 this.worldObj.updateAllLightTypes(i1 + 1, l1, j1); 1576 } 1577 1578 if (Block.lightValue[this.worldObj.getBlockId(i1, l1, j1 - 1)] > 0) 1579 { 1580 this.worldObj.updateAllLightTypes(i1, l1, j1 - 1); 1581 } 1582 1583 if (Block.lightValue[this.worldObj.getBlockId(i1, l1, j1 + 1)] > 0) 1584 { 1585 this.worldObj.updateAllLightTypes(i1, l1, j1 + 1); 1586 } 1587 1588 this.worldObj.updateAllLightTypes(i1, l1, j1); 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}