Modern open-source C# .NET 10 Asheron's Call client. Faithful port of retail client behaviour to Silk.NET with a plugin API.
Find a file
Erik 7b456b49d6 perf(diag): per-frame history export + checkpoint LOH/cache counters + soak capped mode (2026-07-24 audit review)
An adversarial performance review found our own instruments cannot
measure the project's own performance gates:

- FrameProfiler aggregated CPU/GPU/alloc/stage samples into ~5-second
  windows and reset the ring buffers after each report, so route-wide
  p50/p95/p99 distributions across a whole soak could not be
  reconstructed after the fact. ACDREAM_FRAME_HISTORY=<path> now opts
  into a separate per-frame history (one record per frame, ~72
  bytes/record, accumulated in memory with zero frame-thread I/O) that
  a shutdown-only Dispose() writes as CSV. The aggregated [frame-prof]
  report format and its existing metrics are unchanged.

- The canonical checkpoint JSON tracked cache residency (entry/byte
  counts) but never LOH size/fragmentation, process-wide allocated
  bytes, or cache hit/miss/eviction traffic — a committed audit JSON
  showed 65% LOH fragmentation that no tracked instrument recorded,
  and "does a revisit portal hit or miss the caches" was unanswerable
  from an artifact alone. WorldLifecycleResourceSnapshot now carries
  loh_size_bytes/loh_fragmentation_bytes (GCMemoryInfo.GenerationInfo
  index 3), process_total_allocated_bytes (GC.GetTotalAllocatedBytes),
  and Interlocked hit/miss/eviction counters for the CPU mesh cache,
  decoded-texture cache, and the four bounded DAT-object caches
  (portal/cell/highRes/language, aggregated).

- run-connected-r6-soak.ps1 unconditionally forced
  ACDREAM_UNCAPPED_RENDER=1 with no capped mode, while its sibling
  lifecycle-gate script correctly gated it behind a switch. Added
  -Uncapped (default capped, matching the sibling script's pattern),
  fixed the stationary dwell (12s -> 26s, past the 25s
  LiveEntityLivenessController deadline the adjacent comment already
  cited), and now write an env-disclosure.json into the automation
  artifact directory before every launch listing every ACDREAM_* var
  the script sets plus -Uncapped, since the prior audit could only see
  ACDREAM_DUMP_MOVE_TRUTH and nothing else was ever recorded anywhere.

Cache counters are wired via the existing composition path
(ObjectMeshManager already owns the CPU mesh cache and the mesh
extractor directly; content.Dats is threaded into
WorldLifecycleResourceSnapshotSource the same way every other
composition consumer receives it). The DAT-object cache lives behind
IDatReaderWriter, a third-party interface from the DatReaderWriter
package that cannot be extended; RuntimeDatCollection (the one
production implementation) exposes the aggregate stats directly and a
pattern match reads them, degrading to zero for any test double —
no new static registry was introduced (GpuMemoryTracker remains the
one precedented process-wide static).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 1da2c33c875b41fa383dd79694ee2765f0e21896)
2026-07-24 12:00:30 +02:00
.github ci(hygiene): allow dotnet ecosystem (nuget+telemetry) so build/test can run 2026-05-23 08:48:37 +02:00
.vscode ci: add GitHub Agentic Workflows scaffolding + daily hygiene assessment 2026-05-22 23:31:13 +02:00
docs docs(issues): file #236-#244 - deferred P2 findings from the 2026-07-24 audit review 2026-07-24 11:52:31 +02:00
memory docs(architecture): close GameWindow regression handoff 2026-07-23 06:12:47 +02:00
references chore(submodule): advance WB to acdream-fix-floor-rendering 2026-05-19 13:23:19 +02:00
src perf(diag): per-frame history export + checkpoint LOH/cache counters + soak capped mode (2026-07-24 audit review) 2026-07-24 12:00:30 +02:00
tests perf(diag): per-frame history export + checkpoint LOH/cache counters + soak capped mode (2026-07-24 audit review) 2026-07-24 12:00:30 +02:00
tools perf(diag): per-frame history export + checkpoint LOH/cache counters + soak capped mode (2026-07-24 audit review) 2026-07-24 12:00:30 +02:00
.gitattributes ci: add GitHub Agentic Workflows scaffolding + daily hygiene assessment 2026-05-22 23:31:13 +02:00
.gitignore Merge branch 'main' into claude/eloquent-hugle-42119e 2026-07-06 00:47:09 +02:00
.gitmodules phase(N.0): wire up WorldBuilder fork as submodule + project refs 2026-05-08 08:51:49 +02:00
AcDream.slnx fix(pipeline): MP1b review - bake CLI determinism + bounded memory 2026-07-05 22:14:31 +02:00
AGENTS.md fix(ui): keep spell components in authored foreground 2026-07-24 08:08:29 +02:00
analyze_flap_live.py diag(render): flap re-diagnosed as portal-flood re-clip DRIFT; physics + camera REFUTED 2026-06-08 11:21:46 +02:00
CLAUDE.md fix(ui): keep spell components in authored foreground 2026-07-24 08:08:29 +02:00
find_burst.py diag(render): flap re-diagnosed as portal-flood re-clip DRIFT; physics + camera REFUTED 2026-06-08 11:21:46 +02:00
launch-a6-issue98-capture.ps1 docs(research): A6.P3 #98 — comparison harness findings + neighborhood fixtures 2026-05-23 20:12:43 +02:00
launch-a6-issue98-cottage-gfxobj-dump.ps1 test(phys): A6.P3 #98 — comparison harness reproduces cottage-floor cap 2026-05-23 20:44:50 +02:00
launch-a6-issue98-polydump.ps1 docs(research): A6.P3 #98 — comparison harness findings + neighborhood fixtures 2026-05-23 20:12:43 +02:00
launch-flap-capture.ps1 diag(render): flap re-diagnosed as portal-flood re-clip DRIFT; physics + camera REFUTED 2026-06-08 11:21:46 +02:00
launch-flap-churn.ps1 diag(render): launch-flap-churn.ps1 — Phase 1 portal-churn pin capture script 2026-06-08 12:56:44 +02:00
launch-flap-verify.ps1 docs(render): FLAP settled by live-retail measurement — full retail port DECIDED (Option A) + exhaustive handoff 2026-06-08 16:19:34 +02:00
NOTICE.md chore(O-T1): create Core/Rendering/Wb directory + NOTICE.md attribution 2026-05-21 14:59:56 +02:00
README.md docs: reconcile project status and navigation 2026-07-20 13:00:41 +02:00

