001    package net.minecraft.tileentity;
002    
003    import cpw.mods.fml.common.FMLLog;
004    import cpw.mods.fml.relauncher.Side;
005    import cpw.mods.fml.relauncher.SideOnly;
006    import java.util.HashMap;
007    import java.util.Map;
008    import java.util.logging.Level;
009    
010    import net.minecraft.block.Block;
011    import net.minecraft.block.TileEntityRecordPlayer;
012    import net.minecraft.crash.CrashReportCategory;
013    import net.minecraft.nbt.NBTTagCompound;
014    import net.minecraft.network.INetworkManager;
015    import net.minecraft.network.packet.Packet;
016    import net.minecraft.network.packet.Packet132TileEntityData;
017    import net.minecraft.world.World;
018    
019    public 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    }