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