gasm-host 0.6.0

Native host for gasm games (a portable game runtime on WebAssembly): wasmtime, WebGPU via wgpu, audio, gamepads, WebSocket networking and storage. Includes the gasm-run runner
Documentation

gasm-host

Native host for gasm games. A game is one .wasm file, and this crate runs it: wasmtime for the module, wgpu (Metal, Vulkan, D3D12) for gasm:gfx, cpal audio, gilrs gamepads, WebSocket networking (ws:///wss://, rustls) and per-game storage.

Assets are never preloaded: files (--asset) and whole folders (--asset-dir, e.g. a mounted CD) are read on demand into guest memory, with case-insensitive names. Keyboard layouts are configurable (--keymap, --print-keymap); the default gives two players one keyboard.

It ships the gasm-run binary:

cargo install gasm-host
gasm-run game.wasm                       # play in a window
gasm-run game.wasm --headless 600        # CI: run 600 frames, print video/audio hashes
gasm-run game.wasm --compile game.cwasm  # ahead-of-time compile (no JIT at load)
gasm-run game.cwasm --allow-precompiled  # run it (native code: only files you compiled)

A guest call (init, a frame) that runs longer than --call-timeout (default 30 s, 0 = never) traps. The room relay for online play, gasm-relay, is a separate crate in relay/ (release bundles and ghcr.io/emdzej/gasm-relay; not on crates.io).

And a library, for embedding games in your own app or building a runner for another platform. It contains complete runners, headless::run (reproducible runs with hashes) and window::run (winit window, cpal audio, gilrs gamepads, keyboard layouts), and the parts they're made of:

use std::collections::HashMap;
use gasm_host::{assets::Assets, gfx::Gfx, host::{Game, Host, LoadOptions}, net::Net, storage::Storage};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let host = Host::new(Assets::new(), HashMap::new(), None, Gfx::null(), Net::new(false), Storage::memory());
    let mut game = Game::load(&std::fs::read("game.wasm")?, host, LoadOptions::default())?;
    for _ in 0..600 {
        game.frame()?;                    // call at game.host().frame_rate Hz
    }
    let h = game.host();
    println!("{}x{}, video hash {:08x}", h.width, h.height, h.video_hash.0);
    Ok(())
}

LoadOptions sets allow_precompiled and call_timeout; audio output is the audio::AudioOut trait. The windowed runner is the default feature window; default-features = false gives a headless-only library without winit, cpal and gilrs.

Guests are sandboxed: they see only their own memory and the ABI. Every pointer and handle is checked. There's no filesystem (the runner implements its own WASI subset: no preopened directories, env or args), network is opt-in, and the storage namespace is chosen by the host.

On Linux, building needs libasound2-dev libudev-dev pkg-config. MIT licensed.