This is an old revision of the document!
Table of Contents
Adding a BlockEntity
A block entity is primarily used to store data within blocks. Before creating one, you will need a Block. This tutorial will cover the creation of your BlockEntity class, and its registration.
Creating a block entity type
The simplest block entity simply extends BlockEntity
, and uses the default constructor. This is perfectly valid, but will not grant any special functionality to your block.
- DemoBlockEntity.java
public class DemoBlockEntity extends BlockEntity { public DemoBlockEntity(BlockPos pos, BlockState state) { super(TutorialBlockEntityTypes.DEMO_BLOCK, pos, state); } }
Please ensure that the constructor only takes the two parameters, otherwise the method reference DemoBlockEntity::new
that we write later will be invalid. The TutorialBlockEntityTypes.DEMO_BLOCK
field will be created later.
Block entities support a variety of methods to enable functionality such as serialization to and deserialization from NBT, providing inventories, and more. This tutorial covers the most common implementations of block entity functionality.
Registering block and block entities
Once you have created the BlockEntity
class, you will need to register it for it to function. The first step of this process is to create a BlockEntityType
object in our TutorialBlockEntityTypes
class, which links your Block
and BlockEntity
together. Create a Block
object as the static final field DEMO_BLOCK
in the TutorialBlocks
class we created earlier. In this tutorial, the ID of the block entity is tutorial:demo_block
.
The BlockEntityType
can be registered in the initialization of class or in your onInitialize
method. This is to ensure it gets registered at the correct time. In this example, we register them in separate classes (see bocks).
- TutorialBlocks.java
public final class TutorialBlocks { [...] public static final DemoBlock DEMO_BLOCK = register("demo_block", new DemoBlock(AbstractBlock.Settings.create())); [...] }
- TutorialBlockEntityTypes.java
public class TutorialBlockEntityTypes { public static <T extends BlockEntityType<?>> T register(String path, T blockEntityType) { return Registry.register(Registries.BLOCK_ENTITY_TYPE, Identifier.of("tutorial", path), blockEntityType); } public static final BlockEntityType<DemoBlockEntity> DEMO_BLOCK = register( "demo_block", BlockEntityType.Builder.create(DemoBlockEntity::new, TutorialBlocks.DEMO_BLOCK).build() ); public static void initialize() { } }
Remember to refer to the initialize
method in the ModInitializer
:
- ExampleMod.java
public class ExampleMod implements ModInitializer { [...] @Override public void onInitialize() { [...] TutorialBlockEntityTypes.initialize(); } }
For old versions, if you cannot access BlockEntityType.Builder.create
, try FabricBlockEntityTypeBuilder.create
.
The block entity type defines that only the TutorialBlocks.DEMO_BLOCK
can have this block entity type. If you want the block entity type to support more blocks, just add them in the parameters of BlockEntityType.Builder.create
. If the method reference DemoBlockEntity::new
does not parse, check if the constructor of DemoBlockEntity
has the correct parameters.
Connecting the block entity and the block
Once your BlockEntityType
has been created and registered, you'll need a block that is associated with it. You can do this by extending BlockWithEntity
(or implementing BlockEntityProvider
) and overriding createBlockEntity
. Each time your block is placed, your Block Entity will spawn alongside it.
- DemoBlock.java
public class DemoBlock extends BlockWithEntity { public DemoBlock(Settings settings) { super(settings); } @Override protected MapCodec<? extends DemoBlock> getCodec() { return createCodec(DemoBlock::new); } @Override public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { return new DemoBlockEntity(pos, state); } }
To serialize data for bloc kentities, see block_entity_modifying_data.
Block Entity Ticking
Ticking means the block should run something on every tick (which is 1/20 second). For your block to tick, you would normally use getTicker
in Block
, linking back to a static tick
method in the BlockEntity
. See below for the common implementation of ticking.
In your Block
class:
- DemoBlock.java
public class DemoBlock extends BlockWithEntity { [...] @Override public <T extends BlockEntity> BlockEntityTicker<T> getTicker(World world, BlockState state, BlockEntityType<T> type) { // Make sure to check world.isClient if you only want to tick only on serverside. return validateTicker(type, ExampleMod.DEMO_BLOCK_ENTITY, DemoBlockEntity::tick); } }
And in your BlockEntity
class:
- DemoBlockEntity.java
public class DemoBlockEntity extends BlockEntity { [...] @Override public static void tick(World world, BlockPos pos, BlockState state, DemoBlockEntity blockEntity) { [...] } }
Overview
You should now have your very own BlockEntity
, which you can expand in various ways to suit your needs. You registered a BlockEntityType
, and used it to connect your Block
and BlockEntity
classes together. Then, you implemented BlockEntityProvider
in your Block
class, and used the interface to provide an instance of your new BlockEntity
. You also learned how to save data to your BlockEntity
, how to retrieve for use later, and finally, you learned how to add ticking to it.