tutorial:blockentity_modify_data

This is an old revision of the document!


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.

tutorial/blockentity_modify_data.1724632268.txt.gz · Last modified: 2024/08/26 00:31 by solidblock