Appearance
Other languages and the raw ABI
You'll need this when: you're porting a game, or you'd rather write Rust, C, C++, C# or Zig than TypeScript. Status: Built (headless): the raw ABI v1, fuel metering, and generated bindings for C/C++, Rust and C#, tested on a C guest. There's no mandated SDK: any language that compiles to wasm works, with your own bindings if you like.
Two ways in
| JS / TypeScript | Raw wasm | |
|---|---|---|
| Write | logic.ts against @inventery/host | anything that compiles to wasm32 |
| Build | build.mjs bundles it; it runs in QuickJS compiled to wasm | your toolchain makes logic.wasm |
plot.json | "code": "dist/logic.js" | "code": "logic.wasm" |
| Speed | an interpreter: fine for game logic | native wasm speed |
Both get the same calls, hooks, budgets and refusals.
C and C++
plot/sdk/inventery.h is header-only and freestanding.
c
#include "inventery.h"
static void on_player_enter(const char *player) {
inv_ui_chat("\"lobby\"", "Someone arrived", 0); // objects and unions are JSON text
}
static void tick_lobby(double dt) {
inv_vec3 p = { 8, 65, 8 };
inv_world_set_block(p, inv_world_block_id("stone"));
}
INV_PLOT(.tick_lobby = tick_lobby, .on_player_enter = on_player_enter) // once, in one .c filesh
zig cc -target wasm32-freestanding -O2 -nostdlib -Wl,--no-entry -o logic.wasm plot.cA refused call returns 0, false or NULL, and inv_error() gives {"rule","call","message"}.
Rust
Copy plot/sdk/inventery.rs into src/ as mod inventery; (crate-type = ["cdylib"], target wasm32-unknown-unknown, Rust 1.82+; no_std works).
rust
use crate::inventery::{self as inv, Plot};
#[derive(Default)]
struct Game { ticks: u32 }
impl Plot for Game {
fn tick_lobby(&mut self, _dt: f64) { self.ticks += 1; }
fn on_player_enter(&mut self, player: &str) {
let _ = inv::ui::chat(&inv::Targets::PlayerId(player.into()), "hi", None);
}
}
inventery_plot!(Game; tick_lobby, on_player_enter); // only the listed hooks are exportedCalls return Result<T, HostError>.
C#
plot/sdk/Inventery.cs, for NativeAOT-LLVM targeting wasm32. One static class per module (Scene.Create(...)); a refusal throws HostException with .Rule.
csharp
using Inventery;
static class Game {
[System.Runtime.CompilerServices.ModuleInitializer]
internal static void Setup() => Plot.Hooks = new Hooks {
OnPlayerEnter = p => Ui.Chat(Targets.PlayerId(p), "hi"),
};
}The raw ABI v1 (for your own bindings)
Imports from module "inv":
| Import | Does |
|---|---|
call(op, argsPtr, argsLen) -> i32 | call host op op with a UTF-8 JSON array of args. Returns the result's JSON length, or -1 - len for an error JSON {rule, call, message} |
ret(dstPtr) | copy the last result (or error) JSON into your memory |
result(ptr, len) | set a hook's return value (JSON) before the hook returns |
log(ptr, len) | a debug line |
Exports: memory, inv_abi() -> 1, inv_alloc(len) -> ptr, inv_has(namePtr, nameLen) -> i32, inv_hook(namePtr, nameLen, argsPtr, argsLen) -> i32 (1 if a result was set).
- Op numbers are the schema's call order (
INV_OP_*in the C header, constants in the Rust file). They're frozen once API version 1 ships. - Bytes cross as
{"$b": "<base64>"}; a callback argument (forlobbies.runWithin) as{"$fn": id}, which the host calls back by re-enteringinv_hook("$fn", [id]). - Don't export
inv_fuelorinv_oom: the CPU metering adds them. - CPU is metered with fuel injected at function entries and loop heads; memory growth past your cap fails.
Porting checklist
- Export your level and props as glTF (glTF guide).
- Separate game logic from the source engine's runtime; compile it to wasm.
- Decide what the room referees (hits, scores, inventory) and what devices only predict.
- Decide which of the base game's rules your game keeps: a game that is its own server sets their categories off (
"baseRules": { "blocks": false, "pvp": false, ... }in a runtime'splot.json). - Write visible state to the scene; send game messages on channels.
- Map input, camera and UI onto
input.read,players.setCameraandui.*.