No description
  • C# 90%
  • Shell 6.5%
  • PowerShell 2.1%
  • C 0.9%
  • Python 0.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
epistemophiliac af6f33e8ed Gate large-map render_skip on camera idle to stop pan freezes.
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]>
2026-08-03 04:59:24 -04:00
docs/perf-bench Embed WorldBoxPerfMod PNG bake-off charts in README. 2026-07-17 21:06:22 -04:00
examples Update examples/AexoTestEntity/mod.json 2026-07-18 01:25:42 +00:00
libs Fix Windows crash + reliable Forgejo release publish. 2026-07-14 05:50:40 -04:00
scripts Hotfix Windows Crash!!! boot: stop SA bak poison and harden repair. 2026-08-02 18:52:22 -04:00
src Gate large-map render_skip on camera idle to stop pan freezes. 2026-08-03 04:59:24 -04:00
.gitignore Make Jenkins self-contained: in-repo refs + install .NET SDK on agent. 2026-07-14 04:07:45 -04:00
JENKINS.md Release 1.8.2: wait-at-launch updates plus Sentinel hotfixes. 2026-07-24 01:06:04 -04:00
Jenkinsfile ci: Jenkins-only publish with idempotent skip (keep metrics green) 2026-07-23 22:58:07 -04:00
README.md Release 1.8.4: fix Proton Workshop Maps browse path remap. 2026-08-03 00:07:45 -04:00
WorldBoxModManager.csproj Release 1.8.4: fix Proton Workshop Maps browse path remap. 2026-08-03 00:07:45 -04:00

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.x and 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)

  1. Also copy the same DLL to worldbox_Data/StreamingAssets/mods/
  2. Enable Experimental Mode in WorldBox
  3. Launch once — BootInstaller registers ScriptingAssemblies.json + RuntimeInitializeOnLoads.json
  4. 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.5worldbox-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)

Aexo speedup

Stage wall time (lower = better)

Wall time

Throughput (higher = better)

Throughput

20s sustain EMA (lower = better)

Sustain EMA

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 ~7790% 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