The last of V6's three commits, and the one that makes the backend render. Plan sections: 4.5 (pipelines and the persisted cache), 4.6 (shaders and the committed .spv), 4.7 and 3.3 (clip space, the Y flip and winding), 4.9 and 4.10 (swapchain format and the scissor convention), 4.11 (the probe shader V5 deferred), 5.4 (Target: null means the swapchain image, literally). WHAT RUNS. ACDREAM_RENDER_BACKEND=vulkan now renders a real scene through the whole RHI on the RX 9070 XT: 60,000-plus frames per twelve-second run, 4x MSAA resolving into a B8G8R8A8_UNORM swapchain, GPU timer scopes resolving, a screenshot taken through IGpuDevice.CaptureBackbuffer, and a clean CloseMainWindow exit with the allocator reporting three device-memory objects. WHAT IT DRAWS, AND WHY IT IS NOT THE GAME. V6's milestone is "a full game frame on Vulkan" and on this branch that cannot be the game's own frame. V4c and V4d are parked by 5.5.5 so the world renderers are still raw GL; and the two renderers that DO speak the RHI - TextRenderer and DebugLineRenderer, ported at V4a - both throw for any device that is not a GlGpuDevice, because their loose uniforms and their classic texture-unit sprite binding have no home in the pinned contract yet. Converting them is a V4-class change with its own GL pixel gate, outside this slice's file list. So the backend is exercised through the contract by a scene of our own, and it is not a toy. It uses a device-local mesh arena filled through the staging ring, instance and batch data written straight into mapped ring memory, an offscreen render target whose colour is registered into the global texture table and sampled by a later pass, a BC1 texture with a CPU-built mip chain beside an uncompressed one with a vkCmdBlitImage chain, one multi-draw-indirect covering five quads with gl_DrawID selecting per-draw batch data, a second pipeline with line-list topology bound mid-pass, dynamic cull/front-face/depth-write, push constants, timer scopes, and an MSAA colour attachment resolving into the swapchain image. ORIENTATION, BY INSPECTION. Slice V5's screenshot was a uniform clear and its orientation was right "by construction" - which a uniform clear cannot show. The scene is therefore deliberately asymmetric in both axes: a quadrant card that is red top-left, green top-right, blue bottom-left and white bottom-right, four differently tinted markers at four different corners, and an open L of lines whose short stub rises at its right end. The captured PNG reads correctly in every one of those, including a miniature of the same card in the bottom-right whose own quadrants are also the right way up. The negative viewport height, the front-face inversion and the capture path agree. THE SHADER TOOLCHAIN, AND WHAT IT FOUND. tools/compile-shaders.ps1 drives tools/ShaderCompiler, a small out-of-solution .NET tool over Silk.NET.Shaderc - the same shaderc glslc is built on, through the already-pinned Silk 2.23.0 family. glslc is preferred when a Vulkan SDK is present and reported when it is; neither this machine nor CI has one, and requiring a 500 MB manual install between a contributor and a working checkout is not a reasonable price for a build step. The GLSL sources stay the single source of truth: the Vulkan dialect arrives as a preamble injected after the #version line - ACDREAM_UBO_SET becomes "set = 1,", the texture table becomes a set-2 descriptor array with a required nonuniformEXT accessor, and the shared 96-byte push block is declared with each loose uniform name defined onto its member. The only edits to a shader BODY are mechanical and dialect-level: dropping default-block uniform declarations, which Vulkan GLSL has no such thing as, and assigning explicit varying locations BY NAME across a pair, because ordinal assignment would look identical today and silently swap varyings the first time an author reordered a line. Run over the eight production pairs, exactly one thing happened: none of them compiled, and every failure is a specific source-level fact belonging to a renderer-port slice that has not landed. debug_line needs uView/uProjection converged into one uViewProjection - two matrices are 128 bytes and the shared block is 96. mesh_modern and particle still pass a uvec2 bindless handle as a varying, which is V4t's GpuTextureSlot retype. sky has ten loose uniforms and wants a UBO. ui_text needs uScreenSize/uUseTexture/uTex. particle_mesh needs uTextureIndex to become uTextureIndexA. terrain_modern needs V4d-1's matrix convergence. mesh is the legacy pair with no RHI consumer at all. That inventory is committed as shaders.manifest.json, with each source's SHA-256 and the compiler's own message, and a test re-hashes it so an edited shader that never got recompiled fails a build rather than shipping a stale binary. vk_probe is the pair that does compile, and it is the shader 4.11 already asked for: V5 recorded "build one real pipeline from the committed .spv" as its single deliberate deviation because no toolchain existed. It is Vulkan-dialect only and no GL renderer draws with it, so it forks nothing; it retires when the ported world renderers become the backend's own proof. DESCRIPTORS. Sets 0 and 1 are DYNAMIC buffer descriptors bound per flight slot, so a per-draw range change costs a dynamic offset in vkCmdBindDescriptorSets rather than a vkUpdateDescriptorSets in the hot path - which is what keeps 4.4's zero-writes-per-frame property true for buffers as well as for textures. Ten dynamic storage descriptors is above Vulkan's guaranteed minimum of four, so it is a real requirement rather than a free choice, it fails loudly at layout creation on a device that cannot serve it, and V9's lavapipe row must confirm it. Unused bindings point at a shared dummy range so there is ONE set layout and one pipeline layout; that is why binding a second pipeline mid-pass costs nothing and disturbs neither the descriptors nor the push constants. THE ONE MAPPING FUNCTION. VulkanViewportMapping holds the whole coordinate reconciliation: negative viewport height, the front-face inversion that pairs with it, and - separately - the scissor flip, which the viewport sign does NOT perform. The V3 audit flagged that as a concrete V6 acceptance item and it is the subtle one: vkCmdSetScissor is always top-left-origin, NdcScissorRect emits GL bottom-left rectangles, and getting it wrong clips a doorway aperture from the wrong edge in a scene that has one. Clip space needs nothing, as 4.7 concluded: the cameras already build [0,1]-convention projections. CONTRACT GAP, RECORDED NOT PAPERED OVER. GpuPipelineDescription cannot name its colour-attachment format, and Vulkan bakes that into a pipeline. Offscreen targets therefore adopt the swapchain's B8G8R8A8_UNORM rather than a literal RGBA order - invisible above the API, because an image is sampled through its format's component mapping and the one CPU readback swizzles explicitly. The honest fix is a colour-format field added in a reviewed contract commit, exactly as GpuBlendMode.InverseAlpha and GpuVertexFormat.UByte4UInt were added when V4c and V4d met the same wall. It is documented at VulkanTextureFormatMapping.CanonicalColorAttachmentFormat. The pipeline cache is persisted to the cache directory and validated by its 32-byte header against this device's vendor, device and cache UUID before use. Drivers are required to ignore incompatible blobs, but "required to" is a poor foundation for something that runs before anything else in the process, and the check costs 32 bytes of comparison. Two consecutive launches report "cold" then "reused". Gates: Release build clean; App suite 4056 passed / 3 skipped (4037 at V6b plus 19 new); offline pixel gate PASS at a differing fraction of 5.15e-05 with a same-commit control immediately after it at 2.84e-05 - 29 and 16 pixels of 563,200, the same class of ambient variation the campaign's 15-23 band records, and roughly 19x under the 0.001 threshold on a commit that changes no GL code path. Validation layers could not be run: this machine has no Vulkan SDK, no HKLM\SOFTWARE\Khronos\Vulkan\ExplicitLayers key, no VK_LAYER_PATH and no VkLayer_khronos_validation.json anywhere on disk. Plan 7 already requires one validation-clean run at V7; it needs the SDK installed first and is reported rather than assumed here. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| .github | ||
| .vscode | ||
| docs | ||
| memory | ||
| references | ||
| src | ||
| tests | ||
| tools | ||
| .gitattributes | ||
| .gitignore | ||
| .gitmodules | ||
| AcDream.slnx | ||
| AGENTS.md | ||
| analyze_flap_live.py | ||
| CLAUDE.md | ||
| find_burst.py | ||
| launch-a6-issue98-capture.ps1 | ||
| launch-a6-issue98-cottage-gfxobj-dump.ps1 | ||
| launch-a6-issue98-polydump.ps1 | ||
| launch-flap-capture.ps1 | ||
| launch-flap-churn.ps1 | ||
| launch-flap-verify.ps1 | ||
| NOTICE.md | ||
| README.md | ||
acdream
A modern open-source C# / .NET 10 Asheron's Call client.
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,” 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.
The documentation map is the entry point for current milestones, roadmap state, architecture, issues, retail divergences, research, and durable project memory.
Technology
- Runtime: C# and .NET 10
- Graphics: 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.pakproduced byAcDream.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
GameRuntimeis hosted by both the graphical client andAcDream.Headless
The modern renderer is mandatory. There is no legacy renderer fallback. Startup reports an actionable error if the required OpenGL capabilities are missing.
What works
- 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.
Current boundaries
- The active M4 prelude is world interaction completion. Slices 1–3, 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
#225retains 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. - 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.
Prerequisites
- .NET 10 SDK
- Your own retail Asheron's Call DAT directory containing:
client_portal.datclient_cell_1.datclient_highres.datclient_local_English.dat
- A machine-local
acdream.pakbuilt 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
The project does not distribute Microsoft/Turbine DAT files or derived prepared packages.
Build and test
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;
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:
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
$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"
dotnet run --project src\AcDream.App\AcDream.App.csproj -c Release
The DAT directory can instead be supplied as the first positional argument:
dotnet run --project src\AcDream.App\AcDream.App.csproj -c Release -- `
"C:\Games\Asheron's Call"
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:
{
"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:
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_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_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 |
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
src/
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
docs/
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 starts from the named retail oracle in
docs/research/named-retail/:
- Search the named retail pseudo-C and headers by
class::method. - Use the older Ghidra chunks only when the named oracle is insufficient.
- Cross-reference ACE and the relevant client/viewer implementation.
- Record readable pseudocode and exact constants/order.
- Port the retail mechanism into the correct modern owner.
- Add conformance, lifecycle, and failure-boundary tests.
- Run the automated gate and the appropriate connected or visual gate.
- Update architecture, roadmap, divergences, and durable memory with the same change.
Guessing at AC-specific algorithms is forbidden. See AGENTS.md, CLAUDE.md, and the architecture guide for the full rules.
Reference projects
- 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
The retail binary/decomp remains the behavioural oracle when references disagree.
Licence and game assets
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.
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.