Advanced Modding - Tile Entities

Goal

I want to show you how to add a Tile Entity to a block.

Difficulty

3/10 - Relatively Easy

Prerequisites

Forge Version

This Tutorial was created with Forge 11.14.1.1334 for Minecraft 1.8. If anything doesn't work with other versions, please contact me!


Adding a TileEntity to a block

A TileEntity is an addition to a block which has two major additional features a block does not have:

  • The ability to store additional data, not only the block's metadata
  • The ability to be updated every tick (20 times in a second)

Also, the TileEntity can have a special renderer which allows it to be rendered dynamically depending on its current data. Sometimes, TileEntities are only used to apply a special renderer to them, for instance for animations.


Adding a TileEntity to a block is rather simple. There are two different possibilities:

  • The block needs to extend BlockContainer
  • Or the block needs to implement ITileEntityProvider and override a few methods.

The first one is the easier one. A block class extending BlockContainer can look like this:

ModBlockTileEntity.java:
public class ModBlockTileEntity extends BlockContainer {

    protected ModBlockTileEntity(String unlocalizedName) {
        super(Material.iron);
        this.setUnlocalizedName(unlocalizedName);
        this.setHardness(2.0f);
        this.setResistance(6.0f);
        this.setHarvestLevel("pickaxe", 2);
    }

    @Override
    public TileEntity createNewTileEntity(World worldIn, int meta) {
        return new ModTileEntity();
    }
}

This class contains a standard constructor which should be known since the first block tutorial.

Also, the class overrides the abstract method createNewTileEntity. This method is called whenever a new TileEntity needs to be created for the block. This happens either when a new block is placed or when the world is loaded. This method just needs to return a new instance of the TileEntity, in this case an instance of ModTileEntity (will be created in a second!).


The other possibility to add a TileEntity to a block is by implementing the interface ITileEntityProvider and adding a few methods, basically those which can be found in the BlockContainer class).

ModBlockTileEntity.java:
public class ModBlockTileEntity extends Block implements ITileEntityProvider {

    protected ModBlockTileEntity(String unlocalizedName) {
        super(Material.iron);
        this.setUnlocalizedName(unlocalizedName);
        this.setHardness(2.0f);
        this.setResistance(6.0f);
        this.setHarvestLevel("pickaxe", 2);
        this.isBlockContainer = true;
    }

    @Override
    public TileEntity createNewTileEntity(World worldIn, int meta) {
        return new ModTileEntity();
    }

    @Override
    public void breakBlock(World world, BlockPos pos, IBlockState state) {
        super.breakBlock(world, pos, state);
        world.removeTileEntity(pos);
    }

    @Override
    public boolean onBlockEventReceived(World worldIn, BlockPos pos, IBlockState state, int eventID, int eventParam) {
        super.onBlockEventReceived(worldIn, pos, state, eventID, eventParam);
        TileEntity tileentity = worldIn.getTileEntity(pos);
        return tileentity == null ? false : tileentity.receiveClientEvent(eventID, eventParam);
    }
}

Most important here is to set the protected variable isBlockContainer to true when creating the block.

The method createNewTileEntity is needed here as well, so we need to override it. The other two methods are copied from BlockContainer. The breakBlock method performs the cleanup when the block is destoryed. The other one passes block events to the TileEntity. Block events are useful if you want to synchronize data with the client. This is done for instance with the vanilla chests which open when a player uses them.

 

No matter which of the two techniques is used, now we have a block providing a TileEntity. One important thing is left though. The class BlockContainer overrides the method getRenderType. This method returns an integer value defining how the block is rendered. the method in BlockContainer returns -1, which disables every rendering except the TileEntity special renderers. If we want to use the normal block models, we need to override the method again and return 3. On the other hand, if we use the method with the implementation of the interface and want to use the TileEntity special renderer, we need to override the method there and return -1 or 2.

 

The next step is creating our TileEntity. This can be any class that extends TileEntity. I usually place my TileEntities in the package com.<myname>.<mymodname>.tileentity.

