- C# 90%
- Shell 6.5%
- PowerShell 2.1%
- C 0.9%
- Python 0.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Sticky visual cuts stay on for the map session; GC waits for idle+settled EMA; heartbeat logs prove the 100–300ms band. Co-authored-by: Cursor <[email protected]> |
||
| docs/perf-bench | ||
| examples | ||
| libs | ||
| scripts | ||
| src | ||
| .gitignore | ||
| JENKINS.md | ||
| Jenkinsfile | ||
| README.md | ||
| WorldBoxModManager.csproj | ||
AexoLoader — WorldBox Mod Manager
Offline-first · single DLL · stability-first · transparent telemetry
AexoLoader is an independent WorldBox mod loader that replaces stock NeoModLoader runtime while keeping NML mod compatibility (types, folder layout, compile expectations). Everything ships in one file: Harmony, Roslyn, NML API surface, SoftPerf, boot self-install, and Bugsink reporting.
| AexoLoader | Stock NeoModLoader | |
|---|---|---|
| Distribution | One WorldBoxModManager.dll |
Multiple DLLs + NML folder |
| Boot | Managed + RIOL (EarlyBoot) |
Experimental + StreamingAssets/mods |
| Offline | First-class (no Steam auth gate) | Depends on NML setup |
| Diagnostics | Local log + optional Bugsink | Manual log handoff |
| Resource index | SoftPerf-budgeted, coarse splash mode | Full-folder per mod pass |
| Stress suite (v1.7.5+) | Wins every WorldBoxPerfMod stage | Higher times / weaker sustain |
| Known crash guards | HUD, atlas, Units tab, CityBeh build | Mod-dependent |
Latest release: v1.8.4 · all releases
Stable channel starts at v1.8.0. Every older tag (
v1.7.xand below) is unstable / unsupported (Forgejo prerelease). Use 1.8+ only for production and telemetry.
The README pins the current tag manually — check the releases page if this line is behind.
Install
Every update (normal path)
Copy WorldBoxModManager.dll into:
worldbox_Data/Managed/WorldBoxModManager.dll
Harmony, Roslyn, NML API, SoftPerf, and telemetry are already inside that DLL. No extra packages.
First install only (once per game)
- Also copy the same DLL to
worldbox_Data/StreamingAssets/mods/ - Enable Experimental Mode in WorldBox
- Launch once — BootInstaller registers
ScriptingAssemblies.json+RuntimeInitializeOnLoads.json - After that: Experimental off, updates = Managed only
Your content mods go in the game Mods/ folder (or NML-style under StreamingAssets). They do not go in Managed.
Auto-update (1.8+ channel)
Starting with the 1.8 line, AexoLoader can check Forgejo releases over HTTPS, download WorldBoxModManager.dll + .sha256, verify the hash, and stage the file under:
{persistentData}/AexoLoader/update-staging/
By default (wait-at-launch), Bootstrap blocks briefly (timeout) to fetch/verify/apply the latest 1.8.y before mods load. If Managed was updated, the process quits once so the next start loads the new assembly. Mid-session checks still stage only; apply remains disk + restart (never hot-unload).
| Client | Behavior |
|---|---|
| 1.8.x (stable) | May update to the newest 1.8.y only |
| ≤1.7.x (unstable) | Unsupported — no auto-update; install 1.8+ manually from stable releases |
Settings (AexoLoader/settings.json):
{
"autoUpdate": {
"enabled": true,
"channel": "1.8",
"checkIntervalHours": 12,
"waitForLatestOnLaunch": true,
"launchCheckTimeoutSeconds": 15,
"reportUpdateEvents": true
}
}
Privacy: Forgejo releases only; no save uploads; optional BugSink breadcrumbs are version strings only (update_staged / update_applied). Set enabled: false to disable checks; set waitForLatestOnLaunch: false to keep the old stage-only-at-boot behavior.
Windows Crash!!! before any Aexo log
This dies before managed code / BugSink. Signature in Player.log on the
Windows game PC (not the Linux agent host):
Begin MonoManager ReloadAssembly
Unable to find type [UnityEngine.CoreModule.dll]UnityEngine.…
Crash!!!
Repair on that PC (Assembly-CSharp.dll.bak-pre-aexo-loader, Steam Verify, or
scripts/repair-windows-boot.ps1 -LoaderDll …\WorldBoxModManager.dll), then
install v1.8.4. Do not assume the Cursor agent machine can see Windows
Managed files.
Head-to-head vs NeoModLoader (WorldBoxPerfMod v1.0.1)
Paired stress bake-off on identical WorldBox (Unity 2022.3.60f1), two game copies, frozen binaries only:
- AexoLoader v1.7.5 —
worldbox-aexo - NeoModLoader (
02266fe…) —worldbox-nml
Harness: WorldBoxPerfMod (CSV only; no DSN in the mod). Raw files: perf_AexoLoader_20260718_004934 · perf_NeoModLoader_20260718_004751.
Verdict
AexoLoader wins every measured stage. Biggest gaps: spawn reliability (2000/2000 vs 250/2000), sustain frame time (4.4× smoother), and resource churn (3.3× faster).
| Metric | AexoLoader v1.7.5 | NeoModLoader | Winner | Advantage |
|---|---|---|---|---|
| Units spawned (of 2000) | 2000 | 250 | Aexo | 8.0× more |
| Spawn time | 452 ms | 998 ms | Aexo | 2.2× faster |
| Spawn / sec | 4427 | 250 | Aexo | 17.7× higher |
| Building-attempt loop | 167 ms | 749 ms | Aexo | 4.5× faster |
| Resource.Load churn | 134 ms | 448 ms | Aexo | 3.3× faster |
| Loads / sec | 5985 | 1785 | Aexo | 3.4× higher |
| Reflection + GC | 631 ms | 1033 ms | Aexo | 1.6× faster |
| Drop-pressure loop | 127 ms | 497 ms | Aexo | 3.9× faster |
| Sustain EMA avg (20s) | 16.7 ms | 72.8 ms | Aexo | 4.4× smoother |
| Sustain EMA max | 18.9 ms | 89.2 ms | Aexo | 4.7× lower peak |
Chart colors: teal = AexoLoader · red = NeoModLoader
(PNGs from WorldBoxPerfMod scripts/render-charts.py — Mermaid xy is not used here; Forgejo dual-series charts were unreliable.)
Aexo advantage (higher = Aexo better)
Stage wall time (lower = better)
Throughput (higher = better)
20s sustain EMA (lower = better)
Reproduce anytime with the shared harness: https://git.aexoradao.com/epistemophiliac/worldboxperfmod
Measured improvements (Aexo version history — same loader over time)
Numbers below come from aexo-loader.log on this project's Proton + ModernBox test rig (Discovered 2 mod candidates = AexoTestEntity + ModernBox). This section is Aexo vs older Aexo, not vs NeoModLoader (see head-to-head above for that).
Resource index wall-clock (perf resources_total)
---
config:
themeVariables:
xyChart:
plotColorPalette: "#0D9488"
---
xychart-beta
title "Aexo-only history: GameResources index on Proton (ms, lower better)"
x-axis ["Worst v1.6", "Typical v1.6-1.7.0", "v1.7.0", "v1.7.1 double-boot", "v1.7.2 single-flight"]
y-axis "milliseconds" 0 --> 45000
bar "Aexo resources_total (ms)" [42241, 18444, 10103, 17172, 4189]
| Session (log line) | resources_total |
Notes |
|---|---|---|
| Pre-optimization worst | 42,241 ms | Leaf-sliced index, no coarse splash |
| Typical pre-v1.7.2 band | 14,412 – 24,178 ms | Multiple runs, same mod set |
| v1.7.0 stability remake | 10,103 ms | SoftPerf burst + time-budget yields |
| v1.7.1 (two pipeline runs) | 7,603 + 9,569 = 17,172 ms | Duplicate Discovered 2 in one launch |
| v1.7.2+ coarse + single-flight | 4,189 ms | One scan, mode=coarse |
Improvement vs typical pre-v1.7.2 (~18.4 s): ~77% faster (4.2 s vs 18.4 s)
Improvement vs worst case (42.2 s): ~90% faster (10× reduction)
Gains vary by mod pack, disk cache, and platform. Windows native and cold caches may differ; the trend (coarse index + single-flight) holds across environments.
Boot reliability (duplicate pipeline eliminated)
flowchart LR
subgraph before ["v1.7.1 and earlier"]
A1[EarlyBoot RIOL] --> P1[ModPipeline.Run]
A2[SA AutoWire] --> P2[ModPipeline.Run]
P1 --> D1["Discovered 2"]
P2 --> D2["Discovered 2 again"]
D1 --> R1["resources_total ~7.6s"]
D2 --> R2["resources_total ~9.6s"]
end
subgraph after ["v1.7.2+"]
B1[Single boot gate] --> P3[ModPipeline.Run once]
P3 --> D3["Discovered 2"]
D3 --> R3["resources_total ~4.2s"]
end
| Boot metric | Before v1.7.2 | v1.7.2+ |
|---|---|---|
Discovered N per launch |
2× (duplicate) | 1× |
| Wasted index time (observed) | +7.6 s + 9.6 s | 0 s |
| Test entity double-spawn | Yes | No |
Stability surface (guards stock NML path does not ship)
mindmap
root((AexoLoader guards))
Resources
ResourcesTreeGuard wipe-protect
Coarse splash LoadResourceFromFolder
Atlas bake + ItemRenderGuard
UI
HudReady / PowersTab gate
FirstTimeSetup block
PowerButtonInitGuard Units tab
Simulation
CityBehBuildGuard upgradeBuilding
Render
getItemSpriteID null-sprite guard
Boot
SA/ACS heal Windows
Single-flight pipeline
RIOL EarlyBoot
Architecture
flowchart TB
subgraph entry [Boot entry]
RIOL[EarlyBoot BeforeSceneLoad]
SA[Optional SA AutoWire first install]
end
subgraph host [AexoLoader host]
BI[BootInstaller SA + RIOL + Managed sync]
BS[AexoLoaderBehaviour Bootstrap]
MP[ModPipeline Run single-flight]
SP[SoftPerf budgets]
HK[Harmony guards]
end
subgraph mods [Mod loading]
SCAN[ModScanner + DependencyGraph]
COMPILE[Roslyn Code compile]
RES[GameResources index]
ACT[NML-style activate after HudReady]
end
subgraph diag [Diagnostics]
LOG[aexo-loader.log local]
BUG[Bugsink telemetry optional]
end
RIOL --> BS
SA --> BS
BS --> BI
BS --> MP
MP --> SCAN
SCAN --> COMPILE
SCAN --> RES
RES --> ACT
MP --> SP
BS --> HK
MP --> LOG
MP --> BUG
LOG --> BUG
SoftPerf
Built-in frame-budget layer (profile=auto by default). Adjusts loader I/O scheduling and visual throttle knobs during hitches — never skips actor/building/simulation ticks.
| Profile | Use case |
|---|---|
off |
Debugging only |
auto |
Recommended — platform-tuned burst + hitch thresholds |
balanced |
Sim-faithful visuals, render_skip off |
max_fps |
Aggressive particle/sprite throttle under hitch |
Platform defaults (from SoftPerfSettings.ApplyPlatformDefaults):
| Platform | Load burst | Hitch threshold | Hitch sustain |
|---|---|---|---|
| Windows / Proton | 120 ms | 42 ms | 1.5 s |
| Linux native | 90 ms | 36 ms | 1.2 s |
Configure in %persistentData%/AexoLoader/settings.json.
Telemetry — intentional, visible, opt-out
Telemetry is not hidden. It exists so loader and mod failures reach Bugsink without players manually sending log files. This directly feeds stability work on AexoLoader and high-signal game-side errors.
Default configuration (in source)
// src/Config/BugSinkSettings.cs
public bool enabled = true;
public string dsn = "https://[email protected]/4";
public bool reportLoaderErrors = true;
public bool reportLoaderWarnings = true;
public bool reportGameLogErrors = true;
public bool reportPerfWarnings = true;
public int slowResourceIndexMs = 12000;
public int dedupeSeconds = 90; // loader errors
// perf warnings: 10 min dedupe (v1.7.5+)
// compile failures: category=compile, fingerprint by mod+CS codes, 10 min dedupe (v1.7.6+)
// severe hitch reports: 15 s sustained (v1.7.5+)
What gets reported
flowchart LR
subgraph sources [Event sources]
E1[ModLog ERROR]
E1b["Compile failed → category compile"]
E2[High-signal WARN]
E3[Unity Error/Exception]
E4["resources_total > 30s"]
E5["Sustained severe hitch 15s+"]
E6[Unhandled AppDomain]
end
subgraph client [AexoLoader client]
Q[Background queue dedupe 90s errors / 10min perf+compile]
POST["POST /api/4/store/ Sentry envelope"]
end
subgraph server [Your Bugsink]
S[Issues triage]
end
E1 --> Q
E1b --> Q
E2 --> Q
E3 --> Q
E4 --> Q
E5 --> Q
E6 --> Q
Q --> POST --> S
| Category | Sent remotely | Always kept locally |
|---|---|---|
| Loader crash / compile / activate fail | Yes | aexo-loader.log |
| Slow resource index (>12 s default) | Yes | perf line in log |
| Unity game exceptions (filtered) | Yes | Player.log |
| Short gameplay hitches (<15 s) | No | SoftPerf local log only |
| Guard rate-limit spam | No (deduped/filtered) | Yes |
| Full session trace | No | Yes — full local log |
Disable or tune
Edit %persistentData%/AexoLoader/settings.json:
{
"bugSink": {
"enabled": false
}
}
Or override DSN / thresholds while keeping reporting on:
{
"bugSink": {
"enabled": true,
"dsn": "https://[email protected]/PROJECT_ID",
"slowResourceIndexMs": 15000,
"reportGameLogErrors": true
}
}
On boot, when enabled, the log shows:
BugSink telemetry enabled → https://bugsink.aexoradao.com/api/4/store/
Why AexoLoader vs stock NeoModLoader (summary)
| Area | Typical NML pain | AexoLoader response | Measured / observed |
|---|---|---|---|
| Full stress suite | Higher stage times + weaker sustain | SoftPerf + single-flight + guards | Every stage faster — see head-to-head (e.g. 3.3× churn, 4.4× smoother EMA) |
| Resource index | Long splash, frame-yield overhead | Coarse per-root index + SoftPerf burst | ~77–90% faster vs prior Aexo runs on same rig |
| Double boot | RIOL + SA both fire | Single-flight gate + SA loader removal | 2× scan → 1× |
| ModernBox Linux | CS0433 Harmony dup | No self-ref in Roslyn | Compile succeeds (v1.7.1) |
| Units tab crash | Orphan spawn buttons | PowerButtonInitGuard |
Crash console avoided |
| Texture wipe | ResourcesPatch.Initialize reset |
ResourcesTreeGuard |
Assets persist after ingest |
| Windows Mono boot | Cecil ACS hook | No ACS rewrite + SA heal | Crash!!! mitigated (v1.6.4+) |
| Diagnostics | Manual log export | Bugsink + local log | v1.7.3+ |
Where improvement lives now
The loader's core path — boot, compile, resource index, HUD-safe activation — is in a good place on this rig (~3.8 s resources_total, single-flight boot, ModernBox activates cleanly). Big wall-clock wins from loader scheduling are mostly captured.
What remains is iterative, telemetry-driven hardening:
flowchart LR
PLAY[Player session] --> LOG[Local aexo-loader.log]
PLAY --> BUG[Bugsink events]
BUG --> TRIAGE[Triage high-signal errors]
TRIAGE --> GUARD[New Harmony guard in AexoLoader]
TRIAGE --> PATCH[Source-level game/mod patch if needed]
GUARD --> RELEASE[Hotfix release]
PATCH --> RELEASE
| Layer | What we do | Example (already shipped) |
|---|---|---|
| Bugsink triage | Turn real events into targeted fixes | getItemSpriteID NRE → v1.7.4 guard |
| AexoLoader guards | Prefix/finalizer at crash sites | CityBehBuildGuard, ItemRenderGuard, PowerButtonInitGuard |
| WorldBox source patches | Harmony on stock sim/render when mods expose null paths | Only when telemetry proves a repeatable stock call site |
| SoftPerf tuning | Mitigate hitches without hiding real problems | Particle throttle, GC deferral — not a substitute for guards |
Not expecting next: another 50% loader speedup without new evidence. Expecting next: fewer crash reports, quieter logs, and guards added as Bugsink shows repeatable stack traces (render precalc, city AI, atlas misses, etc.).
If you run into something new, you usually do not need to send logs manually — it should already be in Bugsink if telemetry is enabled. Disable only if you prefer local-only:
{ "bugSink": { "enabled": false } }
Version history (recent)
| Version | Focus |
|---|---|
| v1.8.4 | Hotfix: Proton Workshop Maps path remap (Item.Directory → steamapps/workshop/content) |
| v1.8.3 | Hotfix: NCMS EmbededResources (RulerBox UI); flat source discovery; Windows SA bak-poison + repair-windows-boot harden |
| v1.8.2 | Wait-at-launch auto-update; RegionPathFinder NRE guards; filter Unity serialization-layout log noise |
| v1.8.1 | Hotfix: QuantumSpriteGuard — drop drawProjectiles finalizer (IL Compile Error); transpiler SafeFirstFrame |
| v1.8.0 | Channel auto-update (1.8.* → latest 1.8.y): stage mid-session, apply on next boot; sha256-verified Forgejo downloads; privacy-respecting settings; release train CI |
| v1.7.10 (unstable) | Hotfix: defer AssemblyLoad Harmony patches (boot Crash!!!); QuantumSpriteLibrary.drawProjectiles empty-frames guard + BugSink filter |
| v1.7.9 (unstable) | Hotfix: Building setAnimData/minimap guards (ModernBox changeAllBuildings); filter missing locale keys; stable hitch BugSink grouping |
| v1.7.8 (unstable) | Hotfix: PizzaSimulator NRE guard; filter ModernBox NO SPRITES / Diag spam; stop empty Exception collapse; stable hitch fingerprints; resources_total alert at 30s |
| v1.7.7 (unstable) | Hotfix: Harmony compile ref for Linux; Projectile/HoveringBg/StatManager guards; quiet ModernBox Diag BugSink |
| v1.7.6 (unstable) | Quieter BugSink for mod compile failures: one compile event + CS tags; no cascade duplicate/warn spam |
| v1.7.5 (unstable) | Quieter Bugsink: severe hitch reports at 15 s+, perf dedupe 10 min |
| v1.7.4 (unstable) | DynamicSprites.getItemSpriteID null-sprite guard (parallel precalc NRE) |
| v1.7.3 (unstable) | Built-in Bugsink telemetry (loader + game errors + perf warnings) |
| v1.7.2 (unstable) | Single-flight boot, coarse resources, CityBehBuildGuard |
| v1.7.1 (unstable) | Linux CS0433 fix, Windows SA/ACS hardening |
| v1.7.0 (unstable) | Full stability remake, SoftPerf load phase, compile cache |
| v1.6.x (unstable) | Atlas, HUD, Units tab, Windows boot, NML compile refs |
Full changelog detail lives in git history and release notes per tag.
Dev / CI
See JENKINS.md. Jenkins builds the ILRepacked release DLL and publishes tags on main.
Local dev install:
bash scripts/install-to-worldbox.sh
Build only:
bash scripts/ci-build.sh



