How scripts run
A script is a WebAssembly component attached to a prim.
The host calls into it on a fixed schedule, and it calls back out through the wired:* API.
Lifecycle
A script exports three functions, defined on wired:script/lifecycle.
| Function | Runs | Use it for |
|---|---|---|
init | Once, before anything else | Setup |
update | Once per rendered frame | Drawing and animation |
fixed-update | 20 times a second | Input, messages, and physics |
Calls into one script never overlap. If a call runs long, the next one is skipped rather than queued.
Subscriptions
Events reach a script as a subscription it reads from, never as a callback. Input, messages, and portal intents all work this way.
Each subscription queues up to 64 items.
Call drain once per fixed-update to take what has arrived.
fn fixed_update(
&mut self,
_tick: exports::wired::script::lifecycle::Tick,
) -> anyhow::Result<()> {
for event in self.input.drain(32) {
println!("got input: {event:#?}");
}
Ok(())
}
If a script stops draining, the oldest items are dropped once the queue is full.
Permissions
A script can only call the APIs its author is trusted with on this peer. See Trust and permissions for what each trust level unlocks.
Trust can change while a script runs.
wired:script/host.granted checks for a permission ahead of time.
if wired::script::host::granted(wired::core::error::Permission::InputDevice) {
// safe to call wired::input::device::listen
}Errors
Every host call that can fail returns a wired:core/error.
The ones a script should expect to handle are:
permission: the author isn't trusted with this API.rate-limited: a budget such ascreate-primoremithas run dry. It refills over time, so retry later.limit-reached: the script has too many of a resource. Release one first.
Using a handle that was already dropped is a bug rather than an error. The script traps, and the host stops it for good.