package net.minecraft.world.chunk; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.Callable; import net.minecraft.block.Block; import net.minecraft.block.ITileEntityProvider; import net.minecraft.block.material.Material; import net.minecraft.command.IEntitySelector; import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; import net.minecraft.entity.Entity; import net.minecraft.init.Blocks; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.MathHelper; import net.minecraft.util.ReportedException; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.ChunkPosition; import net.minecraft.world.EnumSkyBlock; import net.minecraft.world.World; import net.minecraft.world.biome.BiomeGenBase; import net.minecraft.world.biome.WorldChunkManager; import net.minecraft.world.chunk.storage.ExtendedBlockStorage; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.EntityEvent; import net.minecraftforge.event.world.ChunkEvent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class Chunk { private static final Logger logger = LogManager.getLogger(); /** Determines if the chunk is lit or not at a light value greater than 0. */ public static boolean isLit; /** * Used to store block IDs, block MSBs, Sky-light maps, Block-light maps, and metadata. Each entry corresponds to a * logical segment of 16x16x16 blocks, stacked vertically. */ private ExtendedBlockStorage[] storageArrays; /** Contains a 16x16 mapping on the X/Z plane of the biome ID to which each colum belongs. */ private byte[] blockBiomeArray; /** A map, similar to heightMap, that tracks how far down precipitation can fall. */ public int[] precipitationHeightMap; /** Which columns need their skylightMaps updated. */ public boolean[] updateSkylightColumns; /** Whether or not this Chunk is currently loaded into the World */ public boolean isChunkLoaded; /** Reference to the World object. */ public World worldObj; public int[] heightMap; /** The x coordinate of the chunk. */ public final int xPosition; /** The z coordinate of the chunk. */ public final int zPosition; private boolean isGapLightingUpdated; /** A Map of ChunkPositions to TileEntities in this chunk */ public Map chunkTileEntityMap; /** Array of Lists containing the entities in this Chunk. Each List represents a 16 block subchunk. */ public List[] entityLists; /** Boolean value indicating if the terrain is populated. */ public boolean isTerrainPopulated; public boolean isLightPopulated; public boolean field_150815_m; /** Set to true if the chunk has been modified and needs to be updated internally. */ public boolean isModified; /** Whether this Chunk has any Entities and thus requires saving on every tick */ public boolean hasEntities; /** The time according to World.worldTime when this chunk was last saved */ public long lastSaveTime; /** * Updates to this chunk will not be sent to clients if this is false. This field is set to true the first time the * chunk is sent to a client, and never set to false. */ public boolean sendUpdates; /** Lowest value in the heightmap. */ public int heightMapMinimum; /** the cumulative number of ticks players have been in this chunk */ public long inhabitedTime; /** Contains the current round-robin relight check index, and is implied as the relight check location as well. */ private int queuedLightChecks; private static final String __OBFID = "CL_00000373"; public Chunk(World p_i1995_1_, int p_i1995_2_, int p_i1995_3_) { this.storageArrays = new ExtendedBlockStorage[16]; this.blockBiomeArray = new byte[256]; this.precipitationHeightMap = new int[256]; this.updateSkylightColumns = new boolean[256]; this.chunkTileEntityMap = new HashMap(); this.queuedLightChecks = 4096; this.entityLists = new List[16]; this.worldObj = p_i1995_1_; this.xPosition = p_i1995_2_; this.zPosition = p_i1995_3_; this.heightMap = new int[256]; for (int k = 0; k < this.entityLists.length; ++k) { this.entityLists[k] = new ArrayList(); } Arrays.fill(this.precipitationHeightMap, -999); Arrays.fill(this.blockBiomeArray, (byte) - 1); } public Chunk(World p_i45446_1_, Block[] p_i45446_2_, int p_i45446_3_, int p_i45446_4_) { this(p_i45446_1_, p_i45446_3_, p_i45446_4_); int k = p_i45446_2_.length / 256; boolean flag = !p_i45446_1_.provider.hasNoSky; for (int l = 0; l < 16; ++l) { for (int i1 = 0; i1 < 16; ++i1) { for (int j1 = 0; j1 < k; ++j1) { Block block = p_i45446_2_[l << 11 | i1 << 7 | j1]; if (block != null && block.getMaterial() != Material.air) { int k1 = j1 >> 4; if (this.storageArrays[k1] == null) { this.storageArrays[k1] = new ExtendedBlockStorage(k1 << 4, flag); } this.storageArrays[k1].setExtBlockID(l, j1 & 15, i1, block); } } } } } public Chunk(World p_i45447_1_, Block[] p_i45447_2_, byte[] p_i45447_3_, int p_i45447_4_, int p_i45447_5_) { this(p_i45447_1_, p_i45447_4_, p_i45447_5_); int k = p_i45447_2_.length / 256; boolean flag = !p_i45447_1_.provider.hasNoSky; for (int l = 0; l < 16; ++l) { for (int i1 = 0; i1 < 16; ++i1) { for (int j1 = 0; j1 < k; ++j1) { int k1 = l * k * 16 | i1 * k | j1; Block block = p_i45447_2_[k1]; if (block != null && block != Blocks.air) { int l1 = j1 >> 4; if (this.storageArrays[l1] == null) { this.storageArrays[l1] = new ExtendedBlockStorage(l1 << 4, flag); } this.storageArrays[l1].setExtBlockID(l, j1 & 15, i1, block); this.storageArrays[l1].setExtBlockMetadata(l, j1 & 15, i1, p_i45447_3_[k1]); } } } } } /** * Checks whether the chunk is at the X/Z location specified */ public boolean isAtLocation(int x, int z) { return x == this.xPosition && z == this.zPosition; } /** * Returns the value in the height map at this x, z coordinate in the chunk */ public int getHeightValue(int x, int z) { return this.heightMap[z << 4 | x]; } /** * Returns the topmost ExtendedBlockStorage instance for this Chunk that actually contains a block. */ public int getTopFilledSegment() { for (int i = this.storageArrays.length - 1; i >= 0; --i) { if (this.storageArrays[i] != null) { return this.storageArrays[i].getYLocation(); } } return 0; } /** * Returns the ExtendedBlockStorage array for this Chunk. */ public ExtendedBlockStorage[] getBlockStorageArray() { return this.storageArrays; } /** * Generates the height map for a chunk from scratch */ @SideOnly(Side.CLIENT) public void generateHeightMap() { int i = this.getTopFilledSegment(); this.heightMapMinimum = Integer.MAX_VALUE; for (int j = 0; j < 16; ++j) { int k = 0; while (k < 16) { this.precipitationHeightMap[j + (k << 4)] = -999; int l = i + 16 - 1; while (true) { if (l > 0) { Block block = this.getBlock(j, l - 1, k); if (getBlockLightOpacity(j, l - 1, k) == 0) { --l; continue; } this.heightMap[k << 4 | j] = l; if (l < this.heightMapMinimum) { this.heightMapMinimum = l; } } ++k; break; } } } this.isModified = true; } /** * Generates the initial skylight map for the chunk upon generation or load. */ public void generateSkylightMap() { int i = this.getTopFilledSegment(); this.heightMapMinimum = Integer.MAX_VALUE; for (int j = 0; j < 16; ++j) { int k = 0; while (k < 16) { this.precipitationHeightMap[j + (k << 4)] = -999; int l = i + 16 - 1; while (true) { if (l > 0) { if (this.getBlockLightOpacity(j, l - 1, k) == 0) { --l; continue; } this.heightMap[k << 4 | j] = l; if (l < this.heightMapMinimum) { this.heightMapMinimum = l; } } if (!this.worldObj.provider.hasNoSky) { l = 15; int i1 = i + 16 - 1; do { int j1 = this.getBlockLightOpacity(j, i1, k); if (j1 == 0 && l != 15) { j1 = 1; } l -= j1; if (l > 0) { ExtendedBlockStorage extendedblockstorage = this.storageArrays[i1 >> 4]; if (extendedblockstorage != null) { extendedblockstorage.setExtSkylightValue(j, i1 & 15, k, l); this.worldObj.markBlockForRenderUpdate((this.xPosition << 4) + j, i1, (this.zPosition << 4) + k); } } --i1; } while (i1 > 0 && l > 0); } ++k; break; } } } this.isModified = true; } /** * Propagates a given sky-visible block's light value downward and upward to neighboring blocks as necessary. */ private void propagateSkylightOcclusion(int p_76595_1_, int p_76595_2_) { this.updateSkylightColumns[p_76595_1_ + p_76595_2_ * 16] = true; this.isGapLightingUpdated = true; } private void recheckGaps(boolean p_150803_1_) { this.worldObj.theProfiler.startSection("recheckGaps"); if (this.worldObj.doChunksNearChunkExist(this.xPosition * 16 + 8, 0, this.zPosition * 16 + 8, 16)) { for (int i = 0; i < 16; ++i) { for (int j = 0; j < 16; ++j) { if (this.updateSkylightColumns[i + j * 16]) { this.updateSkylightColumns[i + j * 16] = false; int k = this.getHeightValue(i, j); int l = this.xPosition * 16 + i; int i1 = this.zPosition * 16 + j; int j1 = this.worldObj.getChunkHeightMapMinimum(l - 1, i1); int k1 = this.worldObj.getChunkHeightMapMinimum(l + 1, i1); int l1 = this.worldObj.getChunkHeightMapMinimum(l, i1 - 1); int i2 = this.worldObj.getChunkHeightMapMinimum(l, i1 + 1); if (k1 < j1) { j1 = k1; } if (l1 < j1) { j1 = l1; } if (i2 < j1) { j1 = i2; } this.checkSkylightNeighborHeight(l, i1, j1); this.checkSkylightNeighborHeight(l - 1, i1, k); this.checkSkylightNeighborHeight(l + 1, i1, k); this.checkSkylightNeighborHeight(l, i1 - 1, k); this.checkSkylightNeighborHeight(l, i1 + 1, k); if (p_150803_1_) { this.worldObj.theProfiler.endSection(); return; } } } } this.isGapLightingUpdated = false; } this.worldObj.theProfiler.endSection(); } /** * Checks the height of a block next to a sky-visible block and schedules a lighting update as necessary. */ private void checkSkylightNeighborHeight(int p_76599_1_, int p_76599_2_, int p_76599_3_) { int l = this.worldObj.getHeightValue(p_76599_1_, p_76599_2_); if (l > p_76599_3_) { this.updateSkylightNeighborHeight(p_76599_1_, p_76599_2_, p_76599_3_, l + 1); } else if (l < p_76599_3_) { this.updateSkylightNeighborHeight(p_76599_1_, p_76599_2_, l, p_76599_3_ + 1); } } private void updateSkylightNeighborHeight(int p_76609_1_, int p_76609_2_, int p_76609_3_, int p_76609_4_) { if (p_76609_4_ > p_76609_3_ && this.worldObj.doChunksNearChunkExist(p_76609_1_, 0, p_76609_2_, 16)) { for (int i1 = p_76609_3_; i1 < p_76609_4_; ++i1) { this.worldObj.updateLightByType(EnumSkyBlock.Sky, p_76609_1_, i1, p_76609_2_); } this.isModified = true; } } /** * Initiates the recalculation of both the block-light and sky-light for a given block inside a chunk. */ private void relightBlock(int p_76615_1_, int p_76615_2_, int p_76615_3_) { int l = this.heightMap[p_76615_3_ << 4 | p_76615_1_] & 255; int i1 = l; if (p_76615_2_ > l) { i1 = p_76615_2_; } while (i1 > 0 && this.getBlockLightOpacity(p_76615_1_, i1 - 1, p_76615_3_) == 0) { --i1; } if (i1 != l) { this.worldObj.markBlocksDirtyVertical(p_76615_1_ + this.xPosition * 16, p_76615_3_ + this.zPosition * 16, i1, l); this.heightMap[p_76615_3_ << 4 | p_76615_1_] = i1; int j1 = this.xPosition * 16 + p_76615_1_; int k1 = this.zPosition * 16 + p_76615_3_; int l1; int i2; if (!this.worldObj.provider.hasNoSky) { ExtendedBlockStorage extendedblockstorage; if (i1 < l) { for (l1 = i1; l1 < l; ++l1) { extendedblockstorage = this.storageArrays[l1 >> 4]; if (extendedblockstorage != null) { extendedblockstorage.setExtSkylightValue(p_76615_1_, l1 & 15, p_76615_3_, 15); this.worldObj.markBlockForRenderUpdate((this.xPosition << 4) + p_76615_1_, l1, (this.zPosition << 4) + p_76615_3_); } } } else { for (l1 = l; l1 < i1; ++l1) { extendedblockstorage = this.storageArrays[l1 >> 4]; if (extendedblockstorage != null) { extendedblockstorage.setExtSkylightValue(p_76615_1_, l1 & 15, p_76615_3_, 0); this.worldObj.markBlockForRenderUpdate((this.xPosition << 4) + p_76615_1_, l1, (this.zPosition << 4) + p_76615_3_); } } } l1 = 15; while (i1 > 0 && l1 > 0) { --i1; i2 = this.getBlockLightOpacity(p_76615_1_, i1, p_76615_3_); if (i2 == 0) { i2 = 1; } l1 -= i2; if (l1 < 0) { l1 = 0; } ExtendedBlockStorage extendedblockstorage1 = this.storageArrays[i1 >> 4]; if (extendedblockstorage1 != null) { extendedblockstorage1.setExtSkylightValue(p_76615_1_, i1 & 15, p_76615_3_, l1); } } } l1 = this.heightMap[p_76615_3_ << 4 | p_76615_1_]; i2 = l; int j2 = l1; if (l1 < l) { i2 = l1; j2 = l; } if (l1 < this.heightMapMinimum) { this.heightMapMinimum = l1; } if (!this.worldObj.provider.hasNoSky) { this.updateSkylightNeighborHeight(j1 - 1, k1, i2, j2); this.updateSkylightNeighborHeight(j1 + 1, k1, i2, j2); this.updateSkylightNeighborHeight(j1, k1 - 1, i2, j2); this.updateSkylightNeighborHeight(j1, k1 + 1, i2, j2); this.updateSkylightNeighborHeight(j1, k1, i2, j2); } this.isModified = true; } } public int getBlockLightOpacity(int p_150808_1_, int p_150808_2_, int p_150808_3_) { int x = (xPosition << 4) + p_150808_1_; int z = (zPosition << 4) + p_150808_3_; return this.getBlock(p_150808_1_, p_150808_2_, p_150808_3_).getLightOpacity(worldObj, x, p_150808_2_, z); } /** * Returns the block corresponding to the given coordinates inside a chunk. */ public Block getBlock(final int p_150810_1_, final int p_150810_2_, final int p_150810_3_) { Block block = Blocks.air; if (p_150810_2_ >> 4 < this.storageArrays.length) { ExtendedBlockStorage extendedblockstorage = this.storageArrays[p_150810_2_ >> 4]; if (extendedblockstorage != null) { try { block = extendedblockstorage.getBlockByExtId(p_150810_1_, p_150810_2_ & 15, p_150810_3_); } catch (Throwable throwable) { CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Getting block"); CrashReportCategory crashreportcategory = crashreport.makeCategory("Block being got"); crashreportcategory.addCrashSectionCallable("Location", new Callable() { private static final String __OBFID = "CL_00000374"; public String call() { return CrashReportCategory.getLocationInfo(p_150810_1_, p_150810_2_, p_150810_3_); } }); throw new ReportedException(crashreport); } } } return block; } /** * Return the metadata corresponding to the given coordinates inside a chunk. */ public int getBlockMetadata(int p_76628_1_, int p_76628_2_, int p_76628_3_) { if (p_76628_2_ >> 4 >= this.storageArrays.length) { return 0; } else { ExtendedBlockStorage extendedblockstorage = this.storageArrays[p_76628_2_ >> 4]; return extendedblockstorage != null ? extendedblockstorage.getExtBlockMetadata(p_76628_1_, p_76628_2_ & 15, p_76628_3_) : 0; } } public boolean setBlockIDWithMetadata(int p_150807_1_, int p_150807_2_, int p_150807_3_, Block p_150807_4_, int p_150807_5_) { int i1 = p_150807_3_ << 4 | p_150807_1_; if (p_150807_2_ >= this.precipitationHeightMap[i1] - 1) { this.precipitationHeightMap[i1] = -999; } int j1 = this.heightMap[i1]; Block block1 = this.getBlock(p_150807_1_, p_150807_2_, p_150807_3_); int k1 = this.getBlockMetadata(p_150807_1_, p_150807_2_, p_150807_3_); if (block1 == p_150807_4_ && k1 == p_150807_5_) { return false; } else { ExtendedBlockStorage extendedblockstorage = this.storageArrays[p_150807_2_ >> 4]; boolean flag = false; if (extendedblockstorage == null) { if (p_150807_4_ == Blocks.air) { return false; } extendedblockstorage = this.storageArrays[p_150807_2_ >> 4] = new ExtendedBlockStorage(p_150807_2_ >> 4 << 4, !this.worldObj.provider.hasNoSky); flag = p_150807_2_ >= j1; } int l1 = this.xPosition * 16 + p_150807_1_; int i2 = this.zPosition * 16 + p_150807_3_; int k2 = block1.getLightOpacity(this.worldObj, l1, p_150807_2_, i2); if (!this.worldObj.isRemote) { block1.onBlockPreDestroy(this.worldObj, l1, p_150807_2_, i2, k1); } extendedblockstorage.setExtBlockID(p_150807_1_, p_150807_2_ & 15, p_150807_3_, p_150807_4_); extendedblockstorage.setExtBlockMetadata(p_150807_1_, p_150807_2_ & 15, p_150807_3_, p_150807_5_); // This line duplicates the one below, so breakBlock fires with valid worldstate if (!this.worldObj.isRemote) { block1.breakBlock(this.worldObj, l1, p_150807_2_, i2, block1, k1); // After breakBlock a phantom TE might have been created with incorrect meta. This attempts to kill that phantom TE so the normal one can be create properly later TileEntity te = this.getTileEntityUnsafe(p_150807_1_ & 0x0F, p_150807_2_, p_150807_3_ & 0x0F); if (te != null && te.shouldRefresh(block1, getBlock(p_150807_1_ & 0x0F, p_150807_2_, p_150807_3_ & 0x0F), k1, getBlockMetadata(p_150807_1_ & 0x0F, p_150807_2_, p_150807_3_ & 0x0F), worldObj, l1, p_150807_2_, i2)) { this.removeTileEntity(p_150807_1_ & 0x0F, p_150807_2_, p_150807_3_ & 0x0F); } } else if (block1.hasTileEntity(k1)) { TileEntity te = this.getTileEntityUnsafe(p_150807_1_ & 0x0F, p_150807_2_, p_150807_3_ & 0x0F); if (te != null && te.shouldRefresh(block1, p_150807_4_, k1, p_150807_5_, worldObj, l1, p_150807_2_, i2)) { this.worldObj.removeTileEntity(l1, p_150807_2_, i2); } } if (extendedblockstorage.getBlockByExtId(p_150807_1_, p_150807_2_ & 15, p_150807_3_) != p_150807_4_) { return false; } else { extendedblockstorage.setExtBlockMetadata(p_150807_1_, p_150807_2_ & 15, p_150807_3_, p_150807_5_); if (flag) { this.generateSkylightMap(); } else { int j2 = p_150807_4_.getLightOpacity(this.worldObj, l1, p_150807_2_, i2); if (j2 > 0) { if (p_150807_2_ >= j1) { this.relightBlock(p_150807_1_, p_150807_2_ + 1, p_150807_3_); } } else if (p_150807_2_ == j1 - 1) { this.relightBlock(p_150807_1_, p_150807_2_, p_150807_3_); } if (j2 != k2 && (j2 < k2 || this.getSavedLightValue(EnumSkyBlock.Sky, p_150807_1_, p_150807_2_, p_150807_3_) > 0 || this.getSavedLightValue(EnumSkyBlock.Block, p_150807_1_, p_150807_2_, p_150807_3_) > 0)) { this.propagateSkylightOcclusion(p_150807_1_, p_150807_3_); } } TileEntity tileentity; if (!this.worldObj.isRemote) { p_150807_4_.onBlockAdded(this.worldObj, l1, p_150807_2_, i2); } if (p_150807_4_.hasTileEntity(p_150807_5_)) { tileentity = this.getBlockTileEntityInChunk(p_150807_1_, p_150807_2_, p_150807_3_); if (tileentity != null) { tileentity.updateContainingBlockInfo(); tileentity.blockMetadata = p_150807_5_; } } this.isModified = true; return true; } } } /** * Set the metadata of a block in the chunk */ public boolean setBlockMetadata(int p_76589_1_, int p_76589_2_, int p_76589_3_, int p_76589_4_) { ExtendedBlockStorage extendedblockstorage = this.storageArrays[p_76589_2_ >> 4]; if (extendedblockstorage == null) { return false; } else { int i1 = extendedblockstorage.getExtBlockMetadata(p_76589_1_, p_76589_2_ & 15, p_76589_3_); if (i1 == p_76589_4_) { return false; } else { this.isModified = true; extendedblockstorage.setExtBlockMetadata(p_76589_1_, p_76589_2_ & 15, p_76589_3_, p_76589_4_); if (extendedblockstorage.getBlockByExtId(p_76589_1_, p_76589_2_ & 15, p_76589_3_).hasTileEntity(p_76589_4_)) { TileEntity tileentity = this.getBlockTileEntityInChunk(p_76589_1_, p_76589_2_, p_76589_3_); if (tileentity != null) { tileentity.updateContainingBlockInfo(); tileentity.blockMetadata = p_76589_4_; } } return true; } } } /** * Gets the amount of light saved in this block (doesn't adjust for daylight) */ public int getSavedLightValue(EnumSkyBlock p_76614_1_, int p_76614_2_, int p_76614_3_, int p_76614_4_) { ExtendedBlockStorage extendedblockstorage = this.storageArrays[p_76614_3_ >> 4]; return extendedblockstorage == null ? (this.canBlockSeeTheSky(p_76614_2_, p_76614_3_, p_76614_4_) ? p_76614_1_.defaultLightValue : 0) : (p_76614_1_ == EnumSkyBlock.Sky ? (this.worldObj.provider.hasNoSky ? 0 : extendedblockstorage.getExtSkylightValue(p_76614_2_, p_76614_3_ & 15, p_76614_4_)) : (p_76614_1_ == EnumSkyBlock.Block ? extendedblockstorage.getExtBlocklightValue(p_76614_2_, p_76614_3_ & 15, p_76614_4_) : p_76614_1_.defaultLightValue)); } /** * Sets the light value at the coordinate. If enumskyblock is set to sky it sets it in the skylightmap and if its a * block then into the blocklightmap. Args enumSkyBlock, x, y, z, lightValue */ public void setLightValue(EnumSkyBlock p_76633_1_, int p_76633_2_, int p_76633_3_, int p_76633_4_, int p_76633_5_) { ExtendedBlockStorage extendedblockstorage = this.storageArrays[p_76633_3_ >> 4]; if (extendedblockstorage == null) { extendedblockstorage = this.storageArrays[p_76633_3_ >> 4] = new ExtendedBlockStorage(p_76633_3_ >> 4 << 4, !this.worldObj.provider.hasNoSky); this.generateSkylightMap(); } this.isModified = true; if (p_76633_1_ == EnumSkyBlock.Sky) { if (!this.worldObj.provider.hasNoSky) { extendedblockstorage.setExtSkylightValue(p_76633_2_, p_76633_3_ & 15, p_76633_4_, p_76633_5_); } } else if (p_76633_1_ == EnumSkyBlock.Block) { extendedblockstorage.setExtBlocklightValue(p_76633_2_, p_76633_3_ & 15, p_76633_4_, p_76633_5_); } } /** * Gets the amount of light on a block taking into account sunlight */ public int getBlockLightValue(int p_76629_1_, int p_76629_2_, int p_76629_3_, int p_76629_4_) { ExtendedBlockStorage extendedblockstorage = this.storageArrays[p_76629_2_ >> 4]; if (extendedblockstorage == null) { return !this.worldObj.provider.hasNoSky && p_76629_4_ < EnumSkyBlock.Sky.defaultLightValue ? EnumSkyBlock.Sky.defaultLightValue - p_76629_4_ : 0; } else { int i1 = this.worldObj.provider.hasNoSky ? 0 : extendedblockstorage.getExtSkylightValue(p_76629_1_, p_76629_2_ & 15, p_76629_3_); if (i1 > 0) { isLit = true; } i1 -= p_76629_4_; int j1 = extendedblockstorage.getExtBlocklightValue(p_76629_1_, p_76629_2_ & 15, p_76629_3_); if (j1 > i1) { i1 = j1; } return i1; } } /** * Adds an entity to the chunk. Args: entity */ public void addEntity(Entity p_76612_1_) { this.hasEntities = true; int i = MathHelper.floor_double(p_76612_1_.posX / 16.0D); int j = MathHelper.floor_double(p_76612_1_.posZ / 16.0D); if (i != this.xPosition || j != this.zPosition) { logger.warn("Wrong location! " + p_76612_1_ + " (at " + i + ", " + j + " instead of " + this.xPosition + ", " + this.zPosition + ")"); Thread.dumpStack(); } int k = MathHelper.floor_double(p_76612_1_.posY / 16.0D); if (k < 0) { k = 0; } if (k >= this.entityLists.length) { k = this.entityLists.length - 1; } MinecraftForge.EVENT_BUS.post(new EntityEvent.EnteringChunk(p_76612_1_, this.xPosition, this.zPosition, p_76612_1_.chunkCoordX, p_76612_1_.chunkCoordZ)); p_76612_1_.addedToChunk = true; p_76612_1_.chunkCoordX = this.xPosition; p_76612_1_.chunkCoordY = k; p_76612_1_.chunkCoordZ = this.zPosition; this.entityLists[k].add(p_76612_1_); } /** * removes entity using its y chunk coordinate as its index */ public void removeEntity(Entity p_76622_1_) { this.removeEntityAtIndex(p_76622_1_, p_76622_1_.chunkCoordY); } /** * Removes entity at the specified index from the entity array. */ public void removeEntityAtIndex(Entity p_76608_1_, int p_76608_2_) { if (p_76608_2_ < 0) { p_76608_2_ = 0; } if (p_76608_2_ >= this.entityLists.length) { p_76608_2_ = this.entityLists.length - 1; } this.entityLists[p_76608_2_].remove(p_76608_1_); } /** * Returns whether is not a block above this one blocking sight to the sky (done via checking against the heightmap) */ public boolean canBlockSeeTheSky(int p_76619_1_, int p_76619_2_, int p_76619_3_) { return p_76619_2_ >= this.heightMap[p_76619_3_ << 4 | p_76619_1_]; } public TileEntity getBlockTileEntityInChunk(int p_150806_1_, int p_150806_2_, int p_150806_3_) { ChunkPosition chunkposition = new ChunkPosition(p_150806_1_, p_150806_2_, p_150806_3_); TileEntity tileentity = (TileEntity)this.chunkTileEntityMap.get(chunkposition); if (tileentity != null && tileentity.isInvalid()) { chunkTileEntityMap.remove(chunkposition); tileentity = null; } if (tileentity == null) { Block block = this.getBlock(p_150806_1_, p_150806_2_, p_150806_3_); int meta = this.getBlockMetadata(p_150806_1_, p_150806_2_, p_150806_3_); if (!block.hasTileEntity(meta)) { return null; } tileentity = block.createTileEntity(worldObj, meta); this.worldObj.setTileEntity(this.xPosition * 16 + p_150806_1_, p_150806_2_, this.zPosition * 16 + p_150806_3_, tileentity); } return tileentity; } public void addTileEntity(TileEntity p_150813_1_) { int i = p_150813_1_.xCoord - this.xPosition * 16; int j = p_150813_1_.yCoord; int k = p_150813_1_.zCoord - this.zPosition * 16; this.setBlockTileEntityInChunk(i, j, k, p_150813_1_); if (this.isChunkLoaded) { this.worldObj.addTileEntity(p_150813_1_); } } public void setBlockTileEntityInChunk(int p_150812_1_, int p_150812_2_, int p_150812_3_, TileEntity p_150812_4_) { ChunkPosition chunkposition = new ChunkPosition(p_150812_1_, p_150812_2_, p_150812_3_); p_150812_4_.setWorldObj(this.worldObj); p_150812_4_.xCoord = this.xPosition * 16 + p_150812_1_; p_150812_4_.yCoord = p_150812_2_; p_150812_4_.zCoord = this.zPosition * 16 + p_150812_3_; int metadata = getBlockMetadata(p_150812_1_, p_150812_2_, p_150812_3_); if (this.getBlock(p_150812_1_, p_150812_2_, p_150812_3_).hasTileEntity(metadata)) { if (this.chunkTileEntityMap.containsKey(chunkposition)) { ((TileEntity)this.chunkTileEntityMap.get(chunkposition)).invalidate(); } p_150812_4_.validate(); this.chunkTileEntityMap.put(chunkposition, p_150812_4_); } } public void removeTileEntity(int p_150805_1_, int p_150805_2_, int p_150805_3_) { ChunkPosition chunkposition = new ChunkPosition(p_150805_1_, p_150805_2_, p_150805_3_); if (this.isChunkLoaded) { TileEntity tileentity = (TileEntity)this.chunkTileEntityMap.remove(chunkposition); if (tileentity != null) { tileentity.invalidate(); } } } /** * Called when this Chunk is loaded by the ChunkProvider */ public void onChunkLoad() { this.isChunkLoaded = true; this.worldObj.func_147448_a(this.chunkTileEntityMap.values()); for (int i = 0; i < this.entityLists.length; ++i) { Iterator iterator = this.entityLists[i].iterator(); while (iterator.hasNext()) { Entity entity = (Entity)iterator.next(); entity.onChunkLoad(); } this.worldObj.addLoadedEntities(this.entityLists[i]); } MinecraftForge.EVENT_BUS.post(new ChunkEvent.Load(this)); } /** * Called when this Chunk is unloaded by the ChunkProvider */ public void onChunkUnload() { this.isChunkLoaded = false; Iterator iterator = this.chunkTileEntityMap.values().iterator(); while (iterator.hasNext()) { TileEntity tileentity = (TileEntity)iterator.next(); this.worldObj.markTileEntityForRemoval(tileentity); } for (int i = 0; i < this.entityLists.length; ++i) { this.worldObj.unloadEntities(this.entityLists[i]); } MinecraftForge.EVENT_BUS.post(new ChunkEvent.Unload(this)); } /** * Sets the isModified flag for this Chunk */ public void setChunkModified() { this.isModified = true; } /** * Fills the given list of all entities that intersect within the given bounding box that aren't the passed entity * Args: entity, aabb, listToFill */ public void getEntitiesWithinAABBForEntity(Entity p_76588_1_, AxisAlignedBB p_76588_2_, List p_76588_3_, IEntitySelector p_76588_4_) { int i = MathHelper.floor_double((p_76588_2_.minY - World.MAX_ENTITY_RADIUS) / 16.0D); int j = MathHelper.floor_double((p_76588_2_.maxY + World.MAX_ENTITY_RADIUS) / 16.0D); i = MathHelper.clamp_int(i, 0, this.entityLists.length - 1); j = MathHelper.clamp_int(j, 0, this.entityLists.length - 1); for (int k = i; k <= j; ++k) { List list1 = this.entityLists[k]; for (int l = 0; l < list1.size(); ++l) { Entity entity1 = (Entity)list1.get(l); if (entity1 != p_76588_1_ && entity1.boundingBox.intersectsWith(p_76588_2_) && (p_76588_4_ == null || p_76588_4_.isEntityApplicable(entity1))) { p_76588_3_.add(entity1); Entity[] aentity = entity1.getParts(); if (aentity != null) { for (int i1 = 0; i1 < aentity.length; ++i1) { entity1 = aentity[i1]; if (entity1 != p_76588_1_ && entity1.boundingBox.intersectsWith(p_76588_2_) && (p_76588_4_ == null || p_76588_4_.isEntityApplicable(entity1))) { p_76588_3_.add(entity1); } } } } } } } /** * Gets all entities that can be assigned to the specified class. Args: entityClass, aabb, listToFill */ public void getEntitiesOfTypeWithinAAAB(Class p_76618_1_, AxisAlignedBB p_76618_2_, List p_76618_3_, IEntitySelector p_76618_4_) { int i = MathHelper.floor_double((p_76618_2_.minY - World.MAX_ENTITY_RADIUS) / 16.0D); int j = MathHelper.floor_double((p_76618_2_.maxY + World.MAX_ENTITY_RADIUS) / 16.0D); i = MathHelper.clamp_int(i, 0, this.entityLists.length - 1); j = MathHelper.clamp_int(j, 0, this.entityLists.length - 1); for (int k = i; k <= j; ++k) { List list1 = this.entityLists[k]; for (int l = 0; l < list1.size(); ++l) { Entity entity = (Entity)list1.get(l); if (p_76618_1_.isAssignableFrom(entity.getClass()) && entity.boundingBox.intersectsWith(p_76618_2_) && (p_76618_4_ == null || p_76618_4_.isEntityApplicable(entity))) { p_76618_3_.add(entity); } } } } /** * Returns true if this Chunk needs to be saved */ public boolean needsSaving(boolean p_76601_1_) { if (p_76601_1_) { if (this.hasEntities && this.worldObj.getTotalWorldTime() != this.lastSaveTime || this.isModified) { return true; } } else if (this.hasEntities && this.worldObj.getTotalWorldTime() >= this.lastSaveTime + 600L) { return true; } return this.isModified; } public Random getRandomWithSeed(long p_76617_1_) { 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) ^ p_76617_1_); } public boolean isEmpty() { return false; } public void populateChunk(IChunkProvider p_76624_1_, IChunkProvider p_76624_2_, int p_76624_3_, int p_76624_4_) { if (!this.isTerrainPopulated && p_76624_1_.chunkExists(p_76624_3_ + 1, p_76624_4_ + 1) && p_76624_1_.chunkExists(p_76624_3_, p_76624_4_ + 1) && p_76624_1_.chunkExists(p_76624_3_ + 1, p_76624_4_)) { p_76624_1_.populate(p_76624_2_, p_76624_3_, p_76624_4_); } if (p_76624_1_.chunkExists(p_76624_3_ - 1, p_76624_4_) && !p_76624_1_.provideChunk(p_76624_3_ - 1, p_76624_4_).isTerrainPopulated && p_76624_1_.chunkExists(p_76624_3_ - 1, p_76624_4_ + 1) && p_76624_1_.chunkExists(p_76624_3_, p_76624_4_ + 1) && p_76624_1_.chunkExists(p_76624_3_ - 1, p_76624_4_ + 1)) { p_76624_1_.populate(p_76624_2_, p_76624_3_ - 1, p_76624_4_); } if (p_76624_1_.chunkExists(p_76624_3_, p_76624_4_ - 1) && !p_76624_1_.provideChunk(p_76624_3_, p_76624_4_ - 1).isTerrainPopulated && p_76624_1_.chunkExists(p_76624_3_ + 1, p_76624_4_ - 1) && p_76624_1_.chunkExists(p_76624_3_ + 1, p_76624_4_ - 1) && p_76624_1_.chunkExists(p_76624_3_ + 1, p_76624_4_)) { p_76624_1_.populate(p_76624_2_, p_76624_3_, p_76624_4_ - 1); } if (p_76624_1_.chunkExists(p_76624_3_ - 1, p_76624_4_ - 1) && !p_76624_1_.provideChunk(p_76624_3_ - 1, p_76624_4_ - 1).isTerrainPopulated && p_76624_1_.chunkExists(p_76624_3_, p_76624_4_ - 1) && p_76624_1_.chunkExists(p_76624_3_ - 1, p_76624_4_)) { p_76624_1_.populate(p_76624_2_, p_76624_3_ - 1, p_76624_4_ - 1); } } /** * Gets the height to which rain/snow will fall. Calculates it if not already stored. */ public int getPrecipitationHeight(int p_76626_1_, int p_76626_2_) { int k = p_76626_1_ | p_76626_2_ << 4; int l = this.precipitationHeightMap[k]; if (l == -999) { int i1 = this.getTopFilledSegment() + 15; l = -1; while (i1 > 0 && l == -1) { Block block = this.getBlock(p_76626_1_, i1, p_76626_2_); Material material = block.getMaterial(); if (!material.blocksMovement() && !material.isLiquid()) { --i1; } else { l = i1 + 1; } } this.precipitationHeightMap[k] = l; } return l; } public void func_150804_b(boolean p_150804_1_) { if (this.isGapLightingUpdated && !this.worldObj.provider.hasNoSky && !p_150804_1_) { this.recheckGaps(this.worldObj.isRemote); } this.field_150815_m = true; if (!this.isLightPopulated && this.isTerrainPopulated) { this.func_150809_p(); } } public boolean func_150802_k() { return this.field_150815_m && this.isTerrainPopulated && this.isLightPopulated; } /** * Gets a ChunkCoordIntPair representing the Chunk's position. */ public ChunkCoordIntPair getChunkCoordIntPair() { return new ChunkCoordIntPair(this.xPosition, this.zPosition); } /** * Returns whether the ExtendedBlockStorages containing levels (in blocks) from arg 1 to arg 2 are fully empty * (true) or not (false). */ public boolean getAreLevelsEmpty(int p_76606_1_, int p_76606_2_) { if (p_76606_1_ < 0) { p_76606_1_ = 0; } if (p_76606_2_ >= 256) { p_76606_2_ = 255; } for (int k = p_76606_1_; k <= p_76606_2_; k += 16) { ExtendedBlockStorage extendedblockstorage = this.storageArrays[k >> 4]; if (extendedblockstorage != null && !extendedblockstorage.isEmpty()) { return false; } } return true; } public void setStorageArrays(ExtendedBlockStorage[] p_76602_1_) { this.storageArrays = p_76602_1_; } /** * Initialise this chunk with new binary data */ @SideOnly(Side.CLIENT) public void fillChunk(byte[] p_76607_1_, int p_76607_2_, int p_76607_3_, boolean p_76607_4_) { Iterator iterator = chunkTileEntityMap.values().iterator(); while(iterator.hasNext()) { TileEntity tileEntity = (TileEntity)iterator.next(); tileEntity.updateContainingBlockInfo(); tileEntity.getBlockMetadata(); tileEntity.getBlockType(); } int k = 0; boolean flag1 = !this.worldObj.provider.hasNoSky; int l; for (l = 0; l < this.storageArrays.length; ++l) { if ((p_76607_2_ & 1 << l) != 0) { if (this.storageArrays[l] == null) { this.storageArrays[l] = new ExtendedBlockStorage(l << 4, flag1); } byte[] abyte1 = this.storageArrays[l].getBlockLSBArray(); System.arraycopy(p_76607_1_, k, abyte1, 0, abyte1.length); k += abyte1.length; } else if (p_76607_4_ && this.storageArrays[l] != null) { this.storageArrays[l] = null; } } NibbleArray nibblearray; for (l = 0; l < this.storageArrays.length; ++l) { if ((p_76607_2_ & 1 << l) != 0 && this.storageArrays[l] != null) { nibblearray = this.storageArrays[l].getMetadataArray(); System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); k += nibblearray.data.length; } } for (l = 0; l < this.storageArrays.length; ++l) { if ((p_76607_2_ & 1 << l) != 0 && this.storageArrays[l] != null) { nibblearray = this.storageArrays[l].getBlocklightArray(); System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); k += nibblearray.data.length; } } if (flag1) { for (l = 0; l < this.storageArrays.length; ++l) { if ((p_76607_2_ & 1 << l) != 0 && this.storageArrays[l] != null) { nibblearray = this.storageArrays[l].getSkylightArray(); System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); k += nibblearray.data.length; } } } for (l = 0; l < this.storageArrays.length; ++l) { if ((p_76607_3_ & 1 << l) != 0) { if (this.storageArrays[l] == null) { k += 2048; } else { nibblearray = this.storageArrays[l].getBlockMSBArray(); if (nibblearray == null) { nibblearray = this.storageArrays[l].createBlockMSBArray(); } System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); k += nibblearray.data.length; } } else if (p_76607_4_ && this.storageArrays[l] != null && this.storageArrays[l].getBlockMSBArray() != null) { this.storageArrays[l].clearMSBArray(); } } if (p_76607_4_) { System.arraycopy(p_76607_1_, k, this.blockBiomeArray, 0, this.blockBiomeArray.length); int i1 = k + this.blockBiomeArray.length; } for (l = 0; l < this.storageArrays.length; ++l) { if (this.storageArrays[l] != null && (p_76607_2_ & 1 << l) != 0) { this.storageArrays[l].removeInvalidBlocks(); } } this.isLightPopulated = true; this.isTerrainPopulated = true; this.generateHeightMap(); List<TileEntity> invalidList = new ArrayList<TileEntity>(); iterator = this.chunkTileEntityMap.values().iterator(); while (iterator.hasNext()) { TileEntity tileentity = (TileEntity)iterator.next(); int x = tileentity.xCoord & 15; int y = tileentity.yCoord; int z = tileentity.zCoord & 15; Block block = tileentity.getBlockType(); if ((block != getBlock(x, y, z) || tileentity.blockMetadata != this.getBlockMetadata(x, y, z)) && tileentity.shouldRefresh(block, getBlock(x, y, z), tileentity.blockMetadata, this.getBlockMetadata(x, y, z), worldObj, x, y, z)) { invalidList.add(tileentity); } tileentity.updateContainingBlockInfo(); } for (TileEntity te : invalidList) { te.invalidate(); } } /** * This method retrieves the biome at a set of coordinates */ public BiomeGenBase getBiomeGenForWorldCoords(int p_76591_1_, int p_76591_2_, WorldChunkManager p_76591_3_) { int k = this.blockBiomeArray[p_76591_2_ << 4 | p_76591_1_] & 255; if (k == 255) { BiomeGenBase biomegenbase = p_76591_3_.getBiomeGenAt((this.xPosition << 4) + p_76591_1_, (this.zPosition << 4) + p_76591_2_); k = biomegenbase.biomeID; this.blockBiomeArray[p_76591_2_ << 4 | p_76591_1_] = (byte)(k & 255); } return BiomeGenBase.getBiome(k) == null ? BiomeGenBase.plains : BiomeGenBase.getBiome(k); } /** * Returns an array containing a 16x16 mapping on the X/Z of block positions in this Chunk to biome IDs. */ public byte[] getBiomeArray() { return this.blockBiomeArray; } /** * Accepts a 256-entry array that contains a 16x16 mapping on the X/Z plane of block positions in this Chunk to * biome IDs. */ public void setBiomeArray(byte[] p_76616_1_) { this.blockBiomeArray = p_76616_1_; } /** * Resets the relight check index to 0 for this Chunk. */ public void resetRelightChecks() { this.queuedLightChecks = 0; } /** * Called once-per-chunk-per-tick, and advances the round-robin relight check index by up to 8 blocks at a time. In * a worst-case scenario, can potentially take up to 25.6 seconds, calculated via (4096/8)/20, to re-check all * blocks in a chunk, which may explain lagging light updates on initial world generation. */ public void enqueueRelightChecks() { for (int i = 0; i < 8; ++i) { if (this.queuedLightChecks >= 4096) { return; } int j = this.queuedLightChecks % 16; int k = this.queuedLightChecks / 16 % 16; int l = this.queuedLightChecks / 256; ++this.queuedLightChecks; int i1 = (this.xPosition << 4) + k; int j1 = (this.zPosition << 4) + l; for (int k1 = 0; k1 < 16; ++k1) { int l1 = (j << 4) + k1; if (this.storageArrays[j] == null && (k1 == 0 || k1 == 15 || k == 0 || k == 15 || l == 0 || l == 15) || this.storageArrays[j] != null && this.storageArrays[j].getBlockByExtId(k, k1, l).getMaterial() == Material.air) { if (this.worldObj.getBlock(i1, l1 - 1, j1).getLightValue() > 0) { this.worldObj.updateAllLightTypes(i1, l1 - 1, j1); } if (this.worldObj.getBlock(i1, l1 + 1, j1).getLightValue() > 0) { this.worldObj.updateAllLightTypes(i1, l1 + 1, j1); } if (this.worldObj.getBlock(i1 - 1, l1, j1).getLightValue() > 0) { this.worldObj.updateAllLightTypes(i1 - 1, l1, j1); } if (this.worldObj.getBlock(i1 + 1, l1, j1).getLightValue() > 0) { this.worldObj.updateAllLightTypes(i1 + 1, l1, j1); } if (this.worldObj.getBlock(i1, l1, j1 - 1).getLightValue() > 0) { this.worldObj.updateAllLightTypes(i1, l1, j1 - 1); } if (this.worldObj.getBlock(i1, l1, j1 + 1).getLightValue() > 0) { this.worldObj.updateAllLightTypes(i1, l1, j1 + 1); } this.worldObj.updateAllLightTypes(i1, l1, j1); } } } } public void func_150809_p() { this.isTerrainPopulated = true; this.isLightPopulated = true; if (!this.worldObj.provider.hasNoSky) { if (this.worldObj.checkChunksExist(this.xPosition * 16 - 1, 0, this.zPosition * 16 - 1, this.xPosition * 16 + 1, 63, this.zPosition * 16 + 1)) { for (int i = 0; i < 16; ++i) { for (int j = 0; j < 16; ++j) { if (!this.func_150811_f(i, j)) { this.isLightPopulated = false; break; } } } if (this.isLightPopulated) { Chunk chunk = this.worldObj.getChunkFromBlockCoords(this.xPosition * 16 - 1, this.zPosition * 16); chunk.func_150801_a(3); chunk = this.worldObj.getChunkFromBlockCoords(this.xPosition * 16 + 16, this.zPosition * 16); chunk.func_150801_a(1); chunk = this.worldObj.getChunkFromBlockCoords(this.xPosition * 16, this.zPosition * 16 - 1); chunk.func_150801_a(0); chunk = this.worldObj.getChunkFromBlockCoords(this.xPosition * 16, this.zPosition * 16 + 16); chunk.func_150801_a(2); } } else { this.isLightPopulated = false; } } } private void func_150801_a(int p_150801_1_) { if (this.isTerrainPopulated) { int j; if (p_150801_1_ == 3) { for (j = 0; j < 16; ++j) { this.func_150811_f(15, j); } } else if (p_150801_1_ == 1) { for (j = 0; j < 16; ++j) { this.func_150811_f(0, j); } } else if (p_150801_1_ == 0) { for (j = 0; j < 16; ++j) { this.func_150811_f(j, 15); } } else if (p_150801_1_ == 2) { for (j = 0; j < 16; ++j) { this.func_150811_f(j, 0); } } } } private boolean func_150811_f(int p_150811_1_, int p_150811_2_) { int k = this.getTopFilledSegment(); boolean flag = false; boolean flag1 = false; int l; for (l = k + 16 - 1; l > 63 || l > 0 && !flag1; --l) { int i1 = this.getBlockLightOpacity(p_150811_1_, l, p_150811_2_); if (i1 == 255 && l < 63) { flag1 = true; } if (!flag && i1 > 0) { flag = true; } else if (flag && i1 == 0 && !this.worldObj.updateAllLightTypes(this.xPosition * 16 + p_150811_1_, l, this.zPosition * 16 + p_150811_2_)) { return false; } } for (; l > 0; --l) { if (this.getBlock(p_150811_1_, l, p_150811_2_).getLightValue() > 0) { this.worldObj.updateAllLightTypes(this.xPosition * 16 + p_150811_1_, l, this.zPosition * 16 + p_150811_2_); } } return true; } /** * Retrieves the tile entity, WITHOUT creating it. * Good for checking if it exists. * * @param x * @param y * @param z * @return The tile entity at the specified location, if it exists and is valid. */ public TileEntity getTileEntityUnsafe(int x, int y, int z) { ChunkPosition chunkposition = new ChunkPosition(x, y, z); TileEntity tileentity = (TileEntity)this.chunkTileEntityMap.get(chunkposition); if (tileentity != null && tileentity.isInvalid()) { chunkTileEntityMap.remove(chunkposition); tileentity = null; } return tileentity; } /** * Removes the tile entity at the specified position, only if it's * marked as invalid. * * @param x * @param y * @param z */ public void removeInvalidTileEntity(int x, int y, int z) { ChunkPosition position = new ChunkPosition(x, y, z); if (isChunkLoaded) { TileEntity entity = (TileEntity)chunkTileEntityMap.get(position); if (entity != null && entity.isInvalid()) { chunkTileEntityMap.remove(position); } } } }