Block Configuration

Register the placeable, world-generated blocks that make up terrain, walls, and fluids.

Blocks are the tiles that make up the world — terrain, walls, fluids, and any other placeable tile content. This is the catalog file that registers them. Server and client each load their own copy of this file independently (a few fields, noted below, are server-only).

When many blocks share the same field clusters (the repeated tool-break-types / time-to-break-ms / power-required breaking triplet, for instance), you can factor the shared shape into a preset and give each block a preset: key instead of repeating it — see Config Presets.


File Location

Register the file in your mod’s manifest (mods/<YourMod>/<YourMod>.yaml):

block-config: block-config.yaml

Then create the file it points to, relative to your mod’s folder:

mods/<YourMod>/block-config.yaml

The file is a single list of block entries:

blocks:
  - name: my_block
    tool-break-types: pickaxe

Block names are automatically prefixed with your mod name unless you write one already containing a colon, so my_block becomes YourMod:my_block — this is the full name used anywhere a block is referenced (world generation, item drops, structures, Lua scripts).

Block IDs are assigned dynamically in load order, with one exception: Creation:air is always ID 0.

Top-level field names are validated on load: an unknown or misspelled key (for example time-to-brek-ms instead of time-to-break-ms) is a load-time error that skips that block, rather than being silently ignored. If a block doesn’t behave as expected, check the log for a parse error naming the offending field. The nested blocks are validated the same way — an unrecognized key inside a light, block-shape, particles, scripts, or drop-table entry block is also a load-time error naming the bad key, not a silently-ignored one (see Unknown Fields).


Minimal Example

blocks:
  - name: my_ore_block
    tool-break-types: pickaxe
    power-required: 2
    time-to-break-ms: 1500
    drop-table:
      drops:
        - item: YourMod:my_ore_item
          min-amount: 1
          max-amount: 1
          chance: 1.0

Fields

