Install and update your game's content by downloading only the chunks that changed between builds — straight from a static CDN export, with no game server. A C# client (P/Invoke over the shared Rust core) plans the missing set locally, downloads it over concurrent HTTP Range requests, verifies every chunk (BLAKE3) and reconstructs your files byte-identically before you load them.
Beta, currently untested. The package compiles against the CAVS C ABI and mirrors the shipping Kotlin/Node bindings 1:1, but it has not yet been validated inside the Unity Editor or on a device. It ships with the native libraries bundled so you can try it — feedback and fixes are welcome.
A C# CavsClient over libcavs_sdk via P/Invoke. Async FetchStaticAsync with progress and cancellation; a persistent content-addressable cache.
Publish once with cavs store export --static-plans; host the tree on S3 / R2 / Pages / nginx. The client updates straight from it.
Every chunk BLAKE3-verified, every file reconstructed byte-identically (a torn file is never promoted) — the same guarantee as the Godot client.
The download bundles the native libcavs_sdk for desktop targets, so it is drop-in on Windows, macOS and Linux.
1 — add the package2 — natives are bundled3 — call itYou need the CAVS command-line tools once, to package and export builds. Build them from the repository root with cargo build --release.
Diagnose Unity update cost first with cavs analyze steampipe ./Build_old ./Build_new.
The first launch installs the whole build; later launches download only the chunks that changed, reusing the persistent cache. Do it on your loading screen.
using Cavs; using var client = new CavsClient(); var result = await client.FetchStaticAsync(new FetchStaticRequest { base_ = "https://cdn.example.com/game", // a store export --static-plans tree asset = "game", outputDir = Path.Combine(Application.persistentDataPath, "cavs/install"), cacheDir = Path.Combine(Application.persistentDataPath, "cavs/cache"), connections = 8, }, onProgress: ev => progressBar.value = (float)ev.percentage); Debug.Log($"fetched {result.chunksFetched}, reused {result.chunksReused}, saved {result.savedPercent:F1}%"); // The reconstructed files are under outputDir; load your AssetBundle from there.
Retries are safe by design: verified chunks stay in the content-addressable cache, so a retried fetch — even after the app was killed mid-download — only fetches what is missing.
| Member | Description |
|---|---|
| new CavsClient() | Owns one native context; reuse across calls, dispose when done |
| FetchStaticAsync(req, onProgress, cancel) | Install/update from a static export off the main thread |
| Execute(op, json) → string | Call any SDK operation (analyze, preview, packDirectory, …) synchronously |
| FetchStaticResult | chunksFetched, chunksReused, wireBytes, logicalBytes, savedPercent |
| ProgressEvent | type, phase, currentBytes, totalBytes, percentage |
| CavsException.Code | Stable CAVS-E-* code (e.g. CAVS-E-CANCELLED) |
| Version / AbiVersion | Native SDK and ABI versions |