Skip to main content

đŸ”Ŗ Block States

Introduction​

In Minecraft, every block can have one or more block states. For example, logs have different orientations (axis=x/y/z), and leaves can detect their "distance" from a tree (distance=1~7). These states collectively determine the block's appearance and physical behavior.

In CraftEngine, we need to distinguish two core concepts:

🎨 Visual Block State (what the client sees)​

This is what players actually see on the client side, also called the vanilla block state.

  • The plugin attaches custom models to specific vanilla states (e.g., a particular note block) to achieve unique block rendering.
  • In short: visual state = "what it looks like"

âš™ī¸ Internal Block State (what the server computes)​

This is what is actually stored and processed on the server side, also called the server-side block state.

  • It handles all block logic and physics — collision boxes, redstone signals, entity interactions, etc.
  • The server "maps" the internal state to a visual state via specific network protocols and sends it to the client for rendering.
  • In short: internal state = "how it actually works"


The state/states config section does one thing: assigns each internal block state a visual appearance for the client.

Depending on block complexity, there are two modes:

ModeConfig keyUse case
Single-statestateBlock has only one internal state (e.g., lanterns, decorations)
Multi-statestatesBlock has multiple internal states (e.g., logs with orientation, leaves with distance/waterlogging)

Let's start from the simplest single-state and progressively move to multi-state.

Single-State Blocks​

When a block has only one internal state, use the state config section. Its core is the visual state + model combination: the visual state determines "which vanilla block to use as the carrier," and the model determines "what it renders as."

state:
auto_state: note_block
model:
path: "minecraft:block/custom/my_block"

auto_state lets the plugin auto-pick a note block state, and model specifies the rendering model. Each is explained in detail below.

Visual State Details​

auto_state: Let the Plugin Auto-Assign​

auto_state: note_block

The plugin automatically picks an available vanilla state from a preset state group. All states in the same group share key characteristics (collision box, rendering properties) — you don't need to care which specific one is used.

info

Available auto_state groups

GroupVanilla blocks usedNotes
solidNote Block + Mushroom BlocksMost universal solid block group
note_blockNote BlockCommon, full-block collision box
mushroom_stemMushroom StemFull block
red_mushroom_blockRed Mushroom BlockFull block
brown_mushroom_blockBrown Mushroom BlockFull block
mushroomAll three mushroom blocks aboveAny mushroom block
tintable_leavesOak/Jungle/Acacia/Dark Oak/MangroveTintable leaves, non-waterlogged
waterlogged_tintable_leavesSame as aboveTintable leaves, waterlogged
non_tintable_leavesSpruce/Birch/Cherry/Pale Oak/AzaleaNon-tintable leaves, non-waterlogged
waterlogged_non_tintable_leavesSame as aboveNon-tintable leaves, waterlogged
leavesAll leavesNon-waterlogged
waterlogged_leavesAll leavesWaterlogged
lower_tripwireTripwire (attached=true)Smaller collision box
higher_tripwireTripwire (attached=false)Larger collision box
tripwireTripwireAny tripwire
saplingAll saplings—
pressure_plateWeighted Pressure Plates—
cactus / sugar_cane / weeping_vine / twisting_vine / cave_vine / kelp / chorusCorresponding plants—

Expanded form: sharing visual states

When multiple appearances need to share the same vanilla state, use the expanded form:

# Simple form — each gets its own independent visual state
auto_state: solid

# Expanded form — appearances with the same id share one visual state
auto_state:
type: solid
id: "my_shared_id"

Without id, each auto_state: solid gets its own independent assignment. With the same id, they are bound to the same vanilla block state.

state: Precise Vanilla State Specification​

When you need precise control over which vanilla state to use, use state.

# Format 1: Full vanilla state identifier
state: "minecraft:note_block[instrument=hat,note=0,powered=false]"

# Format 2: Mapped shorthand (requires block_state_mappings to be pre-configured)
state: "note_block:0"
caution

A single vanilla block state can only be bound to one unique custom model. If you bind the same vanilla state to two different custom models, the plugin will log a conflict warning in the console.

Model Configuration​

model determines what the visual state renders as on the client. It has several mutually exclusive configuration combinations:

model:
path: "minecraft:block/custom/my_block"
model:
path: "minecraft:block/custom/my_block"
generation:
parent: "minecraft:block/cube_column"
textures:
end: "minecraft:block/custom/top"
side: "minecraft:block/custom/side"
model:
path: "minecraft:block/custom/my_block"
textures:
- "minecraft:block/custom/bottom"
- "minecraft:block/custom/top"
- "minecraft:block/custom/side"
model:
texture: "minecraft:block/custom/my_block"

Equivalent forms:

# Equivalent to
model:
textures: "minecraft:block/custom/my_block"

