Skip to content

Block Components

Block components store per-block instance data for blocks that need persistent state (e.g., chests with inventory, crafting benches with tier levels). They are standard ECS components registered on the ChunkStore.

  • Block components: com.hypixel.hytale.server.core.modules.block.components
  • BlockModule: com.hypixel.hytale.server.core.modules.block.BlockModule
  • BlockEntity: com.hypixel.hytale.server.core.modules.block.BlockEntity
  • BlockReplaceEvent: com.hypixel.hytale.server.core.modules.block.BlockReplaceEvent

Block components implement Component<ChunkStore> and are registered via the chunk store registry.

CustomBlockComponent.java
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
public class CustomBlockComponent implements Component<ChunkStore> {
public static final BuilderCodec<CustomBlockComponent> CODEC =
BuilderCodec.builder(CustomBlockComponent.class, CustomBlockComponent::new)
.append(new KeyedCodec<>("IsOpen", Codec.BOOLEAN),
(c, v) -> c.isOpen = v, c -> c.isOpen)
.add()
.append(new KeyedCodec<>("Capacity", Codec.SHORT),
(c, v) -> c.capacity = v, c -> c.capacity)
.add()
.build();
private boolean isOpen = false;
private short capacity = 20;
public boolean isOpen() { return isOpen; }
public void setOpen(boolean open) { this.isOpen = open; }
public short getCapacity() { return capacity; }
@Override
public Component<ChunkStore> clone() {
CustomBlockComponent copy = new CustomBlockComponent();
copy.isOpen = this.isOpen;
copy.capacity = this.capacity;
return copy;
}
}

Register block components during plugin setup using the chunk store registry:

Registering a block component
@Override
protected void setup() {
ComponentType<ChunkStore, CustomBlockComponent> componentType =
getChunkStoreRegistry().registerComponent(
CustomBlockComponent.class,
"myplugin:custom_block",
CustomBlockComponent.CODEC
);
}

Block component for blocks that contain item inventories (chests, containers). Replaces the former ItemContainerBlockState and ItemContainerState.

ItemContainerBlock
package com.hypixel.hytale.server.core.modules.block.components;
public class ItemContainerBlock implements Component<ChunkStore> {
public static ComponentType<ChunkStore, ItemContainerBlock> getComponentType();
public SimpleItemContainer getItemContainer();
public short getCapacity();
@Nullable public String getDroplist();
public void setDroplist(@Nullable String droplist);
public Map<UUID, ContainerBlockWindow> getWindows();
public static final BuilderCodec<ItemContainerBlock> CODEC;
}

Use BlockModule.getComponent() to read block components from world positions:

Reading a block component
ItemContainerBlock container = BlockModule.getComponent(
ItemContainerBlock.getComponentType(), world, x, y, z
);
if (container != null) {
SimpleItemContainer items = container.getItemContainer();
}

You can also access block components via chunk store entity references:

Via chunk store entity reference
BlockComponentChunk componentChunk = accessor.getComponent(
chunkRef, BlockComponentChunk.getComponentType()
);
if (componentChunk != null) {
int index = ChunkUtil.indexBlockInColumn(x, y, z);
Ref<ChunkStore> entityRef = componentChunk.getEntityReference(index);
if (entityRef != null) {
ItemContainerBlock container = accessor.getComponent(
entityRef, ItemContainerBlock.getComponentType()
);
}
}

BlockEntity provides a static utility method for setting block entities on chunks. It handles filler block replacement events, cross-chunk entity references, and BlockReplaceEvent dispatch.

BlockEntity.setBlockEntity
BlockEntity.setBlockEntity(
accessor, chunkRef, componentChunk,
x, y, z, blockType, rotation, holder
);

Pass null for the holder to remove an existing block entity.

BlockReplaceEvent is an ECS event dispatched when a block entity is being replaced. It provides the chunk reference, the position of the block being replaced, and the new entity holder.

BlockReplaceEvent methods
public class BlockReplaceEvent extends EcsEvent {
public Ref<ChunkStore> getChunkRef();
public int getSelfX();
public int getSelfY();
public int getSelfZ();
public Holder<ChunkStore> getNewEntity();
}
v3 (Removed)v4 (Replacement)
BlockState base classComponent<ChunkStore>
BlockStateRegistry.registerBlockState(...)getChunkStoreRegistry().registerComponent(...)
BlockStateModuleBlockModule
chunk.getState(x, y, z)BlockModule.getComponent(type, world, x, y, z)
BlockState.ensureState(chunk, x, y, z)Use BlockEntity.setBlockEntity(...)
ItemContainerBlockStateItemContainerBlock
TickableBlockStateEntityTickingSystem<ChunkStore>
DestroyableBlockStateRefSystem<ChunkStore> with remove handling
SendableBlockStateCustom packet logic in systems
BlockState.markNeedsSave()Handled automatically by the component system
  1. Use Component<ChunkStore>: Implement the standard ECS component interface
  2. Register with codec: Provide a BuilderCodec for persistence
  3. Use systems for behavior: Lifecycle logic belongs in RefSystem<ChunkStore> or EntityTickingSystem<ChunkStore>, not in the component
  4. Handle replacement events: Listen for BlockReplaceEvent if your component needs to transfer data when blocks are replaced
  5. Access via BlockModule: Use BlockModule.getComponent(...) for positional lookups