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