Build Pipeline
TL;DR: On every CCK avatar/spawnable/world build, WasmBuildProcessor gathers every WasmBehaviour + every script in CCKWasmProjectDescriptor.includedScripts, generates a WasmModule.csproj, and runs dotnet publish -c Release. Output is written to Assets/WasmModule.wasm and imported as a WasmModuleAsset reference on the root’s WasmVMAnchor. Toolchain components are downloaded on demand to %LOCALAPPDATA%/ChilloutVR/CVRBuildTools/.
Pinned tool versions
Section titled “Pinned tool versions”Source: CVR.CCK.Wasm/CCK/Editor/WasmBuildUtility.cs
private const string DotnetVersion = "10.0.100-preview.5.25277.114";private const string WasiVersion = "25.0";private const string CCKWasmModuleVersion = "0.0.65";- .NET SDK 10.0.100-preview — NativeAOT compiler (ILC) targets WASI/WASM.
- WASI SDK 25.0 —
clang/wasm-ldtoolchain. - CCKWasmModule 0.0.65 — private repo providing
WasmModule.propsand the Unity-shim assemblies the guest compiles against.
Tool install location
Section titled “Tool install location”%LOCALAPPDATA%/ChilloutVR/CVRBuildTools/:
CVRBuildTools/├── local-dotnet/ # dotnet.exe + SDK 10 preview├── wasi-sdk/ # clang + sysroot, version pinned in .version└── CCKWasmModule/ # WasmModule.props + shim assembliesEnsureDotnetInstalled, EnsureWasiInstalled, EnsureCCKWasmModuleInstalled in WasmBuildUtility.cs each check a version file, download the expected version if missing, and extract via tar (Linux/Win 10+). The .NET installer uses the official dotnet-install.ps1/.sh; the WASI SDK is pulled from github.com/WebAssembly/wasi-sdk; the CCK WasmModule currently pulls from a private git.chilloutvr.dev repo.
Generated csproj
Section titled “Generated csproj”For the standard flow (no externalProjectPath), WasmBuildUtility.GenerateCsproj writes:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <WasmOutputPath>{Unity Application.dataPath}/</WasmOutputPath> <DefineConstants>CVR_SCRIPTING_CONTEXT_{AVATAR|PROP|WORLD};{custom defines}</DefineConstants> <!-- Added only if enableReflection == false on the descriptor: --> <IlcDisableReflection>true</IlcDisableReflection> </PropertyGroup>
<ItemGroup> <Compile Include="{absolute path 1}" /> <Compile Include="{absolute path 2}" /> <!-- ... one line per script path collected ... --> </ItemGroup>
<Import Project="{CVRBuildTools/CCKWasmModule[-dev]}/WasmModule.props" /></Project>The WasmModule.props import is what injects:
- the WASI/WASM target framework + RID
- the shim assemblies (Unity types, CVR API surface)
- ILC options
- output path =
{WasmOutputPath}WasmModule.wasm
Script discovery
Section titled “Script discovery”WasmBuildProcessor.BuildScripts (runs on OnPreProcess{Avatar|Spawnable|World}):
- Iterate every
WasmBehaviourcomponent in the current hierarchy — add its sourceMonoScriptpath to the compile set; in-place replace the component withWasmRuntimeBehaviourviaCCKCommonTools.ReplaceComponentInPlace. - Iterate
CCKWasmProjectDescriptor.includedScripts— add eachMonoScript’s file path. Skip abstract types and non-WasmBehaviourdescendants. - Reflect over every resulting type:
ScanForUnityEvents— for eachUnityEventsenum flag, look for a matching method name and set the bit.ScanForGameEvents— same forGameEvents.- Store result in
WasmBehaviourInfo { behaviourId, definedEvents, definedGameEvents }.
- Assign each behaviour a numeric
behaviourIdby hash-sort order (WasmBuildUtility.GetHash— FNV-1a of the class name). - Generate
UnityEventRegister.csfrom theUnityEventSerializationContextand drop it alongside the csproj. This registers all persistent event listeners found in the scene. - Run
dotnet publish -c Release(or-c Debugwhen debugging/playmode). Output is read line-by-line and categorized intoDebug.LogWarning,Debug.LogError, or plainDebug.Logbased onerror CS/warning CS/error NETSDKmarkers. - Import the resulting
Assets/WasmModule.wasmand assign it toWasmVMAnchor.moduleAsset+ populatebehaviourInfosand the union of alldefinedGameEvents.
External project path
Section titled “External project path”If CCKWasmProjectDescriptor.externalProjectPath is set, the pipeline skips csproj generation entirely and runs dotnet publish on the user-managed project. Use this to:
- consume NuGet packages (add
<PackageReference>lines yourself), - organize code into your own folder structure,
- share the WASM module project with non-Unity tooling.
Debug vs Release
Section titled “Debug vs Release”- Release (
enableDebugging == false, not Play Mode):dotnet publish -c Release. Smaller, faster.wasm. No debug info. - Debug (
enableDebugging == trueor Play Mode):dotnet publish -c Debug. Larger.wasmwith DWARF sections; Wasmtime’sWithDebugInfo(true)inWasmManagerconfig makes stack traces usable.
CCKEditorPrefs.WasmVerboseBuild adds -bl to the publish command (binary log) and logs all output lines via Debug.Log.
Reflection
Section titled “Reflection”By default the csproj sets <IlcDisableReflection>true</IlcDisableReflection>, which ILC uses to strip the reflection metadata tables. This keeps the module small but breaks any code path that reflects over types.
Toggle CCKWasmProjectDescriptor.enableReflection to remove that property and publish a full-reflection module. Expect a significant size increase.
Unity event rewiring
Section titled “Unity event rewiring”During BuildScripts, the processor scans every SerializedProperty whose type is PersistentCall (Unity’s serialized event handler data) and rewrites any that target a WasmBehaviour instance to instead target the replacement WasmRuntimeBehaviour + a string method name. Detail: Unity Events Rewiring.
Output
Section titled “Output”Assets/WasmModule.wasm— the module binary. Do not commit or hand-edit — it is regenerated every build.WasmVMAnchoron the root GameObject — runtime pointer to the module + behaviour metadata.{projectDirectory}/UnityEventRegister.cs— regenerated every build, used for event registration at runtime.{projectDirectory}/WasmModule.csproj— regenerated every build.
Common issues
Section titled “Common issues”- Build hangs at 50 % — usually the first-time install of .NET SDK or WASI SDK. Check the progress bar label; if it says “Installing .NET SDK” a download is in progress.
error CS0103insideWasmModuleclasses — the csproj usesMicrosoft.NET.Sdk, not Unity’s built-in compiler. Types likeUnityEngine.Objectare supplied by theWasmModule.propsshim assemblies. Namespace mismatches against what the shim exposes will fail to compile here even when Unity’s editor compile was happy.NotImplementedExceptionfrom bound function — the binding may be stubbed out. Check Not Exposed.
Related
Section titled “Related”- Authoring — the author-facing workflow.
- Runtime Lifecycle — what the module does after it’s produced.
- Limits — what happens if the resulting module takes too long per call.