FieldDefaultDescription
name(required)The block’s registered name.
collidabletrueWhether entities collide with this block.
breakabletrueWhether it can be destroyed at all. false makes it permanently indestructible regardless of the other breaking fields below.
tool-break-typesNoneWhich tool category or categories break it — a single value or a list. Omit for no restriction (breakable by any tool or a bare hand); use breakable: false for a permanent block.
power-required1Minimum tool power level required to break it — compared against a tool’s tool-info.power-level, and unrelated to a crafting station’s tier. See Schema Conventions → Tool power vs. crafting tier.
time-to-break-ms1000How long it takes to break with a tool that meets power-required exactly (faster with a higher-power tool).
tags(none)Membership tags this block carries — a single value or a list. A reference field that accepts a #tag (a block texture’s tiles-with, a mob’s valid-spawn-blocks) then matches every block carrying it. Global and case-insensitive.
block-layer1Which layer this block occupies.
particles(none)Client-only. Ambient particle emission — a list of emitter groups, each with its own emit-random and optional only-emit-when-exposed-below. See the particles: block.
itemtrueWhether registering this block also registers its type: block item, making it obtainable and placeable. Set false for a block that must never exist in an inventory — see Block items.
light(none)Makes the block emit light — same shape as Shared Blocks → light, plus an optional animated frames list.
light-transmission1.0Fraction of light the block lets through: 1.0 passes light unattenuated, 0.0 blocks it completely. Point lights scale continuously (e.g. 0.75 costs 25% of the ray per block crossed); sky light only passes undimmed at exactly 1.0, and costs one level per block below that.
particles(none)Ambient particle emitters for this block — see internal_docs/rendering.md’s Block Particles section (not yet published as a modder-facing doc).
drop-table(none)Items dropped when broken — independent drops: and/or weighted groups:; see Drop Table below. Server-only.
drops-selffalseShorthand for “drops one guaranteed copy of itself” — see Drop Table below. Ignored if drop-table is also set. Server-only.
phase-allfalsePhase in all four directions at once — see Directional Phasing.
phase(none)A list of directions to phase, e.g. [up, down] — see Directional Phasing.
suffocates(derived)Whether an entity buried in this block takes suffocation damage. Unset means “yes, unless the block phases in some direction” — see Suffocation. Server-only.
drag (up, down, left, right)0.0 eachMovement drag applied to entities passing through the block, per direction — used for fluids. Each direction defaults independently, so you can set only the ones you need. Not an {x, y} pair: drag is genuinely per-direction (a fluid can resist sinking more than rising).
contact-damage (damage-per-second, type)(none)Typed damage dealt to any entity overlapping this block — lava, spikes, thorns. See Contact Damage below. Server-only.
block-matter-typeSolidOne of Solid, Liquid, Gas.
fluid-level0.0Fill percentage for fluid blocks.
fluid-flow-per-second0.0Fluid flow rate, in level per second. Server-only.
fluid-minimum-flow-threshold0.0Minimum level difference before fluid flows between cells. Server-only.
variant-of(none)Declares this block as a named variant of another block — e.g. snowy_grass is the snow variant of grass. A back-reference, registered into the base block’s variant table and looked up from Lua via get_block_variation. See Variant Of below. Server-only.
block-shapeFullFull or Slab — see Block Shape below.
scripts.server-on-interact(none)A Lua hook run when a player interacts with a placed instance of this block. Server-only.
interact-cooldown-ms0Minimum time between interacts on a single cell of this block. Server-only.
scripts.server-update(none)A per-tick Lua hook run on every placed instance of this block. Server-only.
falls-when-unsupportedfalseMakes the block fall straight down when nothing solid is beneath it — see Falling Blocks below. Server-only.
unstable-formation (trigger-radius, telegraph-ms, impact-damage, damage-type, impact-height)(none)Makes the block a triggered falling hazard (dripstone stalactite): it arms on player proximity, telegraphs, then falls and crushes what is below — see Unstable Formations below. Server-only.
required-capability(none)Marks the block a sealed barrier. A player interacting with it opens it (permanently) only if they hold this capability. Server-only.
required-item(none)Sealed-barrier requirement: the player must have this item in their inventory to open the barrier. Combined with required-capability by AND. Server-only.
consume-required-itemfalseIf true, opening the barrier removes one required-item from the player. Ignored without required-item. Server-only.
reveals-blockairThe block a sealed barrier becomes when opened (default: cleared to air). Server-only.
sealed-message(none)Message shown to a player who interacts with a sealed barrier without meeting its requirement. Server-only.
auto-openfalseBarrier opens automatically for a qualifying player instead of on interact — see Sealed Barriers. Only applies to pure required-capability barriers. Server-only.

Block items

A block automatically gets a matching item — same name, type: block — so it can be picked up and placed without you writing anything in item-config.yaml. Declaring a block is enough to make it obtainable.

Write an entry in item-config.yaml only when the item needs more than the defaults — a display-name, a tooltip, a material-type for crafting, or a light on the dropped stack. An explicit entry with the same name replaces the generated one entirely, so you keep full control when you want it.

# block-config.yaml — this alone gives you a placeable, obtainable block
blocks:
  - name: marble
    tool-break-types: pickaxe
    drops-self: true

Set item: false for a block that should never be holdable — a marker or internal block placed only by world generation or Lua:

  - name: my_internal_marker
    item: false

You still need an item texture. The generated item is a config entry, not an asset — add the block’s icon to assets/textures/items/items.yaml as normal (see Item Texture Configuration).

Interactables work the same way, via their own item field.


Contact Damage

Give a block a contact-damage: block to make it hurt any entity — players, mobs, mounts — whose hitbox overlaps it. This is how lava, spikes, brambles, and other damaging terrain deal damage:

- name: lava
  collidable: false
  block-matter-type: liquid
  contact-damage:
    damage-per-second: 6.0
    type: fire