acdream

A modern open-source C# / .NET 10 Asheron's Call client.

Faithful port of the retail client's behaviour to Silk.NET with a modern, plugin-friendly architecture. The code is modern; the behaviour is retail.

Status: playable pre-alpha; M3 ("Cast a spell") is active. The connected client supports the complete M2 combat/loot/inventory loop plus retained spellbook, component-book, favorite-spell, enchantment, portal-space, radar, chat, character, and inventory UI. Current work is verification and retail conformance, not a first-playable bring-up.

Start at the documentation map for the current milestone, roadmap, tactical issues, architecture, retail-divergence register, research, and durable memory.

Stack

  • Language: C# .NET 10
  • Graphics: Silk.NET (OpenGL 4.3 context; GL_ARB_bindless_texture and GL_ARB_shader_draw_parameters are mandatory)
  • Audio: OpenAL via Silk.NET
  • Dat parsing: Chorizite.DatReaderWriter
  • Networking: Custom UDP + ISAAC cipher + game-message layer, wire-compatible with ACEmulator server

What works today

  • ACE login, character selection, world entry, chat, client commands, and graceful logout.
  • Outdoor, building, cellar, and dungeon streaming with portal-space travel, terrain/scenery/EnvCell rendering, collision, lighting, sky, fog, audio, and day/night presentation.
  • Local and observed player/NPC/monster movement, animation, selection, radar, combat stances, melee and missile presentation, death, corpses, chests, and looting.
  • Inventory bags, stack splitting, paperdoll/equipment, weapon switching, quick bars, item use, giving items to NPCs, and server-authoritative item movement.
  • Retained retail-style gameplay UI for vitals, chat, toolbar, inventory, character/skills, spellbook/components, helpful/harmful effects, combat and spell bars, radar/compass, indicator pages, and confirmation dialogs.
  • Spell catalog, learned/favorite spells, component preflight, connected casts, enchantments, DAT-driven missiles/effects, recall, Hidden/UnHide, and portal materialization presentation.
  • Plugin host, shared input/command abstractions, and permanent ImGui developer tools behind ACDREAM_DEVTOOLS=1.

Active gaps

  • R6's complete object-frame update path still needs the next local visual comparison; the final M3 gate is a two-client portal-out/materialization observer comparison.
  • Issue #225 still needs the lifestone/particle alpha-order visual check, although its connected lifetime/performance gates pass.
  • Narrow behavior debt remains in issue #153 (an unstreamed far-teleport edge case), issue #116 (slide feel), and registered TS-50/TS-51 effect timing.
  • Many later retail systems remain roadmap work: advanced social/vendor/trade/ crafting UI, full weather/lightning parity, deeper combat/stat systems, graphical Linux, and a true headless multi-session bot host.

See docs/README.md for document authority and current links.

Building + running

Requires:

  • .NET 10 SDK
  • A retail Asheron's Call dat directory (Turbine/Microsoft property — supply your own). Contains client_portal.dat, client_cell_1.dat, client_highres.dat, client_local_English.dat.
  • A running ACE (ACEmulator) server on 127.0.0.1:9000 (or override via env var)

Launch (PowerShell on Windows — bash has trouble with the apostrophe in "Asheron's Call"):

$env:ACDREAM_DAT_DIR   = "$env:USERPROFILE\Documents\Asheron's Call"
$env:ACDREAM_LIVE      = "1"
$env:ACDREAM_TEST_HOST = "127.0.0.1"
$env:ACDREAM_TEST_PORT = "9000"
$env:ACDREAM_TEST_USER = "testaccount"
$env:ACDREAM_TEST_PASS = "testpassword"
$env:ACDREAM_RETAIL_UI  = "1"
dotnet run --project src\AcDream.App\AcDream.App.csproj -c Release

Offline CLI dat inspector (no server needed):

dotnet run --project src/AcDream.Cli -- "C:\path\to\Asheron's Call"

Diagnostic env vars

Variable Effect
ACDREAM_DUMP_SKY=1 Per-second dump of the interpolated SkyKeyframe values + per-SkyObject draw info + texture alpha histograms
ACDREAM_DUMP_MOTION=1 Dump every inbound UpdateMotion + resulting SetCycle
ACDREAM_STREAM_RADIUS=N Legacy override for the configured two-tier streaming radii
ACDREAM_NO_AUDIO=1 Suppress OpenAL init
ACDREAM_RETAIL_UI=1 Enable retained retail gameplay UI
ACDREAM_DEVTOOLS=1 Enable ImGui developer tools
ACDREAM_UNCAPPED_RENDER=1 Disable normal presentation pacing for throughput measurement
ACDREAM_DAY_GROUP=N Force a specific DayGroup index for A/B-testing weather presets
ACDREAM_RUN_SKILL=N / ACDREAM_JUMP_SKILL=N Client-side run/jump skill (default 200)

Layout

src/
  AcDream.App/                   window, retained UI, rendering, audio, runtime wiring
  AcDream.Content/               GL-free DAT mesh/texture extraction
  AcDream.Core/                  game state, physics, movement, world, gameplay logic
  AcDream.Core.Net/              UDP + ISAAC + game-message layer
  AcDream.UI.Abstractions/       shared UI/input ViewModels, commands, contracts
  AcDream.UI.ImGui/              permanent developer-tool renderer
  AcDream.Cli/                   offline dat-inspector console app
  AcDream.Bake/                  offline content bake tooling
  AcDream.Plugin.Abstractions/   plugin host interfaces
  AcDream.Plugins.Smoke/         example plugin

tests/
  AcDream.*.Tests/               layer-matched xUnit projects (6,452 passing,
                                 5 intentional skips at the 2026-07-20 baseline)

tools/
  RetailTimeProbe/               Win32 P/Invoke ReadProcessMemory probe of
                                 the live retail acclient.exe — dumps
                                 TimeOfDay + sky-lighting globals so we
                                 can compare against acdream's state
  SkyObjectInspect/              dat-inspector for Region sky objects

references/                      vendored read-only reference code — ACE,
                                 ACViewer, WorldBuilder, holtburger,
                                 AC2D, Chorizite, DatReaderWriter.
                                 Gitignored.

docs/
  README.md                      documentation map + authority order
  architecture/                  architecture, structure, divergence, WB inventory
  plans/                         phase roadmaps + per-phase specs
  research/                      decompile-derived research, per-phase
                                 findings, deep-dive agent reports
  audit/                         phase-completion audits

Development workflow

All AC-specific behaviour is ported from the named retail client oracle (docs/research/named-retail/). The workflow is:

  1. Grep named retail first. Find the matching class::method in the Sept 2013 named pseudo-C and headers.
  2. Decompile only as fallback. Use the older Ghidra chunks when the named oracle lacks the function.
  3. Cross-reference. Check against ACE's C# port and ACViewer / WorldBuilder.
  4. Write pseudocode. Translate C to readable pseudocode first.
  5. Port faithfully. Translate line-by-line, preserving variable names and control flow.
  6. Conformance test. Add tests using golden values from retail.
  7. Integrate surgically. Minimise churn in the surrounding pipeline.

Guessing at AC-specific algorithms is explicitly forbidden — see CLAUDE.md for the full workflow rationale and the list of failure modes we've paid for in the past.

Reference repos

We cross-reference six external projects for retail behavior:

  • ACE (ACEmulator) — authoritative server-side protocol
  • ACViewer — MonoGame dat viewer; good for character appearance
  • WorldBuilder — Silk.NET dat editor; matches our stack
  • Chorizite.ACProtocol — clean-room C# protocol library
  • holtburger — most complete non-retail client; Rust TUI, full client-side behaviour
  • AC2D — C++ AC-client emulator; has the real terrain split formula and 0xF61C movement packet format

See CLAUDE.md for which reference is authoritative for which domain.

Licence

Not yet chosen. All external reference code is vendored under its own licence; see references/*/LICENSE. The acdream source code itself is unreleased — not yet distributed to the public. Once the licence choice is made it will go in a top-level LICENSE file.

The AC dat files and the game's intellectual property remain the property of Microsoft / Turbine. This project does not distribute any of those files or assets — you must supply your own retail install.