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}