ModTileEntity.java:
public class ModTileEntity extends TileEntity {

}

This TileEntity now needs to be registered. Therefore, you can either create a new class (named ModTileEntities or similar) or add a few lines to your ModBlocks class.

Here, I'll create a new class looking like this:

ModTileEntities.java:
package com.bedrockminer.tutorial.tileentity;

import net.minecraftforge.fml.common.registry.GameRegistry;

public final class ModTileEntities {

    public static void init() {
        GameRegistry.registerTileEntity(ModTileEntity.class, "tutorial_tile_entity");
    }

}

The init() method now registers the Tile Entity with the game. You can add every other Tile Entity to this method as well. The name or id for the Tile Entity class can be anything, but it must be unique. Thus, it is useful if you add your modid in front of the name like I did here.

The init() method of this class now should be called during the preInit phase from the Common Proxy.


Finished. You've just created a TileEntity. Quite simple, isn't it?

 

We could add several variables and methods to it now, but what I want to show you first is how to achieve a update every tick (variables are done in the next tutorial: Tile Entity Data). To mark the TileEntity as updateable, we need to implement the interface IUpdatePlayerListBox. This adds the method update():

ModTileEntity.java:
public class ModTileEntity extends TileEntity implements IUpdatePlayerListBox {

    @Override
    public void update() {
    }

}

update() is now called for every instance of the TileEntity every tick (20 times per second). To demonstrate this, we can add a print statement that prints something into the console.

ModTileEntity.java:
public class ModTileEntity extends TileEntity implements IUpdatePlayerListBox {

    @Override
    public void update() {
        System.out.println("Hello, I'm a TileEntity!");
    }
}

When we start Minecraft with this TileEntity code (The block needs to be registered of course) and place a TileEntity down, the console fills with these lines:

Console:
[10:02:06] [Server thread/INFO] [STDOUT]: [com.bedrockminer.tutorial.tileentity.ModTileEntity:update:11]: Hello, I'm a TileEntity!
[10:02:06] [Client thread/INFO] [STDOUT]: [com.bedrockminer.tutorial.tileentity.ModTileEntity:update:11]: Hello, I'm a TileEntity!
[10:02:06] [Server thread/INFO] [STDOUT]: [com.bedrockminer.tutorial.tileentity.ModTileEntity:update:11]: Hello, I'm a TileEntity!
[10:02:06] [Client thread/INFO] [STDOUT]: [com.bedrockminer.tutorial.tileentity.ModTileEntity:update:11]: Hello, I'm a TileEntity!
[10:02:06] [Server thread/INFO] [STDOUT]: [com.bedrockminer.tutorial.tileentity.ModTileEntity:update:11]: Hello, I'm a TileEntity!
[10:02:06] [Client thread/INFO] [STDOUT]: [com.bedrockminer.tutorial.tileentity.ModTileEntity:update:11]: Hello, I'm a TileEntity!
[10:02:06] [Server thread/INFO] [STDOUT]: [com.bedrockminer.tutorial.tileentity.ModTileEntity:update:11]: Hello, I'm a TileEntity!
[...]

This shows that our TileEntity is updating correctly. If we want to do certain actions only on server or client side, not on both like here, we can add an if statement checking if the world object is remote.

ModTileEntity.java:
public class ModTileEntity extends TileEntity implements IUpdatePlayerListBox {

    @Override
    public void update() {
        if (!this.worldObj.isRemote)
            System.out.println("Hello, I'm a TileEntity!");
    }
}

This method now only prints on server side.


The TileEntities here are quite useless, but in the next tutorials we'll add some features to make them useful.


You can download the code used in this tutorial as a .zip file from here.


Recommended tutorials to continue with


Comments and Questions:

If you want to report modding problems, please make sure to include the code in a pastebin link or something else! Don't just write "It doesn't work", otherwise your post will be deleted. For more complicated problems, please use the troubleshooter form.