Gamend
The balaur_gamend plugin connects scripts to a Gamend backend through the
gamend module: login, REST endpoints, lobbies and server hooks. It is on by
default; building without the gamend feature drops it.
- Delivery is the networking contract: all I/O on worker threads, completions entering the simulation once per tick.
- Dispatched to handler methods or waking
awaittokens, in arrival order. - Handlers never run from an I/O thread. A recorded snapshot replays a whole online session.
The sequential shape
pub async fn init(this) {
gamend::configure(()); // [gamend] url: gamend.org unless the project names its own
let login = task::wait(gamend::login(#{ device_id: "player-1" })).await;
this.socket = gamend::connect(this.node);
let hook = task::wait(gamend::call_hook(this.socket, "arena", "start", #{})).await;
}
pub fn on_gamend_event(this, e) {
gamend::client::route(this.node, e);
}
pub fn on_lobby_member_joined(this, e) {
log::info(`${e["user_id"]} joined`);
}
pub fn on_socket_reopened(this, e) {
log::info("back online");
}
- One-shot operations (
login,rest,call_hook,join,push,leave) wake their returned id and, when given a node, also dispatch to its handler. - Socket events are a stream and only dispatch.
- Every event map carries a
kind:login,rest,reply,error,open,message,reconnecting,reopened,closed. One handler takes them all. gamend::client::routehands each to its own handler. A server message goes toon_<kind>by its name ingamend::events, and the socket's own go toon_socket_<kind>.
Staying connected
A socket dropped without gamend::close comes back by itself.
- It waits 1, 2, 4 seconds and on up to 30 between tries, eight tries in all.
- Each try renews an expired token and joins every topic the game had joined again.
- The handler hears
reconnectingbefore each try, withattempt,waitandreason. Then comesreopened, whoselostnames any topic the server refused, orerroronce the socket gives up. - A call made while it waits fails at once.
- A channel the server crashes is joined again the same way. One it closes on purpose, a kicked player's lobby, is let go, and a reconnect does not ask for it again.
gamend::interrupt(socket)cuts the connection as a network failure would, to try a game's handling of it.- A REST call that meets an expired token refreshes it and runs again.
The full function list is in the script modules reference.
Accounts
The addon's client.rn covers the ways a player gets an account. Every
file of the addon is a module, so it is gamend::client.
task::wait(gamend::client::login_device((), engine::device_id())).await;
task::wait(gamend::client::register_email((), "ana@example.com", "a long password", ())).await;
task::wait(gamend::client::login_email((), "ana@example.com", "a long password")).await;
- A device id the server has not seen makes an anonymous account.
register_emailmakes an account with an email and a password and signs it in. The server mails the address a confirmation link, as its web sign-up does;usernamemay be nil and gets generated.gamend::users::delete_current_user(node, params)deletes the signed-in account. An account with a password sends#{ "current_password": ... }.
The server
gamend::configure() with no URL reads the project's [gamend] settings.
[gamend]
url = "https://gamend.org"
local_url = "http://localhost:4000"
plugin = "skyrace"
urlis the server a shipped game talks to. A new project starts onhttps://gamend.org.local_urlis a Gamend on your machine, asmix dev.startserves it.gamend/targetpicks between the two for a game played from the editor. It is kept in the editor's own settings, so an exported game always readsurl.configure(url)with a URL still wins, for a game that picks its server at run time.
The Gamend dock
A project that carries addons/gamend gets a Gamend tab in the editor.

- The header switches between the two servers, opens the server and its admin site, and says who is signed in.
- Overview, User, Lobby, Data, Activity and Logs read what the played game holds: the session, its sockets, and every call with its reply.
- One tab per server feature, from Leaderboards to Hooks, reads that
feature's
GETendpoints as trees. - Tokens are masked, with show and copy.
- The editor uses the game's own user data folder and device id. A game played in the editor and run alone share saves and a device sign-in.
Player prefs
addons/gamend/prefs.rn keeps a player's settings on the device, in the
gamend_prefs save slot.
gamend::prefs::set("music_volume", 0.6);
let volume = gamend::prefs::get("music_volume", 1.0);
Prefs never leave the device. A value that should follow the player to another device belongs in the server's key-value store.
Client logs
Nothing is sent to Gamend unless the game asks. Put
addons/gamend/log_sink.rn on a node that lives as long as the game, and it
ships what log::since reads.
- It asks the server's
/api/v1/client_logs/policywhat to collect, and collects nothing until it answers. - A batch goes at 50 lines, every 10 seconds, and at once on an error.
- A line repeated back to back is sent once with its count.
- Unsent lines are saved, and the next launch sends them with the tail of the last log when that run logged an error.
- Every call, the socket and the log batches carry one run id,
gamend::run_id(), so the server shows a run's lines together.