Talebench

The guide: a first add-on

This guide builds a small add-on that counts the passages a player enters, shows the count on a step of its own and answers a formula. Talebench's own test add-on does the same; its files are on the examples page.

What you need

1. The crate

An add-on is a Rust library built as a cdylib. Its handler answers one message at a time, as JSON text, and may ask the host (the engine) for what it needs.

use serde_json::{Value, json};

pub fn handle(message: &str, host: &mut dyn FnMut(&str) -> String) -> String {
    let m: Value = serde_json::from_str(message).unwrap_or(Value::Null);
    match m["point"].as_str() {
        // a passage entered: count it in the add-on's own part of the save
        Some("on") if m["moment"]["moment"] == "passage_entered" => {
            let state: Value = serde_json::from_str(&host(r#"{"call":"state"}"#)).unwrap_or(Value::Null);
            let count = state["count"].as_u64().unwrap_or(0) + 1;
            host(&json!({ "call": "set_state", "state": { "count": count } }).to_string());
            "{}".into()
        }
        // the formula entered()
        Some("formula") if m["name"] == "entered" => {
            json!({ "value": m["state"]["count"].as_u64().unwrap_or(0) }).to_string()
        }
        _ => "{}".into(),
    }
}

talebench_sdk::package!(handle, fresh);

Every message names its point; anything an add-on does not answer is answered with {}, and the game goes on as without it. The reference lists every point, host call, proposal and answer.

2. module.json

Beside the crate, a package.json describes the add-on; the build turns it into the file's module.json and adds the logic's size and hash.

{
  "id": "acme.counter",
  "name": { "en": "Counter", "de": "Zähler" },
  "description": { "en": "Counts the passages entered.", "de": "Zählt die betretenen Abschnitte." },
  "version": "1.0.0",
  "interface": "1.0",
  "formatVersions": [5, 5],
  "test": { "story": "test/story.json", "walkthrough": "test/walkthrough.json" },
  "capabilities": {
    "points": ["on", "formula"],
    "owns": [],
    "changesCoreValues": false,
    "functions": [{ "name": "entered", "params": 0, "returns": "number" }]
  }
}

3. A test story

Every add-on carries a small story and a walkthrough: the choices by their labels and the ending they reach. Talebench plays it twice when the file is uploaded; both runs must reach the ending alike and use the add-on.

{ "steps": [{ "choose": "To the garden" }, { "choose": "The hidden door" }], "endingId": "end_done" }

4. Build the file

Build for wasm32-unknown-unknown in release mode and zip module.json, logic.wasm and the test story's files. Talebench's repository does this with scripts/build-package.sh <name>, which writes <id>-<version>.talebench-module.zip.

5. Upload and add it to a game

  1. In Talebench, open Workspace settings → Add-ons and upload the file. It is checked as the engine reads it and its test story is played; a file with a problem is refused with every problem named.
  2. In a project, switch to the full view and open Features → Add-ons. Add shows what the add-on may do and what it adds to the download, then adds it.
  3. Play: the test play runs the add-on; publishing copies its files into the game, so later versions change nothing in a published game.

Settings

An add-on may describe settings in module.json's content: a schema (texts, numbers, yes/no, choices, lists) and each field's label and help in English and German. The editor draws the form on the add-on's card, and the add-on reads what was set through the host call content.

Screens

An add-on answers the point view with parts: texts, headings, values, progress, rows, lists, pictures of the game and buttons that send it commands. The player draws them in the game's look, on a page of the game's menu or on a step of the story. Add-ons never draw HTML on the game's page; a page of their own runs apart (below).

A page of its own

When the parts are not enough (a board to drag on, a small game), an add-on can bring one HTML file of its own: page/index.html in the crate (scripts and styles inline, at most 1 MB), with "page": true in its capabilities and "interface": "1.2". A view shows it with a frame part (label, height 80–800, data). It runs in a sandboxed frame on modules.talebench.com: no network, no storage, no cookies and no way to the game's page. The page finds window.talebench: data, state, look (the game's colours and font, also as CSS variables --tb-bg, --tb-fg, --tb-accent, --tb-font), lang, asset(name) (a picture as a data URL), send(name, data, { done }) (a command the add-on owns, taken as a button's), resize(height) and onState(callback) (its new state after each command). What the page decides is a command, so saves and replays hold.

A scene of your own

A puzzle or a minigame no kit can make is an add-on with one step and a page of its own. Own the step (step:<name> in owns), answer its view with a frame part, and let the page send a command when the player has played: the add-on takes it and proposes the effects (set a value such as puzzle_solved, which needs changesCoreValues). The page's last command carries done: true, and the story goes on to the passage's options; give them only if on the values the scene set, as a check's outcomes lead on. Every decision is a command, so saves, replays and test runs hold; give the step a stand-in in module.json's book for the printed book.

Its part of a save

Describe what the add-on keeps in a save in module.json's state: { "schema": … }, the same kind of schema as its settings. Talebench then refuses a set_state that does not fit, so a mistake never ends up in a player's save.

Pictures

Put pictures (PNG, JPEG or WebP, at most 512 KB each) in the crate's assets/ folder; the build names each in module.json's assets. A view shows one with a picture part whose asset is <add-on id>/<name>, for example acme.counter/board. Publishing copies them into the game with the add-on.

Its own checks

An add-on can find problems in a game that only it understands: answer the point check with problems, each with a code it owns as check:<code>, a pointer into the story and its words in English and German. Creators see them under Check add-ons in the editor's Problems list, and an error stops publishing.

In the printed book

Give each of the add-on's steps and changes a stand-in text in module.json's book (steps and effects, each in English and German). The book prints it where the step or change stands; without one, the book says it leaves it out.

New versions

A version never changes once uploaded: a new one needs a new number. When the content or state of a new version differs, answer the point migrate and name it in points: Talebench brings a game's settings forward when its creator moves the game to the new version, and a player's saves when they load in the new release.

The library

Offer a version to every Talebench workspace from Workspace settings → Add-ons: Offer in the library…, the name shown as its maker and a licence (Free to use in your games, MIT, Apache 2.0 or CC BY 4.0). Talebench's admins review it: its checks run again, and they look at what it may do. If it is not taken, the reason shows beside the version; fix it and offer a newer version. An approved version is listed under From the library; others install it as if they had uploaded it, and their games keep it even if it is later withdrawn or taken down. The first part of an add-on's ID (acme of acme.counter) belongs in the library to the workspace whose version was approved first.