TalebenchThe 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
- Rust with the target
wasm32-unknown-unknown(rustup target add wasm32-unknown-unknown). - The SDK,
talebench-sdk: a small crate that exports what the engine's sandbox calls. Add-ons are built on your own machine; Talebench builds nothing for you. - A workspace on Talebench where you may edit: add-ons are uploaded there.
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" }]
}
}idis<publisher>.<name>;talebench.is Talebench's own.capabilitiessays what the add-on may do. Creators see it before they add the add-on to a game, and the engine refuses anything outside it.functionsnames the formula functions it answers, so that a story callingentered()checks clean in the editor.
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
- 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.
- 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.
- 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.