This is an old revision of the document!
Table of Contents
Modify BlockEntity data
Introduction
In the previous tutorial, we have created a block entity. Now we try to modify the block entity data. The DemoBlockEntity
class should be written like this:
- DemoBlockEntity.class
public class DemoBlockEntity extends BlockEntity { public int number = 0; public DemoBlockEntity(BlockPos pos, BlockState state) { super(ExampleMod.DEMO_BLOCK_ENTITY, pos, state); } @Override public void writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { nbt.putInt("number", number); super.writeNbt(nbt, registryLookup); } @Override public void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { super.readNbt(nbt); number = nbt.getInt("number", registryLookup); } }
Make sure the number
field is public, as we are going to change it in the DemoBlock
class. You could also make getter and setter methods, but we just expose the field here for simplicity..
Modifying the data
This gets the BlockEntity
at the right-clicked the block's position and if it's of the type DemoBlockEntity
, increments its number
field and sends a chat message to the player.
- DemoBlock.class
public class DemoBlock extends BlockWithEntity { [...] @Override public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) { if (!world.isClient){ BlockEntity blockEntity = world.getBlockEntity(pos); if (blockEntity instanceof DemoBlockEntity demoBlockEntity) { demoBlockEntity.number++; player.sendMessage(Text.literal("Number is " + demoBlockEntity.number)); } } return ActionResult.success(world.isClient); } }
Using data components
Since 1.20.5, you can also use data components to store data. In this case, as data components have codecs that can serialize and deserialize themselves, you do not need to write readNbt
and writeNbt
methods for them.
If you want to have a try, remove the readNbt
and writeNbt
methods before. And then create a data component type for it:
- ExampleMod.java
public static final ComponentType<Integer> NUMBER = Registry.register(Registries.DATA_COMPONENT_TYPE, Identifier.of("tutorial", "number"), ComponentType.<Integer>builder().codec(Codec.INT).packetCodec(PacketCodecs.INTEGER).build());
Here for simplicity, we just place them in the
ModInitializer
. When actually developing mods, if you have multiple data component types, consider creating a separate class to store data components.
And then in the block entity:
- DemoBlockEntity.java
// removed the `readNbt` and `writeNbt` methods. @Override protected void readComponents(ComponentsAccess components) { super.readComponents(components); this.number = components.getOrDefault(ExampleMod.NUMBER, 0); } @Override protected void addComponents(ComponentMap.Builder componentMapBuilder) { super.addComponents(componentMapBuilder); componentMapBuilder.add(ExampleMod.NUMBER, number); }
Now these components can also be stored normally. If you pick stack (which means press the mouse wheel to the block write pressing Ctrl
), the components will be transferred to the stack.
In some cases, you can also retain the readNbt
and writeNbt
methods, and override removeFromCopiedStackNbt
method to remove the "number"
field from the nbt.