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