• Forums

Navigation

  • Home
  • Style Guide
  • Getting Started
    • Home
    • Structuring Your Mod
    • Forge Update Checker
    • Debug Profiler
  • Concepts
    • Sides
    • Resources
    • Data
    • Registries
    • Mod Lifecycle
    • Internationalization and localization
  • Blocks
    • Home
    • Blockstates
    • Interaction
  • Tile Entities
    • Home
      • Creating a TileEntity
      • Attaching a TileEntity to a Block
      • Storing Data within your TileEntity
      • Ticking TileEntities
      • Synchronizing the Data to the Client
    • Renderer
  • Items
    • Home
    • Loot Modification
  • Models
    • Intro to Models
    • Model Files
    • Blockstates
      • Intro to Blockstate JSONs
    • Coloring Textures
    • Item Property Overrides
    • Advanced Models
      • IBakedModel
      • Perspective
      • ItemOverrideList
  • Rendering
    • ItemStackTileEntityRenderer
  • Data Generation
    • Introduction
    • Model Providers
  • Events
    • Basic Usage
  • Networking
    • Home
    • Overview
    • SimpleImpl
    • Entities
  • Data Storage
    • Capabilities
  • Utilities
    • Recipes
    • Tags
  • Effects
    • Particles
    • Sounds
  • Conventions
    • Versioning
    • Locations
  • Advanced Topics
    • Access Transformers
  • Contributing to Forge
    • Getting Started
    • PR Guidelines
  • Legacy Versions
    • Home
    • Porting to 1.15

TileEntities

TileEntities are like simplified Entities that are bound to a Block. They are used to store dynamic data, execute tick based tasks, and dynamic rendering. Some examples from vanilla Minecraft would be handling of inventories on chests, smelting logic on furnaces, or area effects on beacons. More advanced examples exist in mods, such as quarries, sorting machines, pipes, and displays.

Note

TileEntities aren’t a solution for everything and they can cause lag when used wrongly. When possible, try to avoid them.

Creating a TileEntity

In order to create a TileEntity, you need to extend the TileEntity class. To register it, listen for the appropriate registry event and create a TileEntityType:

@SubscribeEvent
public static void registerTE(RegistryEvent.Register<TileEntityType<?>> evt) {
  TileEntityType<?> type = TileEntityType.Builder.create(factory, validBlocks).build(null);
  type.setRegistryName("mymod", "myte");
  evt.getRegistry().register(type);
}
In this example, factory is a function that creates a new instance of your TileEntity. A method reference or a lambda is commonly used. The variable validBlocks is one or more blocks (TileEntityType$Builder#create is varargs) that the tile entity can exist for.

Attaching a TileEntity to a Block

To attach your new TileEntity to a Block, you need to override 2 (two) methods within your Block subclass:

IForgeBlock#hasTileEntity(BlockState state)

IForgeBlock#createTileEntity(BlockState state, IBlockReader world)
Using the parameters, you can choose if the block should have a TileEntity or not. Usually, you will return true in the first method and a new instance of your TileEntity in the second method.

Storing Data within your TileEntity

In order to save data, override the following two methods:

TileEntity#write(CompoundNBT nbt)

TileEntity#read(CompoundNBT nbt)
These methods are called whenever the Chunk containing the TileEntity gets loaded from/saved to NBT. Use them to read and write to the fields in your tile entity class.

Note

Whenever your data changes, you need to call `TileEntity#markDirty`; otherwise, the `Chunk` containing your `TileEntity` might be skipped while the world is saved.

Important

It is important that you call the `super` methods!

The tag names id, x, y, z, ForgeData and ForgeCaps are reserved by the super methods.

Ticking TileEntities

If you need a ticking TileEntity, for example to keep track of the progress during a smelting process, you need to add the net.minecraft.tileentity.ITickableTileEntity interface to your TileEntity. Now you can implement all your calculations within ITickableTileEntity#tick.

Note

This method is called each tick; therefore, you should avoid having complicated calculations in here. If possible, you should make more complex calculations every X ticks. (The amount of ticks in a second may be lower then 20 (twenty) but won’t be higher)

Synchronizing the Data to the Client

There are three ways of syncing data to the client: synchronizing on chunk load, on block updates, and with a custom network message.

Synchronizing on Chunk Load

For this you need to override

TileEntity#getUpdateTag()

IForgeTileEntity#handleUpdateTag(CompoundNBT nbt)
Again, this is pretty simple, the first method collects the data that should be sent to the client, while the second one processes that data. If your TileEntity doesn’t contain much data, you might be able to use the methods out of the Storing Data within your TileEntity section.

Important

Synchronizing excessive/useless data for tile entities can lead to network congestion. You should optimize your network usage by sending only the information the client needs when the client needs it. For instance, it is more often than not unnecessary to send the inventory of a tile entity in the update tag, as this can be synchronized via its GUI.

Synchronizing on Block Update

This method is a bit more complicated, but again you just need to override two methods. Here is a tiny example implementation of it:

@Override
public SUpdateTileEntityPacket getUpdatePacket(){
    CompoundNBT nbtTag = new CompoundNBT();
    //Write your data into the nbtTag
    return new SUpdateTileEntityPacket(getPos(), -1, nbtTag);
}

@Override
public void onDataPacket(NetworkManager net, SUpdateTileEntityPacket pkt){
    CompoundNBT tag = pkt.getNbtCompound();
    //Handle your Data
}
The Constructor of SUpdateTileEntityPacket takes:

  • The position of your TileEntity.
  • An ID, though it isn’t really used besides by Vanilla; therefore, you can just put a -1 in there.
  • An CompoundNBT which should contain your data.

Now, to send the packet, an update notification must be given on the server.

World#notifyBlockUpdate(BlockPos pos, BlockState oldState, BlockState newState, int flags)
The pos should be your TileEntity’s position. For oldState and newState, you can pass the current BlockState at that position. flags is a bitmask that should contain 2, which will sync the changes to the client. See Constants$BlockFlags for more info as well as the rest of the flags. The flag 2 is equivalent to Constants$BlockFlags#BLOCK_UPDATE.

Synchronizing Using a Custom Network Message

This way of synchronizing is probably the most complicated but is usually the most optimized, as you can make sure that only the data you need to be synchronized is actually synchronized. You should first check out the Networking section and especially SimpleImpl before attempting this. Once you’ve created your custom network message, you can send it to all users that have the TileEntity loaded with SimpleChannel#send(PacketDistributor$PacketTarget, MSG).

Warning

It is important that you do safety checks, the TileEntity might already be destroyed/replaced when the message arrives at the player! You should also check if the chunk is loaded (World#isBlockLoaded(BlockPos)).

Built with MkDocs using a custom theme. Hosted by Read the Docs.
Enable Dark Theme