Skip to content

Toolchain Setup

Bundle code is written in C# and compiled ahead of time to a WebAssembly component. Everything you need ships as NuGet packages published to nuget.org under the replay.blackice.* name. You never build the BlackICE source, and you do not need a game installation to compile a bundle.

Requirements

  • .NET 10 SDK or newer
  • Windows or Linux (x64 and arm64 hosts)
winget install Microsoft.DotNet.SDK.10
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel 10.0

Alternatively install the dotnet-sdk package from your distribution.

That is the complete toolchain. The NativeAOT compiler and the WASI SDK are provisioned automatically by the packages (see What the packages do for you).

NuGet packages

Package What it provides Reference it from
replay.blackice.server Bindings to the server's APIs, the component glue, and the build configuration for server blobs The project that becomes your server blob
replay.blackice.client Bindings to the client's APIs, the component glue, and the build configuration for client blobs The project that becomes your client blob
replay.blackice.shared Bindings to the shared APIs, shared types, and the framework runtime used by code that compiles into both blobs A library you can use in a shared project between realms
replay.blackice.msgpack A WebAssembly AOT compatible MessagePack serializer used internally. Can be used by bundle developers too Pulled in automatically by the packages above

replay.blackice.client and replay.blackice.server also bring in the native AOT compiler (Microsoft.DotNet.ILCompiler.LLVM and its OS-specific runtime pack) and pin its version, so you never add compiler packages yourself.

SDK and game versions match

The nuget packages are continously updated to support new features added to the BlackICE client or REServer. We try our hardest to keep breakages to a minimum, but it might still happen from time to time.

We publish nuget packages with semver MAJOR.MINOR.PATCH

  • Major - A breaking change to the ABI has happened. You need to recompile your bundle against a nuget package of the same major version to support the newest BlackICE / REServer builds
  • Minor - New functions have been added to the host(s). Build your bundle against a newer package version to get access to these features
  • Patch - A minor correction has happened inside the nuget packacges

The examples below use a floating version (1.*.*) that resolves to the newest patch version at restore time. You need to decide for yourself what level of pinning makes sense for your workflow. For reproducible builds, pin the exact version shown on the package's nuget.org page.

WASI SDK auto-provisioning

Linking the AOT-compiled blob needs the WASI SDK. A props file shipped inside the replay.BlackICE.Client/Server packages downloads and extracts it on the first build into a shared, versioned location:

Linux:      ~/.wasi-sdk/wasi-sdk-XX.YY
Windows:    %USERPROFILE%\.wasi-sdk\wasi-sdk-XX.YY

Every project that imports the props reuses that one installation, so the SDK downloads once per machine, not once per project. Version 29.0 is the current default based on the expectations of the AOT compiler.

The provisioning is configurable:

Property / variable Effect
WasiSdkVersion Fetch a different SDK version (-p:WasiSdkVersion=34.0)
WasiSdkRoot Install into a different directory
WASI_SDK_PATH Point at an existing WASI SDK installation, bypasses the automatic provisioning
WasiSdkUrl Full tarball URL, bypasses OS/architecture selection

What the packages do for you

The build props imported from the replay.blackice.* packages configure the project automatically. They:

  • Set the wasi-wasm runtime identifier and enable trimming, self-contained deployment, and invariant globalization
  • Run the publish step after every build, so a plain dotnet build produces the compiled blob
  • Link the blob as a WASI component implementing the world for that realm (client or server), which is also why each side must reference its own package rather than replay.blackice.shared only
  • Provide the auto-provisioning above and the entry-point glue

Example projects

Instead of creating a project from scratch, check out our example bundles on github

First build

Create a class library for the server side and reference the package:

sv/sv.csproj
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Library</OutputType>
        <TargetFramework>net10.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="replay.blackice.server" Version="1.0.*" />
    </ItemGroup>
</Project>

AllowUnsafeBlocks is required because the entrypoint glue that the package adds to your project uses unsafe code.

Add the entry point:

sv/App.cs
public static class App
{
    public static void Start()
    {
        // Runs once when the bundle is started
    }
}

Build:

dotnet build

The first build downloads the WASI SDK, then compiles. The blob lands at:

sv/bin/Debug/net10.0/wasi-wasm/native/sv.wasm

The blob file name comes from your project (assembly) name.

The blob is now ready to be declared in the bundle manifest. See Bundle Config for how the client and server blobs are named there, and Project Layout for how a full bundle project is organized.

Release builds

dotnet build -c Release produces a release artifact under bin/Release/. These are significantly smaller and more optimized than debug builds. We strongly recommend only distributing release builds in production.