    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}