docs: reconcile project state and refresh README
Some checks failed
Headless portability / portable-headless (ubuntu-latest) (push) Has been cancelled
Headless portability / portable-headless (windows-latest) (push) Has been cancelled
Headless portability / linux-graphical (push) Has been cancelled

This commit is contained in:
Erik 2026-07-27 12:53:31 +02:00
parent 97a095d628
commit f6275f4501
12 changed files with 432 additions and 295 deletions

385
README.md
View file

@ -2,195 +2,294 @@
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.
acdream ports the observable behaviour of the September 2013 retail client to
Silk.NET and 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.
**Status:** playable pre-alpha. M3, “Cast a spell,” landed on 2026-07-21 and
M4, “Live in the world,” is active. The graphical client supports the connected
combat, magic, movement, portal, inventory, loot, and retained-UI loops used by
the current test characters. The presentation-independent `GameRuntime` and
the Linux/Windows multi-session headless host are complete. Native Linux
graphics are intentionally parked at the L1 capability checkpoint; Windows is
the currently validated graphical platform.
Start at the [documentation map](docs/README.md) for the current milestone,
roadmap, tactical issues, architecture, retail-divergence register, research,
and durable memory.
The [documentation map](docs/README.md) is the entry point for current
milestones, roadmap state, architecture, issues, retail divergences, research,
and durable project memory.
## Stack
## Technology
- **Language:** C# .NET 10
- **Graphics:** [Silk.NET](https://github.com/dotnet/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](https://github.com/Chorizite/DatReaderWriter)
- **Networking:** Custom UDP + ISAAC cipher + game-message layer, wire-compatible
with ACEmulator server
- **Runtime:** C# and .NET 10
- **Graphics:** [Silk.NET](https://github.com/dotnet/Silk.NET), OpenGL 4.3
core, bindless textures, shader draw parameters, SSBOs, and multi-draw
indirect
- **Audio:** OpenAL through Silk.NET
- **Content:** retail DAT files plus a machine-local, memory-mapped
`acdream.pak` produced by `AcDream.Bake`
- **Networking:** custom UDP, ISAAC cipher, and game-message layers compatible
with ACEmulator
- **UI:** retained retail gameplay UI plus opt-in ImGui developer tools
- **Automation:** the same presentation-independent `GameRuntime` is hosted by
both the graphical client and `AcDream.Headless`
## What works today
The modern renderer is mandatory. There is no legacy renderer fallback.
Startup reports an actionable error if the required OpenGL capabilities are
missing.
- 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`.
## What works
## Active gaps
- ACE login, character selection, world entry, chat, client commands,
reconnect, and graceful logout.
- Outdoor, building, cellar, and dungeon streaming with prepared terrain,
scenery, buildings, EnvCells, collision, portal visibility, sky, fog,
lighting, audio, and day/night presentation.
- Local and observed movement, animation, jumping, selection, radar, combat
stances, melee, bows, crossbows, spell projectiles, death, corpses, chests,
and looting.
- Inventory bags, stable server ordering, stack splitting, ground drops,
paperdoll equipment, weapon switching, quick bars, item use, cooldowns, and
giving items to NPCs.
- Retail-style retained UI for vitals, chat, toolbar, inventory, character,
attributes, skills, spellbook, components, effects, combat/spell/jump bars,
radar/compass, dialogs, external containers, and assessment.
- Complete end-of-retail spell catalog, learned and favorite spells,
component preflight, connected casts, enchantments, DAT-driven projectiles
and effects, recall, portal-space travel, Hidden/UnHide, and remote
materialization.
- One presentation-independent runtime owner for session, entities, objects,
inventory, character state, selection, interactions, combat, magic,
movement, physics, projectiles, world environment, and portal transit.
- A no-window Windows/Linux host with deterministic bot commands/events,
shared immutable content, multi-session scheduling, isolation, reconnect,
resource telemetry, and tested 1/5/10/30-session ownership.
- Plugin loading, shared command/input abstractions, retained markup panels,
and permanent ImGui developer tools behind `ACDREAM_DEVTOOLS=1`.
- 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.
## Current boundaries
See [docs/README.md](docs/README.md) for document authority and current links.
- The active M4 prelude is
[world interaction completion](docs/plans/2026-07-23-world-interaction-completion.md).
Slices 13, including assessment and its final formula/icon/layout
correction, are user-accepted. Equipped-child picking and vendor
browse/buy/sell are the next uncompleted slices.
- Issue `#225` retains the lifestone/particle shared-alpha visual comparison.
Its connected lifetime and performance routes already pass.
- Narrow carried behaviour debt includes issue `#153` (an unstreamed
far-teleport edge), issue `#116` (slide feel), issue `#235` (30 Hz capped/RDP
jump presentation), and the live temporary-stopgap rows in the
[retail divergence register](docs/architecture/retail-divergence-register.md).
- Native Linux graphics are deferred. L0 portability and L1 backend/capability
reporting are implemented; WSLg reaches the GPU through Mesa D3D12 but does
not expose mandatory `GL_ARB_bindless_texture`. Resume with a supported
physical Linux AMD/NVIDIA driver before beginning later Slice L work.
- Advanced vendor/trade/crafting/social surfaces and larger M4 quest,
character-creation, and emote bodies remain roadmap work.
## Building + running
## Prerequisites
**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)
- Your own retail Asheron's Call DAT directory containing:
- `client_portal.dat`
- `client_cell_1.dat`
- `client_highres.dat`
- `client_local_English.dat`
- A machine-local `acdream.pak` built from those DATs
- A running ACE server for connected play; the examples use
`127.0.0.1:9000`
- For the graphical client, a driver exposing the mandatory modern OpenGL
capabilities
**Launch (PowerShell on Windows — bash has trouble with the apostrophe
in "Asheron's Call"):**
The project does not distribute Microsoft/Turbine DAT files or derived
prepared packages.
## Build and test
```powershell
$env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call"
dotnet restore AcDream.slnx
dotnet build AcDream.slnx -c Release
dotnet test AcDream.slnx -c Release --no-build
```
The current baseline is a successful Release build with **8,826 passing tests
and 5 intentional skips**. The build currently reports 17 test-project
warnings tracked by [`#228`](docs/ISSUES.md#228--clean-release-build-emits-17-test-project-warnings);
production compilation has zero errors.
## Prepare content
Production rendering and collision use the validated prepared package rather
than decoding world meshes on the frame path:
```powershell
dotnet run --project src\AcDream.Bake\AcDream.Bake.csproj -c Release -- `
--dat-dir "C:\Games\Asheron's Call" `
--out "C:\Games\Asheron's Call\acdream.pak"
```
A complete package is approximately 30 GB. It is machine-local and must not be
committed. `ACDREAM_PAK_PATH` overrides the default
`<DAT directory>\acdream.pak`.
## Run the graphical client
```powershell
$env:ACDREAM_DAT_DIR = "C:\Games\Asheron's Call"
$env:ACDREAM_PAK_PATH = "C:\Games\Asheron's Call\acdream.pak"
$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"
$env:ACDREAM_RETAIL_UI = "1"
dotnet run --project src\AcDream.App\AcDream.App.csproj -c Release
```
Offline CLI dat inspector (no server needed):
The DAT directory can instead be supplied as the first positional argument:
```
dotnet run --project src/AcDream.Cli -- "C:\path\to\Asheron's Call"
```powershell
dotnet run --project src\AcDream.App\AcDream.App.csproj -c Release -- `
"C:\Games\Asheron's Call"
```
## Diagnostic env vars
## Run a headless session
`AcDream.Headless` loads no App, UI, OpenGL, native-window, or audio assembly.
Create a version-1 configuration such as `bot.json`:
```json
{
"version": 1,
"process": {
"content": {
"datDirectory": "/opt/ac",
"preparedAssetPath": "/opt/ac/acdream.pak"
}
},
"sessions": [
{
"id": "bot-1",
"endpoint": { "host": "127.0.0.1", "port": 9000 },
"account": "testaccount",
"character": { "index": 0 },
"policy": { "id": "idle" },
"credential": {
"provider": "environment",
"reference": "ACDREAM_BOT_PASSWORD"
}
}
]
}
```
Then validate and run it:
```bash
export ACDREAM_BOT_PASSWORD='testpassword'
dotnet run --project src/AcDream.Headless/AcDream.Headless.csproj -c Release -- \
validate --config bot.json
dotnet run --project src/AcDream.Headless/AcDream.Headless.csproj -c Release -- \
run --config bot.json
```
For a single local session, `run` also accepts
`--user <account> --password <password>`. Add uniquely identified session
entries and credential references for a multi-session process. Available
built-in policies are `idle`, `lifecycle-smoke`, `observer-movement`, and
`portal-route-smoke`.
## Useful startup options
| 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_DAT_DIR` | Retail DAT directory |
| `ACDREAM_PAK_PATH` | Prepared package path; defaults to `<DAT dir>/acdream.pak` |
| `ACDREAM_LIVE=1` | Enable connected mode |
| `ACDREAM_TEST_HOST` / `ACDREAM_TEST_PORT` | ACE endpoint |
| `ACDREAM_TEST_USER` / `ACDREAM_TEST_PASS` | Graphical-client credentials |
| `ACDREAM_RETAIL_UI=1` | Enable the 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) |
| `ACDREAM_NO_AUDIO=1` | Suppress OpenAL initialization |
| `ACDREAM_UNCAPPED_RENDER=1` | Disable normal frame pacing for diagnostics |
| `ACDREAM_DISPLAY_PROTOCOL=auto\|x11\|wayland` | Select the Linux GLFW backend |
| `ACDREAM_DAY_GROUP=N` | Force a day-group index for weather/lighting comparisons |
| `ACDREAM_STREAM_RADIUS=N` | Legacy override over configured streaming radii |
| `ACDREAM_DUMP_SKY=1` | Dump sky interpolation and draw diagnostics |
| `ACDREAM_DUMP_MOTION=1` | Dump inbound movement and motion-cycle decisions |
## Layout
Additional diagnostic and budget controls are documented beside their typed
owners and in the linked research plans; they are not stable user settings.
```
## Repository layout
```text
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
AcDream.Runtime/ presentation-independent GameRuntime
AcDream.App/ graphical host, retained UI, renderer, audio
AcDream.Headless/ Windows/Linux no-window multi-session host
AcDream.Core/ retail gameplay, movement, physics, world logic
AcDream.Core.Net/ UDP, ISAAC, protocol and message routing
AcDream.Content/ GL-free DAT and prepared-package content
AcDream.Bake/ offline acdream.pak builder
AcDream.Cli/ offline DAT inspector
AcDream.UI.Abstractions/ shared UI/input models and contracts
AcDream.UI.ImGui/ developer-tool presentation
AcDream.Plugin.Abstractions/ BCL-only plugin contracts
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.
AcDream.*.Tests/ layer-matched xUnit projects
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
README.md documentation authority and current map
architecture/ ownership, structure, divergence, WB inventory
plans/ milestone, roadmap, and execution plans
research/ retail pseudocode, traces, fixtures, evidence
audit/ completion and conformance audits
memory/ durable engineering references
references/ gitignored external reference repositories
```
## Development workflow
All AC-specific behaviour is ported from the named retail client oracle
(`docs/research/named-retail/`). The workflow is:
All AC-specific behaviour starts from the named retail oracle in
`docs/research/named-retail/`:
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.
1. Search the named retail pseudo-C and headers by `class::method`.
2. Use the older Ghidra chunks only when the named oracle is insufficient.
3. Cross-reference ACE and the relevant client/viewer implementation.
4. Record readable pseudocode and exact constants/order.
5. Port the retail mechanism into the correct modern owner.
6. Add conformance, lifecycle, and failure-boundary tests.
7. Run the automated gate and the appropriate connected or visual gate.
8. Update architecture, roadmap, divergences, and durable memory with the
same change.
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.
Guessing at AC-specific algorithms is forbidden. See
[AGENTS.md](AGENTS.md), [CLAUDE.md](CLAUDE.md), and the
[architecture guide](docs/architecture/acdream-architecture.md) for the full
rules.
## Reference repos
## Reference projects
We cross-reference six external projects for retail behavior:
- **ACE / ACEmulator:** authoritative server and protocol behaviour
- **ACViewer:** character appearance and DAT presentation cross-check
- **WorldBuilder:** extracted Silk.NET DAT/rendering foundation
- **Chorizite.ACProtocol:** clean-room protocol reference
- **holtburger:** broad non-retail client behaviour reference
- **AC2D:** terrain and movement-packet cross-checks
- **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
The retail binary/decomp remains the behavioural oracle when references
disagree.
See `CLAUDE.md` for which reference is authoritative for which domain.
## Licence and game assets
## Licence
The acdream source has not yet been assigned a top-level licence and is not
ready for public redistribution. External reference code retains its own
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.
Asheron's Call DAT files, art, names, and other game assets remain the property
of Microsoft/Turbine. This repository does not distribute them; users must
supply their own retail installation.