Rewrote the reference repos section with: 1. Clear domain-to-oracle mapping table — every domain (terrain, rendering, networking, movement, etc.) has a named primary oracle and secondary reference. No ambiguity about which repo to check first. 2. NEVER GUESS policy: "read the reference FIRST, write code SECOND. Always." Explicitly calls out the triangle-boundary Z bug as the cautionary tale (5 failed fix attempts from guessing vs 1 fix from checking ACME's ClientReference.cs). 3. Quick-reference file lists for ACME (6 key files) and holtburger (6 key files) so future sessions can jump straight to the right code. 4. WorldBuilder original explicitly marked as SUPERSEDED for terrain algorithms (ACME has conformance tests, original doesn't). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
262 lines
15 KiB
Markdown
262 lines
15 KiB
Markdown
# acdream — project instructions for Claude
|
|
|
|
## Goal
|
|
|
|
Build **acdream**, a modern open-source C# .NET 10 Asheron's Call client. The
|
|
end state is a working client that:
|
|
|
|
- Loads the retail AC dat files and renders the world (terrain, static meshes,
|
|
dynamic entities, characters)
|
|
- Connects to an ACE server and plays as a character
|
|
- Exposes a **first-class plugin API** so players can write native scripts and
|
|
macros to automate gameplay — this is a core architectural requirement, not
|
|
a bolt-on
|
|
|
|
The codebase is organized by phase. Current phase state lives in memory
|
|
(`memory/project_phase_*_state.md`), current phase plans live in `docs/plans/`,
|
|
and the long-term vision lives in `memory/project_acdream.md`.
|
|
|
|
## How to operate
|
|
|
|
**You are the lead engineer on this project at all times. Stop as little as
|
|
possible.** Drive work autonomously and continuously through full phases and
|
|
across commit boundaries. Do not stop mid-phase for routine progress check-ins,
|
|
permission asks on low-stakes design calls, or "should I continue?" confirmations.
|
|
The user has repeatedly authorized direct-to-main commits, multi-commit sessions,
|
|
and cross-phase jumps when the work is sequenced in the roadmap.
|
|
|
|
The only thing that genuinely requires stopping is **visual confirmation** — the
|
|
user needs to look at the running client and tell you whether it matches
|
|
retail. Everything else is your call.
|
|
|
|
**Only stop and wait for the user when:**
|
|
|
|
- Visual verification is the acceptance test ("does the drudge look right now?")
|
|
- The roadmap and the observed bug disagree and you need to brainstorm a
|
|
new phase or sub-step (use `superpowers:brainstorming`, not a freeform chat)
|
|
- A genuinely destructive or hard-to-reverse action is on the table outside
|
|
the normal commit workflow (force push, history rewrite, deleting memory
|
|
files, reverting multiple commits)
|
|
- Memory or committed history shows a clear user preference you're about to
|
|
diverge from
|
|
|
|
**Things you should just do without asking:**
|
|
|
|
- Continue to the next planned sub-step of a phase after the previous one
|
|
lands clean — including immediately starting work on the next phase if the
|
|
current one is done
|
|
- Pick between two roughly equivalent implementations; justify the choice in
|
|
the commit message
|
|
- Refactor small amounts of surrounding code when genuinely needed to land a
|
|
change cleanly (but not "while I'm here" scope creep)
|
|
- Run the test suite, build the project, commit to main with co-author
|
|
attribution
|
|
- Add diagnostic logging when you need evidence, then strip it when the
|
|
evidence is in hand
|
|
- Spawn subagents for bounded implementation chunks (see Subagent policy)
|
|
|
|
Before claiming a phase or sub-step is done: run `dotnet build` and
|
|
`dotnet test` green, commit with a message that explains the "why", update
|
|
memory if there's a durable lesson, update the roadmap's "shipped" table if
|
|
a phase just landed, and move to the next todo item.
|
|
|
|
**If you catch yourself about to ask "should I continue?", the answer is
|
|
always yes — keep going.** The single exception is visual verification;
|
|
otherwise, act.
|
|
|
|
## Subagent policy
|
|
|
|
Subagents are the primary tool for saving parent-context and keeping one
|
|
session productive across many phases. Use them liberally for:
|
|
|
|
- Bounded implementation chunks with a clear spec (one file, one test suite,
|
|
a targeted refactor)
|
|
- Parallel independent tasks with no shared state
|
|
- Research that would otherwise fill the parent context with file reads
|
|
|
|
**Model selection:**
|
|
|
|
- **Default: Sonnet.** Use Sonnet for all execution work — implementers,
|
|
research agents, spec-following work, test writing, refactors, repeated
|
|
patterns. Sonnet is the right cost/context/capability tradeoff for this
|
|
codebase and has been validated on every phase since Phase 2a. Do not
|
|
reach for Opus unless you have a specific reason.
|
|
- **Opus only for load-bearing quality review** — code review of a phase
|
|
boundary, a design that must be right the first time, a gnarly
|
|
cross-system refactor. "This feels hard" is not enough; specify why it
|
|
needs Opus in the task description.
|
|
- Never use Haiku for acdream work unless the task is literally checking
|
|
whether another process is alive.
|
|
|
|
**Prompt discipline:** when dispatching a subagent, include the relevant
|
|
spec path, the files it should read, the acceptance criteria (build + test
|
|
green), and the commit message style. Subagents inherit CLAUDE.md so they
|
|
follow the same rules.
|
|
|
|
## Roadmap discipline
|
|
|
|
acdream's plan lives in two files committed to the repo:
|
|
|
|
- **`docs/plans/2026-04-11-roadmap.md`** — the strategic roadmap. Single
|
|
source of truth for what's shipped, what's next, and the agreed order.
|
|
When you're about to pick up new work, read this first. When you ship a
|
|
phase or sub-step, move it from "ahead" to "shipped" in the same commit
|
|
that lands the work (or the very next commit).
|
|
|
|
- **`docs/superpowers/specs/*.md`** — per-phase detailed implementation
|
|
specs. Each active phase has one. When you're about to write code for a
|
|
named phase, read its spec, follow its component boundaries, and match its
|
|
acceptance criteria. Do not drift from the spec without explicit user
|
|
approval.
|
|
|
|
**Rules:**
|
|
|
|
1. Before starting a new phase or sub-piece, re-read the roadmap and the
|
|
relevant spec. State which phase you're on in the first action you take.
|
|
|
|
2. When reality and the plan diverge — the user observes a bug that doesn't
|
|
fit any existing phase, a technical discovery makes a phase description
|
|
wrong, a sub-piece turns out to be larger than expected — **pause and
|
|
brainstorm** with the `superpowers:brainstorming` skill before writing
|
|
code. Update the roadmap in the same session.
|
|
|
|
3. When shipping a phase, update the roadmap's "shipped" table and commit
|
|
the update in the same commit as (or immediately after) the
|
|
implementation commit.
|
|
|
|
4. Do not invent new phase numbers / letters on the fly. If you need a new
|
|
phase, add it to the roadmap first with the user, then reference it by
|
|
its assigned identifier. "Phase 11" and "Phase 9.3" conjured
|
|
mid-sentence are process smells — they mean the plan got out of sync
|
|
with the work.
|
|
|
|
5. If a single session ends up shipping work that spans multiple roadmap
|
|
phases, that's fine, but each commit message should name the phase it
|
|
belongs to (e.g. `feat(core): Phase A.1 — streaming region`).
|
|
|
|
The roadmap is not sacred — it changes. It IS the source of truth at any
|
|
given moment. When it's wrong, fix it. When it's right, follow it.
|
|
|
|
## Reference repos: check ALL FOUR, not just one
|
|
|
|
When researching a protocol detail, dat format, rendering algorithm, or
|
|
any "how does AC do X" question, **check all four of the vendored
|
|
references in `references/`** before committing to an approach. Do not
|
|
settle on the first hit and move on — cross-reference at least two of
|
|
these, ideally all four:
|
|
|
|
- **`references/ACE/`** — ACEmulator server. Authority on the wire
|
|
protocol (packet framing, ISAAC, game message opcodes, serialization
|
|
order). The things a server has to know to parse and produce bytes.
|
|
- **`references/ACViewer/`** — MonoGame-based dat viewer that actually
|
|
renders characters + world. Authority on the client-side visual
|
|
pipeline: ObjDesc application, palette overlays, texture decoding
|
|
for the palette-indexed formats. See
|
|
`ACViewer/Render/TextureCache.cs::IndexToColor` for the canonical
|
|
subpalette overlay algorithm.
|
|
- **`references/WorldBuilder/`** — C# + Silk.NET dat editor. Exact-stack
|
|
match to acdream for rendering approaches: terrain blending, texture
|
|
atlases, shader patterns. Most useful for "how do I do this GL thing
|
|
with Silk.NET on net10 idiomatically?" Less useful for protocol or
|
|
character appearance (dat editor, not game client).
|
|
- **`references/Chorizite.ACProtocol/`** — clean-room C# protocol
|
|
library generated from a protocol XML description. Useful sanity check
|
|
on field order, packed-dword conventions, type-prefix handling. The
|
|
generated Types/*.cs files have accurate field comments (e.g. "If
|
|
it is 0, it defaults to 256*8") that ACE's server-side code doesn't.
|
|
- **`references/holtburger/`** — **Almost-complete Rust TUI AC client.**
|
|
Not just a crate or a handshake reference: it's a full client that
|
|
logs in, plays the game, sends/receives chat, handles combat, and
|
|
renders state in a terminal. **This is acdream's most authoritative
|
|
reference for client-side behavior** — anything about how a client
|
|
is *supposed* to talk to the server lives here. Specifically:
|
|
- Handshake / login flow including all the post-EnterWorld
|
|
messages retail clients send (LoginComplete, ack pump,
|
|
DDDInterrogation responses, etc).
|
|
- The proper ACK_SEQUENCE pattern (every received packet with
|
|
sequence > 0 gets an ack queued back; not periodic).
|
|
- Outbound game-action message construction with sequence
|
|
numbering.
|
|
- Message routing and session lifecycle.
|
|
Look here FIRST when implementing anything in `WorldSession` or
|
|
the message-builder layer. ACE shows what the server expects;
|
|
holtburger shows what a real client actually sends.
|
|
|
|
- **`references/AC2D/`** — **C++ AC client emulator.** Oldest reference,
|
|
fixed-function OpenGL, but has the **real AC terrain split formula**
|
|
(`FSplitNESW` with constants `0x0CCAC033`, `0x421BE3BD`, `0x6C1AC587`,
|
|
`0x519B8F25`) which differs from WorldBuilder's physics-path formula.
|
|
Also has the complete `0xF61C` movement packet format with flag bits
|
|
and the `stMoveInfo` sequence counters. Key lesson from AC2D: it does
|
|
NOT do client-side terrain Z — it sends movement keys to the server
|
|
and uses the server's authoritative Z. See
|
|
`docs/research/2026-04-12-movement-deep-dive.md` for the full analysis.
|
|
|
|
Pattern: when you encounter an unknown behavior, grep all four for the
|
|
relevant term, read each hit, and compose a multi-source understanding
|
|
BEFORE writing acdream code. A single reference can be misleading; the
|
|
intersection of all four is almost always the truth. The user has
|
|
repeatedly had to remind me about this when I narrowly searched one ref
|
|
and missed obvious answers in another.
|
|
|
|
### Reference hierarchy by domain
|
|
|
|
**NEVER GUESS an algorithm, formula, constant, wire format, or coordinate
|
|
convention.** Every AC-specific behavior has a reference implementation in
|
|
one of the repos below. If you find yourself writing AC-specific code
|
|
without having read the matching reference first, STOP and read it. The
|
|
triangle-boundary Z bug cost 5 failed fix attempts because we guessed
|
|
instead of checking ACME's `ClientReference.cs` — which had the exact
|
|
decompiled client code and would have fixed it in minutes.
|
|
|
|
**The rule: read the reference FIRST, write code SECOND. Always.**
|
|
|
|
| Domain | Primary Oracle | Secondary | Notes |
|
|
|--------|---------------|-----------|-------|
|
|
| **Terrain** (split direction, height sampling, palCode, vertex position, normals) | **ACME `ClientReference.cs`** — decompiled retail client with exact offsets | ACME `TerrainGeometryGenerator.cs` (matches the mesh index buffer) | WorldBuilder original is SUPERSEDED for terrain algorithms. AC2D confirms the same formula. |
|
|
| **Terrain blending** (texture atlas, alpha masks, road overlays) | **ACME `LandSurfaceManager.cs`** | WorldBuilder original `LandSurfaceManager.cs` (same code, less tested) | Both use the same TexMerge pipeline. ACME has conformance tests. |
|
|
| **GfxObj / Setup rendering** (mesh extraction, multi-part assembly, ObjDesc) | **ACME `StaticObjectManager.cs`** — includes CreaturePalette, GfxObjRemapping, HiddenParts | ACViewer `Render/` namespace | ACME has the complete creature appearance pipeline in one file. |
|
|
| **Texture decoding** (INDEX16, P8, DXT, BGRA, alpha) | **ACME `TextureHelpers.cs`** | ACViewer `Render/TextureCache.cs` (palette overlay = `IndexToColor`) | For subpalette overlay specifically, ACViewer's `IndexToColor` is the canonical algorithm. |
|
|
| **EnvCell / dungeon rendering** (cell geometry, portal visibility, collision mesh) | **ACME `EnvCellManager.cs`** — portal traversal, mixed landblock detection, collision cache | ACViewer `Physics/Common/EnvCell.cs` | ACME is significantly more complete than original WorldBuilder for dungeons. |
|
|
| **Network protocol** (wire format, packet framing, fragment assembly, ISAAC) | **holtburger** `crates/holtburger-session/` | AC2D `cNetwork.cpp` (simpler, good for cross-check) | ACE shows the server side; holtburger + AC2D show the client side. |
|
|
| **Client behavior** (what to send when, login flow, ack pattern, keepalive) | **holtburger** `crates/holtburger-core/src/client/` | AC2D `cNetwork.cpp` + `cInterface.cpp` | holtburger is the most complete; AC2D is simpler but confirmed working. |
|
|
| **Movement** (MoveToState format, AutonomousPosition, sequence counters, speed) | **holtburger** `client/movement/` | AC2D `cNetwork.cpp:2592-2664` (0xF61C format) | See `docs/research/2026-04-12-movement-deep-dive.md` for the full cross-reference. |
|
|
| **Server expectations** (what ACE accepts/rejects, validation thresholds) | **ACE** `Source/ACE.Server/Network/` | — | Only ACE knows what the server actually validates. |
|
|
| **Silk.NET / .NET 10 idioms** (GL calls, shader setup, VAO patterns) | **WorldBuilder original** | ACME (same stack) | Both use the same backend; original has cleaner isolated examples. |
|
|
| **Protocol field order** (packed dwords, type prefixes, flag enums) | **Chorizite.ACProtocol** `Types/*.cs` | holtburger (cross-check) | Generated from protocol XML; has accurate field comments. |
|
|
|
|
### ACME key files quick reference
|
|
|
|
These are the files you should open FIRST when working on any rendering
|
|
or dat-interpretation task:
|
|
|
|
- **`WorldBuilder.Tests/ClientReference.cs`** — decompiled retail AC
|
|
client C# port. `IsSWtoNECut`, `GetPalCode`, `GetVertexHeight`,
|
|
`GetVertexPosition`. **The ground truth.** If your code disagrees
|
|
with this file, your code is wrong.
|
|
- **`WorldBuilder.Tests/TerrainConformanceTests.cs`** — 4M+ cell sweep
|
|
proving ACME matches retail. Port these into acdream's test suite for
|
|
any algorithm you touch.
|
|
- **`StaticObjectManager.cs`** — GfxObj+Setup+CreaturePalette pipeline.
|
|
- **`EnvCellManager.cs`** — dungeon cells + portal visibility.
|
|
- **`TerrainGeometryGenerator.cs`** — `GetHeight()`, `GetNormal()`,
|
|
`CalculateSplitDirection()` matching the mesh index buffer.
|
|
- **`TextureHelpers.cs`** — INDEX16, BGRA, DXT decode helpers.
|
|
|
|
### holtburger key files quick reference
|
|
|
|
These are the files you should open FIRST when working on any networking
|
|
or client-behavior task:
|
|
|
|
- **`client/movement/system.rs`** — the movement state machine (when to
|
|
send MoveToState vs AutonomousPosition, deduplication logic).
|
|
- **`client/movement/actions.rs`** — MoveToState, AutonomousPosition,
|
|
Jump wire format builders.
|
|
- **`client/movement/types.rs`** — RawMotionState packed format with
|
|
all flag bits documented.
|
|
- **`session/send.rs`** — packet construction, checksum, ISAAC, ACK
|
|
piggybacking.
|
|
- **`client/messages.rs`** — post-login message handlers (PlayerCreate
|
|
→ LoginComplete, DddInterrogation → response, PlayerTeleport).
|
|
- **`spatial/physics.rs`** — dead-reckoning solver (how the client
|
|
advances position between server updates).
|