# Also equivalent to
model:
textures:
- "minecraft:block/custom/my_block"

With a single texture, path can be omitted (defaults to the texture path). texture and textures are equivalent; list and string forms are also equivalent.

models:
- path: "minecraft:block/custom/flower_1"
weight: 8
- path: "minecraft:block/custom/flower_2"
weight: 5

Used to remove the block's original model, typically to free up a state for block entity renderer use.

transparent: true

All sub-keys of model:

KeyTypeDescription
pathstringModel file path. Required when using textures (2+ entries) or generation, to specify where the generated model is saved
textures / texturelist or stringSimplified texture config; infers model structure from texture count (mutually exclusive with generation). Both key names are equivalent; a single texture can use string shorthand
generationobjectAuto-generate the model; requires parent and textures (mutually exclusive with textures list)
x / y / zintegerRotation angle around axis, must be a multiple of 90 (z requires 1.21.11+)
uvlockbooleanLock texture direction, default false
weightintegerOnly used in models lists; selection weight, default 1

textures auto-inference rules:

Texture countInferred modelpath required?Texture orderExample blocks
1cube_allNo (defaults to texture path)All six faces sameNetherrack
2cube_columnYesEnd faces, sideLog
3cube_bottom_topYesBottom, top, sideGrass Block
4orientableYesBottom, top, front, sideFurnace
5+Five-face independentYesBottom, top, north, east, south, westCrafter

Prefix a texture with ^ to also use it as the particle texture (particles emitted when breaking the block):

textures:
- "^minecraft:block/custom/top" # top face → also used as particle
- "minecraft:block/custom/bottom"
- "minecraft:block/custom/side"
tip

textures vs generation

Both are under model and mutually exclusive:

textures (list)generation (object)
Syntaxmodel with path + texturesmodel with path + generation
Use caseSimple blocks, textures onlyBlocks needing a specific parent (e.g., leaves, cross, stairs)
pathOptional for 1 texture, required for 2+Required
# textures list (auto-infer model structure)
model:
path: "minecraft:block/custom/my_block"
textures:
- "minecraft:block/custom/my_block_bottom"
- "minecraft:block/custom/my_block_top"
- "minecraft:block/custom/my_block_side"

# generation object (manually specify parent model)
model:
path: "minecraft:block/custom/my_leaves"
generation:
parent: "minecraft:block/leaves"
textures:
all: "minecraft:block/custom/my_leaves"

Entity Renderer​

The main use of a block entity renderer is to work around the limit on block variants — each vanilla block only provides so many states, so the number of custom blocks you can create is bounded. By clearing a vanilla block's state with transparent: true and placing a display entity on the block, you can render items, floating text, or 3D models and create virtually unlimited custom blocks.

state:
auto_state: # share one cleared vanilla block across many custom blocks
type: sugar_cane
id: transparent
transparent: true # clear the vanilla block's own model
entity_renderer: # render the block through a display entity
type: item_display
item: default:my_decoration
tip

The transparent convention

Always give the transparent auto_state a fixed id — the recommended name is transparent. Blocks that use the same id bind to the same cleared vanilla block, so instead of each block consuming its own state, they all share one. Following this convention also makes your transparent renders predictable across packs and compatible with others'. Other developers and users are encouraged to adopt the same transparent id.

entity_renderer accepts a single object or a list, and type can often be omitted (inferred from item / text). For all element types (item_display, text_display, item, armor_stand, â€Ļ), display parameters, tinting, culling, and conditions, see Block Entity Renderer.

important

transparent + entity_renderer

Without transparent: true, the block keeps its model and the entity renders on top of it — both are visible. Set transparent: true on the same state/appearance to strip the block's own model entirely, so the block is rendered solely by the entity_renderer. This frees up vanilla block capacity, since one cleared vanilla block can back many different renders.

Multi-State Blocks​

When a block needs multiple internal states, use the states config section.

tip