KeyDefaultMeaning
damage-per-second0.0Continuous damage rate while an entity stays in contact, scaled by the tick delta (like drowning) — not a per-hit amount. A quick brush costs a fraction; standing in it costs the full rate each second. A value of 0 (or omitting the block) means no contact damage.
type(required)The damage type, one of physical, fire, blast, decay, radiant (see Damage Types). An unknown name is a config error: it is logged and the block is left dealing no contact damage.
ward-capability(none)Optional. Turns the block into a hazard-field floor: a player holding this capability takes no contact damage, and a player without it takes it bypassing armor (a ward, not gear, is the only defense). Omitted → an ordinary armor-mitigated contact hazard. Mobs and mounts hold no capabilities, so they always take the block’s contact damage, mitigated as usual.

Behaviour:

  • Passable blocks only. Contact damage applies while an entity overlaps the block’s cell, so it fits fluids and other non-collidable hazards you walk into (collidable: false). A solid block is stood on, not entered, so a full collidable block never deals contact damage through this property.
  • Mitigated by armor (unless warded). Contact damage is reduced by the victim’s armor (a fire-resistant set will one day reduce lava specifically), but it is not stopped by an active block/parry and is not raised to the min-damage floor — a per-tick rate stays proportional to the tick. A ward-capability block is the exception: for a player without the ward it bypasses armor entirely (and holding the ward negates it outright).
  • Environmental. It has no attacker, so it never triggers or is blocked by invulnerability frames, and it never counts as a kill credit for another entity.
  • Server-authoritative. The check runs in the server-side entity tick; noclipping players pass through unharmed.

Falling Blocks

Setting falls-when-unsupported: true makes a placed block (sand, gravel, and the like) fall straight down whenever the cell directly below it is not solid support:

- name: sand
  tool-break-types: shovel
  falls-when-unsupported: true

Behaviour:

  • Vertical only. The block drops straight down one cell at a time and settles as soon as it lands on a solid block. There is no sideways sliding or pile-forming.
  • Fluids are destroyed. A falling block falls through fluid, erasing the fluid in each cell it passes.
  • Runtime only. Naturally world-generated blocks of a falling type do not fall on their own — a falling block only starts falling when it (or the block beneath it) is placed or removed during play. This matches Minecraft: generated overhangs of sand stay put until disturbed.
  • Foreground layer only. Falling applies to the foreground collision layer (block-layer: 1); it has no effect on background/wall layers.

Unstable Formations (falling hazard)

An unstable-formation: block is a triggered falling hazard — a dripstone stalactite, a collapsing crust. Unlike falls-when-unsupported, it does not fall just because nothing is beneath it: it hangs inert until a player comes close, telegraphs its collapse, then detaches and falls through the shared falling-block system, crushing whatever is below where it lands.

- name: dripstone
  tool-break-types: pickaxe
  drops-self: true
  unstable-formation:
    trigger-radius: 3.0     # blocks; a player this close arms it
    telegraph-ms: 700       # dust-tell delay before it detaches
    impact-damage: 25.0     # dealt once, on landing
    damage-type: physical   # physical | fire | blast
    impact-height: 3        # blocks above the landing cell that get crushed

Lifecycle:

  1. Inert. The block sits like any other solid block.
  2. Armed / telegraphing. When a player’s position comes within trigger-radius blocks, the block arms and emits a repeating dust tell (the same particle burst as block-breaking) for telegraph-ms.
  3. Falling. After the telegraph it detaches and falls straight down one cell at a time, reusing the falling-block system (so it destroys fluid it passes through and settles on the first solid block).
  4. Impact. On landing it deals impact-damage once, of damage-type, to every entity whose feet are in the block column reaching impact-height cells up from the landing cell (plus the cell it lands on). The hit is mitigated by armor and typed resistance, like contact damage. Set impact-damage: 0 for a harmless (visual-only) collapse.

Fields (all optional; sensible defaults shown above):

FieldDefaultMeaning
trigger-radius3.0Player distance in blocks that arms the formation.
telegraph-ms700Delay between arming and detaching, during which the dust tell plays.
impact-damage0.0Damage dealt once on landing. 0 = falls harmlessly.
damage-typephysicalMitigation channel of the impact hit (physical / fire / blast).
impact-height3Height of the crushed column, measured up from the landing cell.

Notes:

  • One-shot. A formation only arms while the cell directly below it is open (air or fluid) — i.e. it can actually fall. Once it lands on solid ground it becomes inert rubble: it will not re-arm, re-telegraph, or re-crush when a player walks past. (This is why it must hang with open space beneath it, like a ceiling stalactite.)
  • Proximity trigger only (players). Only player proximity arms a formation today; mobs walking underneath do not.
  • Foreground layer only, like falling blocks.
  • Reusable. The property is data-driven, so it is not dripstone-specific — any block can be an environmental collapse hazard (e.g. an unstable ceiling crust) by declaring unstable-formation:.

Sealed Barriers (capability-gated)

A sealed barrier is a wall block that a player can only pass once they meet a requirement — the Metroidvania “locked door” primitive. A block becomes a barrier as soon as it declares required-capability and/or required-item:

# Opens only for a player who holds the `storm_ward` capability.
- name: sealed_stormward
  breakable: false
  required-capability: storm_ward
  reveals-block: air            # what it becomes when opened (default: air)
  sealed-message: "The way is sealed."

# Opens for a player carrying a `void_key` item, consuming one on use.
- name: sealed_vault_door
  breakable: false
  required-item: void_key
  consume-required-item: true

Behaviour:

  • Interact to open. When a player interacts (uses) a barrier and meets the requirement, the block is permanently swapped to its reveals-block (default: cleared to air). The change is broadcast to every client and saved with the world, so it stays open.
  • Requirements combine by AND. If both required-capability and required-item are set, the player needs both. consume-required-item removes one matching item on a successful open.
  • Gates who opens, not who passes. Once opened the way is open for everyone (co-op friendly); the requirement controls who can open it.
  • Feedback. A player who interacts without qualifying is sent sealed-message (or a default message if unset).
  • Foreground layer only, checked on the collision layer (block-layer: 1). Make barriers breakable: false so they can only be removed by opening them.

Auto-opening barriers

Interacting with every block of a large sealed wall is tedious. Set auto-open: true and the barrier clears on its own for a qualifying player — no interact:

- name: sealed_stormward
  breakable: false
  required-capability: storm_ward
  auto-open: true

It triggers both when a qualifying player streams the chunk (walks into the area with the capability) and the moment they gain the capability while standing among already-loaded barriers — so a whole wall falls away at once. Notes:

  • Capability barriers only. auto-open is ignored for required-item barriers (a key must never be consumed without a deliberate interact); it logs a warning at load if combined with required-item or used without required-capability.
  • World-wide, first-come. Because opening broadcasts and persists, an auto-open barrier vanishes the instant the first qualifying player reaches it — for everyone. That fits “once you have the ability, these gates are just gone”; leave auto-open off if you want a barrier the player must deliberately open.

Capabilities are free-form string ids. Grant them from Lua with player:grant_capability("storm_ward") (see Server Entity API) — typically from a boss-death, item-use, mount, or interactable script. required-item refers to an item name (namespaced to the block’s mod, like drop-table items).

Placement. Barriers are ordinary blocks, so place them by authoring them into a structure or via world generation — no special placement mode is needed.

Tool-tier region seals

A sealed barrier gates who passes (opened by an interact once you hold the key). The other Metroidvania hard-gate is a tool-tier seal: a wall you can’t mine until you carry a high-enough-power tool. It needs no special fields — it’s just power-required and tool-break-types tuned above the tool tier available before the gate:

