Skip to main content
reskate.world.physics lets a plugin make rigid bodies of its own in the local physics world: a ball, a cart, a crate, a vehicle. Each body is a compound of capsules. The plugin reads where the body is, asks for a force, torque or impulse on the next physics step, and reads what happened. What a body is for, and how it is steered, is up to your plugin. Every function needs the world.physics permission. probe_batch needs world.raycast as well, and three of its options need world.physics.observe. create’s reuse option needs world.physics.reuse. Calling a function without its permission raises an error.
Bodies are local. They exist on your game only, and nothing about them is sent to other players. Every function works in the client copy only: in a server copy it raises native bodies are client phase only. Making, reading and pushing bodies is refused during a multiplayer session and while joining a lobby.
The game has room for eight bodies, shared by every plugin, every reload and every level, until it restarts. A released body is stopped and parked, not destroyed, and keeps its room. Once the eight are used, create answers nil, "the eight retained body slots are exhausted; native destruction is unproven". A plugin you reload while you work on it runs out after eight bodies: restart the game. A plugin with world.physics.reuse can bring one of its own parked bodies back instead of using another place: see bringing a parked body back.

How a body moves

The physics world steps 60 times a second. Each read gives a sample of the body at one step, with an epoch: an opaque token for that step of that body. You compute a command from the sample and submit it with the epoch. The command is applied on the very next step and on no other: if that step is missed, or 100 ms pass, it expires. Every command gets a receipt. The receipt says whether the command was applied, and later carries the first sample taken after it (its successor), so your plugin reads what actually happened. Acknowledge each receipt once you are done with it. A body keeps at most four receipts; while all four wait, submit is refused.
When your plugin stops, reloads or fails, ReSkate releases all of its bodies. A level change, a multiplayer session starting, or the local skater’s world changing lets go of every body too: it stops answering read, and still takes its place among the eight.

Preparing

reskate.world.physics.prepare()

Asks for the game’s one-time setup for bodies, so that create is not the call that finds it pending. Call it, and after true, make your first create. Takes no arguments; passing any raises an error.
boolean
True when the setup is done and the local skater’s physics step can be read now. It reserves nothing: the room for bodies can still run out before create.
string
Why not:
create answers a pending setup and a skater, world or step it cannot read now alike, with body_preallocation_unavailable. prepare tells them apart.
Needs the world.physics permission. Once a tick.

Making and reading

reskate.world.physics.create(definition, options)

Makes a body, or with reuse brings back one of your own parked bodies.
table
required
Any field not listed raises an error.
table
Any field not listed raises an error, and so does a value that is not a table or nil.
userdata or nil
The body: an opaque handle that only this plugin, until it reloads, can use.
string
nil with a body. Otherwise why not. These are fixed codes, to compare against (see refusal codes):The other refusals are sentences meant for your log, such as "owned bodies require the current local client phase outside multiplayer", "the eight retained body slots are exhausted; native destruction is unproven" or "a previous partial allocation in this world prohibits another native allocation".
string
Only when you pass options, with a body: "reused" (a parked body came back) or "allocated" (a new body).
string
Only for a reuse that made a new body: why none of your parked bodies came back. See bringing a parked body back.
Without options, or with nil in their place, create returns the body alone, or nil and why. With options it returns body, nil, origin, and miss after them for a reuse that made a new body. The capsules collide with the world only. A plugin cannot choose what a body collides with. A failed create is not tried again for you.
Needs the world.physics permission, and world.physics.reuse for reuse = true. A definition out of these ranges raises invalid native body: mass 1..1000, inertia .01..10000, unit XYZW rotation, capsule endpoints +/-10m and radius .01..2m.

reskate.world.physics.read(body)

The body at its latest physics step.
userdata
required
A body from create. Another plugin’s body raises an error.
table or nil
nil and why when the body cannot be read now, such as "body owner or generation unavailable" for a released body, or "body snapshot or native epoch changed; no sample published".
Needs the world.physics permission.

Pushing

reskate.world.physics.submit(body, epoch, command)

Asks for a force, torque or impulse on the body for the step after epoch.
userdata
required
A body from create.
userdata
required
The epoch of a sample of the same body, from read or a receipt’s successor.
table
required
Any field not listed raises an error. A vector left out is zero.
boolean
True when the command waits for its step.
string or nil
nil when it waits. Otherwise why not. These two are fixed codes, to compare against (see refusal codes):The other refusals are sentences meant for your log: "body owner or generation changed", "body commands require the current local client phase" or "body command invalid, repeated, stopped, pending or receipt ring full".
string
The command’s number, as a decimal string, for receipt and acknowledge.
Neither code assigns a sequence, takes the body’s waiting place or changes a receipt. One command waits at a time for each body, and each must come from a newer step than the last. A plugin that reads and submits every step drives its body at 60 Hz. A command the game takes only in part fails the body for good: it is quarantined, and only receipt, acknowledge and release still answer for it.
Needs the world.physics permission. A command out of these bounds, or an epoch of another body, raises an error.

