sandbox

package
v0.1.73 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 33 Imported by: 0

Documentation

Overview

Package sandbox runs built mods without a game. A Mod is one WebAssembly module behind the host boundary, kept to modlock-host's imports and limits; a Sandbox runs a session of mods with a stand-in player, as a server would.

Index

Constants

View Source
const DataDir = "data"

DataDir is the built mod's directory of the files the mod reads.

View Source
const ManifestFile = "mod.json"

ManifestFile is the manifest's file name in a built mod.

Variables

This section is empty.

Functions

func Interpreter

func Interpreter(runtime manifestpb.Manifest_Runtime) string

Interpreter names the module in Modlock's host package that runs an interpreted runtime, or is empty for WebAssembly and unknown runtimes.

Types

type Build

type Build struct {
	// Manifest is the mod's mod.json.
	Manifest *manifestpb.Manifest
	// Entry is the file the manifest names: the module of a WebAssembly mod,
	// or the source an interpreter runs.
	Entry []byte
	// Data holds the files the mod ships in its data/ directory.
	Data fs.FS
}

Build is a built mod as the host reads it.

func ReadBuild

func ReadBuild(dist fs.FS) (*Build, error)

ReadBuild reads the built mod in dist through its manifest.

type Config

type Config struct {
	// Mods lists the built mods to run.
	Mods []string
	// Map names the world the mods start in, or is empty for none.
	Map string
	// Args are passed to each mod's start handlers.
	Args []string
	// Interpreters returns the directory that holds the interpreted
	// runtimes' modules, such as quickjs.wasm. It is called the first time an
	// interpreted mod loads.
	Interpreters func(ctx context.Context) (string, error)
	// Events receives each event a server would report, on one goroutine.
	Events func(*control.HostEvent)
	// Settings is the file that keeps the player's settings, or empty to
	// keep them for the session only.
	Settings string
	// Files is the directory that keeps the files mods write, or empty to
	// refuse their writes.
	Files string
	// Compiled is the directory that keeps compiled modules across sessions,
	// so a later session loads the interpreters and an unchanged mod without
	// compiling them, or empty to compile each module once per session.
	Compiled string
}

Config describes a sandbox session.

type Divergence added in v0.1.73

type Divergence struct {
	// Event counts the events delivered, from 1.
	Event int
	// Method is the event's method.
	Method string
	// Reference is what the reference did where the builds part.
	Reference string
	// Candidate is what the candidate did there.
	Candidate string
}

Divergence is the first event on which a Twin's builds differ, and how.

func (*Divergence) Error added in v0.1.73

func (d *Divergence) Error() string

Error describes the divergence.

type Mod

type Mod struct {
	// contains filtered or unexported fields
}

Mod is one instance of a mod's module. It handles one event at a time.

func Load

func Load(ctx context.Context, module []byte, options Options) (*Mod, error)

Load checks that module imports only from modlock and WASI preview 1 and exports memory and modlock_event, then instantiates and initializes it within the host's memory and time limits.

func (*Mod) Close

func (m *Mod) Close(ctx context.Context) error

Close stops the mod.

func (*Mod) Deliver

func (m *Mod) Deliver(ctx context.Context, method string, request, response message) error

Deliver calls the mod's method with request within the event budget and decodes its answer into response, which is nil for a method that answers nothing. Start gets the start budget instead, since a script mod evaluates its script there. An error the mod returns is the call's error; a trap or an exhausted budget also stops the mod.

func (*Mod) Stopped

func (m *Mod) Stopped() bool

Stopped reports whether a trap or an exhausted budget stopped the mod.

type Options

type Options struct {
	// Calls answers the mod's host calls, or is nil to answer each with an
	// empty reply, as a host with no game would.
	Calls func(call *wasm.Call) *wasm.Reply
	// Output receives the mod's standard output and error, or is nil to drop
	// them.
	Output io.Writer
	// Cache keeps compiled modules for later loads, or is nil for none.
	Cache wazero.CompilationCache
	// Interpret runs the module in wazero's interpreter, which starts at once,
	// rather than compiling it, which can take a minute for a large module.
	// It suits a mod loaded to run a few calls, such as a check.
	Interpret bool
	// Clock is the time the mod reads from both WASI clocks, or nil for the
	// system clocks. A check that compares two runs gives each the same
	// clock, so the time a mod reads cannot tell the runs apart.
	Clock func() time.Time
	// Random is the source of the mod's WASI random bytes, or nil for the
	// system's.
	Random io.Reader
}