# Unbreakable by every tool in the pre-gate tier ladder; opens only once a
# power-5 pickaxe exists (e.g. a per-world material upgrade).
- name: sealed_deepstone
  tool-break-types: pickaxe   # a bare hand is power 1 and has no pickaxe, so hands never work
  power-required: 5

Why this is a genuine seal, not just slow:

  • A below-tier tool does zero damage — it is a hard gate, not a speed penalty, so no amount of grinding chips through. Mining progress only accrues once the wielded tool’s power-level meets power-required.
  • Bare hands are power 1 with tool types [shovel, axe, shears] (no pickaxe). Requiring tool-break-types: pickaxe alone already stops hand-mining; a high power-required then stops every tool below the intended tier.
  • Tune it against your ladder. The base game ships tools at power-level 2–4, so power-required: 5 seals a region against all current tools until a higher-tier tool is introduced. Pick a value one step above whatever the player can obtain before the gate.
  • Sandbox-robust. Because a builder can neither dig through nor (with a tall enough wall / sealed ceiling) tower past a wall they can’t break, this is the map’s backbone gate — the level geometry decides “can’t pass,” not a scripted check.

Like barriers, tool-tier seals are ordinary blocks — author them into a structure or lay them in world generation.


Tags

tags are free-form membership labels a block carries, so that another config can refer to a group of blocks at once instead of listing each by name. A block declares them plural, as a single value or a list:

- name: iron_ore
  tags: ore            # one tag
- name: coal_ore
  tags: [ore, fuel]    # several

Wherever a field accepts a #tag reference, writing #ore matches every block carrying the ore tag. Two fields accept it today:

# In block-texture-config.yaml — connect to every ore block, whoever defined it:
tiles-with: ["#ore"]

The # sigil is what distinguishes a tag from a block name: #ore is the tag, a bare ore is the single block literally named ore. (You’ll recognise # from Minecraft’s tag syntax.)

Tags differ from a content name in two ways worth knowing:

  • Global, never mod-prefixed. A tag is not qualified with your mod name the way a block name is — the whole point of #ore is that ore blocks from any mod match it. Two mods both tagging their ores ore contribute to the same group.
  • Case-insensitive. ore, Ore and ORE are one tag (they normalize to upper-case), unlike the other open registries where casing is preserved. A tag is still an open registry like tool-break-types — you invent whatever you need, so a typo is a new (empty) tag rather than a load error. A #tag that no block carries is reported where it’s used (e.g. a valid-spawn-blocks entry fails to load), not silently ignored.

tool-break-types

One tool category, or a list of them — each one of shovel, pickaxe, axe, shears — matched against the wielded tool’s own tool-types list. A block breaks if the wielded tool matches any listed category. This is the same shape interactables and trees use, so a single value (tool-break-types: Shovel) and a list (tool-break-types: [Pickaxe, Axe]) are both accepted.

Those four are what the base game ships, not the complete set — tool categories are an open registry, so tool-break-types: hammer is a valid requirement as soon as some item declares tool-types: hammer.

The consequence to know is what a typo does. tool-break-types: pickaxee is not a load error — it names a category, just one no item has — so the block becomes breakable by nothing. That is deliberately the safe direction: it used to resolve to “no tool requirement”, leaving a pickaxe-only block breakable by hand, which is both the opposite of what was written and indistinguishable from omitting the field. The server also warns at load, naming the block and the unmatched category.

Leaving this unset means “no tool requirement” — the block breaks with a bare hand or any tool (still subject to power-required). List one or more categories to restrict which tools work; use breakable: false to make a block permanent. Bare hands are internally treated as a tool with tool-types: [Shovel, Axe, Shears] (no pickaxe) at power level 1, so:

  • A block with no tool-break-types breaks with anything, including a bare hand.
  • A block with tool-break-types: Axe and the default power-required: 1 can be broken by hand (a hand has Axe).
  • A block with tool-break-types: Pickaxe can never be broken by hand — only by a wielded item with pickaxe in its own tool-types.