reskate.world.physics.receipt(body, sequence)

What became of a command.
userdata
required
A body from create.
string
A sequence exactly as submit gave it. Left out, the oldest receipt not yet acknowledged.
table or nil
nil and why for a sequence this body does not hold.
"applied" says the game took the force and torque. It does not say how the body moved: read the successor.
Needs the world.physics permission. A sequence that is not a decimal string raises an error.

reskate.world.physics.acknowledge(body, sequence)

Frees the oldest receipt, so another command can be submitted.
userdata
required
A body from create.
string
required
The oldest receipt’s sequence.
boolean
True when it was freed.
string
"receipt unavailable, already acknowledged or awaiting a measured successor": receipts are freed oldest first, a queued one cannot be, and an applied one waits for its successor.
Needs the world.physics permission.

reskate.world.physics.release(body)

Stops the body: its waiting command is cancelled, its force and velocity are cleared, it stops colliding, and it is parked. It still takes one of the eight places. With world.physics.reuse, your plugin can bring it back later.
userdata
required
A body from create.
boolean
True when the body was released.
string
Why not, such as "body owner or generation unavailable".
The receipts of a released body can still be read and acknowledged.
Needs the world.physics permission.

Bringing a parked body back

A released body keeps one of the eight places until the game restarts, so a plugin that makes and releases the same body again and again, such as a bike made on each mount and released on each return, would run out. create(definition, {reuse = true}) brings back one of its own parked bodies in place of a new one, at the definition’s position and rotation.
reuse looks only among the bodies this copy of your plugin parked itself: never another plugin’s, and not those from before your plugin reloaded. A body can come back when:
  • your plugin released it with release, in the same world it was made or brought back in;
  • it never failed and was never quarantined;
  • its capsules, mass and inertia are exactly those of the new definition, to the last bit. Its position and rotation may differ: it comes back at the new ones.
Any tick on which the world is not ready, such as a level load or the game leaving play, starts a new world. Every body parked before it stays parked, still takes its place among the eight, and is never brought back. Before it writes anything, ReSkate checks that the body is still as release left it. Then it places the body, lets it collide with the world again and sets it moving, as at the end of a creation. It comes back with a new handle: the old handle, its epochs and its receipts are gone. When no body can come back, create makes a new one, as without reuse, and its fourth result, miss, says why, from your own bodies only: Two refusals come only from reuse: create without reuse never brings a body back, so it is how a plugin asks for a new one.
Needs world.physics.reuse as well as world.physics; a manifest with the first and not the second fails to load. A plugin may ask to reuse once a tick. A second request that tick, after a refused one too, returns nil, "asked to reuse a native body more than once in one tick" without reaching the game.

Refusal codes

Five refusals are fixed codes rather than sentences, so a plugin can compare why with them and decide whether to try again. Every other refusal on this page is a sentence meant for your log, whose words can change. probe_batch has its own reasons, in batch.reason.

Ray batches

reskate.world.physics.probe_batch(body, epoch, rays, options)

Casts up to 32 rays at the moment of one sample of a body: what is under a wheel, what a bumper is about to meet. A batch is all or nothing. When anything about the body or the world changed between the first ray and the last, every ray is discarded and the batch is "unavailable".
userdata
required
A body from create. Its own capsules are never a hit.
userdata
required
The epoch of the latest sample of that body.
table
required
1 to 32 rays, each exactly {from = vector, to = vector}, in a list with no gaps. Each end within 100 km; each ray at least 10 micrometres and at most 1000 m long.
table
Any field not listed raises an error. Each flag takes only true or false.
table
Without require_contact, a hit’s normal and surface velocity are left out. A hit never names the body, object or face it met.
Needs the world.physics and world.raycast permissions, and world.physics.observe for query_preset = "all", include_shape_categories and include_hit_owner. Each ray is one of the plugin’s 64 raycasts a tick, shared with reskate.world.raycast, pick, probe and reskate.session.raycast; past them the call raises an error. Batch rays and reskate.world.probe also share 64 casts in the game a tick, each ray using at most two; when they run out the batch is "unavailable" with budget_exhausted.