# acdream — project instructions for Claude ## Goal Build **acdream**, a modern open-source C# .NET 10 Asheron's Call client. A faithful port of the retail AC client's behavior to modern C# + Silk.NET, with a plugin API the original never had. **The code is modern. The behavior is retail.** Every AC-specific algorithm is ported from the **named retail decomp** at `docs/research/named-retail/` — Sept 2013 EoR build PDB (18,366 named functions, 5,371 named struct/class types) + Binary Ninja pseudo-C with 99.6% function-name recovery + verbatim retail header struct definitions. The older Ghidra `FUN_xxx` chunks under `docs/research/decompiled/` (22,225 functions, 688K lines) remain a fallback for chunk-by-chunk address-range navigation. **Grep `named-retail/acclient_2013_pseudo_c.txt` by `class::method` BEFORE decompiling fresh.** The code around those algorithms is modern C# with clean architecture. The plugin API exposes game state through well-defined interfaces. **Architecture:** `docs/architecture/acdream-architecture.md` is the single source of truth for how the client is structured. All work must align with this document. When the architecture doc and reality diverge, update one or the other — never leave them out of sync. **WorldBuilder code lives in our tree.** Phase O extracted ~33 WB files (~7.7K LOC) into our own namespaces and dropped the two external project references. `DatCollection` is the **only** dat reader in process — `DefaultDatReaderWriter` is gone. `references/WorldBuilder/` remains in-tree as a read-reference (MIT-licensed; grep it freely), but nothing in `src/AcDream.*` references it as a project dependency. **Where the extracted code lives (post-Phase O):** - `src/AcDream.Core/Rendering/Wb/` — pure dat/mesh helpers (5 files, ~782 LOC): `TerrainUtils`, `TerrainEntry`, `RegionInfo`, `SceneryHelpers`, `TextureHelpers`. No GL dependency; safe to use from Core. - `src/AcDream.App/Rendering/Wb/` — GL infrastructure + mesh pipeline (~27 files, ~7K LOC): `ObjectMeshManager`, `WbMeshAdapter`, `WbDrawDispatcher`, `LandblockSpawnAdapter`, `EntitySpawnAdapter`, `TextureCache`, `GlobalMeshBuffer`, shader infrastructure, and the EnvCell/portal/scenery/terrain-blending pipeline classes. **Modern rendering path is MANDATORY.** The N.5 ship amendment deleted `WbFoundationFlag`, `InstancedMeshRenderer`, and `StaticMeshRenderer`; Campaign V (`docs/plans/2026-07-27-vulkan-campaign.md`, closed 2026-07-29) then ported the renderer to Vulkan behind the RHI contract and deleted the OpenGL backend outright — `AcDream.App` references only `Silk.NET.Vulkan`. There is no legacy fallback. Engineering cribs (WbMeshAdapter seams, N.5 SSBO layout, translucency model, gotchas) live in `memory/reference_modern_rendering_pipeline.md`. Before re-implementing any AC-specific rendering or dat-handling algorithm, **read `docs/architecture/worldbuilder-inventory.md` FIRST**. The inventory describes what we extracted (now in our tree) and what we still write ourselves. Re-porting from retail decomp when we already have a tested port is how subtle bugs (the scenery edge-vertex bug, the triangle-Z bug) keep slipping in. Retail decomp remains the oracle for network, physics, animation, movement, UI, plugin, audio, chat — see the inventory doc's 🔴 list. **Execution model:** the active source of truth is the **milestones doc** (`docs/plans/2026-05-12-milestones.md`) for "what are we building right now" and the **strategic roadmap** (`docs/plans/2026-04-11-roadmap.md`) for the per-phase ledger of what's shipped, what's in flight, and what comes next. **Ignore the old "R1→R8" sequence** — it was an early refactor sketch that no longer matches reality (see the "Roadmap Model" section in `docs/architecture/acdream-architecture.md`). Per-phase detailed specs live under `docs/superpowers/specs/`. The codebase is organized by layer (see architecture doc + the **Code Structure Rules** section below). Plans live in `docs/plans/`, research in `docs/research/`, persistent project memory in `memory/` and `~/.claude/projects/.../memory/` (the latter is browsable in Obsidian via the `claude-memory/` junction in the repo root; see `memory/reference_obsidian_vault.md`). **UI strategy:** one presentation stack — the retained retail `UiHost`/`UiRoot` tree in `AcDream.App/UI`, imported from LayoutDesc/DAT assets and bound by focused controllers over shared state, ViewModels, events, and commands (the ViewModels/commands seam from the earlier two-stack era remains the stable boundary between state and presentation). The ImGui.NET developer-tools frontend (`AcDream.UI.ImGui`) and the OpenGL backend it required were deleted at Campaign V slice V11 (`docs/plans/2026-07-27-vulkan-campaign.md`); `ACDREAM_DEVTOOLS=1` now only selects the optional Vulkan validation/debug-utils extensions (see the flag's log line in `Program.cs`). `TextRenderer` + `BitmapFont` serve D.6 world-space HUD elements in the 3D scene. Plugin gameplay UI uses the BCL-only `AcDream.Plugin.Abstractions.IUiRegistry.AddMarkupPanel` contract; plugins never import App namespaces. Full design: [`docs/plans/2026-04-24-ui-framework.md`](docs/plans/2026-04-24-ui-framework.md). Memory cribs: `claude-memory/project_chat_pipeline.md` (chat pipeline as of Phase I), `claude-memory/project_input_pipeline.md` (input pipeline as of Phase K). **Input pipeline:** `src/AcDream.UI.Abstractions/Input/` (action enum, `KeyChord`, `KeyBindings`, multicast `InputDispatcher` with scope stack + modal capture for rebind UX) + `src/AcDream.App/Input/` (Silk.NET adapters). Retail-default keymap loaded from `%LOCALAPPDATA%\acdream\keybinds.json` at startup (falls back to `KeyBindings.RetailDefaults()` matching `docs/research/named-retail/retail-default.keymap.txt`). The Settings panel (F11 / View → Settings) lets users remap any action via click-to-rebind. As of Phase K, ALL keyboard / mouse input flows through the dispatcher — no IsKeyPressed polling outside the per-frame movement queries. ## Current state **M3 — Cast a spell LANDED 2026-07-21. M4 — Live in the world ACTIVE.** Retail casting, spellbook/component-book UI, active effects, recall/portal-space presentation, the R6 locomotion/collision/projectile/teleport/radar rebaseline, and the final two-client portal-out/materialization observer flow are user-gated. Deterministic world-lifecycle automation protects fresh login, outdoor/world-edge/dungeon travel, same-location revisit, exact graceful disconnect, and uncapped fresh-process reconnect with JSON/PNG artifacts. Release builds; the current performance-program checkpoint passes 3,763 App tests / 3 skips and 8,826 complete-solution tests / 5 skips. **M4 feature work order (active after the Modern Runtime closeout and Slice L deferral):** the six-slice pre-M4 world-interaction completion program in `docs/plans/2026-07-23-world-interaction-completion.md`: favorite spell-bar overflow, status Use/Assess, assessment information, equipped-child picking, vendor browsing, and authoritative vendor transactions. Slices 1–3 plus the adjacent shared-cooldown presentation are user-accepted, including exact response flags, independent examination window, inscription transaction, complete creature/item/spell reports, favorite-spell press/right-click behavior, modern scarab/prismatic formula, DAT component icons, foreground stacking, and authored 310 x 400 extent. Slice 4 equipped-child world picking passed its two-client Coldeve gate and was user-accepted 2026-07-29. **Slices 5 and 6 (the complete vendor experience — browse, staged buying, selling, walk-to-use, the authored panel) closed user-accepted 2026-08-08; the six-slice program is COMPLETE (see the plan's PROGRAM CLOSEOUT). The vendor arc also exposed and fixed two latent client-wide crashers (#348 cursor-handle exhaustion, #350 render-ledger overflow).** **Campaign P — physics retail-feel parity (`docs/plans/2026-07-29-physics-parity-campaign.md`) is CLOSED 2026-07-31 — final user matrix accepted.** Every physics-scope gap from the 2026-07-29 audit landed and user-gated: #266 run speed (retail's ==800 sentinel — ACE's >=800 is a misread; never re-import), the #265/#166 landing-momentum + bounce family (`docs/research/2026-07-30-landing-bounce-family.md`), the #267 vitae panel, #268 (panel colors + augmentation bonuses), #269 (slope-stop slide — the live-trace contact-plane-restore fix), and TS-8 (0x02C2 StatMod parse). See the plan doc for the retired-row ledger. **Campaign A — audio retail parity (`docs/plans/2026-08-08-audio-parity-campaign.md`) is CODE-COMPLETE 2026-08-08** with slices A1–A6 landed and listening-gate rounds user-driven; open tail: #358 (Ctrl+M mute chord never fires) and the formal plan-status flip. **Campaign CH — chat & interface-text retail parity (`docs/plans/2026-08-09-chat-parity-campaign.md`) is CLOSED USER-ACCEPTED 2026-08-10** after five connected gate rounds: retail colors, the SpewBox with retail's two-plane glyph outlines, working side channels, the 152-verb command registry, the CH6 window shell (floating windows, all-corner resize, opacity), and verbatim /help. Carried tail: #360/#361, #366, #369, AP-177/190/191, and the round-5 review S1–S3 polish items. **Campaign OP — the retail four-tab Options panel (`docs/plans/2026-08-10-options-panel-campaign.md`) is CODE-COMPLETE 2026-08-11.** Retail's Options panel (Gameplay Options / Character / Chat / Config, LayoutDesc `0x2100002B`) plus the Configure Keyboard screen are acdream's ONE in-client settings surface (design D1): F11/toolbar open the authored tab host; `RuntimeCharacterOptionsState` + the 53-id `CharacterOptionTable` own option storage; retail's wire split ships exactly (21 auto-save ids → `0x0005` immediate, the rest ride the real `0x01A1` PlayerModule blob with Apply/logout/480 s flushes, header always `0x460`); headless bots declare options by name (OP7's live bot-vs-ACE gate PASSED); OP9 retired the dead F11 `SettingsPanel`/`SettingsVM` surface and the `GameplaySettings` record outright. OP1/OP2/OP7/OP9 CLOSED through dual/ combined Opus review. **2026-08-14 re-gate round:** the whole gate-4 fix batch (#372 both halves, #374, #375, #378–#382, #385) is USER-PASSED; the OP8 first look filed + same-day-fixed #394/#395/#396 (authored 18px-serif row-caption font; the retail `GetNameFromKey` key-name pipeline — DAT tables `0x2300000A`/`0x2300000B`/`0x23000007` via GetDIDByEnum category 4, OS-localized fallback, register AD-96; the `InitiateBinding` capture- instruction WAIT dialog) plus the WaitDialog-type-0x19 crash (`2a81e813`, live-verified no-crash). **STILL OWED: the full §OP3–§OP6 script sections and §OP8's visual re-check** — script `docs/research/2026-08-11-campaign-op-test-script.md`, launch with `ACDREAM_RETAIL_UI=1`. Tail: #371, #373, AP-198/199/201/202/203. START at `claude-memory/project_settings_options_digest.md`. **Campaign FA — the retail social panel (Fellowship & Allegiance) (`docs/plans/2026-08-11-fellowship-allegiance-campaign.md`) is CODE-COMPLETE 2026-08-12.** Retail authors ONE four-tab `gmPanelUI` social panel (Friends / Allegiance / Fellowship / Squelch, host slot `0x1000018F`, id 12; F3 = Allegiance, F4 = Fellowship, keyboard-only — Allegiance is the authored DEFAULT tab), mounted with the OP3 Options-panel recipe. The Fellowship and Allegiance pages are LIVE end-to-end: real wire (FA1 repaired the never-called H.2 builders + parsers — retail's FOUR tree-rejection rules, ELEVEN version gates, the byte-decoded `>=9` size and the truncated XP-share table), two session-scoped Runtime owners (`RuntimeFellowshipState`/`RuntimeAllegianceState`, both clear at generation reset — D2 corrected), and the authored panels through `LayoutImporter`. Friends/Squelch bind read-only to J4.1's owners. **The fellowship two-session flow is PROVEN over the live wire** — FA6's automated bot-vs-ACE gate (`testaccount`/`+Acdream` + `testaccount2`/ `+Horan`) passed: the recruited bot's OWN `RuntimeFellowshipState` flips `IsInFellowship`. Six FA slices, each dual-lens Opus reviewed → fix round → narrow re-review; the reviews caught what tests can't (retail's 4th tree rule, the D2 reset-lifetime inversion, the D6 server-side invite filter, a seam-map entry that would have re-introduced a fixed bug). OWED: the user's connected gates (§FA3-§FA6 of `docs/research/2026-08-12-campaign-fa-test-script.md`, several `[TWO-CLIENT]`), and **#384** — the allegiance-swear bot gate is deferred/disabled because ACE returns NOTHING to the `0x001D` swear at 0.005 m (no confirmation, no tree update, no error; needs ACE-console disambiguation — the swear CODE is done+reviewed, only its automated two-session proof is unverified; register AD-87). Tail: #383 (installed- DAT vs committed-fixture drift, found at FA3). START at `claude-memory/project_fellowship_allegiance_campaign.md`. **2026-08-13/14 gate block — SOCIAL GATES + SECURE TRADE all USER-PASSED.** The social panel's connected gate rounds closed (border-only move cursor, amber row selection, wrapped empty-state text, composed confirmation sentences via the new `DatStringResolver.ResolveTemplate` StringTable-interleave port, the refused-drop SpewBox notice via the `InventoryTransactionState.RequestFailed` seam, live friends Online/Offline through the authored row state machine + the new UiText per-state string swap). Same block: powerbar mode captions (jump 'Height' right-aligned per-STATE justify / 'Power'↔'Accuracy' by combat mode), release-edge airborne jump refusal (supersedes CH round-1's press-edge report), and **SECURE TRADE SHIPPED + two-client user gate PASSED 2026-08-14** — gmSecureTradeUI window (LayoutDesc `0x2100000D`), full `0x1F6`–`0x208` wire, `RuntimeTradeState` as the third sibling J-owner, both retail open paths, staged-item trading marker (`ClientObject.TradeState` now live), cancel text. START at `claude-memory/project_secure_trade.md`; the deferred-Func lesson is `claude-memory/feedback_resolve_deferred_funcs_per_call.md`. Register: AD-93/AD-94 filed, AD-85 narrowed, AD-81 amended, AD-89/AD-95 retired. Filed: #393 (texture-detail options, post-M4). **Campaign LA — the alpha launcher (ACTIVE 2026-08-14):** Avalonia launcher/installer/updater (Windows+Linux) + the retail character- management screen, driven autonomously under a user-set goal: Fable plans, Sonnet implements, Opus dual-lens reviews (architectural + retail-faithful). Spec: `docs/superpowers/specs/2026-08-14-launcher-campaign-design.md`; plan + ledger: `docs/plans/2026-08-14-launcher-campaign.md`; START at `claude-memory/project_launcher_direction.md`. Key recon corrections already binding: retail's select screen (`gmCharacterManagementUI`) has NO 3D preview (chargen-only machinery); UI Studio no longer exists (deleted at Campaign V — ignore stale memory/docs claims otherwise); App `Program.cs` has no subcommand dispatch (the `--session-config` flag is additive). LA0 through LA11's automated scope are review-closed. The launcher composer is now compiled into both host test suites, and Launcher.Core runs in the portable Windows/Ubuntu CI closure. The self-contained Avalonia launcher, transactional two-host plugin lifetime, shared login-command route, Runtime-owned retail selection state, authored DAT character screen, and crash-safe verified installer plus atomic cross-platform updater/self-updater are integrated. Windows group-isolated Headless stop, isolated update fixtures, strict status/redaction evidence, and the exact Windows/Ubuntu operator script are landed; the integrated preflight passes 32/32 commands and 14,012 tests / 5 skips. Only the connected/visual/real-DAT user gate remains before shipment. **Campaign CC — retail character creation (CODE-COMPLETE 2026-08-16, all seven slices REVIEW-CLOSED; the user's connected gate is the sole outstanding acceptance step).** The full retail creation flow: Create button (retail's exact `UpdateButtons` roster` reads any acclient.exe and prints whether it pairs with our PDB (`MATCH` / `MISMATCH (expected GUID = ...)`). Always run this on a candidate binary BEFORE attaching. - **PDB metadata dumper**: `tools/pdb-extract/dump_pdb_info.py refs/acclient.pdb` prints the PDB's expected timestamp + GUID + age. Use to figure out which build to look for if the chain ever breaks. ### Workflow 1. **Verify the binary matches the PDB:** ```bash py tools/pdb-extract/check_exe_pdb.py "C:/Users/erikn/Downloads/acclient.exe" ``` Expect: `=== MATCH: this exe pairs with our acclient.pdb ===` 2. **Have the user launch retail client** and connect to local ACE. Retail must already be in-world before attaching. 3. **Write a `.cdb` script** that arms breakpoints with non-blocking actions (count + log + `gc`). Pattern: ``` .logopen .sympath C:\Users\erikn\source\repos\acdream\refs .symopt+ 0x40 .reload /f acclient.exe r $t0 = 0 bp acclient!CTransition::transitional_insert "r $t0 = @$t0 + 1; .if (@$t0 % 5000 == 0) { .printf \"...\" }; .if (@$t0 >= 30000) { qd } .else { gc }" bp acclient!OBJECTINFO::kill_velocity "r $t1 = @$t1 + 1; gc" ... g ``` `gc` = "go conditional" (continue without breaking). Auto-detach via `qd` after a hit-count threshold to avoid manual cleanup. 4. **Launch cdb in the background** via a PowerShell wrapper: ```powershell & "C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe" ` -pn acclient.exe -cf