tool-break-types: pickaxe        # single category
# or
tool-break-types: [pickaxe, axe] # any of several

block-layer

Which of the world’s layers this block occupies:

LayerUse
0Background/walls — non-collidable.
1Foreground — default. Collision, entities, players. Fluids belong here too, so entities passing through them pick up the block’s drag-* physics.
2+Overlay — effects drawn on top of the foreground.

Drop Table

Blocks use the same shared drop-table block as interactables, mobs, trees, and mounts. It has two optional parts — independent drops: and weighted pick-one groups::

drop-table:
  groups:                      # weighted pick-one — each group yields at most one entry
    - entries:
        - item: YourMod:dirt
          min-amount: 1
          max-amount: 1
          weight: 0.9
        - item: YourMod:stone
          min-amount: 1
          max-amount: 1
          weight: 0.1
  drops:                       # optional: independent rolls (e.g. a rare bonus drop)
    - item: YourMod:flint
      chance: 0.1

Each group rolls once, picking one entry weighted by weight relative to the group’s total (the classic “always drop the block, weighted by variant” pattern); each drops entry rolls independently on its own chance (a 0.01.0 probability). The two keys are different words because they behave differently — a group weight is a relative weight (a lone entry always wins), a drops chance is a probability. See the shared Drop Table reference for full semantics, and Drop Table vs Loot Table for how this differs from standalone Loot Tables.

Shorthand: drops-self

The most common case — a block that drops exactly one guaranteed copy of itself — has a one-line shorthand:

- name: stone
  tool-break-types: pickaxe
  drops-self: true

drops-self: true is exactly equivalent to writing:

drop-table:
  groups:
    - entries:
        - item: YourMod:stone      # this block's own full name
          min-amount: 1
          max-amount: 1
          weight: 1.0

It requires an item registered under the block’s own full name (blocks and items share the Mod:name namespace) — the same requirement as spelling the drop table out by hand. If you set both drops-self and an explicit drop-table, the drop-table wins and a warning is logged.


Variant Of

Registers this block as an alternate visual form of another already-defined block — for example, a snow-covered variant of grass:

- name: snowy_grass
  # ...this block's own fields...
  variant-of:
    - base-block-name: grass
      variant-name: snow

base-block-name must be defined somewhere the loader can see it — in this same file, or in a dependency loaded earlier. Order within a file doesn’t matter: a variant may be listed before or after its base block, since variations are resolved after every block in the file has been registered. At runtime, code can look up snowy_grass’s ID via GRASS’s ID plus the variant name SNOW, without either block needing to know about the other beyond this declaration.


Directional Phasing

Controls which directions a block can visually “phase” through a neighbor (used for logs, leaves, platforms, and other blocks that render flush against adjacent tiles). There are two interchangeable ways to set it — a direction is phased if either of them enables it, so you can mix them freely:

# 1. The most common case — all four directions:
phase-all: true

# 2. An explicit subset (directions: up, down, left, right):
phase: [up, down]

phase-all: true is the concise form for the usual “phase in every direction” case; phase: [...] is for an explicit subset. Both forms produce the same underlying per-direction flags.


Suffocation

An entity whose body a block occupies is buried, and takes the suffocation damage configured by its health.suffocation-damage-per-second (see health-config.md). suffocates controls whether this block counts:

# A solid block that should never suffocate — decorative fog, a soft snowdrift:
suffocates: false

# A phasable block that should suffocate anyway — a bramble the player chokes in:
phase-all: true
suffocates: true

Left unset, it is derived: a block that phases in any direction does not suffocate, and every other block does. Phasable blocks (leaves, platforms, logs) are meant to be walked into, so standing inside one is ordinary movement — a player crossing their base on wooden platforms must not choke on them. Set the field explicitly only to override that default in either direction.

