====== Adding a BlockEntity ======
A **block entity** is primarily used to store data within blocks. Before creating one, you will need a [[blocks|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.
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 [[blocks]]).
public final class TutorialBlocks {
[...]
public static final DemoBlock DEMO_BLOCK = register("demo_block", new DemoBlock(AbstractBlock.Settings.create()));
[...]
}
public class TutorialBlockEntityTypes {
public static > T register(String path, T blockEntityType) {
return Registry.register(Registries.BLOCK_ENTITY_TYPE, Identifier.of("tutorial", path), blockEntityType);
}
public static final BlockEntityType DEMO_BLOCK = register(
"demo_block",
// For versions 1.21.2 and above,
// replace `BlockEntityType.Builder` with `FabricBlockEntityTypeBuilder`.
BlockEntityType.Builder.create(DemoBlockEntity::new, TutorialBlocks.DEMO_BLOCK).build()
);
public static void initialize() {
}
}
Remember to refer to the ''initialize'' method in the ''ModInitializer'':
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.
> **Note:** Like other blocks, the block also needs a block model and an item model, and may also need a loot table, see [[blocks]] for how to create them. As for loot tables, [[blockentity_sync_itemstack|subsequent tutorials]] will cover how to improve the loot tables to include block entity data.
===== 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 be created.
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);
}
@Override
protected BlockRenderType getRenderType(BlockState state) {
return BlockRenderType.MODEL;
}
}
Overriding ''getRenderType'' is because ''BlockWithEntity'' makes it invisible by default.
===== 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:
public class DemoBlock extends BlockWithEntity {
[...]
@Override
public BlockEntityTicker getTicker(World world, BlockState state, BlockEntityType 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:
public class DemoBlockEntity extends BlockEntity {
[...]
@Override
public static void tick(World world, BlockPos pos, BlockState state, DemoBlockEntity blockEntity) {
[...]
}
}
===== Next steps =====
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 extended ''BlockWithEntity'', and used its interface ''BlockEntityProvider'' to provide an instance of your new ''BlockEntity''.
You also learned how to add ticking for it. Next step, you can try some other complex operations for the block entities, such as:
* [[blockentity_modify_data|Modifying block entity data]]
* [[inventory|Storing items in the block entity as an inventory]]
* [[blockentityrenderers|Using block entity renderers to dynamically render]]
* [[screenhandler|Creating a container block]]