Walt

building

You do not install walt - the docker image has it, and the current null0 bindings, baked in. There is nothing in your project to keep in sync with the engine.

docker run --rm --user $(id -u):$(id -g) \
  -v ./cart:/src -v ./webroot:/out \
  ghcr.io/notnullgames/null0-cart-walt:latest mygame

The template wraps that in npm start, which also serves the result and rebuilds when you save.

Walt notes

  • Imports are per-module: copy the type + import pair for each function you use.
  • You need a local memory (const memory: Memory = { initial: 1 }) even for a plain store.
  • A Color is a *pointer to 4 bytes* you write into memory yourself.
  • No malloc, so struct-returning functions do not work on the web host.

a cart, in Walt

This is carts/walt/simple/main.walt in the engine repo - the same file CI builds into the cart below, so it always compiles.

// simple walt cart for null0 (see null0.walt for the full bindings)
//
// Color is a pointer to 4 bytes (r, g, b, a) in memory, and a string is a
// pointer to null-terminated bytes - neither is a packed scalar, so both are
// written into memory below and passed as addresses.

// two pages because everything above lives at 65536, one page in; exported
// because the host reads those pointers out of cart.memory
export const memory: Memory = { initial: 2 };

const BLUE: i32 = 65536;  // rgba(0, 121, 241, 255)
const RED: i32 = 65540;   // rgba(230, 41, 55, 255)
const WHITE: i32 = 65544; // rgba(255, 255, 255, 255)
const TEXT: i32 = 65548;  // "hello from Walt"

type Clear = (i32) => void;
type DrawCircle = (i32, i32, i32, i32) => void;
type DrawText = (i32, i32, i32, i32, i32) => void;
import { clear: Clear, draw_circle: DrawCircle, draw_text: DrawText } from 'null0';

// called on load
export function load(): void {
  // walt has no data segments, so the bytes go in at runtime. these stores
  // must be inline in a function body - wrapping them in their own function
  // triggers a walt-compiler codegen bug (invalid bytecode)
  i32.store8(65536, 0);
  i32.store8(65537, 121);
  i32.store8(65538, 241);
  i32.store8(65539, 255);
  i32.store8(65540, 230);
  i32.store8(65541, 41);
  i32.store8(65542, 55);
  i32.store8(65543, 255);
  i32.store8(65544, 255);
  i32.store8(65545, 255);
  i32.store8(65546, 255);
  i32.store8(65547, 255);
  i32.store8(65548 + 0, 104);
  i32.store8(65548 + 1, 101);
  i32.store8(65548 + 2, 108);
  i32.store8(65548 + 3, 108);
  i32.store8(65548 + 4, 111);
  i32.store8(65548 + 5, 32);
  i32.store8(65548 + 6, 102);
  i32.store8(65548 + 7, 114);
  i32.store8(65548 + 8, 111);
  i32.store8(65548 + 9, 109);
  i32.store8(65548 + 10, 32);
  i32.store8(65548 + 11, 87);
  i32.store8(65548 + 12, 97);
  i32.store8(65548 + 13, 108);
  i32.store8(65548 + 14, 116);
  i32.store8(65548 + 15, 0);

  clear(BLUE);
  draw_circle(100, 100, 50, RED);
  draw_text(0, TEXT, 170, 92, WHITE);
}

// called on every frame
export function update(): void {}
carts/walt/simple/main.walt

callbacks

A cart implements the callbacks it cares about. In Walt they look like this:

export function update() {}

See anatomy of a cart for the full list and what each one is passed.