Skip to content

Architecture

TL;DR: CVR ships Wasmtime 34.0.2. A single shared Engine + Linker + Config is created once per Unity session; each avatar/prop/world root that carries a compiled .wasm gets its own Store + Instance. All host APIs are bound into the Linker by a central BindingManager. Preemption is epoch-based at 100 kHz with a per-call deadline.

+-------------------------------------------------------------+
| Guest (WASM module, compiled from author C#) |
| - imports: UnityEngine.*, CVR.*, env.*, wasi_snapshot_* |
| - exports: _initialize, scripting_initialize, |
| _createInstance, _deserializeInstance, |
| scripting_call_* (events), _alloc |
+-------------------------------------------------------------+
|
Wasmtime ABI (i32/i64/f32/f64, memory)
|
+-------------------------------------------------------------+
| Host (CVR C#, Wasmtime.NET 34) |
| WasmVM --------+-- Store (per root) -- Instance |
| | |
| WasmManager ---+-- Engine (shared) |
| +-- Linker (shared) |
| +-- BindingManager.BindMethods() |
+-------------------------------------------------------------+

CVR-GameFiles/WasmScripting/WasmManager.cs owns the shared Wasmtime objects for the whole Unity process.

public static Config Config { get; private set; }
public static Engine Engine { get; private set; }
public static Linker Linker { get; private set; }

Awake() configures the Config with:

  • WithEpochInterruption(true) — preemption hook
  • WithReferenceTypes(true) — guest can hold externref
  • WithMultiValue(true), WithBulkMemory(true), WithSIMD(true)
  • WithDebugInfo(true)
  • plus wasmtime_config_wasm_exceptions_set(..., true) via P/Invoke
  • WASM threads are disabled (see comment in source)

Then it creates the shared Engine and Linker, starts the epoch timer thread, and calls BindingManager.BindMethods(Linker) to register every host function.

Each scene root that has a built WasmVMAnchor (avatar / prop / world GameObject) creates one WasmVM, which owns:

  • a Store — isolated WASM state
  • a compiled Module — reused from disk cache if the hash matches
  • an Instance produced by linking module against the shared Linker
  • per-behaviour entries in WasmAccessManager

StoreData (set via store.SetData(...)) carries:

  • AccessManager — object-handle table + permission enforcement
  • Memory — the guest linear memory accessor
  • Alloc — the guest-exported allocator (host can allocate in guest memory)
  • RaiseException — the guest-exported exception hook
  • VM — back-reference to the owning WasmVM
  • NetworkId — hash used for routing network messages

Source: CVR-GameFiles/WasmScripting/WasmVM.cs, WasmAccessManager.cs, StoreData.cs.

All host APIs go through CVR-GameFiles/WasmScripting/BindingManager.cs:

WasmStubs.DefineWasmStubs(linker); // WASI stubs (filesystem, random, clock)
WasmBinder.Links.UnityEngine.RegisterLinks.RegisterAllLinks(linker);
WasmBinder.Links.TMPro.RegisterLinks.RegisterAllLinks(linker);
WasmBinder.Links.CVR.RegisterLinks.RegisterAllLinks(linker);
ObjectLinksManual.BindMethods(linker); // manual UnityEngine overrides
DebugLinksManual.BindMethods(linker);
GraphicsBufferLinksManual.BindMethods(linker);
GameObjectLinksManual.BindMethods(linker);
UtilBindings.BindMethods(linker); // CVR utilities
FileStorageLinks.BindMethods(linker); // persistent storage
PlayerBindings.BindMethods(linker);
LocalPlayerBindings.BindMethods(linker);
AvatarPointBindings.BindMethods(linker);
NetworkingBindings.BindMethods(linker);
PropBindings.BindMethods(linker);
PortalBindings.BindMethods(linker);
WorldPermissionsBindings.BindMethods(linker);

The two categories:

  • Generated bindings (WasmBinder.Links.* / WasmBinder.LinksManual.*) — cover Unity types. One file per type, e.g. WasmBinder.Links.UnityEngine/TransformLink.cs.
  • Hand-written bindings (WasmScripting/*Bindings.cs) — cover CVR gameplay objects (player, avatar, prop, portal, world, networking, file storage, world permissions).

Any guest import that wasn’t bound is filled with an empty stub at instantiation time via BindingManager.FillNonLinkedWithEmptyStubs so the module links regardless.

The shared Engine runs an epoch counter. WasmManager spawns a background thread that calls Engine.IncrementEpoch() on a Stopwatch.Frequency / 100_000 cadence (~10 µs wall-clock between ticks).

Before every WASM call, the host sets the store’s epoch deadline (store.SetEpochDeadline(...)). When the counter reaches the deadline the guest traps — the host catches TrapException and marks the VM IsCrashed.

  • Runtime events: EpochDeadlineTicks = 2000 → ~20 ms per call, uniform across avatar/prop/world VMs.
  • Startup: _store.SetEpochDeadline(100000uL) inside WasmVM.Setup gives the initial scripting_initialize + _createInstance + _deserializeInstance passes a wider budget before the runtime deadline kicks in.

Host-owned objects cross the ABI as 64-bit handles packed as (int index, int version):

[StructLayout(LayoutKind.Explicit)]
public struct WrappedHandle {
[FieldOffset(0)] public long Handle;
[FieldOffset(0)] public int Index;
[FieldOffset(4)] public int Version;
}

See CVR-GameFiles/WasmScripting/WrappedHandle.cs, WrappedObject.cs, WeakObjectMap.cs. Garbage-collected or deleted objects have their slots swept periodically (WasmAccessManager.Cleanup).

Separate document: Build Pipeline. One paragraph summary:

At CCK build, WasmBuildProcessor scans all WasmBehaviour components + anything listed in CCKWasmProjectDescriptor.includedScripts, generates WasmModule.csproj, invokes dotnet publish -c Release, and drops Assets/WasmModule.wasm to be loaded by WasmVMAnchor at runtime.

  • CVR-GameFiles/WasmScripting/WasmManager.cs — shared Config/Engine/Linker, epoch timer thread, feature-flag setup.
  • CVR-GameFiles/WasmScripting/WasmVM.cs — per-root Store, module caching, event dispatch.
  • CVR-GameFiles/WasmScripting/BindingManager.cs — binding registration order + FillNonLinkedWithEmptyStubs.
  • CVR-GameFiles/WasmScripting/StoreData.csAccessManager, Memory, Alloc, RaiseException, NetworkId.
  • CVR-GameFiles/WasmScripting/WrappedHandle.cs, WrappedObject.cs, WeakObjectMap.cs — 64-bit (index, version) handle system.
  • CVR-GameFiles/ABI_RC.Systems.WasmScripting/WasmScriptingBridge.cs — on-scene wiring for avatar/prop/world VMs.