Block States
Block States
Section titled “Block States”Block states store per-block instance data for blocks that need persistent state (e.g., chests with inventory, doors with open/closed state).
Package Location
Section titled “Package Location”- BlockState:
com.hypixel.hytale.server.core.universe.world.meta.BlockState - BlockStateRegistry:
com.hypixel.hytale.server.core.universe.world.meta.BlockStateRegistry
BlockState Class
Section titled “BlockState Class”public abstract class BlockState implements Component<ChunkStore> { public Vector3i getPosition(); public Vector3i getBlockPosition(); public Vector3d getCenteredBlockPosition(); public int getBlockX(); public int getBlockY(); public int getBlockZ();
public WorldChunk getChunk(); public BlockType getBlockType(); public int getRotationIndex();
public boolean initialize(BlockType blockType); public void onUnload(); public void invalidate();
public void markNeedsSave(); public BsonDocument saveToDocument(); public Component<ChunkStore> clone();
public Ref<ChunkStore> getReference(); public Holder<ChunkStore> toHolder();
public static BlockState ensureState(WorldChunk chunk, int x, int y, int z); public static BlockState getBlockState(Ref<ChunkStore> ref, ComponentAccessor<ChunkStore> accessor); public static BlockState getBlockState(Holder<ChunkStore> holder);}Block State Interfaces
Section titled “Block State Interfaces”Block states can implement these interfaces to opt into engine-managed behaviors. The system auto-detects them at registration time.
TickableBlockState
Section titled “TickableBlockState”Makes your block state tick every frame. The engine registers a ticking system for any block state class that implements this.
public interface TickableBlockState { void tick(float dt, int index, ArchetypeChunk<ChunkStore> archetypeChunk, Store<ChunkStore> store, CommandBuffer<ChunkStore> commandBuffer);}DestroyableBlockState
Section titled “DestroyableBlockState”Called when the block is destroyed (removed from the world). Useful for cleanup like dropping items.
public interface DestroyableBlockState { void onDestroy();}SendableBlockState
Section titled “SendableBlockState”Sends/removes packets when a chunk loads or unloads for a player. Used for block states that need client-side representation.
public interface SendableBlockState { void sendTo(List<Packet> packets); void unloadFrom(List<Packet> packets); default boolean canPlayerSee(PlayerRef player) { return true; }}Registering Block States
Section titled “Registering Block States”Register custom block states during plugin setup:
@Overrideprotected void setup() { // Register a block state type getBlockStateRegistry().registerBlockState( MyBlockState.class, "MyPlugin_CustomState", MyBlockState.CODEC );
// With associated data class getBlockStateRegistry().registerBlockState( MyBlockState.class, "MyPlugin_CustomState", MyBlockState.CODEC, MyStateData.class, MyStateData.CODEC );}Custom Block State Example
Section titled “Custom Block State Example”public class ChestBlockState extends BlockState { public static final BuilderCodec<ChestBlockState> CODEC = BuilderCodec.builder(ChestBlockState.class, ChestBlockState::new) .inherit(BlockState.BASE_CODEC) .addField( new KeyedCodec<>("IsOpen", Codec.BOOLEAN), (state, open) -> state.isOpen = open, state -> state.isOpen ) .build();
private boolean isOpen = false; private ItemContainer inventory;
@Override public boolean initialize(BlockType blockType) { this.inventory = new SimpleItemContainer((short) 27); return true; }
public void toggle() { this.isOpen = !this.isOpen; markNeedsSave(); }
public boolean isOpen() { return isOpen; }
public ItemContainer getInventory() { return inventory; }}Position Methods
Section titled “Position Methods”getPosition() returns chunk-local coordinates (0-31 on X/Z). getBlockPosition() returns world coordinates. If you need the center of the block (accounting for rotation), use getCenteredBlockPosition().
BlockState state = chunk.getState(localX, y, localZ);
Vector3i local = state.getPosition(); // chunk-localVector3i world = state.getBlockPosition(); // world coordinatesVector3d center = state.getCenteredBlockPosition(); // world center, adjusted for rotation
int wx = state.getBlockX(); // individual world coordinate accessorsint wy = state.getBlockY();int wz = state.getBlockZ();Accessing Block States
Section titled “Accessing Block States”// Get state from chunkWorldChunk chunk = world.getChunk(chunkX, chunkZ);BlockState state = chunk.getState(localX, y, localZ);
// Ensure state exists (creates if needed)BlockState state = BlockState.ensureState(chunk, x, y, z);
// Cast to specific typeif (state instanceof ChestBlockState chestState) { chestState.toggle();}StateData Configuration
Section titled “StateData Configuration”Configure block states in JSON:
{ "Id": "MyPlugin_Chest", "State": { "Type": "MyPlugin_CustomState", "Data": { "DefaultCapacity": 27 } }}Lifecycle Methods
Section titled “Lifecycle Methods”| Method | When Called |
|---|---|
initialize(BlockType) | When state is first created. Return false to reject creation. |
onUnload() | When chunk is unloaded |
invalidate() | When block is invalidated |
onDestroy() | When block is destroyed (requires DestroyableBlockState) |
markNeedsSave() | Call when state changes |
Best Practices
Section titled “Best Practices”- Minimize usage: Only use block states when truly needed for persistent data
- Mark saves: Always call
markNeedsSave()when modifying state - Handle null: States may be null if block doesn’t require state
- Prepare for removal: This API is deprecated; consider ECS alternatives
Related
Section titled “Related”- Block Types - BlockType properties
- ECS Overview - Entity Component System
- Serialization - Data persistence