Shared Blocks
The nested blocks that appear identically across many configs — hitbox, light, drop-table, scripts and particles — documented once, with the per-config differences called out.
Most of a config file’s fields belong to that file. A handful don’t: the same nested block, with the same keys and the same meaning, appears inside blocks, items, mobs, mounts, trees, interactables and biomes. This page is the reference for those, so a light: block means one thing everywhere and you only have to learn it once.
Each section below says which configs accept the block and flags any per-config difference. Where a config genuinely bends the shared shape, its own page says so — but the shape starts here.
| Block | Accepted by |
|---|---|
hitbox | items (inside wielded-item-data), mobs, mounts, trees, interactables, projectiles, player |
light | blocks, items, interactables, particles |
drop-table | blocks, mobs, mounts, trees, interactables |
scripts | blocks, items, mobs, mounts, interactables, weather, player |
particles | blocks, trees, interactables, biomes |
hitbox
A rectangle, in blocks, as floats. Both keys are required when the block is present.
hitbox:
width: 2.0
height: 3.0
| Field | Required | Description |
|---|---|---|
width | Yes | Width in blocks. |
height | Yes | Height in blocks. |
Two notes on shape and units:
- It is
width/height, notx/y. Sizes usewidth/heightthroughout the schema;x/yis for positions, offsets and velocities. See Schema Conventions → 2D vectors. - Blocks, not pixels. A
2.0 × 3.0hitbox is two blocks wide and three tall (32 × 48 px at the defaultBLOCK_SIZE_PX).
The rectangle is anchored at the entity’s position and is the box used for collision and for hit detection. A mob may additionally declare damage-hitbox, an identically-shaped block that replaces the collision hitbox for taking hits — use it when a mob’s art is much larger or smaller than the volume it should physically occupy. When absent, the mob is hit on its hitbox.
Whether
hitboxis optional is decided by the config, not by this block. Both keys are required within the block, so a half-writtenhitbox: { width: 2.0 }always fails — but what an entirely absenthitboxdoes varies: a mob or interactable is skipped with an error (it’s a required field there), a projectile falls back to1.0 × 1.0, a tree variation inherits the tree-level default, and the player falls back to the engine baseline. Each config’s own page gives its rule.
light
Makes the thing that carries it emit light. Accepted on a block (the placed block glows), an item (dropped copies lying on the ground glow), an interactable, and a particle.
light:
magnitude: 8.0 # optional, default 4.0. Radius of the full-brightness core, in blocks.
intensity: 1.0 # optional, default 1.0. Peak brightness multiplier.
color: # all four channels required if `light` is present
r: 1.0
g: 0.945
b: 0.4627
a: 0.5
drop-off-distance: 3.0 # optional, default 4.0. Length of the fade band beyond the core, in blocks.
is-static: false # optional, default false. See below.
| Field | Default | Description |
|---|---|---|
magnitude | 4.0 | Radius of the full-brightness core, in blocks. |
intensity | 1.0 | Peak brightness multiplier, 0.0–1.0. |
color | (required) | RGBA, float channels 0.0–1.0. All four required. |
drop-off-distance | 4.0 | Length of the fade band beyond the core, in blocks. |
is-static | false | Whether the light is baked into its world sector. See below. |
frames | (none) | Blocks only. Per-texture-frame magnitude/intensity overrides — see Animated lights. Accepted but unused on items, interactables and particles. |
How the three numbers combine
magnitude, intensity and drop-off-distance are often mistaken for three ways of saying “brighter”. They are not — they describe three different parts of the same falloff curve:
| Field | Unit | What it controls |
|---|---|---|
magnitude | blocks | The radius of the full-brightness core. Everything within this distance is lit at full intensity. It is a distance, not a brightness. |
intensity | multiplier (0.0–1.0) | The peak brightness the colour is drawn at, inside the core. This is the “brighter/dimmer” knob. |
drop-off-distance | blocks | The length of the fade band outside the core, across which brightness falls linearly from intensity to zero. |
The light’s total reach is magnitude + drop-off-distance — so the example above reaches 11 blocks: 8 at full strength, then a 3-block fade to nothing.
That gives you two independent shapes to tune. A torch that lights a small area harshly is a small magnitude with a large drop-off-distance (a tight core, a long soft glow). A lantern that fills a room evenly is a large magnitude with a small drop-off-distance (a wide even pool, a sharp edge). Turning intensity down dims the whole curve without changing how far it reaches.
Setting drop-off-distance: 0 is treated as a very small positive number rather than dividing by zero, which gives you a hard-edged circle of light.
is-static
is-static controls how the light is tracked. A static light (true) is baked once into its world sector — cheaper, but it must not move; use it for lights fixed in the world. A dynamic light (false, the default) is recomputed every frame and may move with whatever carries it, at slightly higher cost. Anything that moves — a dropped item that can be kicked around, a drifting particle — must be dynamic, or its light will be left behind at the baked position.
Animated lights
A block light may add a frames: list, one entry per frame of the block’s texture animation, to pulse the light in step with the art:
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 entry takes magnitude and intensity, both optional, each falling back to the light’s own base value when omitted. Declaring any frames forces the light dynamic regardless of is-static — reserve it for a handful of accent lights rather than bulk content, since dynamic lights cost considerably more to render.
drop-table
What something leaves behind when it breaks or dies. Accepted on blocks, mobs, mounts, trees (and each tree variations entry) and interactables.
A drop-table is written one of two ways, and they are mutually exclusive:
- Inline — declare
drops:and/orgroups:here. - By reference —
loot-table:names a standalone loot table to roll instead.
drop-table:
drops: # independent rolls — each entry rolls on its own
- item: MyMod:my_item
min-amount: 1
max-amount: 1
chance: 1.0
groups: # weighted pick-one — each group yields at most one entry
- entries:
- item: MyMod:common_drop
weight: 0.9
- item: MyMod:rare_drop
weight: 0.1
drops — independent rolls. Each entry rolls independently: chance is the probability (0.0–1.0) that entry drops at all, and if it does, the amount is randomized between min-amount and max-amount. Any number of entries can drop on a single break — zero, some, or all of them.
groups — weighted pick-one. Each group is a list of entries and rolls once, selecting exactly one entry, weighted by each entry’s weight relative to the group’s total. Multiple groups are independent of each other (e.g. one group “always drop the object itself”, plus a separate “rare bonus” group). weight is a relative weight, not a probability — a lone entry in a group always wins it regardless of its number, so weight: 0.5 on a single-entry group drops every time, not half the time. (Use a drops: entry with chance: 0.5 if you want a 50% roll.)
The two selection keys are deliberately different words because they behave differently. Writing the wrong one for the context is a load-time error, not a silent mis-roll: weight inside drops:, or chance inside a groups: entry, fails the table with a message saying which key that context takes.
Entry fields
| Field | Default | Description |
|---|---|---|
item | (required) | What to drop — a bare item name, or a full nested itemstack reference. See below. |
chance | 1.0 | drops: entries only. Independent probability, 0.0–1.0. |
weight | 1.0 | groups: entries only. Relative weight within the group’s pick-one. |
min-amount / max-amount | 1 / 1 | Quantity range, inclusive. min-amount must be ≥ 0 and max-amount ≥ min-amount, or the whole table fails. |
amount | (none) | Shorthand fixing the quantity: amount: N equals min-amount: N + max-amount: N. Mutually exclusive with them — setting both is a load-time error. |
item — the thing to drop. Either a bare item name (item: MyMod:my_item, the shorthand used in every example above) or a full nested itemstack reference (item: with a name: and optional overrides — the same block an item entry uses). Every other key in a drop entry is validated: an unrecognized key — including an itemstack field written at the entry’s top level instead of nested under item: — is a load-time error naming the bad key.
loot-table: — defer to a named table
Instead of listing entries, point at a standalone loot table:
drop-table:
loot-table: boss_hoard
The table is rolled exactly as it would be when filling a container — every guaranteed entry, then min-rolls..max-rolls weighted picks — and the results drop as loose items. loot-table is mutually exclusive with drops/groups; declaring both is a load-time error rather than a silent precedence rule. The name is mod-prefixed like any other reference, and resolved when the drop happens rather than at load, so mod load order never matters. See Loot Table Configuration.
drops-self — the one-line shorthand
Blocks and interactables only. Mobs, trees and mounts share drop-table but have no drops-self.
drops-self: true
Exactly equivalent to a drop-table with a single group holding this object’s own item at chance: 1.0 and min/max of 1, and it requires an item registered under the object’s own name. If both drops-self and drop-table are given, drop-table wins and a warning is logged.
scripts
A map of Lua hook name → path to a .lua file that returns the handler function.
scripts:
server-update: /mobs/my_mob/scripts/server_update.lua
client-on-interact: /mobs/my_mob/scripts/client_interact.lua
Paths are relative to your mod’s folder, with or without a leading slash — /mobs/my_mob/x.lua and mobs/my_mob/x.lua resolve identically. It is never a filesystem-absolute path. (This is a different rule from require, which resolves module names under your mod’s scripts/ and lib/ folders — see Lua Modding Overview.)
A hook name the config doesn’t recognize is a load-time error that fails the entry. Hook sets differ per config — a mob has no server-on-primary-use, an item has no client-update — so a name that is valid on one config is a typo on another, and is reported as one. This is deliberate: a silently-unrecognized hook name is a script that never runs, with nothing in the log to say why.
Which hooks each config takes
| Config | Hooks |
|---|---|
| Mob | server-update, server-on-create, server-on-death, server-on-mounted, server-on-dismounted, server-on-dialogue-open, server-on-dialogue-choice, client-update, client-on-create, client-on-death, client-on-mounted, client-on-dismounted, client-on-interact |
| Mount | server-update, server-on-create, server-on-death, server-on-mounted, server-on-dismounted, client-update, client-on-create, client-on-death, client-on-mounted, client-on-dismounted, client-on-interact |
| Interactable | server-on-create, server-on-interact, server-on-inventory-close, server-update, client-on-create, client-on-interact, client-update |
| Block | server-on-interact, server-update |
| Item | server-on-primary-use |
| Weather | server-update, server-on-start, server-on-end |
| Player | server-update |
Hook names follow one rule, so you can predict them.
<side>-updateis the per-tick hook — it isn’t an event, so it takes noon-. Everything else is an event, and every event reads<side>-on-<event>:server-on-create,client-on-death,server-on-interact,server-on-inventory-close. The only thing left to check per config is which events it offers, which is what the table above is for.
The API each hook is called with lives in the Lua reference — Server Entity API, Client Entity API, Server Weather API and NPC Dialogue.
particles
Ambient particle emission — a list of emitter groups, accepted on blocks, trees (and tree variations), interactables and biomes. The shape is shared, but the filters a group may carry depend on the owner, because each owner answers a different question about where particles appear.
This block is documented in full on the particle page, alongside the particle definitions it references: Emitting particles — the particles: block, with the per-emitter fields at Emit Random.
Changing how something looks: three mechanisms
Not a shared block, but the question most often asked of one — the schema has three ways to change an object’s appearance at runtime, they are not interchangeable, and only one of them is named:
| Mechanism | Applies to | Written as | Persisted as |
|---|---|---|---|
| Block state | blocks | block-state: 2 in the block texture config; block.state = 2 in Lua | one byte, per block |
| Block variant | blocks | variant-of: {base-block-name, variant-name}; get_block_variation(id, "snow") in Lua | a different block id |
| Object state | interactables (and other static objects) | states[].name; set_state("door_open") in Lua | a name string, per object |
Which to reach for:
- Block state — a block that cycles or toggles appearance in place, cheaply, driven by a script (a lamp, a machine). It’s a bare number
0–255with no name anywhere, and the texture config’sblock-state:is the only place the number appears in config. Keep a comment next to it. - Block variant — a lasting swap to a different block, where the two are separate blocks that happen to be related (
grass→snowy_grass). The variant is fully registered in its own right, with its own drops and hardness. - Object state — a named pose for a placed object. This is the readable one, because static objects are few enough to afford a string each.
Why blocks don’t get names. A chunk is 32 × 32 blocks across three layers — about 3000 blocks, each carrying one state byte, in every saved chunk and every chunk packet. Names would cost a length-prefixed string per block, tens of kilobytes per chunk against a 64 KB packet limit. The numeric form isn’t an oversight; it’s what makes per-block state affordable at all. Interactables are orders of magnitude rarer, so they can spend the bytes.
Where to go next
- Schema Conventions — units, casing, colours, chance vs. weight, and the other cross-cutting rules these blocks follow.
- Documentation home — every config file’s own page.