Networking
The balaur_net plugin gives scripts HTTP requests (http.request) and
websocket connections (websocket.connect / send / close). It is on by
default; building without the net feature drops the entire HTTP/TLS stack
from the binary.
The delivery model
All I/O runs on background threads. Completions cross back over a channel and enter the simulation once per tick, at the start of the frame, recorded in a snapshot — the same model as input, which means recording the snapshot per tick is all a replay of a networked session needs. Nothing blocks the frame, and handlers never run from an I/O thread.
Scripts never poll. There are two shapes:
Handler methods. A request or connection names the node that handles its
results, and results are dispatched as method calls — the same signal shape a
widget's on_click or an animation method track uses:
function S:init()
self.request = http.request(self.node, "https://example.com")
end
function S:on_response(r)
print(r.status, r.body)
end
Sequential await. A request made without a node is a token the script suspends on until the result arrives:
local r = await(http.request(url)) -- Luau
let r = task::wait(http::request(url)).await; // Rune
Both resume at the start of a later tick, in arrival order — deterministic given the same recorded arrivals.
Why there are no signals
The engine has no connect-style signal registration: a script cannot
register a persistent callback and be handed a payload three frames later
(persistent callbacks would need an id space with explicit release). Events
go the other way — the engine calls a method the script declares by name.
Most events also have a polling twin (input.just_pressed,
animation.just_finished) for scripts that would rather ask than declare a
method: an event is a frame-scoped snapshot, not a subscription.
For a hosted game backend — auth, storage, realtime rooms — see Gamend, which is built on the same delivery contract.