dot-props

A sandbox layer — a catalogue of spawnable props, per-player budgets and an undo stack, a physics gun that holds and freezes, and a gravity gun that punts.

Spawnable props, the limits that keep a server alive, and the tools that move them.

Requires dot-core.

Server-authoritative, and not predicted

Rigid-body physics is not reproducible across machines — the solver’s iteration order and the last bits of every float differ — so a predicted prop is a prop that is corrected constantly. The server owns every prop; the client draws what it is told; the tools send intent.

var spawner := DotPropSpawner.new()
spawner.authoritative = true                  # on the server only
spawner.catalogue = DotPropCatalogue.load_json("res://props.json").value
spawner.limits = DotPropLimits.new()
spawner.world_ref = DotNodeRef.of_path(^"../World")
add_child(spawner)

spawner.refused.connect(func(player, prop, reason): tell(player, reason))

spawner.advance(delta)
spawner.spawn(&"crate", player_id, aim_point, Basis.IDENTITY)
{
  "format": 1,
  "props": [
    {"id": "crate", "name": "Crate", "category": "props",
     "scene": "res://props/crate.tscn", "mass": 20},
    {"id": "safe", "category": "props",
     "scene": "res://props/safe.tscn", "mass": 900, "cost": 8},
    {"id": "pillar", "category": "scenery",
     "scene": "res://props/pillar.tscn", "can_grab": false}
  ]
}

The tools

var gun := DotPhysGun.new()
gun.spawner = spawner
gun.wielder = player_id

gun.grab(space_state, eye_position, aim_direction, view_basis)
gun.hold(eye_position, aim_direction, view_basis, delta)   # every physics tick
gun.push(scroll_amount, delta)
gun.release()
gun.freeze_held()
var grav := DotGravGun.new()
grav.pull(space_state, eye_position, aim_direction)
grav.carry(eye_position, aim_direction, delta)
grav.punt(space_state, eye_position, aim_direction)

The physics gun holds with a spring, not a transform: setting a transform on a rigid body fights the solver and produces a prop that jitters and shoves everything it touches.

Where a game plugs in

DotPropCatalogue / DotPropDef

What can be spawned. Id, name, category, scene, mass, cost, can_grab, and a meta dictionary.

DotPropDef.meta.scripta PATH

A prop that has code. A path, not a class_name — a mounted .pck cannot register class_name globals, so a path is the only shape a delivered prop can take. See game-playground.

DotPropLimits

Per-player counts, per-player frozen counts, a spawn cooldown, an undo depth, a total cap, and grab mass limits. Each one is defending against a specific way a sandbox server dies.

DotPropTool subclass

effective_reach, target, may_act_on. A new sandbox tool, or a weapon. DotPhysGun and DotGravGun are the shipped ones.

Signals

spawned, removed, refused.