Skip to content

Block Architecture & State System ​

Blocks form the static and dynamic foundation of Minecraft worlds. In Pumpkin, block handling is cleanly bifurcated into two distinct layers:

  1. Static Data Layer (pumpkin-data): Compile-time generated metadata including numeric block IDs, block state IDs, hardness, blast resistance, light emission, opacity, collision shapes, and block state properties generated by pumpkin-codegen.
  2. Dynamic Behavior Layer (pumpkin::block): Server-side behavioral hooks—such as opening containers, toggling redstone signals, growing crops, breaking triggers, and collision effects—handled via the BlockBehaviour trait.

Static Blocks vs. Interactive Behaviors ​

Because Minecraft contains thousands of block state combinations, Pumpkin avoids allocating runtime behavior structs for simple, non-interactive blocks:

CategoryDescriptionExamplesRuntime Behavior Required?
Static BlocksRigid solid voxels without interactive logic or custom physicsstone, dirt, cobblestone, bedrock, obsidianNo — Placement, breaking times, and tool requirements are handled entirely by pumpkin-data.
Interactive BlocksBlocks triggered by right-clicks or tool actionsdoor, trapdoor, lever, button, crafting_tableYes — Implements BlockBehaviour::normal_use or use_with_item.
Collision BlocksBlocks that modify entity movement or apply forceshoney_block, slime_block, soul_sand, powder_snowYes — Implements on_entity_collision or on_landed_upon.
Redstone ComponentsPower sources, conductors, and logic gatesredstone_wire, repeater, comparator, observerYes — Implements emits_redstone_power and on_neighbor_update.
Block EntitiesBlocks with persistent, serialized custom stateschest, furnace, sign, decorated_pot, spawnerYes — Implements create_block_entity and links a BlockEntity.

Block IDs, States, and Properties ​

Minecraft distinguishes between a Block type and a specific Block State:

1. BlockId and BlockStateId ​

  • BlockId: An identifier representing a base block type (e.g., minecraft:oak_door).
  • BlockStateId: A 16-bit integer representing a unique combination of block properties (e.g., minecraft:oak_door[facing=north,half=lower,hinge=left,open=false,powered=false]).

In Pumpkin, block data is looked up from precomputed lookup tables in pumpkin-data:

rust
use pumpkin_data::Block;
use pumpkin_data::BlockStateId;

// Access static properties directly from generated data
let door_block = Block::OAK_DOOR;
let default_state_id: BlockStateId = door_block.default_state_id;

let hardness = door_block.hardness;
let blast_resistance = door_block.blast_resistance;
let is_air = door_block.is_air;

2. Generated State Property Helpers ​

Rather than parsing raw strings or bitmasks manually, pumpkin-codegen generates strongly typed property helpers in pumpkin_data::block_properties:

rust
use pumpkin_data::block_properties::{DoorLikeProperties, DoubleBlockHalf, Facing};

// Read typed properties from a numeric BlockStateId
let is_open = DoorLikeProperties::open(state_id);
let half = DoorLikeProperties::half(state_id);
let facing = DoorLikeProperties::facing(state_id);

// Generate a new BlockStateId with mutated properties
let toggled_state_id = DoorLikeProperties::with_open(state_id, !is_open);

Physics, Bounding Boxes & Raycasting ​

Pumpkin calculates collision and raycasting bounding boxes directly using pre-baked shapes in pumpkin-data:

  • Solid Collisions: During physics ticks, an entity's axis-aligned bounding box (AABB) is tested against surrounding block collision voxels.
  • Raycasting: When a player targets a block to place or break, client raycasts are validated against the server-side block outline shapes.
  • Custom Movement Hooks: Blocks like slime blocks or honey blocks override vertical rebound or horizontal velocity:
    • bounce_entity_after_fall: Dampens fall damage and multiplies rebound velocity.
    • stop_vertical_movement_after_fall: Zeros fall velocity upon contact.

Block Entities (Tile Entities) ​

When a block must store arbitrary dynamic data that cannot fit into standard block state bits (such as container inventories, custom sign text, brewing timers, or mob spawner delays), it uses a Block Entity:

ComponentResponsibilityImplementation File
BlockEntity TraitDefines NBT serialization (to_nbt, from_nbt) and tick executioncrates/pumpkin/src/block/entities/
create_block_entityHook in BlockBehaviour called when placing the blockAttached to relevant block behaviors
Chunk Entity StorageManages active block entities and coordinates within chunk sectionscrates/pumpkin-world

The Block Registry & Dispatch System ​

Block interaction events are routed through the central BlockRegistry in crates/pumpkin/src/block/registry.rs:

  1. Registration: At server initialization, all behavior structs are registered into the BlockManager.
  2. Mapping: The manager maps numeric BlockId keys to thread-safe behavior instances (Arc<dyn BlockBehaviour>).
  3. Dispatch: When the network layer receives block action packets (such as CUseItemOn or block dig packets), the server looks up the behavior in $O(1)$ time and invokes the relevant hook.

Next Steps ​

To implement a new interactive block behavior, continue to the practical guide:

  • Adding a Block: Complete walk-through of declaring attributes, implementing interaction hooks, creating block entities, and registering behaviors.

Released under the MIT License.