When do you need multi-state?

  • Block has orientation (logs, pillars, stairsâ€Ļ)
  • Block can be waterlogged (waterlogged)
  • Block needs multiple variants (leaves' distance/persistent)
  • Block needs redstone interaction (powered, open)

The states section consists of three sub-sections, configured in order:

states:
properties: # ① Define internal properties
appearances: # ② Define available visual appearances
variants: # â‘ĸ Map internal states to appearances (optional)

Progressive Examples​

Using a log block as an example, showing the configuration from simplest to most complete.

blocks:
default:palm_log:
states:
properties:
axis:
type: axis
default: "y"
appearances:
default:
auto_state: solid
model:
path: "minecraft:block/custom/palm_log"

Only one default appearance. Since the plugin falls back to the first appearance when no variant matches, all three internal states (axis=x, axis=y, axis=z) will all use this default appearance. Because the property name is axis (a special name), the plugin automatically handles placement direction.

blocks:
default:palm_log:
states:
properties:
axis:
type: axis
default: "y"
appearances:
axisY:
auto_state: solid
model:
path: "minecraft:block/custom/palm_log"
generation:
parent: "minecraft:block/cube_column"
textures:
end: "minecraft:block/custom/palm_log_top"
side: "minecraft:block/custom/palm_log"
axisX:
auto_state: solid
model:
x: 90
y: 90
path: "minecraft:block/custom/palm_log_horizontal"
generation:
parent: "minecraft:block/cube_column_horizontal"
textures:
end: "minecraft:block/custom/palm_log_top"
side: "minecraft:block/custom/palm_log"
axisZ:
auto_state: solid
model:
x: 90
path: "minecraft:block/custom/palm_log_horizontal"
generation:
parent: "minecraft:block/cube_column_horizontal"
textures:
end: "minecraft:block/custom/palm_log_top"
side: "minecraft:block/custom/palm_log"
variants:
axis=y:
appearance: axisY
axis=x:
appearance: axisX
axis=z:
appearance: axisZ

Three directions mapped to three appearances. axis=y gets the vertical model, axis=x and axis=z get horizontal models (with different rotation angles).

blocks:
default:palm_log:
states:
properties:
axis:
type: axis
default: "y"
appearances:
axisY:
auto_state: solid
model:
path: "minecraft:block/custom/palm_log"
textures:
- "minecraft:block/custom/palm_log_top"
- "minecraft:block/custom/palm_log_side"
axisX:
auto_state: solid
model:
x: 90
y: 90
path: "minecraft:block/custom/palm_log_horizontal"
generation:
parent: "minecraft:block/cube_column_horizontal"
textures:
end: "minecraft:block/custom/palm_log_top"
side: "minecraft:block/custom/palm_log"
axisZ:
auto_state: solid
model:
x: 90
path: "minecraft:block/custom/palm_log_horizontal"
variants:
axis=y:
appearance: axisY
axis=x:
appearance: axisX
axis=z:
appearance: axisZ

The axisY appearance uses textures shorthand (2 textures → plugin infers cube_bottom_top), no need to write generation manually.

Note: axisX and axisZ need a different parent model that textures inference doesn't cover, so they still use model + generation.

Each appearance can carry its own entity_renderer, just like model. This block rotates a display item to match its facing direction. All four appearances share the same cleared vanilla block (a sugar_cane state, via the transparent id) and set transparent: true, so one cleared block backs every facing — the block is drawn entirely by the entity in each direction.

blocks:
default:sign_post:
states:
properties:
facing:
type: horizontal_direction
default: north
appearances:
north:
auto_state: # share one cleared vanilla block across all facings
type: sugar_cane
id: transparent
transparent: true # drop the block's own model
entity_renderer: # render solely via the entity
type: item_display
item: default:sign_post
rotation: 0 # facing north
east:
auto_state:
type: sugar_cane
id: transparent # same id → same cleared vanilla block
transparent: true
entity_renderer:
type: item_display
item: default:sign_post
rotation: 90 # facing east
south:
auto_state:
type: sugar_cane
id: transparent
transparent: true
entity_renderer:
type: item_display
item: default:sign_post
rotation: 180
west:
auto_state:
type: sugar_cane
id: transparent
transparent: true
entity_renderer:
type: item_display
item: default:sign_post
rotation: 270
variants:
facing=north:
appearance: north
facing=east:
appearance: east
facing=south:
appearance: south
facing=west:
appearance: west

See Block Entity Renderer for rotation, translation, and the other element types.

Property Details​

Properties define all possible internal states of a block. The plugin computes the Cartesian product of all properties to automatically generate every internal state.

properties:
waterlogged:
type: boolean # true / false
default: false
distance:
type: int # integer range
default: 7
range: 1~7 # min~max
facing:
type: horizontal_direction # north / south / west / east
default: north

Each type corresponds to a fixed set of possible values. E.g., boolean = {true, false}, horizontal_direction = {north, south, west, east}. The Cartesian product of all property values gives all possible states.

Example: waterlogged(2) × distance(7) = 14 internal states

For a complete list of property types, see the â„šī¸ Properties subpage.

tip

Special property names Some property names trigger hardcoded placement behaviors:

Property nameTriggered behavior
axisAuto-align to player's facing axis on placement
facingAuto-align to player's facing direction (6 directions)
facing_clockwiseAuto-align to player's facing direction (4 directions, rotated 90°)
rotationPrecise rotation control (type: int, range: 0~7 or 0~15)
waterloggedDetermines whether the block is waterlogged

If you don't use these special names (e.g., custom_axis), no automatic rotation is triggered — the block always uses the default value.

Appearance Details​

appearances defines all possible visual appearances for a block. Each appearance follows the exact same configuration rules as single-state blocks (auto_state/state + model/models/textures/transparent/entity_renderer). See Block Entity Renderer for the entity_renderer option.

Appearance names can be freely defined, as long as they are unique within the same block. The first defined appearance is the "default" — when an internal state doesn't match any variant's appearance, it automatically uses this one.

appearances:
anyNameA: # ← first = default appearance
auto_state: solid
model: ...
anyNameB:
auto_state: solid
model: ...

Variant Matching Rules​

variants maps internal states to visual appearances. Its core is the variant key matching logic.

Variant Key Format​

variants:
waterlogged=false: # constrains one property
appearance: default
waterlogged=true: # constrains one property + overrides settings
appearance: waterlogged
settings:
resistance: 1200.0
fluid_state: water
facing=up: # constrains one property, overrides settings only
settings:
luminance: 15

Variant key = property_name=value pairs, multiple properties separated by commas. Order does not affect matching.

Three Core Matching Rules​

Rule 1 — Wildcard semantics

Properties not listed in a variant key match all possible values of that property. You constrain what you write; what you don't write has no restriction.

Variant keyActual match
waterlogged=falseAll waterlogged=false states, regardless of persistent and distance values
facing=upAll facing=up states, other properties unrestricted
axis=yAll axis=y states, other properties unrestricted

Rule 2 — Multi-entry overlay

When the same internal state matches multiple variant entries:

  • appearance: takes the value from the first matching entry (later entries don't override)
  • settings: all matching entries' settings are merged and overlaid; later entries override earlier ones for the same key

Rule 3 — Default fallback

States that don't match any variant with an appearance → use the first appearance under appearances.

Complete Example: Leaves​

A leaves block with 3 properties: waterlogged(2) × persistent(2) × distance(7) = 28 internal states.

blocks:
default:palm_leaves:
states:
properties:
waterlogged:
type: boolean
default: false
persistent:
type: boolean
default: true
distance:
type: int
default: 7
range: 1~7

appearances:
default: # first appearance → default fallback
auto_state: leaves
model:
path: "minecraft:block/custom/palm_leaves" # path optional for 1 texture, shown explicitly
textures:
- "minecraft:block/custom/palm_leaves"
waterlogged:
auto_state: waterlogged_leaves
model:
path: minecraft:block/custom/palm_leaves

variants:
waterlogged=false: # → covers 14 states
appearance: default
waterlogged=true: # → covers 14 states
appearance: waterlogged
settings:
resistance: 1200.0
burnable: false
fluid_state: water

Matching walkthrough — using internal state waterlogged=true, distance=7, persistent=true as an example:

Iterating variants:
waterlogged=false → ❌ no match (waterlogged value differs)
waterlogged=true → ✅ match → appearance=waterlogged
→ inherits settings: {resistance:1200, burnable:false, fluid_state:water}

Result:
appearance = waterlogged
settings = {resistance:1200, burnable:false, fluid_state:water}

Just 2 variant entries cover all 28 internal states — using wildcard semantics, waterlogged=false and waterlogged=true each cover 14.

Example A: Single variant constraining multiple properties

variants:
waterlogged=true,facing=north:
appearance: waterlogged_north

Only matches internal states that satisfy both waterlogged=true and facing=north.

Example B: Override settings only, no appearance change

variants:
powered=true:
settings:
luminance: 7

All powered=true states get luminance 7, appearance falls back to default.

Example C: Same state matching multiple variants

variants:
waterlogged=true:
settings:
resistance: 100.0
fluid_state: water
facing=up:
settings:
resistance: 50.0 # ← overrides the previous 100.0, final resistance=50.0
luminance: 15 # ← new addition, final luminance=15

If an internal state matches both waterlogged=true and facing=up, its final settings are: {resistance:50.0, fluid_state:water, luminance:15} (resistance overridden by the second entry).

Variant Count Quick Reference​

When configuring multi-state blocks, knowing the variant total helps estimate ID usage:

Total variants = ∏ possibleValues count of each property

boolean → 2
int(range) → max - min + 1
axis → 3
direction → 6
horizontal_dir → 4
half / hinge → 2
slab_type → 3
stairs_shape → 5

E.g., waterlogged(2) × persistent(2) × distance(7) = 28 variants

Configuring Internal Block ID​

Usually unnecessary. Only configure when you need fixed internal IDs (e.g., for data pack compatibility).

# Single-state: directly specify
state:
id: 0
auto_state: note_block
model: ...

# Multi-state: specify starting ID; subsequent variants auto-occupy a continuous range
states:
id: 100 # 28 variants → occupies 100~127
properties: ...
  • ID must be globally unique
  • Cannot exceed the serverside-blocks limit in config.yml