    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.crash.CrashReportCategory;
    012import net.minecraft.nbt.NBTTagCompound;
    013import net.minecraft.network.INetworkManager;
    014import net.minecraft.network.packet.Packet;
    015import net.minecraft.network.packet.Packet132TileEntityData;
    016import net.minecraft.server.MinecraftServer;
    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    /**
    066     * Returns the worldObj for this tileEntity.
    067     */
    068    public World getWorldObj()
    069    {
    070        return this.worldObj;
    071    }
    072
    073    /**
    074     * Sets the worldObj for this tileEntity.
    075     */
    076    public void setWorldObj(World par1World)
    077    {
    078        this.worldObj = par1World;
    079    }
    080
    081    public boolean func_70309_m()
    082    {
    083        return this.worldObj != null;
    084    }
    085
    086    /**
    087     * Reads a tile entity from NBT.
    088     */
    089    public void readFromNBT(NBTTagCompound par1NBTTagCompound)
    090    {
    091        this.xCoord = par1NBTTagCompound.getInteger("x");
    092        this.yCoord = par1NBTTagCompound.getInteger("y");
    093        this.zCoord = par1NBTTagCompound.getInteger("z");
    094    }
    095
    096    /**
    097     * Writes a tile entity to NBT.
    098     */
    099    public void writeToNBT(NBTTagCompound par1NBTTagCompound)
    100    {
    101        String s = (String)classToNameMap.get(this.getClass());
    102
    103        if (s == null)
    104        {
    105            throw new RuntimeException(this.getClass() + " is missing a mapping! This is a bug!");
    106        }
    107        else
    108        {
    109            par1NBTTagCompound.setString("id", s);
    110            par1NBTTagCompound.setInteger("x", this.xCoord);
    111            par1NBTTagCompound.setInteger("y", this.yCoord);
    112            par1NBTTagCompound.setInteger("z", this.zCoord);
    113        }
    114    }
    115
    116    /**
    117     * Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count
    118     * ticks and creates a new spawn inside its implementation.
    119     */
    120    public void updateEntity() {}
    121
    122    /**
    123     * Creates a new entity and loads its data from the specified NBT.
    124     */
    125    public static TileEntity createAndLoadEntity(NBTTagCompound par0NBTTagCompound)
    126    {
    127        TileEntity tileentity = null;
    128
    129        Class oclass = null;
    130
    131        try
    132        {
    133            oclass = (Class)nameToClassMap.get(par0NBTTagCompound.getString("id"));
    134
    135            if (oclass != null)
    136            {
    137                tileentity = (TileEntity)oclass.newInstance();
    138            }
    139        }
    140        catch (Exception exception)
    141        {
    142            exception.printStackTrace();
    143        }
    144
    145        if (tileentity != null)
    146        {
    147            try
    148            {
    149                tileentity.readFromNBT(par0NBTTagCompound);
    150            }
    151            catch (Exception e)
    152            {
    153                FMLLog.log(Level.SEVERE, e,
    154                        "A TileEntity %s(%s) has thrown an exception during loading, its state cannot be restored. Report this to the mod author",
    155                        par0NBTTagCompound.getString("id"), oclass.getName());
    156                tileentity = null;
    157            }
    158        }
    159        else
    160        {
    161            MinecraftServer.getServer().func_98033_al().func_98236_b("Skipping TileEntity with id " + par0NBTTagCompound.getString("id"));
    162        }
    163
    164        return tileentity;
    165    }
    166
    167    /**
    168     * Returns block data at the location of this entity (client-only).
    169     */
    170    public int getBlockMetadata()
    171    {
    172        if (this.blockMetadata == -1)
    173        {
    174            this.blockMetadata = this.worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord);
    175        }
    176
    177        return this.blockMetadata;
    178    }
    179
    180    /**
    181     * Called when an the contents of an Inventory change, usually
    182     */
    183    public void onInventoryChanged()
    184    {
    185        if (this.worldObj != null)
    186        {
    187            this.blockMetadata = this.worldObj.getBlockMetadata(this.xCoord, this.yCoord, this.zCoord);
    188            this.worldObj.updateTileEntityChunkAndDoNothing(this.xCoord, this.yCoord, this.zCoord, this);
    189
    190            if (this.getBlockType() != null)
    191            {
    192                this.worldObj.func_96440_m(this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID);
    193            }
    194        }
    195    }
    196
    197    @SideOnly(Side.CLIENT)
    198
    199    /**
    200     * Returns the square of the distance between this entity and the passed in coordinates.
    201     */
    202    public double getDistanceFrom(double par1, double par3, double par5)
    203    {
    204        double d3 = (double)this.xCoord + 0.5D - par1;
    205        double d4 = (double)this.yCoord + 0.5D - par3;
    206        double d5 = (double)this.zCoord + 0.5D - par5;
    207        return d3 * d3 + d4 * d4 + d5 * d5;
    208    }
    209
    210    @SideOnly(Side.CLIENT)
    211    public double func_82115_m()
    212    {
    213        return 4096.0D;
    214    }
    215
    216    /**
    217     * Gets the block type at the location of this entity (client-only).
    218     */
    219    public Block getBlockType()
    220    {
    221        if (this.blockType == null)
    222        {
    223            this.blockType = Block.blocksList[this.worldObj.getBlockId(this.xCoord, this.yCoord, this.zCoord)];
    224        }
    225
    226        return this.blockType;
    227    }
    228
    229    /**
    230     * Overriden in a sign to provide the text.
    231     */
    232    public Packet getDescriptionPacket()
    233    {
    234        return null;
    235    }
    236
    237    /**
    238     * returns true if tile entity is invalid, false otherwise
    239     */
    240    public boolean isInvalid()
    241    {
    242        return this.tileEntityInvalid;
    243    }
    244
    245    /**
    246     * invalidates a tile entity
    247     */
    248    public void invalidate()
    249    {
    250        this.tileEntityInvalid = true;
    251    }
    252
    253    /**
    254     * validates a tile entity
    255     */
    256    public void validate()
    257    {
    258        this.tileEntityInvalid = false;
    259    }
    260
    261    /**
    262     * Called when a client event is received with the event number and argument, see World.sendClientEvent
    263     */
    264    public boolean receiveClientEvent(int par1, int par2)
    265    {
    266        return false;
    267    }
    268
    269    /**
    270     * Causes the TileEntity to reset all it's cached values for it's container block, blockID, metaData and in the case
    271     * of chests, the adjcacent chest check
    272     */
    273    public void updateContainingBlockInfo()
    274    {
    275        this.blockType = null;
    276        this.blockMetadata = -1;
    277    }
    278
    279    public void func_85027_a(CrashReportCategory par1CrashReportCategory)
    280    {
    281        par1CrashReportCategory.addCrashSectionCallable("Name", new CallableTileEntityName(this));
    282        CrashReportCategory.func_85068_a(par1CrashReportCategory, this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID, this.getBlockMetadata());
    283        par1CrashReportCategory.addCrashSectionCallable("Actual block type", new CallableTileEntityID(this));
    284        par1CrashReportCategory.addCrashSectionCallable("Actual block data value", new CallableTileEntityData(this));
    285    }
    286
    287    static Map getClassToNameMap()
    288    {
    289        return classToNameMap;
    290    }
    291
    292    static
    293    {
    294        addMapping(TileEntityFurnace.class, "Furnace");
    295        addMapping(TileEntityChest.class, "Chest");
    296        addMapping(TileEntityEnderChest.class, "EnderChest");
    297        addMapping(TileEntityRecordPlayer.class, "RecordPlayer");
    298        addMapping(TileEntityDispenser.class, "Trap");
    299        addMapping(TileEntityDropper.class, "Dropper");
    300        addMapping(TileEntitySign.class, "Sign");
    301        addMapping(TileEntityMobSpawner.class, "MobSpawner");
    302        addMapping(TileEntityNote.class, "Music");
    303        addMapping(TileEntityPiston.class, "Piston");
    304        addMapping(TileEntityBrewingStand.class, "Cauldron");
    305        addMapping(TileEntityEnchantmentTable.class, "EnchantTable");
    306        addMapping(TileEntityEndPortal.class, "Airportal");
    307        addMapping(TileEntityCommandBlock.class, "Control");
    308        addMapping(TileEntityBeacon.class, "Beacon");
    309        addMapping(TileEntitySkull.class, "Skull");
    310        addMapping(TileEntityDaylightDetector.class, "DLDetector");
    311        addMapping(TileEntityHopper.class, "Hopper");
    312        addMapping(TileEntityComparator.class, "Comparator");
    313    }
    314
    315    // -- BEGIN FORGE PATCHES --
    316    /**
    317     * Determines if this TileEntity requires update calls.
    318     * @return True if you want updateEntity() to be called, false if not
    319     */
    320    public boolean canUpdate()
    321    {
    322        return true;
    323    }
    324
    325    /**
    326     * Called when you receive a TileEntityData packet for the location this
    327     * TileEntity is currently in. On the client, the NetworkManager will always
    328     * be the remote server. On the server, it will be whomever is responsible for
    329     * sending the packet.
    330     *
    331     * @param net The NetworkManager the packet originated from
    332     * @param pkt The data packet
    333     */
    334    public void onDataPacket(INetworkManager net, Packet132TileEntityData pkt)
    335    {
    336    }
    337
    338    /**
    339     * Called when the chunk this TileEntity is on is Unloaded.
    340     */
    341    public void onChunkUnload()
    342    {
    343    }
    344
    345    /**
    346     * Called from Chunk.setBlockIDWithMetadata, determines if this tile entity should be re-created when the ID, or Metadata changes.
    347     * Use with caution as this will leave straggler TileEntities, or create conflicts with other TileEntities if not used properly.
    348     *
    349     * @param oldID The old ID of the block
    350     * @param newID The new ID of the block (May be the same)
    351     * @param oldMeta The old metadata of the block
    352     * @param newMeta The new metadata of the block (May be the same)
    353     * @param world Current world
    354     * @param x X Postion
    355     * @param y Y Position
    356     * @param z Z Position
    357     * @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}
    358     */
    359    public boolean shouldRefresh(int oldID, int newID, int oldMeta, int newMeta, World world, int x, int y, int z)
    360    {
    361        return true;
    362    }
    363
    364    public boolean shouldRenderInPass(int pass)
    365    {
    366        return pass == 0;
    367    }
    368    /**
    369     * Sometimes default render bounding box: infinite in scope. Used to control rendering on {@link TileEntitySpecialRenderer}.
    370     */
    371    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);
    372
    373    /**
    374     * Return an {@link AxisAlignedBB} that controls the visible scope of a {@link TileEntitySpecialRenderer} associated with this {@link TileEntity}
    375     * Defaults to the collision bounding box {@link Block#getCollisionBoundingBoxFromPool(World, int, int, int)} associated with the block
    376     * at this location.
    377     *
    378     * @return an appropriately size {@link AxisAlignedBB} for the {@link TileEntity}
    379     */
    380    @SideOnly(Side.CLIENT)
    381    public AxisAlignedBB getRenderBoundingBox()
    382    {
    383        AxisAlignedBB bb = INFINITE_EXTENT_AABB;
    384        Block type = getBlockType();
    385        if (type == Block.enchantmentTable)
    386        {
    387            bb = AxisAlignedBB.getAABBPool().getAABB(xCoord, yCoord, zCoord, xCoord + 1, yCoord + 1, zCoord + 1);
    388        }
    389        else if (type == Block.chest)
    390        {
    391            bb = AxisAlignedBB.getAABBPool().getAABB(xCoord - 1, yCoord, zCoord - 1, xCoord + 2, yCoord + 2, zCoord + 2);
    392        }
    393        else if (type != null && type != Block.beacon)
    394        {
    395            AxisAlignedBB cbb = getBlockType().getCollisionBoundingBoxFromPool(worldObj, xCoord, yCoord, zCoord);
    396            if (cbb != null)
    397            {
    398                bb = cbb;
    399            }
    400        }
    401        return bb;
    402    }
    403}