001package net.minecraft.tileentity; 002 003import cpw.mods.fml.common.FMLLog; 004import cpw.mods.fml.relauncher.Side; 005import cpw.mods.fml.relauncher.SideOnly; 006import java.util.HashMap; 007import java.util.Map; 008import java.util.logging.Level; 009 010import net.minecraft.block.Block; 011import net.minecraft.block.TileEntityRecordPlayer; 012import net.minecraft.crash.CrashReportCategory; 013import net.minecraft.nbt.NBTTagCompound; 014import net.minecraft.network.INetworkManager; 015import net.minecraft.network.packet.Packet; 016import net.minecraft.network.packet.Packet132TileEntityData; 017import net.minecraft.util.AxisAlignedBB; 018import net.minecraft.world.World; 019 020public class TileEntity 021{ 022 /** 023 * A HashMap storing string names of classes mapping to the actual java.lang.Class type. 024 */ 025 private static Map nameToClassMap = new HashMap(); 026 027 /** 028 * A HashMap storing the classes and mapping to the string names (reverse of nameToClassMap). 029 */ 030 private static Map classToNameMap = new HashMap(); 031 032 /** The reference to the world. */ 033 public World worldObj; 034 035 /** The x coordinate of the tile entity. */ 036 public int xCoord; 037 038 /** The y coordinate of the tile entity. */ 039 public int yCoord; 040 041 /** The z coordinate of the tile entity. */ 042 public int zCoord; 043 protected boolean tileEntityInvalid; 044 public int blockMetadata = -1; 045 046 /** the Block type that this TileEntity is contained within */ 047 public Block blockType; 048 049 /** 050 * Adds a new two-way mapping between the class and its string name in both hashmaps. 051 */ 052 public static void addMapping(Class par0Class, String par1Str) 053 { 054 if (nameToClassMap.containsKey(par1Str)) 055 { 056 throw new IllegalArgumentException("Duplicate id: " + par1Str); 057 } 058 else 059 { 060 nameToClassMap.put(par1Str, par0Class); 061 classToNameMap.put(par0Class, par1Str); 062 } 063 } 064 065 @SideOnly(Side.CLIENT) 066 067 /** 068 * Returns the worldObj for this tileEntity. 069 */ 070 public World getWorldObj() 071 { 072 return this.worldObj; 073 } 074 075 /** 076 * Sets the worldObj for this tileEntity. 077 */ 078 public void setWorldObj(World par1World) 079 { 080 this.worldObj = par1World; 081 } 082 083 public boolean func_70309_m() 084 { 085 return this.worldObj != null; 086 } 087 088 /** 089 * Reads a tile entity from NBT. 090 */ 091 public void readFromNBT(NBTTagCompound par1NBTTagCompound) 092 { 093 this.xCoord = par1NBTTagCompound.getInteger("x"); 094 this.yCoord = par1NBTTagCompound.getInteger("y"); 095 this.zCoord = par1NBTTagCompound.getInteger("z"); 096 } 097 098 /** 099 * Writes a tile entity to NBT. 100 */ 101 public void writeToNBT(NBTTagCompound par1NBTTagCompound) 102 { 103 String var2 = (String)classToNameMap.get(this.getClass()); 104 105 if (var2 == null) 106 { 107 throw new RuntimeException(this.getClass() + " is missing a mapping! This is a bug!"); 108 } 109 else 110 { 111 par1NBTTagCompound.setString("id", var2); 112 par1NBTTagCompound.setInteger("x", this.xCoord); 113 par1NBTTagCompound.setInteger("y", this.yCoord); 114 par1NBTTagCompound.setInteger("z", this.zCoord); 115 } 116 } 117 118 /** 119 * Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count 120 * ticks and creates a new spawn inside its implementation. 121 */ 122 public void updateEntity() {} 123 124 /** 125 * Creates a new entity and loads its data from the specified NBT. 126 */ 127 public static TileEntity createAndLoadEntity(NBTTagCompound par0NBTTagCompound) 128 { 129 TileEntity var1 = null; 130 131 Class var2 = null; 132 133 try 134 { 135 var2 = (Class)nameToClassMap.get(par0NBTTagCompound.getString("id")); 136 137 if (var2 != null) 138 { 139 var1 = (TileEntity)var2.newInstance(); 140 } 141 } 142 catch (Exception var3) 143 { 144 var3.printStackTrace(); 145 } 146 147 if (var1 != null) 148 { 149 try 150 { 151 var1.readFromNBT(par0NBTTagCompound); 152 } 153 catch (Exception e) 154 { 155 FMLLog.log(Level.SEVERE, e, 156 "A TileEntity %s(%s) has thrown an exception during loading, its state cannot be restored. Report this to the mod author", 157 par0NBTTagCompound.getString("id"), var2.getName()); 158 var1 = null; 159 } 160 } 161 else 162 { 163 System.out.println("Skipping TileEntity with id " + par0NBTTagCompound.getString("id")); 164 } 165 166 return var1; 167 } 168 169 /** 170 * Returns block data at the location of this entity (client-only). 171 */ 172 public int getBlockMetadata() 173 { 174 if (this.blockMetadata == -1) 175 { 176 this.blockMetadata = this.worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord); 177 } 178 179 return this.blockMetadata; 180 } 181 182 /** 183 * Called when an the contents of an Inventory change, usually 184 */ 185 public void onInventoryChanged() 186 { 187 if (this.worldObj != null) 188 { 189 this.blockMetadata = this.worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord); 190 this.worldObj.updateTileEntityChunkAndDoNothing(this.xCoord, this.yCoord, this.zCoord, this); 191 } 192 } 193 194 @SideOnly(Side.CLIENT) 195 196 /** 197 * Returns the square of the distance between this entity and the passed in coordinates. 198 */ 199 public double getDistanceFrom(double par1, double par3, double par5) 200 { 201 double var7 = (double)this.xCoord + 0.5D - par1; 202 double var9 = (double)this.yCoord + 0.5D - par3; 203 double var11 = (double)this.zCoord + 0.5D - par5; 204 return var7 * var7 + var9 * var9 + var11 * var11; 205 } 206 207 @SideOnly(Side.CLIENT) 208 public double func_82115_m() 209 { 210 return 4096.0D; 211 } 212 213 /** 214 * Gets the block type at the location of this entity (client-only). 215 */ 216 public Block getBlockType() 217 { 218 if (this.blockType == null) 219 { 220 this.blockType = Block.blocksList[this.worldObj.getBlockId(this.xCoord, this.yCoord, this.zCoord)]; 221 } 222 223 return this.blockType; 224 } 225 226 /** 227 * Overriden in a sign to provide the text. 228 */ 229 public Packet getDescriptionPacket() 230 { 231 return null; 232 } 233 234 /** 235 * returns true if tile entity is invalid, false otherwise 236 */ 237 public boolean isInvalid() 238 { 239 return this.tileEntityInvalid; 240 } 241 242 /** 243 * invalidates a tile entity 244 */ 245 public void invalidate() 246 { 247 this.tileEntityInvalid = true; 248 } 249 250 /** 251 * validates a tile entity 252 */ 253 public void validate() 254 { 255 this.tileEntityInvalid = false; 256 } 257 258 /** 259 * Called when a client event is received with the event number and argument, see World.sendClientEvent 260 */ 261 public void receiveClientEvent(int par1, int par2) {} 262 263 /** 264 * Causes the TileEntity to reset all it's cached values for it's container block, blockID, metaData and in the case 265 * of chests, the adjcacent chest check 266 */ 267 public void updateContainingBlockInfo() 268 { 269 this.blockType = null; 270 this.blockMetadata = -1; 271 } 272 273 public void func_85027_a(CrashReportCategory par1CrashReportCategory) 274 { 275 par1CrashReportCategory.addCrashSectionCallable("Name", new CallableTileEntityName(this)); 276 CrashReportCategory.func_85068_a(par1CrashReportCategory, this.xCoord, this.yCoord, this.zCoord, this.blockType != null ? this.blockType.blockID : 0, this.blockMetadata); 277 } 278 279 static Map func_85028_t() 280 { 281 return classToNameMap; 282 } 283 284 static 285 { 286 addMapping(TileEntityFurnace.class, "Furnace"); 287 addMapping(TileEntityChest.class, "Chest"); 288 addMapping(TileEntityEnderChest.class, "EnderChest"); 289 addMapping(TileEntityRecordPlayer.class, "RecordPlayer"); 290 addMapping(TileEntityDispenser.class, "Trap"); 291 addMapping(TileEntitySign.class, "Sign"); 292 addMapping(TileEntityMobSpawner.class, "MobSpawner"); 293 addMapping(TileEntityNote.class, "Music"); 294 addMapping(TileEntityPiston.class, "Piston"); 295 addMapping(TileEntityBrewingStand.class, "Cauldron"); 296 addMapping(TileEntityEnchantmentTable.class, "EnchantTable"); 297 addMapping(TileEntityEndPortal.class, "Airportal"); 298 addMapping(TileEntityCommandBlock.class, "Control"); 299 addMapping(TileEntityBeacon.class, "Beacon"); 300 addMapping(TileEntitySkull.class, "Skull"); 301 } 302 303 // -- BEGIN FORGE PATCHES -- 304 /** 305 * Determines if this TileEntity requires update calls. 306 * @return True if you want updateEntity() to be called, false if not 307 */ 308 public boolean canUpdate() 309 { 310 return true; 311 } 312 313 /** 314 * Called when you receive a TileEntityData packet for the location this 315 * TileEntity is currently in. On the client, the NetworkManager will always 316 * be the remote server. On the server, it will be whomever is responsible for 317 * sending the packet. 318 * 319 * @param net The NetworkManager the packet originated from 320 * @param pkt The data packet 321 */ 322 public void onDataPacket(INetworkManager net, Packet132TileEntityData pkt) 323 { 324 } 325 326 /** 327 * Called when the chunk this TileEntity is on is Unloaded. 328 */ 329 public void onChunkUnload() 330 { 331 } 332 333 /** 334 * Called from Chunk.setBlockIDWithMetadata, determines if this tile entity should be re-created when the ID, or Metadata changes. 335 * Use with caution as this will leave straggler TileEntities, or create conflicts with other TileEntities if not used properly. 336 * 337 * @param oldID The old ID of the block 338 * @param newID The new ID of the block (May be the same) 339 * @param oldMeta The old metadata of the block 340 * @param newMeta The new metadata of the block (May be the same) 341 * @param world Current world 342 * @param x X Postion 343 * @param y Y Position 344 * @param z Z Position 345 * @return True to remove the old tile entity, false to keep it in tact {and create a new one if the new values specify to} 346 */ 347 public boolean shouldRefresh(int oldID, int newID, int oldMeta, int newMeta, World world, int x, int y, int z) 348 { 349 return true; 350 } 351 352 public boolean shouldRenderInPass(int pass) 353 { 354 return pass == 0; 355 } 356 /** 357 * Sometimes default render bounding box: infinite in scope. Used to control rendering on {@link TileEntitySpecialRenderer}. 358 */ 359 public static final AxisAlignedBB INFINITE_EXTENT_AABB = AxisAlignedBB.getBoundingBox(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); 360 361 /** 362 * Return an {@link AxisAlignedBB} that controls the visible scope of a {@link TileEntitySpecialRenderer} associated with this {@link TileEntity} 363 * Defaults to the collision bounding box {@link Block#getCollisionBoundingBoxFromPool(World, int, int, int)} associated with the block 364 * at this location. 365 * 366 * @return an appropriately size {@link AxisAlignedBB} for the {@link TileEntity} 367 */ 368 @SideOnly(Side.CLIENT) 369 public AxisAlignedBB getRenderBoundingBox() 370 { 371 AxisAlignedBB bb = INFINITE_EXTENT_AABB; 372 Block type = getBlockType(); 373 if (type == Block.enchantmentTable) 374 { 375 bb = AxisAlignedBB.getAABBPool().addOrModifyAABBInPool(xCoord, yCoord, zCoord, xCoord + 1, yCoord + 1, zCoord + 1); 376 } 377 else if (type == Block.chest) 378 { 379 bb = AxisAlignedBB.getAABBPool().addOrModifyAABBInPool(xCoord - 1, yCoord, zCoord - 1, xCoord + 2, yCoord + 1, zCoord + 2); 380 } 381 else if (type != null && type != Block.beacon) 382 { 383 AxisAlignedBB cbb = getBlockType().getCollisionBoundingBoxFromPool(worldObj, xCoord, yCoord, zCoord); 384 if (cbb != null) 385 { 386 bb = cbb; 387 } 388 } 389 return bb; 390 } 391}