Coming soon toSteam
Modding docsAPI 1

Blocks and building

Examples: Glow Sphere Block (a new block with logic), Thruster Tuner (settings and limits), Block Mover (overlapping moves), Gear Works (gear blocks and a mechanism declared in data).

New blocks

A block is a JSON definition. List the files in mod.json:

mod.json
"content": { "blocks": ["content/blocks/*.json"] },
"permissions": ["building"]
content/blocks/crystal.json
{
  "id": "crystal",
  "name": "Power Crystal",
  "description": "Glows. Heavy.",
  "base": "Cube",
  "category": "Advanced",
  "model": "/models/crystal.glb",
  "texture": "/textures/crystal.png",
  "icon": "/icons/crystal.png",
  "collider": "box",
  "mass": 4,
  "health": 400,
  "sounds": { "place": "/sounds/crystal-place.wav" }
}
FieldDescription
idLower-case letters, digits, - and _. The block's full id is your.mod-id:id.
name, descriptionShown in the palette.
baseThe game block this copies (default Cube). It decides how the block connects, paints, collides and what it does. Based on Thruster it thrusts. Based on Wheel it rolls.
categoryBasic, Advanced, Movement, Weaponary or Logic.
modelA .glb, .gltf, .obj or .fbx. See models.
fittrue (default) scales and centres the model to fill one block (or its size). false keeps the file's size. Gear models are not fitted: see gears.
modelScaleAn extra scale on top of fitting.
textureA picture that replaces the model's own colour texture.
tintA "#rrggbb" colour multiplied over the material.
paintabletrue lets players paint it.
iconA .png for the block palette.
colliderbox (default), sphere (or round) or mesh (the first mesh of the model).
massKilograms, 0.001–100 000.
health1–1 000 000.
thrustRatingFor thrust mechanisms: the part's rated maximum force, in newtons.
scalable, minScale, maxScaleWhether players can stretch it, and how far (1–64 blocks, or [x, y, z]).
logicReplace the base's mechanism list, for example ["Thruster"].
mirrorMirror symmetry for the building editor's mirror tool: detect (default, worked out from the model), AxisX, AxisY, AxisZ, DiagonalXY, DiagonalXYInverse, DiagonalXZ, DiagonalXZInverse, DiagonalYZ, DiagonalYZInverse.
soundsplace and destroy, as .wav files.

Paths are relative to the JSON file. Start with / for the mod root.

Redescribing the block. size, faces, spin (meshes that turn), footprint, scaleLink, connectableTo, gear (it becomes a gear that meshes with every other) and mechanism (its own settings, keys, wires and drive, run natively) are described in Gears and turning parts.

Models for blocks are limited to 16 meshes, 16 materials, 100 000 vertices and 60 000 triangles, because the building system draws thousands of blocks at once.

Ids are stable. A mod block's number in saves and network messages comes from its full id, so creations with your blocks save, load and synchronise with no code. Players need your mod to load them, and servers send it to joining players.

Finding blocks and reacting to them

building.onPlaced("crystal", function(block)          -- placed, loaded, or received from the server
  effects.burst("glow", block.position, { color = color("#80ffff") })
end)
building.onRemoved("crystal", function(block) log.info("a crystal was removed") end)

for _, thruster in ipairs(building.blocks("Thruster", 500)) do
  log.info(thruster.id, thruster.creation, thruster.position, thruster.velocity:length())
end

local selected = building.selection()                 -- client: what the player has selected in the editor
building.onSelectionChanged(function(blocks)           -- client: runs whenever that selection changes (no polling)
  log.info(#blocks, "selected")
end)

Types: your own block by its short id ("crystal"), another mod's block by full id ("a.mod:gizmo"), and the game's blocks by name ("Thruster"). building.blockTypes() lists them all.

Block fields: id, type, name, mechanism, creation, position, rotation, velocity. Blocks disappear, so check block:isValid() before using one you kept.

Settings on working parts

local settings = block:settings()     -- { Force = { value = 25000, min = 0, max = 50000 }, Enabled = { value = true }, … }
local force = block:get("Force")
block:set("Force", 40000)             -- client, needs building.edit

local ok, err = block:trySet("Force", 40000)   -- false and the reason instead of an error when it is refused
if not ok then notify.toast(err) end

Block:set works exactly like the block's own panel. The request goes to the server, which checks that the player owns the creation and clamps the value to the current limit. Send changes a few times a second at most, not on every frame of a slider drag.

Raising part limits

A setting's maximum comes from its mechanism and the part's rating. A server script with building.limits can raise it for everyone on the server:

scripts/server.lua
building.raiseLimit("Thruster", "Force", 250000)
building.raiseLimit("JetEngine", "Force", 8000000)
log.info(building.limit("Thruster", "Force"))     -- 250000; nil when normal
building.resetLimit("Thruster", "Force")

Moving blocks into other blocks

The editor refuses moves that make blocks overlap. A server script with building.overlap can make them:

scripts/server.lua
net.handle("nudge", { blocks = "string[]" }, function(player, data)
  local blocks = {}
  for _, id in ipairs(data.blocks) do blocks[#blocks + 1] = building.block(id) end
  local ok, err = building.tryMoveInto(player, blocks, vec3(0, 0.05, 0))   -- false, reason when refused
  if not ok then return false, err end                                     -- answered on "denied"
end, { rate = 20 })

Pushing creations

A server script with building.physics can apply force to a moving creation: block:push(vec3(0, 5000, 0)), or block:push(force, true) for a one-off impulse.

Building tools

Add a button to the building editor's toolbar (needs only building):

scripts/client.lua
local window
local tool = building.addTool({ name = "⚙ Tuner", tooltip = "Tune the selected parts" }, function()
  if window then window:destroy(); window = nil else window = openWindow() end
  tool:setActive(window ~= nil)
end)

The button is removed when the mod stops. See the building tool template.