# Spike M0-11: one simulation (ADR-029) — report

The Rust `zoen-sim` step as a C ABI plugin (`crates/zoen-sim-ffi`) inside the Unity client. Golden check: scenario
`walk_eight_directions` from `packages/sim-golden/vectors.json`, final hash `bb46012ea5aaaebf`, every tick compared.
Data: `spike-report.json`; per-target build reports `plugin-*.json` in each `logs/evidence/M0-11*` folder.

| Target | Result | Plugin | Step cost (median, ns/tick: 1 / 5,000 movers) |
|---|---|---|---|
| macOS (Apple M3 player) | **pass** — hash match, all scenarios | dylib arm64 406,288 B | 108 / 333,200 |
| Android (nubia NX709S, APK) | **pass** — 4 tiers, hash match | .so arm64 316,552 B (stripped, 16 KB aligned) | 54.5 / 149,075–158,175 |
| Web (headless Chrome) | **pass** — 0 console errors, hash match | static archive 3,662,566 B; Web build +37,947 B (+0.32%) | 70–100 / 160,000–215,000 (timer-quantized) |
| iOS (aarch64-apple-ios) | **built, not tested** (ADR-032) | static archive 2,333,096 B (debug stripped) | — |
| Windows (x86_64-pc-windows-msvc) | **staticlib built; DLL blocked**, not tested | .lib 3,701,330 B; no DLL | — |

5,000-mover costs include the C# input fill; all stay under 0.35 ms of a 16.7 ms frame.

## Toolchains
Unity 6000.3.25f1 · rustc 1.99.0 (LLVM 23) · Android NDK r27c (bundled) · Emscripten 3.1.39-git with LLVM 17 (bundled) ·
Xcode 26.6, iPhoneOS SDK 26.5, minimum iOS 15.0.

## Web specifics
- Emscripten feature rewrite: rustc's objects list the LLVM 20+ `target_features` names `bulk-memory-opt` and
  `call-indirect-overlong`; the bundled LLVM 17/Binaryen rejects them, so `tools/sim/build-plugin.mjs web` drops them
  (implied by `bulk-memory` and `reference-types`, which stay). Re-check on every rustc or Unity upgrade.
- Browser floor: Chrome 95, Firefox 100, Safari 15.2 (the precompiled std keeps legacy wasm EH pads, bulk memory and
  reference types). Lowering it needs `-Zbuild-std` with `target-cpu=mvp` (nightly flags).
- `panic=abort` on the Web: no `catch_unwind`, so the plugin relies on its panic-free code (clippy denies indexing/panic).

## iOS (recorded not tested)
`node tools/sim/build-plugin.mjs ios`: staticlib at iOS 15.0, every member iOS platform, 9 header exports, needs only
`-lSystem -lc -lm`; the C ABI smoke links with the iPhoneOS SDK (not run: no device or simulator target here).
229 of 375 members (Rust's prebuilt std/compiler_builtins) carry embedded LLVM 23 bitcode that Xcode's LLVM cannot
parse; the linker drops it (the linked smoke has no `__LLVM` segment). Installed at
`Assets/Zoen/Plugins/iOS/libzoen_sim_ffi.a` (iOS player only); iOS builds get `ZOEN_SIM_STATIC`. The Unity Xcode
export and an unsigned `xcodebuild` are M0-11D2.

## Windows (recorded not tested)
`node tools/sim/build-plugin.mjs windows`: the staticlib builds (COFF x86-64, 9 exports). The DLL does not link with
rust-lld: it needs the Windows SDK import libraries (kernel32, ntdll, userenv, ws2_32, dbghelp) and the MSVC CRT
(msvcrt, vcruntime, ucrt). Getting them means accepting Microsoft's licence (owner): Visual Studio Build Tools on a
Windows machine, or an xwin download here (`ZOEN_WIN_LIBS` then passes them to the link). Import rules for
`Assets/Zoen/Plugins/Windows/x86_64/zoen_sim_ffi.dll` (64-bit Windows player only) are ready and tested.

## Decision
Confirmed with a qualification: `zoen-sim` is the one simulation shared by client prediction and the server; the C#
prediction subset of ADR-024 is not written. Open risks: Windows (DLL licence, no test machine), iOS (not run),
the pinned Emscripten's LLVM gap and the Web browser floor.
