Server World API
Reading and placing blocks from server-side Lua, and the per-block interact and update hooks.
Weather hooks and block hooks receive a world object representing the world they run in. It lets scripts inspect and change blocks; every change is saved and broadcast to clients automatically.
All coordinates are block coordinates (integers). layer selects the block layer: 0 = background walls, 1 = foreground (the colliding layer), 2+ = overlay (fluids, effects).
Fields
| Field | Type | Description |
|---|---|---|
world_name | string | The world’s name (read-only). |
Reading Blocks
get_block_at(x, y, layer)
Returns a block table describing the block at that position, or an empty table if there is no block there — check for id before using the result:
local block = world:get_block_at(x, y, 1)
if block.id ~= nil then
log_info("found " .. block.name)
end
Block table keys:
| Key | Type | Description |
|---|---|---|
name | string | The block’s registered name. |
id | number | The block’s numeric ID. |
facing | string | "Up", "Down", "Left", or "Right". |
state | number | The block’s state value (drives state-based block textures). |
biome_id | number | ID of the biome this block belongs to. |
block_light_level | number | Light emitted/held from block light sources (0–255). |
sky_light_level | number | Light from the sky (0–255). |
fluid_level | number | Fluid fill amount for fluid blocks. |
is_surface_block | boolean | Whether this is the top-of-world surface block in its column. |
block_matter_type | string | The block’s matter type (solid, liquid, …). |
block_shape | table | { type = "Full" }, or { type = "Slab", divisions = n, side = "...", count = n } for partial blocks. |
Placing Blocks
set_block_at_from_name(x, y, block_name)
Places a fresh block by name (e.g. "STONE"). This is the simplest and usually best way to place blocks. The layer is determined by the block type itself; the biome is inherited from the block previously at that position. Raises an error if the name is unknown.
set_block_at_from_block_id(x, y, block_id)
Same as above, but takes a numeric block ID — useful together with get_block_variation (see lua-server-globals.md).
set_block_at(x, y, block_table)
Places a fully specified block. Use this only when you need control over facing, state, fluid level, or shape — otherwise prefer the two functions above.
Required keys: id, facing, biome_id, block_light_level, sky_light_level, fluid_level, state, collidable (boolean), and block_shape.
Note: a table returned by get_block_at cannot be passed straight back into set_block_at — it lacks the required collidable key. Copy the fields you want and add collidable yourself:
local old = world:get_block_at(x, y, 1)
if old.id ~= nil then
world:set_block_at(x, y, {
id = old.id,
facing = "Left", -- the change we wanted
biome_id = old.biome_id,
block_light_level = old.block_light_level,
sky_light_level = old.sky_light_level,
fluid_level = old.fluid_level,
state = old.state,
collidable = true,
block_shape = old.block_shape,
})
end
Block Hooks
Blocks can run server-side Lua of their own. Both hooks are declared per block entry in your block-config.md file and receive the world object as their first argument.
blocks:
- name: ritual_stone
# ...
scripts:
server-on-interact: /blocks/ritual_stone_interact.lua
server-update: /blocks/ritual_stone_update.lua
interact-cooldown-ms: 500 # optional, default 0
Each value is a bare path to a .lua file that returns its handler function.
scripts: key | When Called | Signature |
|---|---|---|
server-on-interact | A player interacts with a block of this type | function(world, x, y, layer, player_entity_id) |
server-update | Every server tick, for each placed block of this type | function(world, x, y, layer, delta_ns) |
interact-cooldown-mssets the minimum time between interactions on a single block cell (0= no cooldown).- Update hooks only run for blocks of types that declare one, so ordinary blocks cost nothing. A block type with an update hook ticks for every placed block of that type in loaded chunks — keep the handler light, and use block
stateplus early returns to skip idle blocks. delta_nsis the tick delta in nanoseconds (delta_ns * 1e-9= seconds).
-- Turns the block into stone when a player interacts with it.
return function(world, x, y, layer, player_entity_id)
world:set_block_at_from_name(x, y, "STONE")
end
See Also
- lua-server-globals.md —
get_block_variation, spawning, locations - lua-server-weather.md — weather hooks, which also receive the world object