Skip to main content

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 await tokens, 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::route hands each to its own handler. A server message goes to on_<kind> by its name in gamend::events, and the socket's own go to on_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 reconnecting before each try, with attempt, wait and reason. Then comes reopened, whose lost names any topic the server refused, or error once 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_email makes 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; username may 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"
  • url is the server a shipped game talks to. A new project starts on https://gamend.org.
  • local_url is a Gamend on your machine, as mix dev.start serves it.
  • gamend/target picks between the two for a game played from the editor. It is kept in the editor's own settings, so an exported game always reads url.
  • 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 Gamend dock on the Leaderboards tab

  • 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 GET endpoints 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/policy what 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.