The same flag decides whether a block counts as terrain a player is embedded in at spawn: a player who loads in inside a non-suffocating block is left where they are rather than lifted out of it.


Block Shape

block-shape:
  type: slab
  divisions: 8
  side: bottom
  count: 1
FieldDefaultDescription
typeFullFull (a normal full-size block) or Slab (a partial-height block).
divisions2Slab only. How many pieces a full block is divided into. Must be greater than 0.
count1Slab only. How many of those divisions the slab fills. Must be greater than 0. E.g. divisions: 8, count: 1 is a one-eighth-thick slab.
sideBottomSlab only. Which edge of the cell the slab sits against: Top, Bottom, Left, or Right. See the note below — this does not control which way a player-placed slab faces.

These fields set the shape a block starts with wherever it is placed without a player aiming at it — world generation, Lua, and similar.

side and player placement: when a player places a slab, its side is always taken from where the cursor sits within the target cell — the lower half of the cell gives a Bottom slab, the upper half gives Top, and with horizontal placement toggled off the left/right halves give Left/Right. The configured side is overridden and has no effect on that placement. So setting side: Top does not make a block place as a top slab; it only affects instances placed by world generation or a mod script. divisions and count are always used as configured.


Animated Light Frames

If a block’s light block declares frames, its emitted light pulses in step with its texture animation instead of staying static:

light:
  magnitude: 3.0
  color: { r: 1.0, g: 0.9, b: 0.4, a: 1.0 }
  frames:
    - magnitude: 4.0
      intensity: 0.8
    - magnitude: 3.0

Each frame entry corresponds to one frame of the block’s texture animation (see the block’s texture config). magnitude/intensity are both optional per frame and fall back to the light’s own base magnitude/intensity when omitted. Declaring any frames forces the light to be dynamic rather than static — reserve this for a handful of accent lights, not bulk content, since dynamic lights are considerably more expensive to render.


Lua Hooks

Both hooks are declared under a single scripts: map, keyed by the event name, with the bare path to a .lua file that returns its handler function (the same shape as items’ server-on-primary-use):

scripts:
  server-on-interact: /blocks/scripts/my_block_interact.lua
  server-update: /blocks/scripts/my_block_tick.lua
interact-cooldown-ms: 200
  • server-on-interact runs when a player interacts with a placed instance of this block. Its handler receives (world, x, y, layer, actor_entity_id, actor), where actor is the interacting player’s entity object — use actor:has_item(...), actor:grant_capability(...), actor:send_message(...), etc. (Older five-argument handlers that ignore actor keep working.) interact-cooldown-ms sets a minimum time between interacts on the same cell (0 means no cooldown).
  • server-update runs every server tick for every placed instance of this block. Use sparingly — a block with this hook joins the world’s ticking-blocks registry, so placing many instances means many per-tick Lua calls.

Complete Example

Based on a real light-emitting, interactable block:

blocks:
  - name: glow_rock
    tool-break-types: pickaxe
    time-to-break-ms: 1000
    power-required: 2
    scripts:
      server-on-interact: /blocks/scripts/glow_rock_interact.lua
    interact-cooldown-ms: 200
    light:
      magnitude: 3.0
      color:
        r: 1.0
        g: 0.945
        b: 0.4627
        a: 0.9
      drop-off-distance: 4.0
      is-static: true
    drop-table:
      drops:
        - item: YourMod:glow_rock
          min-amount: 1
          max-amount: 1
          chance: 1.0

And a fluid block:

  - name: water
    collidable: false
    breakable: false
    block-layer: 1
    drag:
      up: 0.5
      down: 0.5
      left: 0.8
      right: 0.8
    block-matter-type: liquid
    fluid-level: 1.0
    fluid-flow-per-second: 5.0
    fluid-minimum-flow-threshold: 0.5

Last updated