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.
Layered picture
Section titled “Layered picture”+-------------------------------------------------------------+| 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() |+-------------------------------------------------------------+Shared singletons: WasmManager
Section titled “Shared singletons: WasmManager”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 hookWithReferenceTypes(true)— guest can holdexternrefWithMultiValue(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.
Per-root runtime: WasmVM
Section titled “Per-root runtime: WasmVM”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
Instanceproduced by linking module against the sharedLinker - per-behaviour entries in
WasmAccessManager
StoreData (set via store.SetData(...)) carries:
AccessManager— object-handle table + permission enforcementMemory— the guest linear memory accessorAlloc— the guest-exported allocator (host can allocate in guest memory)RaiseException— the guest-exported exception hookVM— back-reference to the owningWasmVMNetworkId— hash used for routing network messages
Source: CVR-GameFiles/WasmScripting/WasmVM.cs, WasmAccessManager.cs, StoreData.cs.
Binding layer
Section titled “Binding layer”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 overridesDebugLinksManual.BindMethods(linker);GraphicsBufferLinksManual.BindMethods(linker);GameObjectLinksManual.BindMethods(linker);UtilBindings.BindMethods(linker); // CVR utilitiesFileStorageLinks.BindMethods(linker); // persistent storagePlayerBindings.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.
Preemption
Section titled “Preemption”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)insideWasmVM.Setupgives the initialscripting_initialize+_createInstance+_deserializeInstancepasses a wider budget before the runtime deadline kicks in.
Object handles
Section titled “Object handles”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).
Build-time pipeline summary
Section titled “Build-time pipeline summary”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.
Source
Section titled “Source”CVR-GameFiles/WasmScripting/WasmManager.cs— sharedConfig/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.cs—AccessManager,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.
Related
Section titled “Related”- Runtime Lifecycle — step-by-step call flow.
- Permissions — how
CheckAccessenforces the three axes. - Limits — exact deadlines, quotas, and disabled features.