Skip to main content
iconPowerNukkitX
Custom Components

Block Entities

How to define, register, and bind custom block entities (tile entities) to custom blocks.

Block Entities (historically known as Tile Entities) are background objects associated with specific block coordinates. They store extra NBT data, handle tick updates, and manage complex block behaviors (like chests, furnaces, or custom machinery).


1. Defining a Custom Block Entity

To create a custom block entity, extend org.powernukkitx.blockentity.BlockEntity:

package org.example.blockentity;

import org.powernukkitx.blockentity.BlockEntity;
import org.powernukkitx.level.format.IChunk;
import org.powernukkitx.nbt.tag.CompoundTag;

public class MyMachineEntity extends BlockEntity {

    public MyMachineEntity(IChunk chunk, CompoundTag nbt) {
        super(chunk, nbt);
    }

    @Override
    public boolean isBlockEntityValid() {
        // Return true if the block entity matches the block type at its coordinates
        return this.getLevel().getBlock(this).getId().equals("powernukkitx:my_machine");
    }

    @Override
    public void saveNBT() {
        super.saveNBT();
        // Save any custom properties to NBT here
        this.namedTag.putInt("energy", 100);
    }

    // Override onUpdate() if your block entity needs to run tick updates
    @Override
    public boolean onUpdate() {
        // Run tick logic here
        return false; // Return true if the block entity needs to keep ticking
    }
}

2. Registering the Block Entity

Register your custom block entity inside your plugin's onLoad() method:

import org.powernukkitx.registry.RegisterException;
import org.powernukkitx.registry.Registries;

@Override
public void onLoad() {
    try {
        Registries.BLOCKENTITY.register("MyMachine", MyMachineEntity.class);
    } catch (RegisterException e) {
        throw new RuntimeException(e);
    }
}

3. Binding to a Custom Block (BlockEntityHolder)

To link your custom block entity to your custom block, implement the BlockEntityHolder<MyMachineEntity> interface on your block class.

Use BlockEntityHolder.setBlockAndCreateEntity(this) inside the block's placement logic to ensure the block entity is instantiated when the block is placed:

package org.example.block;

import org.powernukkitx.block.Block;
import org.powernukkitx.block.BlockProperties;
import org.powernukkitx.block.BlockState;
import org.powernukkitx.block.BlockEntityHolder;
import org.powernukkitx.block.customblock.CustomBlock;
import org.powernukkitx.block.customblock.CustomBlockDefinition;
import org.powernukkitx.blockentity.BlockEntity;
import org.powernukkitx.item.Item;
import org.powernukkitx.math.BlockFace;
import org.powernukkitx.Player;
import org.example.blockentity.MyMachineEntity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class MyMachineBlock extends Block implements CustomBlock, BlockEntityHolder<MyMachineEntity> {

    public static final BlockProperties PROPERTIES = new BlockProperties("powernukkitx:my_machine");

    public MyMachineBlock() {
        super(PROPERTIES.getDefaultState());
    }

    public MyMachineBlock(@Nullable BlockState blockState) {
        super(blockState);
    }

    @Override
    public @NotNull BlockProperties getProperties() {
        return PROPERTIES;
    }

    @Override
    public CustomBlockDefinition getDefinition() {
        return CustomBlockDefinition.builder(this)
                .texture("my_machine")
                .breakTime(3)
                .build();
    }

    // --- BlockEntityHolder Implementation ---

    @Override
    public @NotNull Class<MyMachineEntity> getBlockEntityClass() {
        return MyMachineEntity.class;
    }

    @Override
    public @NotNull String getBlockEntityType() {
        return "MyMachine"; // Must match registered BlockEntity name
    }

    // Place the block and create the corresponding block entity
    @Override
    public boolean place(@NotNull Item item, @NotNull Block block, @NotNull Block target, @NotNull BlockFace face, double fx, double fy, double fz, @Nullable Player player) {
        // Sets block state on the level and instantiates the block entity automatically
        return BlockEntityHolder.setBlockAndCreateEntity(this) != null;
    }
}

On this page