Coming soon toSteam
Modding docsAPI 1

Gears and turning parts

Example: Gear Works (a gear from a model, a data-declared clockwork motor, a placement guide).

Gears are part of the game, in Movement: Gear Size 1 to Gear Size 8 (16 teeth a size), Hollow Gear Size 3 to 8 (teeth outside and in, its middle open), Rack Gear and Gear Shaft. The palette shows a Gear tile (every solid size, Size 1 at first) and a Hollow Gear tile (Size 3 at first), each remembering the size last placed; while placing, + and − change the size and H switches solid and hollow. A placed gear is resized with the scale tool (E): drag across its face for its size (it is replaced by that size on the same axis, keeping its settings, wires and mount, in one undo step; the mirror twin follows) or along its axis for its thickness. Players place one next to another and the placement guide shows where they mesh (grey: the teeth would not touch; green: they mesh). Everything that follows lets a mod add its own — and they mesh with the game's gears and with every other mod's.

For now the game's own gears are hidden from the block palette until a player types /gears in the developer console (/gears off hides them again). They are only hidden: creations with gears load and run, "base": "Gear 16" works, and a mod's gear blocks always appear.

Gears simulate on the Jolt physics engine (Settings → Physics → Physics Engine). On PhysX they are drawn but do not turn, and the game says so once. Nothing a mod does changes that; kinetics.available() tells a server script which it is.

How gears behave

A gear block

Add gear to a block definition:

content/blocks/great-wheel.json
{
  "id": "great-wheel",
  "name": "Great Wheel 96",
  "base": "Cube",
  "model": "/models/great-wheel.glb",
  "gear": { "kind": "spur", "size": 6 },
  "spin": { "meshes": ["Wheel"] }
}

With size (1–8: 96 teeth and 1.2 m of pitch radius at size 6) the gear gets exactly the game's geometry for that size: a box (2 × size + 1) cells square and one deep, faces on the cells its rim covers, only those cells occupied (a hollow gear's middle stays free for other blocks), stretching only along its axle, and the game's own teeth drawn when there is no model. "hollow": true adds teeth inside (size 3 and up). With size, leave out the keys it decides (teeth, module, axis, centre, hubs, faceWidth, and the block's size).

From gear the game also gives the block the Gear mechanism: no controls, bolted to its part (above), RPM, Angle and Jammed on wires; for a motor, servo or brake, give the block a mechanism of its own. A gear given teeth instead of a size gets the older shape — hub faces on a half-cell grid, a round footprint, a collider a little smaller than its teeth — and meshes, but off the grid; the mod's log then says "add gear.size for grid placement". Every key below can still be set explicitly.

A block based on one of the game's gears ("base": "Gear Size 3") is a gear already: give it a model and a name and you have a new-looking gear with the same teeth. A gear table then changes only what it names: new teeth that are a grid size (a multiple of 16) make it that size, anything else that reshapes it (another module, axis or box) makes it a plain, off-grid gear. The older names ("Gear 24" …) still work as bases.

gear fieldDescription
kindspur, internal (a ring gear: teeth inside), bevel (45°: two of the same size mesh at a right angle), rack (straight), worm or axle (no teeth; carries the turn along).
sizeA grid gear of the game's family, 1–8: 16 teeth and one cell (0.2 m) of pitch radius a size (above).
hollowWith size (3–8): teeth inside too, 16 fewer, and its middle open.
teethTooth count, 6–1024. At the game's module, multiples of 16 are the grid sizes — size gives them the grid geometry.
moduleTooth size in metres (default 0.025, the game's). The pitch radius is teeth × module ÷ 2.
faceWidthThickness of the teeth along the axle, metres (default 0.18).
lengthRack, worm or axle length, in cells.
radiusWorm and axle radius, metres. (For toothed gears it follows from the teeth.)
startsWorm threads (default 1): one turn moves the wheel this many teeth.
axisThe axle, block-local: "+z" (default), "x", [0, 1, 0] … For a rack, its direction of travel.
centreThe pitch circle's centre, in cells (default the block's centre). Rack: a point on its pitch line where a tooth sits.
normalRack: from its teeth toward the gear.
referenceThe direction tooth 0 points (default "+x"), so the teeth are drawn between the neighbours'.
apexBevel: distance from the pitch circle to the cone's tip, metres (default the radius: a 45° miter).
hubs[back, front]: where the hub faces are along the axle, in cells (default [-0.5, 0.5]).
strengthTooth force before the teeth jump, newtons (default worked out from size: about 340 kN for the game's). The weaker gear of a pair decides.
frictionTooth friction. A worm with 0.15 (its default) cannot be turned back by its wheel.

Gear models are taken as they are — in metres, about their own origin — rather than fitted to a block and centred like other models, because the teeth must sit exactly on the pitch circle the physics rolls. Model the axle along +Z with tooth 0 centred on +X. modelUnits, fit and centre still override that.

Block shape, faces and spinning parts

These work on any block, gear or not.

FieldDescription
sizeThe block's box in cells: 2 or [4, 1, 1]. The model is fitted into it and faces cover it.
colliderSize, colliderCentreA collision box different from size, in cells.
facesWhere other blocks attach: "box" (every side of the box), "hub" (a gear's hubs), "none", or a list of { "at": [x, y, z], "facing": "+x", "size": [w, h], "connect": true, "aero": true } in cells. Left out: a new gear gets hubs, a block with a new size gets its box, others keep their base's.
spinMeshes drawn turning with the block's axle: true (all), or { "meshes": ["Wheel"], "axis": "+z", "pivot": [0, 0, 0] } with the model's mesh names and a point on the axle in cells.
footprintWhat counts as overlapping in the editor: "box", or { "shape": "cylinder", "axis": "z", "radius": 6 } (or "ring" with an innerRadius), in cells. Round parts with parallel axes may sit as close as their radii; round parts fit in a ring's hole.
scalable, minScale, maxScaleStretching, as a number or [x, y, z] of cells.
scaleLinkAxes that stretch together, e.g. "xy" to keep a disc round.
connectableToMechanisms it may be wired to beyond the defaults, e.g. ["Engine"].
modelUnits"cells" or "metres", for a model that is not fitted ("fit": false).
centrefalse keeps the model's own origin instead of centring its bounds.

A mechanism in data

mechanism gives a block its own settings panel, keys, wires and — on anything that turns — a shaft drive. The engine runs it natively every physics step, exactly like the game's own parts; there is no per-step script.

content/blocks/clockwork.json
{
  "id": "clockwork", "name": "Clockwork Motor", "base": "Gear 16",
  "mechanism": {
    "name": "Clockwork",
    "settings": [
      { "name": "Spring", "type": "choice", "options": ["Run", "Hold", "Coast"], "default": "Run" },
      { "name": "Speed", "type": "number", "default": 30, "min": 0, "max": 240, "unit": "rpm",
        "writable": true, "visibleWhen": { "Spring": "Run" } },
      { "name": "Torque", "type": "number", "default": 150, "min": 0, "max": 2000, "unit": "N·m" },
      { "name": "Wind", "type": "key", "default": "None" }
    ],
    "drive": { "modeSetting": "Spring", "modes": ["motor", "brake", "free"],
               "speed": "Speed", "speedUnit": "rpm", "torque": "Torque", "forward": "Wind" },
    "channels": [ { "name": "RPM", "source": "rpm" } ]
  }
}

Settings — up to 32, each { name, type, label?, hint?, section? }:

typeExtra fields
numberdefault, min, max, unit, decimals, slider: [from, to] (the slider's span when the allowed range is wider), step, percent
choiceoptions (2–16 names), default (a name or an index)
booldefault
keydefault (a key name like "R", "Space", "LeftShift", or "None"), color
textdefault, maxLength

Any setting may add "writable": true (numbers and bools: a wire may set it while running) and "visibleWhen": { "Other": value or [values] } (shown only then; choices by option name). section is mode, controls, tuning or options.

drive (only on blocks that turn — a gear, or based on one):

FieldDescription
modeA fixed mode: free, motor, servo, brake or none.
modeSetting, modesA choice setting that picks the mode, and the mode for each of its options (default: the option names).
speed, speedUnitThe number setting for motor speed (and servo speed), in rpm (default), deg/s or rad/s.
torqueThe number setting for the most torque, N·m — or a fixed number.
angle, angleUnitServo target setting, in deg (default), rad or turns. Required for a servo mode.
forward, reverseKey settings that command it. Wires command it too.
runWhenUnboundWith no key bound and no wire, a motor runs and a brake holds on its own (default true).
enginesWired Engines turn it at their speed and torque (default true).

A motor turns while commanded; a brake holds while commanded; a servo turns to its angle and holds it.

channels — values it reports on wires, each { name, source, unit?, hint? } where source is speed (in the drive's unit), rpm, angle (degrees, not wrapped), torque, command (-1…1 from keys and wires) or jammed. signal chooses what a wire carries when it names no channel (default rpm). Every number and bool setting is readable on wires as well.

The panel gets the rows a drive needs without declaring them: Toggle Mode and Require Seat when it has keys, Invert when engines may drive it, and Mount and Bearing Friction on a gear.

The mechanism's id is your.mod-id:<id> (default the block's id). Several blocks can share one: declare it on the first and write "mechanism": "clockwork" on the others.

Placement guides

building.placement guides players placing a block type — what the game's gear guide does for gears. The function runs every frame of the drag (client scripts, building permission):

scripts/client.lua
building.placement("clockwork", function(q)
  local s = q.suggested                          -- the game's own guide, for blocks it guides (gears)
  if not s or s.state ~= "valid" then return nil end
  return { position = s.position, rotation = s.rotation, state = "valid",
           label = s.label .. " · clockwork", settings = s.settings, attachTo = s.attachTo }
end)

q has position and rotation (where the editor would snap it), snapped, hit (position, normal, block), ray (origin, direction), scale, chosenRotation, creation and — for blocks the game guides — suggested (position, rotation, state, label, settings, attachTo).

Return a table to take over: position + rotation (world), state ("valid" green, "neutral" grey, "invalid" red, "default"), a short label by the cursor, settings the block is placed with, attachTo (a Block), and guides — circles { centre, normal, radius, state or color }, lines { from, to, … }, dots { position, radius, … }, up to 64 each. Without guides of your own the game's are kept. Return nil to keep the game's suggestion, false for no guidance. An error in the guide is logged and the block is left to the editor for a few seconds.

The kinetics module

Gear blocks mesh by themselves wherever players put them. The kinetics module is for everything else: a custom block that turns on an axle, teeth on any block, gears meshed on command. Server scripts; making and driving needs the building.kinetics permission.

scripts/server.lua
building.onPlaced("turntable", function(block)
  local shaft = kinetics.shaft(block, { axis = "+y", friction = 2 })     -- it turns about its own Y
  local ring = kinetics.gear(block, { shaft = shaft, teeth = 64, axis = "+y" })
  shaft:motor(6, 800)                                                      -- 6 rpm, at most 800 N·m
end)

kinetics.onTeethJumped(function(mesh, teeth) log.warn("teeth jumped on", mesh.a.block.name) end)
FunctionDescription
kinetics.shaft(block, options)An axle for a block: axis, pivot (cells), friction, damping, inertia, gyroscopic. Not for gear blocks, which have theirs.
kinetics.shaftOf(block)The shaft a block turns with — yours, another mod's, or an older own-axle gear's. nil for a game gear on Auto: it is bolted to its part.
kinetics.gear(block, options)Teeth on a block, turning with shaft or fixed to its part: kind (circle, line, screw), centre, axis, normal, reference, radius or teeth + module, lead, internal, extent, strength, friction.
kinetics.gearsOn(block)The teeth on a block.
kinetics.mesh(a, b, options)Meshes two gears (on one creation), wherever they are: strength, friction, engage.
kinetics.onTeethJumped(fn)fn(mesh, teeth) when teeth jump.
kinetics.spin(block), kinetics.rpm(block)How a block's spinning parts are drawn turned (degrees) and how fast they turn — client or server. A gear bolted to its part has none spinning (the part turns): read its RPM channel.
kinetics.available(), kinetics.stats()Whether gears simulate, and counts for profiling.

Shaft: angle, rpm, torque, jammed, simulated, block, mine; :motor(rpm, torque), :servo(degrees, rpm, torque), :brake(torque), :free(), :setFriction(n, damping), :setInertia(kg·m²), :add(block), :remove(block), :blocks(), :destroy(). Gear: kind, radius, teeth, shaft, block, :meshes(), :setShaft(shaft), :destroy(). GearMesh: a, b, engaged, force, slip, skips, :destroy().

A mod changes only what it made; the game's gears are changed through their blocks' settings. Everything a mod made is removed when it stops. Units: rpm and degrees in scripts, metres for radii, cells for positions on a block.

Saves with mod blocks

A creation records which mods its blocks came from. Loading one shows which mods it was built with and whether each is active. If any is not, Load without mod blocks loads a copy with those blocks left out; the save file itself is never changed, so it loads whole again once the mods are back.