Campaign VT slice 7 (docs/plans/2026-09-07-campaign-vt-slice7-tabs.md), S7.3 (Monsters). Owner's bar: looks basically the same as VTank and behaves the same from the player's seat. The pre-S7.3 Monsters tab was a select-then-edit adaptation (one padded-text list + a separate expression/priority/damage-menu/equip editor row + 14 standalone toggles) forced by acdream's earlier single-column-only <list>. Campaign VT slice 1 Part B already closed that gap (the <list><column> markup extension, docs/plugin-ui-markup.md "Columns"), so this slice replaces the whole tab body with VTank's own one 23-column HudList grid (docs/research/vtank-kb/08-ui-views.md §1 "Tab: Monsters"): 14 check columns (F B G I Y V A R S WC FC Cp DC Cs, each 20px = VVS's forced 13px check width + WPadding per the markup doc's PITCH convention), 7 text columns (Name 127/P 27/Dmg type 63/ Ex. Vuln 63/Weapon 87/Offhand 87/PetDmg 63, each pitch = fixedwidth+7), 2 icon columns (move up/down, DAT ids 0x060028FC/0x060028FD), 15 header <label>s carrying VTank's own decompiled tooltip text (PluginCore.cs:1693-1707 — corrects "Broadside of a Tumerok" to the decompile's actual "Broadside of a Barn"), and the txtNewMonster field + Add + Add Sel row at VTank's exact geometry (8,136,388,16 / 404,136,50,16 / 462,136,50,16). Selection is unused, matching VTank — every real cell is its own click target. Per-cell behavior ported from PluginCore.cs:7885-8105's `b(object,int, int)` handler: Name click deletes the row (guarded on the rule's own IsDefault rather than literal row 0, so the invariant holds even if a caller reorders DEFAULT); P cycles -1->0->1->2->3->4->-1; Dmg type cycles every eDamageElement 0..13; Ex. Vuln and PetDmg reproduce the decompile's own non-contiguous jumps (6->8->98 and 6->101->8->98 respectively — eDamageElement.cs); move up/down reorder but never displace DEFAULT (a deliberate symmetric guard — VTank's own case 21 keeps MoveUp off row 0 but case 22 has no matching MoveDown guard, which reads as an authoring oversight, not intended asymmetric behavior, so a literal port was not preserved here). Case 6's "A" column write is inverted in VTank's own obfuscated field, but that inversion is an artifact of VTank's internal storage, not the checkbox the player sees — our Attack flag is already declared un-inverted, so no inversion is needed for parity. Deviation: cases 18/19's Weapon/Offhand cycle VTank's own runtime list of owned weapon-TYPE ids via an opaque item-kind classifier the decompile alone doesn't resolve. MossTank has no "weapon type" abstraction — Weapon/Offhand are always concrete owned items — so these columns instead cycle the same registered weapon roster the Items tab already maintains (ordinal-sorted for a deterministic order), preserving the click-to-cycle-through-<AUTO> contract without inventing a type abstraction outside this slice's scope. MossTankPanel: the whole select-then-edit surface (SelectedMonsterRule selection, RefreshMonsterEditor's cached row formatting, Apply/Remove/ priority-stepper/damage-menu/equip-from-selection actions, HasMonsterFlag/ToggleMonsterFlag) is retired in favor of per-row column-array properties (14 flag columns, 7 text columns, 2 icon columns) and per-row mutation actions, all still writing through the unchanged CombatSettings.Rules/MonsterRule/MonsterRuleActions model. EnsureDefaultMonsterRule replaces RefreshMonsterEditor's DEFAULT invariant at the two remaining call sites (ctor init, profile reload, and the /vt refresh command). MonsterEquipmentText is kept as a read-only DEFAULT-rule readout for the (out-of-scope) Items tab label that already depended on it. Tests (every pin below shown to fail against a targeted mutation, reverted after confirming the failure — mutations: wrong priority-wrap boundary, dropped DEFAULT-delete guard, dropped DEFAULT-displacement guard on move, spliced Harm into the Ex.Vuln cycle, flag toggle targeting row 0 unconditionally, a wrong header tooltip string; the xml column-count contract's 205->202 pin was independently shown to fail against the pre-edit test file and the new markup): MossTankMarkupContractTests gained MonstersGridHasVtanksTwentyThreeColumnsInOrderWithRetailHeaderTooltips (column type/order + the 15 header tooltips) and updated the 205->202 interactive-control count plus the tooltip-check's <column> skip (columns have no text/tooltip grammar). MossTankPanelTests replaced the three old select-then-edit tests with MonstersGridMutationsPersistAcrossSessions, ToggleMonsterFlagAtWritesOnlyTheTargetedRow, CycleMonsterPriorityAtWrapsExactlyNegativeOneThroughFour, MonsterDamageColumnsCycleInTheExactRetailOrder (all three cycles), DeleteMonsterRuleAtRemovesNonDefaultRowsButNeverDefault, MoveMonsterRuleAtReordersButNeverDisplacesDefault, AddMonsterRuleUsesTheDraftTextAndAddSelectedMonsterUsesTheWorldTarget, and MonsterWeaponColumnCyclesTheRegisteredRosterAndPersistsByName AcrossSessions, and updated MacroWieldsCasterEntersMagicBuffsThen WieldsWeaponFightsThenIdlePeace for the new weapon-cycle API. Full suite green: 651/651 MossTank tests (645 + 6 net), 109/109 App markup-filtered tests. Deviation not requiring a divergence-register row: this campaign ports VTank plugin UI behavior, not retail AC client behavior, so docs/architecture/retail-divergence-register.md's scope (acdream vs. retail) does not apply here. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
||
|---|---|---|
| .gitea/workflows | ||
| .github | ||
| .vscode | ||
| assets/icons | ||
| docs | ||
| memory | ||
| references | ||
| samples | ||
| src | ||
| tests | ||
| tools | ||
| .gitattributes | ||
| .gitignore | ||
| .gitmodules | ||
| AcDream.slnx | ||
| AGENTS.md | ||
| analyze_flap_live.py | ||
| CLAUDE.md | ||
| Directory.Build.props | ||
| Directory.Packages.props | ||
| find_burst.py | ||
| global.json | ||
| 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 | ||
| NuGet.Config | ||
| 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 Vulkan capabilities validated at startup
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 CI-filtered Windows baseline is a successful Release build with 16,151 passing tests and zero failures; opt-in live, installed-DAT, prepared-package, manual, timing, and platform-specific lanes run separately.
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 format-2 package from the standard installed DAT set is about
570 MiB (the former format-1 package was about 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"
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=0 |
Disable the retained retail gameplay UI for diagnostics; it is enabled by default |
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.MossTank/ 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.