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