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 net.minecraft.block.Block; import net.minecraft.block.BlockContainer; import net.minecraft.block.material.Material; import net.minecraft.command.IEntitySelector; import net.minecraft.entity.Entity; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.MathHelper; 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; public class Chunk { /** * 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; /** * 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; public boolean deferRender; public int field_82912_p; /** * Contains the current round-robin relight check index, and is implied as the relight check location as well. */ private int queuedLightChecks; boolean field_76653_p; public Chunk(World par1World, int par2, int par3) { this.storageArrays = new ExtendedBlockStorage[16]; this.blockBiomeArray = new byte[256]; this.precipitationHeightMap = new int[256]; this.updateSkylightColumns = new boolean[256]; this.isGapLightingUpdated = false; this.chunkTileEntityMap = new HashMap(); this.isTerrainPopulated = false; this.isModified = false; this.hasEntities = false; this.lastSaveTime = 0L; this.deferRender = false; this.field_82912_p = 0; this.queuedLightChecks = 4096; this.field_76653_p = false; this.entityLists = new List[16]; this.worldObj = par1World; this.xPosition = par2; this.zPosition = par3; this.heightMap = new int[256]; for (int var4 = 0; var4 < this.entityLists.length; ++var4) { this.entityLists[var4] = new ArrayList(); } Arrays.fill(this.precipitationHeightMap, -999); Arrays.fill(this.blockBiomeArray, (byte) - 1); } public Chunk(World par1World, byte[] par2ArrayOfByte, int par3, int par4) { this(par1World, par3, par4); int var5 = par2ArrayOfByte.length / 256; for (int var6 = 0; var6 < 16; ++var6) { for (int var7 = 0; var7 < 16; ++var7) { for (int var8 = 0; var8 < var5; ++var8) { /* FORGE: The following change, a cast from unsigned byte to int, * fixes a vanilla bug when generating new chunks that contain a block ID > 127 */ int var9 = par2ArrayOfByte[var6 << 11 | var7 << 7 | var8] & 0xFF; if (var9 != 0) { int var10 = var8 >> 4; if (this.storageArrays[var10] == null) { this.storageArrays[var10] = new ExtendedBlockStorage(var10 << 4, !par1World.provider.hasNoSky); } this.storageArrays[var10].setExtBlockID(var6, var8 & 15, var7, var9); } } } } } /** * Metadata sensitive Chunk constructor for use in new ChunkProviders that * use metadata sensitive blocks during generation. * * @param world The world this chunk belongs to * @param ids A ByteArray containing all the BlockID's to set this chunk to * @param metadata A ByteArray containing all the metadata to set this chunk to * @param chunkX The chunk's X position * @param chunkZ The Chunk's Z position */ public Chunk(World world, byte[] ids, byte[] metadata, int chunkX, int chunkZ) { this(world, chunkX, chunkZ); int var5 = ids.length / 256; for (int x = 0; x < 16; ++x) { for (int z = 0; z < 16; ++z) { for (int y = 0; y < var5; ++y) { int idx = x << 11 | z << 7 | y; int id = ids[idx] & 0xFF; int meta = metadata[idx]; if (id != 0) { int var10 = y >> 4; if (this.storageArrays[var10] == null) { this.storageArrays[var10] = new ExtendedBlockStorage(var10 << 4, !world.provider.hasNoSky); } this.storageArrays[var10].setExtBlockID(x, y & 15, z, id); this.storageArrays[var10].setExtBlockMetadata(x, y & 15, z, meta); } } } } } /** * A Chunk Constructor which handles shorts to allow block ids > 256 (full 4096 range) * Meta data sensitive * NOTE: The x,y,z order of the array is different from the native Chunk constructor to allow for generation > y127 * NOTE: This is possibly more efficient than the standard constructor due to less memory skipping * * @param world The world this chunk belongs to * @param ids A ShortArray containing all the BlockID's to set this chunk to (x is low order, z is mid, y is high) * @param metadata A ByteArray containing all the metadata to set this chunk to * @param chunkX The chunk's X position * @param chunkZ The Chunk's Z position */ public Chunk(World world, short[] ids, byte[] metadata, int chunkX, int chunkZ) { this(world, chunkX, chunkZ); int max = ids.length / 256; for (int y = 0; y < max; ++y) { for (int z = 0; z < 16; ++z) { for (int x = 0; x < 16; ++x) { int idx = y << 8 | z << 4 | x; int id = ids[idx] & 0xFFFFFF; int meta = metadata[idx]; if (id != 0) { int storageBlock = y >> 4; if (this.storageArrays[storageBlock] == null) { this.storageArrays[storageBlock] = new ExtendedBlockStorage(storageBlock << 4, !world.provider.hasNoSky); } this.storageArrays[storageBlock].setExtBlockID(x, y & 15, z, id); this.storageArrays[storageBlock].setExtBlockMetadata(x, y & 15, z, meta); } } } } } /** * Checks whether the chunk is at the X/Z location specified */ public boolean isAtLocation(int par1, int par2) { return par1 == this.xPosition && par2 == this.zPosition; } /** * Returns the value in the height map at this x, z coordinate in the chunk */ public int getHeightValue(int par1, int par2) { return this.heightMap[par2 << 4 | par1]; } /** * Returns the topmost ExtendedBlockStorage instance for this Chunk that actually contains a block. */ public int getTopFilledSegment() { for (int var1 = this.storageArrays.length - 1; var1 >= 0; --var1) { if (this.storageArrays[var1] != null) { return this.storageArrays[var1].getYLocation(); } } return 0; } /** * Returns the ExtendedBlockStorage array for this Chunk. */ public ExtendedBlockStorage[] getBlockStorageArray() { return this.storageArrays; } @SideOnly(Side.CLIENT) /** * Generates the height map for a chunk from scratch */ public void generateHeightMap() { int var1 = this.getTopFilledSegment(); for (int var2 = 0; var2 < 16; ++var2) { int var3 = 0; while (var3 < 16) { this.precipitationHeightMap[var2 + (var3 << 4)] = -999; int var4 = var1 + 16 - 1; while (true) { if (var4 > 0) { int var5 = this.getBlockID(var2, var4 - 1, var3); if (getBlockLightOpacity(var2, var4 - 1, var3) == 0) { --var4; continue; } this.heightMap[var3 << 4 | var2] = var4; } ++var3; break; } } } this.isModified = true; } /** * Generates the initial skylight map for the chunk upon generation or load. */ public void generateSkylightMap() { int var1 = this.getTopFilledSegment(); this.field_82912_p = Integer.MAX_VALUE; int var2; int var3; for (var2 = 0; var2 < 16; ++var2) { var3 = 0; while (var3 < 16) { this.precipitationHeightMap[var2 + (var3 << 4)] = -999; int var4 = var1 + 16 - 1; while (true) { if (var4 > 0) { if (this.getBlockLightOpacity(var2, var4 - 1, var3) == 0) { --var4; continue; } this.heightMap[var3 << 4 | var2] = var4; if (var4 < this.field_82912_p) { this.field_82912_p = var4; } } if (!this.worldObj.provider.hasNoSky) { var4 = 15; int var5 = var1 + 16 - 1; do { var4 -= this.getBlockLightOpacity(var2, var5, var3); if (var4 > 0) { ExtendedBlockStorage var6 = this.storageArrays[var5 >> 4]; if (var6 != null) { var6.setExtSkylightValue(var2, var5 & 15, var3, var4); this.worldObj.markBlockForRenderUpdate((this.xPosition << 4) + var2, var5, (this.zPosition << 4) + var3); } } --var5; } while (var5 > 0 && var4 > 0); } ++var3; break; } } } this.isModified = true; for (var2 = 0; var2 < 16; ++var2) { for (var3 = 0; var3 < 16; ++var3) { this.propagateSkylightOcclusion(var2, var3); } } } /** * Propagates a given sky-visible block's light value downward and upward to neighboring blocks as necessary. */ private void propagateSkylightOcclusion(int par1, int par2) { this.updateSkylightColumns[par1 + par2 * 16] = true; this.isGapLightingUpdated = true; } /** * Runs delayed skylight updates. */ private void updateSkylight_do() { this.worldObj.theProfiler.startSection("recheckGaps"); if (this.worldObj.doChunksNearChunkExist(this.xPosition * 16 + 8, 0, this.zPosition * 16 + 8, 16)) { for (int var1 = 0; var1 < 16; ++var1) { for (int var2 = 0; var2 < 16; ++var2) { if (this.updateSkylightColumns[var1 + var2 * 16]) { this.updateSkylightColumns[var1 + var2 * 16] = false; int var3 = this.getHeightValue(var1, var2); int var4 = this.xPosition * 16 + var1; int var5 = this.zPosition * 16 + var2; int var6 = this.worldObj.func_82734_g(var4 - 1, var5); int var7 = this.worldObj.func_82734_g(var4 + 1, var5); int var8 = this.worldObj.func_82734_g(var4, var5 - 1); int var9 = this.worldObj.func_82734_g(var4, var5 + 1); if (var7 < var6) { var6 = var7; } if (var8 < var6) { var6 = var8; } if (var9 < var6) { var6 = var9; } this.checkSkylightNeighborHeight(var4, var5, var6); this.checkSkylightNeighborHeight(var4 - 1, var5, var3); this.checkSkylightNeighborHeight(var4 + 1, var5, var3); this.checkSkylightNeighborHeight(var4, var5 - 1, var3); this.checkSkylightNeighborHeight(var4, var5 + 1, var3); } } } 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 par1, int par2, int par3) { int var4 = this.worldObj.getHeightValue(par1, par2); if (var4 > par3) { this.updateSkylightNeighborHeight(par1, par2, par3, var4 + 1); } else if (var4 < par3) { this.updateSkylightNeighborHeight(par1, par2, var4, par3 + 1); } } private void updateSkylightNeighborHeight(int par1, int par2, int par3, int par4) { if (par4 > par3 && this.worldObj.doChunksNearChunkExist(par1, 0, par2, 16)) { for (int var5 = par3; var5 < par4; ++var5) { this.worldObj.updateLightByType(EnumSkyBlock.Sky, par1, var5, par2); } 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 par1, int par2, int par3) { int var4 = this.heightMap[par3 << 4 | par1] & 255; int var5 = var4; if (par2 > var4) { var5 = par2; } while (var5 > 0 && this.getBlockLightOpacity(par1, var5 - 1, par3) == 0) { --var5; } if (var5 != var4) { this.worldObj.markBlocksDirtyVertical(par1 + this.xPosition * 16, par3 + this.zPosition * 16, var5, var4); this.heightMap[par3 << 4 | par1] = var5; int var6 = this.xPosition * 16 + par1; int var7 = this.zPosition * 16 + par3; int var8; int var12; if (!this.worldObj.provider.hasNoSky) { ExtendedBlockStorage var9; if (var5 < var4) { for (var8 = var5; var8 < var4; ++var8) { var9 = this.storageArrays[var8 >> 4]; if (var9 != null) { var9.setExtSkylightValue(par1, var8 & 15, par3, 15); this.worldObj.markBlockForRenderUpdate((this.xPosition << 4) + par1, var8, (this.zPosition << 4) + par3); } } } else { for (var8 = var4; var8 < var5; ++var8) { var9 = this.storageArrays[var8 >> 4]; if (var9 != null) { var9.setExtSkylightValue(par1, var8 & 15, par3, 0); this.worldObj.markBlockForRenderUpdate((this.xPosition << 4) + par1, var8, (this.zPosition << 4) + par3); } } } var8 = 15; while (var5 > 0 && var8 > 0) { --var5; var12 = this.getBlockLightOpacity(par1, var5, par3); if (var12 == 0) { var12 = 1; } var8 -= var12; if (var8 < 0) { var8 = 0; } ExtendedBlockStorage var10 = this.storageArrays[var5 >> 4]; if (var10 != null) { var10.setExtSkylightValue(par1, var5 & 15, par3, var8); } } } var8 = this.heightMap[par3 << 4 | par1]; var12 = var4; int var13 = var8; if (var8 < var4) { var12 = var8; var13 = var4; } if (var8 < this.field_82912_p) { this.field_82912_p = var8; } if (!this.worldObj.provider.hasNoSky) { this.updateSkylightNeighborHeight(var6 - 1, var7, var12, var13); this.updateSkylightNeighborHeight(var6 + 1, var7, var12, var13); this.updateSkylightNeighborHeight(var6, var7 - 1, var12, var13); this.updateSkylightNeighborHeight(var6, var7 + 1, var12, var13); this.updateSkylightNeighborHeight(var6, var7, var12, var13); } this.isModified = true; } } public int getBlockLightOpacity(int par1, int par2, int par3) { int x = (xPosition << 4) + par1; int z = (zPosition << 4) + par3; Block block = Block.blocksList[getBlockID(par1, par2, par3)]; return (block == null ? 0 : block.getLightOpacity(worldObj, x, par2, z)); } /** * Return the ID of a block in the chunk. */ public int getBlockID(int par1, int par2, int par3) { if (par2 >> 4 >= this.storageArrays.length || par2 >> 4 < 0) { return 0; } else { ExtendedBlockStorage var4 = this.storageArrays[par2 >> 4]; return var4 != null ? var4.getExtBlockID(par1, par2 & 15, par3) : 0; } } /** * Return the metadata corresponding to the given coordinates inside a chunk. */ public int getBlockMetadata(int par1, int par2, int par3) { if (par2 >> 4 >= this.storageArrays.length || par2 >> 4 < 0) { return 0; } else { ExtendedBlockStorage var4 = this.storageArrays[par2 >> 4]; return var4 != null ? var4.getExtBlockMetadata(par1, par2 & 15, par3) : 0; } } /** * Sets a blockID for a position in the chunk. Args: x, y, z, blockID */ public boolean setBlockID(int par1, int par2, int par3, int par4) { return this.setBlockIDWithMetadata(par1, par2, par3, par4, 0); } /** * Sets a blockID of a position within a chunk with metadata. Args: x, y, z, blockID, metadata */ public boolean setBlockIDWithMetadata(int par1, int par2, int par3, int par4, int par5) { int var6 = par3 << 4 | par1; if (par2 >= this.precipitationHeightMap[var6] - 1) { this.precipitationHeightMap[var6] = -999; } int var7 = this.heightMap[var6]; int var8 = this.getBlockID(par1, par2, par3); int var9 = this.getBlockMetadata(par1, par2, par3); if (var8 == par4 && var9 == par5) { return false; } else { if (par2 >> 4 >= storageArrays.length || par2 >> 4 < 0) { return false; } ExtendedBlockStorage var10 = this.storageArrays[par2 >> 4]; boolean var11 = false; if (var10 == null) { if (par4 == 0) { return false; } var10 = this.storageArrays[par2 >> 4] = new ExtendedBlockStorage(par2 >> 4 << 4, !this.worldObj.provider.hasNoSky); var11 = par2 >= var7; } int var12 = this.xPosition * 16 + par1; int var13 = this.zPosition * 16 + par3; if (var8 != 0 && !this.worldObj.isRemote) { Block.blocksList[var8].onSetBlockIDWithMetaData(this.worldObj, var12, par2, var13, var9); } var10.setExtBlockID(par1, par2 & 15, par3, par4); if (var8 != 0) { if (!this.worldObj.isRemote) { Block.blocksList[var8].breakBlock(this.worldObj, var12, par2, var13, var8, var9); } else if (Block.blocksList[var8] != null && Block.blocksList[var8].hasTileEntity(var9)) { TileEntity te = worldObj.getBlockTileEntity(var12, par2, var13); if (te != null && te.shouldRefresh(var8, par4, var9, par5, worldObj, var12, par2, var13)) { this.worldObj.removeBlockTileEntity(var12, par2, var13); } } } if (var10.getExtBlockID(par1, par2 & 15, par3) != par4) { return false; } else { var10.setExtBlockMetadata(par1, par2 & 15, par3, par5); if (var11) { this.generateSkylightMap(); } else { if (getBlockLightOpacity(par1, par2, par3) > 0) { if (par2 >= var7) { this.relightBlock(par1, par2 + 1, par3); } } else if (par2 == var7 - 1) { this.relightBlock(par1, par2, par3); } this.propagateSkylightOcclusion(par1, par3); } TileEntity var14; if (par4 != 0) { if (!this.worldObj.isRemote) { Block.blocksList[par4].onBlockAdded(this.worldObj, var12, par2, var13); } if (Block.blocksList[par4] != null && Block.blocksList[par4].hasTileEntity(par5)) { var14 = this.getChunkBlockTileEntity(par1, par2, par3); if (var14 == null) { var14 = Block.blocksList[par4].createTileEntity(this.worldObj, par5); this.worldObj.setBlockTileEntity(var12, par2, var13, var14); } if (var14 != null) { var14.updateContainingBlockInfo(); var14.blockMetadata = par5; } } } this.isModified = true; return true; } } } /** * Set the metadata of a block in the chunk */ public boolean setBlockMetadata(int par1, int par2, int par3, int par4) { ExtendedBlockStorage var5 = (par2 >> 4 >= storageArrays.length || par2 >> 4 < 0 ? null : storageArrays[par2 >> 4]); if (var5 == null) { return false; } else { int var6 = var5.getExtBlockMetadata(par1, par2 & 15, par3); if (var6 == par4) { return false; } else { this.isModified = true; var5.setExtBlockMetadata(par1, par2 & 15, par3, par4); int var7 = var5.getExtBlockID(par1, par2 & 15, par3); if (var7 > 0 && Block.blocksList[var7] != null && Block.blocksList[var7].hasTileEntity(par4)) { TileEntity var8 = this.getChunkBlockTileEntity(par1, par2, par3); if (var8 != null) { var8.updateContainingBlockInfo(); var8.blockMetadata = par4; } } return true; } } } /** * Gets the amount of light saved in this block (doesn't adjust for daylight) */ public int getSavedLightValue(EnumSkyBlock par1EnumSkyBlock, int par2, int par3, int par4) { ExtendedBlockStorage var5 = (par3 >> 4 >= storageArrays.length || par3 >> 4 < 0 ? null : storageArrays[par3 >> 4]); return var5 == null ? (this.canBlockSeeTheSky(par2, par3, par4) ? par1EnumSkyBlock.defaultLightValue : 0) : (par1EnumSkyBlock == EnumSkyBlock.Sky ? (this.worldObj.provider.hasNoSky ? 0 : var5.getExtSkylightValue(par2, par3 & 15, par4)) : (par1EnumSkyBlock == EnumSkyBlock.Block ? var5.getExtBlocklightValue(par2, par3 & 15, par4) : par1EnumSkyBlock.defaultLightValue)); } /** * 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 par1EnumSkyBlock, int par2, int par3, int par4, int par5) { if (par3 >> 4 >= storageArrays.length || par3 >> 4 < 0) { return; } ExtendedBlockStorage var6 = this.storageArrays[par3 >> 4]; if (var6 == null) { var6 = this.storageArrays[par3 >> 4] = new ExtendedBlockStorage(par3 >> 4 << 4, !this.worldObj.provider.hasNoSky); this.generateSkylightMap(); } this.isModified = true; if (par1EnumSkyBlock == EnumSkyBlock.Sky) { if (!this.worldObj.provider.hasNoSky) { var6.setExtSkylightValue(par2, par3 & 15, par4, par5); } } else if (par1EnumSkyBlock == EnumSkyBlock.Block) { var6.setExtBlocklightValue(par2, par3 & 15, par4, par5); } } /** * Gets the amount of light on a block taking into account sunlight */ public int getBlockLightValue(int par1, int par2, int par3, int par4) { ExtendedBlockStorage var5 = (par2 >> 4 >= storageArrays.length || par2 >> 4 < 0 ? null : storageArrays[par2 >> 4]); if (var5 == null) { return !this.worldObj.provider.hasNoSky && par4 < EnumSkyBlock.Sky.defaultLightValue ? EnumSkyBlock.Sky.defaultLightValue - par4 : 0; } else { int var6 = this.worldObj.provider.hasNoSky ? 0 : var5.getExtSkylightValue(par1, par2 & 15, par3); if (var6 > 0) { isLit = true; } var6 -= par4; int var7 = var5.getExtBlocklightValue(par1, par2 & 15, par3); if (var7 > var6) { var6 = var7; } return var6; } } /** * Adds an entity to the chunk. Args: entity */ public void addEntity(Entity par1Entity) { this.hasEntities = true; int var2 = MathHelper.floor_double(par1Entity.posX / 16.0D); int var3 = MathHelper.floor_double(par1Entity.posZ / 16.0D); if (var2 != this.xPosition || var3 != this.zPosition) { System.out.println("Wrong location! " + par1Entity); Thread.dumpStack(); } int var4 = MathHelper.floor_double(par1Entity.posY / 16.0D); if (var4 < 0) { var4 = 0; } if (var4 >= this.entityLists.length) { var4 = this.entityLists.length - 1; } MinecraftForge.EVENT_BUS.post(new EntityEvent.EnteringChunk(par1Entity, this.xPosition, this.zPosition, par1Entity.chunkCoordX, par1Entity.chunkCoordZ)); par1Entity.addedToChunk = true; par1Entity.chunkCoordX = this.xPosition; par1Entity.chunkCoordY = var4; par1Entity.chunkCoordZ = this.zPosition; this.entityLists[var4].add(par1Entity); } /** * removes entity using its y chunk coordinate as its index */ public void removeEntity(Entity par1Entity) { this.removeEntityAtIndex(par1Entity, par1Entity.chunkCoordY); } /** * Removes entity at the specified index from the entity array. */ public void removeEntityAtIndex(Entity par1Entity, int par2) { if (par2 < 0) { par2 = 0; } if (par2 >= this.entityLists.length) { par2 = this.entityLists.length - 1; } this.entityLists[par2].remove(par1Entity); } /** * Returns whether is not a block above this one blocking sight to the sky (done via checking against the heightmap) */ public boolean canBlockSeeTheSky(int par1, int par2, int par3) { return par2 >= this.heightMap[par3 << 4 | par1]; } /** * Gets the TileEntity for a given block in this chunk */ public TileEntity getChunkBlockTileEntity(int par1, int par2, int par3) { ChunkPosition var4 = new ChunkPosition(par1, par2, par3); TileEntity var5 = (TileEntity)this.chunkTileEntityMap.get(var4); if (var5 != null && var5.isInvalid()) { chunkTileEntityMap.remove(var4); var5 = null; } if (var5 == null) { int var6 = this.getBlockID(par1, par2, par3); int meta = this.getBlockMetadata(par1, par2, par3); if (var6 <= 0 || !Block.blocksList[var6].hasTileEntity(meta)) { return null; } if (var5 == null) { var5 = Block.blocksList[var6].createTileEntity(this.worldObj, meta); this.worldObj.setBlockTileEntity(this.xPosition * 16 + par1, par2, this.zPosition * 16 + par3, var5); } var5 = (TileEntity)this.chunkTileEntityMap.get(var4); } return var5; } /** * Adds a TileEntity to a chunk */ public void addTileEntity(TileEntity par1TileEntity) { int var2 = par1TileEntity.xCoord - this.xPosition * 16; int var3 = par1TileEntity.yCoord; int var4 = par1TileEntity.zCoord - this.zPosition * 16; this.setChunkBlockTileEntity(var2, var3, var4, par1TileEntity); if (this.isChunkLoaded) { this.worldObj.addTileEntity(par1TileEntity); } } /** * Sets the TileEntity for a given block in this chunk */ public void setChunkBlockTileEntity(int par1, int par2, int par3, TileEntity par4TileEntity) { ChunkPosition var5 = new ChunkPosition(par1, par2, par3); par4TileEntity.setWorldObj(this.worldObj); par4TileEntity.xCoord = this.xPosition * 16 + par1; par4TileEntity.yCoord = par2; par4TileEntity.zCoord = this.zPosition * 16 + par3; Block block = Block.blocksList[getBlockID(par1, par2, par3)]; if (block != null && block.hasTileEntity(getBlockMetadata(par1, par2, par3))) { TileEntity old = (TileEntity)chunkTileEntityMap.get(var5); if (old != null) { old.invalidate(); } par4TileEntity.validate(); this.chunkTileEntityMap.put(var5, par4TileEntity); } } /** * Removes the TileEntity for a given block in this chunk */ public void removeChunkBlockTileEntity(int par1, int par2, int par3) { ChunkPosition var4 = new ChunkPosition(par1, par2, par3); if (this.isChunkLoaded) { TileEntity var5 = (TileEntity)this.chunkTileEntityMap.remove(var4); if (var5 != null) { var5.invalidate(); } } } /** * Called when this Chunk is loaded by the ChunkProvider */ public void onChunkLoad() { this.isChunkLoaded = true; this.worldObj.addTileEntity(this.chunkTileEntityMap.values()); for (int var1 = 0; var1 < this.entityLists.length; ++var1) { this.worldObj.addLoadedEntities(this.entityLists[var1]); } MinecraftForge.EVENT_BUS.post(new ChunkEvent.Load(this)); } /** * Called when this Chunk is unloaded by the ChunkProvider */ public void onChunkUnload() { this.isChunkLoaded = false; Iterator var1 = this.chunkTileEntityMap.values().iterator(); while (var1.hasNext()) { TileEntity var2 = (TileEntity)var1.next(); this.worldObj.markTileEntityForDespawn(var2); } for (int var3 = 0; var3 < this.entityLists.length; ++var3) { this.worldObj.unloadEntities(this.entityLists[var3]); } 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 par1Entity, AxisAlignedBB par2AxisAlignedBB, List par3List) { int var4 = MathHelper.floor_double((par2AxisAlignedBB.minY - World.MAX_ENTITY_RADIUS) / 16.0D); int var5 = MathHelper.floor_double((par2AxisAlignedBB.maxY + World.MAX_ENTITY_RADIUS) / 16.0D); if (var4 < 0) { var4 = 0; } if (var5 >= this.entityLists.length) { var5 = this.entityLists.length - 1; } for (int var6 = var4; var6 <= var5; ++var6) { List var7 = this.entityLists[var6]; for (int var8 = 0; var8 < var7.size(); ++var8) { Entity var9 = (Entity)var7.get(var8); if (var9 != par1Entity && var9.boundingBox.intersectsWith(par2AxisAlignedBB)) { par3List.add(var9); Entity[] var10 = var9.getParts(); if (var10 != null) { for (int var11 = 0; var11 < var10.length; ++var11) { var9 = var10[var11]; if (var9 != par1Entity && var9.boundingBox.intersectsWith(par2AxisAlignedBB)) { par3List.add(var9); } } } } } } } /** * Gets all entities that can be assigned to the specified class. Args: entityClass, aabb, listToFill */ public void getEntitiesOfTypeWithinAAAB(Class par1Class, AxisAlignedBB par2AxisAlignedBB, List par3List, IEntitySelector par4IEntitySelector) { int var5 = MathHelper.floor_double((par2AxisAlignedBB.minY - World.MAX_ENTITY_RADIUS) / 16.0D); int var6 = MathHelper.floor_double((par2AxisAlignedBB.maxY + World.MAX_ENTITY_RADIUS) / 16.0D); if (var5 < 0) { var5 = 0; } else if (var5 >= this.entityLists.length) { var5 = this.entityLists.length - 1; } if (var6 >= this.entityLists.length) { var6 = this.entityLists.length - 1; } else if (var6 < 0) { var6 = 0; } for (int var7 = var5; var7 <= var6; ++var7) { List var8 = this.entityLists[var7]; for (int var9 = 0; var9 < var8.size(); ++var9) { Entity var10 = (Entity)var8.get(var9); if (par1Class.isAssignableFrom(var10.getClass()) && var10.boundingBox.intersectsWith(par2AxisAlignedBB) && (par4IEntitySelector == null || par4IEntitySelector.isEntityApplicable(var10))) { par3List.add(var10); } } } } /** * Returns true if this Chunk needs to be saved */ public boolean needsSaving(boolean par1) { if (par1) { if (this.hasEntities && this.worldObj.getTotalWorldTime() != this.lastSaveTime) { return true; } } else if (this.hasEntities && this.worldObj.getTotalWorldTime() >= this.lastSaveTime + 600L) { return true; } return this.isModified; } public Random getRandomWithSeed(long par1) { 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); } public boolean isEmpty() { return false; } public void populateChunk(IChunkProvider par1IChunkProvider, IChunkProvider par2IChunkProvider, int par3, int par4) { if (!this.isTerrainPopulated && par1IChunkProvider.chunkExists(par3 + 1, par4 + 1) && par1IChunkProvider.chunkExists(par3, par4 + 1) && par1IChunkProvider.chunkExists(par3 + 1, par4)) { par1IChunkProvider.populate(par2IChunkProvider, par3, par4); } 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)) { par1IChunkProvider.populate(par2IChunkProvider, par3 - 1, par4); } 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)) { par1IChunkProvider.populate(par2IChunkProvider, par3, par4 - 1); } if (par1IChunkProvider.chunkExists(par3 - 1, par4 - 1) && !par1IChunkProvider.provideChunk(par3 - 1, par4 - 1).isTerrainPopulated && par1IChunkProvider.chunkExists(par3, par4 - 1) && par1IChunkProvider.chunkExists(par3 - 1, par4)) { par1IChunkProvider.populate(par2IChunkProvider, par3 - 1, par4 - 1); } } /** * Gets the height to which rain/snow will fall. Calculates it if not already stored. */ public int getPrecipitationHeight(int par1, int par2) { int var3 = par1 | par2 << 4; int var4 = this.precipitationHeightMap[var3]; if (var4 == -999) { int var5 = this.getTopFilledSegment() + 15; var4 = -1; while (var5 > 0 && var4 == -1) { int var6 = this.getBlockID(par1, var5, par2); Material var7 = var6 == 0 ? Material.air : Block.blocksList[var6].blockMaterial; if (!var7.blocksMovement() && !var7.isLiquid()) { --var5; } else { var4 = var5 + 1; } } this.precipitationHeightMap[var3] = var4; } return var4; } /** * Checks whether skylight needs updated; if it does, calls updateSkylight_do */ public void updateSkylight() { if (this.isGapLightingUpdated && !this.worldObj.provider.hasNoSky) { this.updateSkylight_do(); } } /** * 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 par1, int par2) { if (par1 < 0) { par1 = 0; } if (par2 >= 256) { par2 = 255; } for (int var3 = par1; var3 <= par2; var3 += 16) { ExtendedBlockStorage var4 = this.storageArrays[var3 >> 4]; if (var4 != null && !var4.isEmpty()) { return false; } } return true; } public void setStorageArrays(ExtendedBlockStorage[] par1ArrayOfExtendedBlockStorage) { this.storageArrays = par1ArrayOfExtendedBlockStorage; } @SideOnly(Side.CLIENT) /** * Initialise this chunk with new binary data */ public void fillChunk(byte[] par1ArrayOfByte, int par2, int par3, boolean par4) { Iterator iterator = chunkTileEntityMap.values().iterator(); while(iterator.hasNext()) { TileEntity tileEntity = (TileEntity)iterator.next(); tileEntity.updateContainingBlockInfo(); tileEntity.getBlockMetadata(); tileEntity.getBlockType(); } int var5 = 0; boolean var6 = !this.worldObj.provider.hasNoSky; int var7; for (var7 = 0; var7 < this.storageArrays.length; ++var7) { if ((par2 & 1 << var7) != 0) { if (this.storageArrays[var7] == null) { this.storageArrays[var7] = new ExtendedBlockStorage(var7 << 4, var6); } byte[] var8 = this.storageArrays[var7].getBlockLSBArray(); System.arraycopy(par1ArrayOfByte, var5, var8, 0, var8.length); var5 += var8.length; } else if (par4 && this.storageArrays[var7] != null) { this.storageArrays[var7] = null; } } NibbleArray var9; for (var7 = 0; var7 < this.storageArrays.length; ++var7) { if ((par2 & 1 << var7) != 0 && this.storageArrays[var7] != null) { var9 = this.storageArrays[var7].getMetadataArray(); System.arraycopy(par1ArrayOfByte, var5, var9.data, 0, var9.data.length); var5 += var9.data.length; } } for (var7 = 0; var7 < this.storageArrays.length; ++var7) { if ((par2 & 1 << var7) != 0 && this.storageArrays[var7] != null) { var9 = this.storageArrays[var7].getBlocklightArray(); System.arraycopy(par1ArrayOfByte, var5, var9.data, 0, var9.data.length); var5 += var9.data.length; } } if (var6) { for (var7 = 0; var7 < this.storageArrays.length; ++var7) { if ((par2 & 1 << var7) != 0 && this.storageArrays[var7] != null) { var9 = this.storageArrays[var7].getSkylightArray(); System.arraycopy(par1ArrayOfByte, var5, var9.data, 0, var9.data.length); var5 += var9.data.length; } } } for (var7 = 0; var7 < this.storageArrays.length; ++var7) { if ((par3 & 1 << var7) != 0) { if (this.storageArrays[var7] == null) { var5 += 2048; } else { var9 = this.storageArrays[var7].getBlockMSBArray(); if (var9 == null) { var9 = this.storageArrays[var7].createBlockMSBArray(); } System.arraycopy(par1ArrayOfByte, var5, var9.data, 0, var9.data.length); var5 += var9.data.length; } } else if (par4 && this.storageArrays[var7] != null && this.storageArrays[var7].getBlockMSBArray() != null) { this.storageArrays[var7].clearMSBArray(); } } if (par4) { System.arraycopy(par1ArrayOfByte, var5, this.blockBiomeArray, 0, this.blockBiomeArray.length); int var10000 = var5 + this.blockBiomeArray.length; } for (var7 = 0; var7 < this.storageArrays.length; ++var7) { if (this.storageArrays[var7] != null && (par2 & 1 << var7) != 0) { this.storageArrays[var7].removeInvalidBlocks(); } } this.generateHeightMap(); List<TileEntity> invalidList = new ArrayList<TileEntity>(); iterator = 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 == null || block.blockID != getBlockID(x, y, z) || tileEntity.getBlockMetadata() != getBlockMetadata(x, y, z)) { invalidList.add(tileEntity); } tileEntity.updateContainingBlockInfo(); } for (TileEntity tileEntity : invalidList) { tileEntity.invalidate(); } } /** * This method retrieves the biome at a set of coordinates */ public BiomeGenBase getBiomeGenForWorldCoords(int par1, int par2, WorldChunkManager par3WorldChunkManager) { int var4 = this.blockBiomeArray[par2 << 4 | par1] & 255; if (var4 == 255) { BiomeGenBase var5 = par3WorldChunkManager.getBiomeGenAt((this.xPosition << 4) + par1, (this.zPosition << 4) + par2); var4 = var5.biomeID; this.blockBiomeArray[par2 << 4 | par1] = (byte)(var4 & 255); } return BiomeGenBase.biomeList[var4] == null ? BiomeGenBase.plains : BiomeGenBase.biomeList[var4]; } /** * 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[] par1ArrayOfByte) { this.blockBiomeArray = par1ArrayOfByte; } /** * 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 per-storage-block by up to 8 * blocks at a time. In a worst-case scenario, can potentially take up to 1.6 seconds, calculated via * (4096/(8*16))/20, to re-check all blocks in a chunk, which could explain both lagging light updates in certain * cases as well as Nether relight */ public void enqueueRelightChecks() { for (int var1 = 0; var1 < 8; ++var1) { if (this.queuedLightChecks >= 4096) { return; } int var2 = this.queuedLightChecks % 16; int var3 = this.queuedLightChecks / 16 % 16; int var4 = this.queuedLightChecks / 256; ++this.queuedLightChecks; int var5 = (this.xPosition << 4) + var3; int var6 = (this.zPosition << 4) + var4; for (int var7 = 0; var7 < 16; ++var7) { int var8 = (var2 << 4) + var7; if (this.storageArrays[var2] == null && (var7 == 0 || var7 == 15 || var3 == 0 || var3 == 15 || var4 == 0 || var4 == 15) || this.storageArrays[var2] != null && this.storageArrays[var2].getExtBlockID(var3, var7, var4) == 0) { if (Block.lightValue[this.worldObj.getBlockId(var5, var8 - 1, var6)] > 0) { this.worldObj.updateAllLightTypes(var5, var8 - 1, var6); } if (Block.lightValue[this.worldObj.getBlockId(var5, var8 + 1, var6)] > 0) { this.worldObj.updateAllLightTypes(var5, var8 + 1, var6); } if (Block.lightValue[this.worldObj.getBlockId(var5 - 1, var8, var6)] > 0) { this.worldObj.updateAllLightTypes(var5 - 1, var8, var6); } if (Block.lightValue[this.worldObj.getBlockId(var5 + 1, var8, var6)] > 0) { this.worldObj.updateAllLightTypes(var5 + 1, var8, var6); } if (Block.lightValue[this.worldObj.getBlockId(var5, var8, var6 - 1)] > 0) { this.worldObj.updateAllLightTypes(var5, var8, var6 - 1); } if (Block.lightValue[this.worldObj.getBlockId(var5, var8, var6 + 1)] > 0) { this.worldObj.updateAllLightTypes(var5, var8, var6 + 1); } this.worldObj.updateAllLightTypes(var5, var8, var6); } } } } /** FORGE: Used to remove only invalid TileEntities */ public void cleanChunkBlockTileEntity(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); } } } }