Options configure a loaded Mod.

type Sandbox

type Sandbox struct {
	// contains filtered or unexported fields
}

Sandbox runs mods without a game, as a server would with one player who never has a hero: it starts each mod, gives it the world and the frame clock, passes the player's commands and button presses to it, and reports its logs, the messages it shows the player and its interface as host events. Host calls that need the game answer empty.

func Start

func Start(ctx context.Context, config Config) *Sandbox

Start starts the mods in a new sandbox. A mod that fails to load reports the failure and leaves the others running.

func (*Sandbox) Command

func (s *Sandbox) Command(ctx context.Context, line string) (bool, error)

Command passes line to the mods as the player's command, as if typed in chat, and reports whether a mod claimed it. A leading slash is optional.

func (*Sandbox) Input added in v0.1.42

func (s *Sandbox) Input(ctx context.Context, mod string, event *ui.UiInputEvent) (bool, error)

Input delivers a change to an input in the interface mod shows the event's player, and reports whether the mod is running to receive it. The creator's own preview sends it, so the sandbox passes the value on as sent.

func (*Sandbox) Press

func (s *Sandbox) Press(ctx context.Context, mod string, slot int32, node string) (bool, error)

Press presses the button node in the interface mod shows the player in slot, and reports whether the mod is running to receive it.

func (*Sandbox) Reload

func (s *Sandbox) Reload(ctx context.Context, path string) error

Reload replaces the running mod with the build at path, or reports why it did not load and keeps the running one. It returns once the build loaded or failed to.

func (*Sandbox) Stop

func (s *Sandbox) Stop()

Stop ends the session.

func (*Sandbox) Wait

func (s *Sandbox) Wait() error

Wait waits for the session to end.

type Twin added in v0.1.73

type Twin struct {
	// contains filtered or unexported fields
}

Twin runs two builds of one mod in lockstep, a reference and a candidate, and reports the first event on which they differ. A mod's only inputs are its events, the replies to its host calls, the clocks and random_get, and its only effects are its host calls and its event replies, so two builds behave the same exactly when, fed the same inputs, they make the same calls and the same replies. The Twin feeds both the same inputs: one clock that moves only between events, one stream of random bytes each from one seed, and for each of the candidate's calls the reply the reference got to the equal call.

Each event goes to the reference first, recording its calls, then to the candidate, so a Twin holds one event's calls and takes twice its time.

The candidate loads during the first event, after the reference has run it: a module's initialization runs its top level, which QuickJS runs inside Start, so the calls the top level makes belong to that event's comparison.

A line a build writes to its standard output counts as a Log call, as the sandbox logs both alike: QuickJS's console makes Log calls, while a scriptc module's console writes its standard output.

func NewTwin added in v0.1.73

func NewTwin(ctx context.Context, reference, candidate []byte, answer func(*wasm.Call) *wasm.Reply, cache wazero.CompilationCache) (*Twin, error)

NewTwin loads the reference module and keeps the candidate for the first event to load. answer replies to the reference's calls, or is nil to answer each empty, as the sandbox does; cache keeps compiled modules, or is nil.

func (*Twin) Advance added in v0.1.73

func (t *Twin) Advance(d time.Duration)

Advance moves both builds' clock forward by d.

func (*Twin) Close added in v0.1.73

func (t *Twin) Close(ctx context.Context) error

Close stops both builds.

func (*Twin) Deliver added in v0.1.73

func (t *Twin) Deliver(ctx context.Context, method string, request message, response message) error

Deliver gives the event to both builds and decodes the reference's answer into response, which is nil for a method that answers nothing. It returns a *Divergence when the builds' calls, answers or failures differ, and otherwise the reference's own error.

func (*Twin) Events added in v0.1.73

func (t *Twin) Events() int

Events counts the events delivered so far.

func (*Twin) Play added in v0.1.73

func (t *Twin) Play(ctx context.Context, source []byte, mapName string, frames int) error

Play gives the pair what a sandbox session gives a mod: Start with source, its players, the world named mapName, then frames frames of the frame clock when the mod asks for frames. It stops at the first divergence and returns it, ignoring the reference's own errors, as a session logs them and plays on.

func (*Twin) Stopped added in v0.1.73

func (t *Twin) Stopped() bool

Stopped reports whether a trap or an exhausted budget stopped either build.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL