Modern open-source C# .NET 10 Asheron's Call client. Faithful port of retail client behaviour to Silk.NET with a plugin API.
Find a file
Erik 6ab24c9982 feat(net+app): AnimPartChanges + Name extraction — characters clothed,
statue identified (Phase 4.7h/i/j/k/l)

Makes three big improvements to the CreateObject decode path:

1. Extract AnimPartChanges from the ModelData section instead of
   skipping them. Each change is (PartIndex, NewModelId); the server
   uses these to replace base Setup parts with armor/clothing/statue
   meshes. The player character has ~34 of them on a normal login.

2. Flow AnimPartChanges through WorldSession.EntitySpawn into
   GameWindow.OnLiveEntitySpawned, which now patches the flattened
   Setup's part list BEFORE uploading GfxObjs. Patching is a simple
   "parts[change.PartIndex] = new MeshRef(change.NewModelId, oldTransform)"
   keeping the base Setup's placement transform but swapping the mesh.

3. Read the WeenieHeader Name (String16L) that follows the PhysicsData
   section. Required walking past every remaining physics flag (Parent,
   Children, ObjScale, Friction, Elasticity, Translucency, Velocity,
   Acceleration, Omega, DefaultScript, DefaultScriptIntensity) plus the
   9 sequence timestamps (2 bytes each) plus 4-byte alignment. The
   Name field is then the second thing in the WeenieHeader after
   u32 weenieFlags.

Critical bug fix in the same commit: ACE's WritePackedDwordOfKnownType
STRIPS the known-type high-byte prefix (e.g. 0x01000000 for GfxObj ids)
before writing the PackedDword. The first version of AnimPartChange
decoding called plain ReadPackedDword, so it got 0x0000XXXX instead of
0x0100XXXX and every GfxObj dat lookup silently failed — the drop
counter showed 19+ noMeshRef drops including +Acdream himself.

Added ReadPackedDwordOfKnownType that ORs the knownType bit back in
on read (with zero preserved as the "no value" sentinel). After the
fix, noMeshRef drops = 0 across a full login.

LIVE RUN after all three changes:

  live: spawn guid=0x5000000A name="+Acdream" setup=0x02000001
        pos=(58.5,156.2,66.0)@0xA9B40017 animParts=34
  live: spawn guid=0x7A9B4035 name="Holtburg" setup=0x020006EF
        pos=(94.6,156.0,66.0)@0xA9B4001F animParts=0
  live: spawn guid=0x7A9B4000 name="Door" setup=0x020019FF
        pos=(84.1,131.5,66.1)@0xA9B40100 animParts=0
  live: spawn guid=0x7A9B4001 name="Chest" setup=0x0200007C
        pos=(78.1,136.9,69.5)@0xA9B40105 animParts=0
  live: spawn guid=0x7A9B4036 name="Well" setup=0x02000180
        pos=(90.1,157.8,66.0)@0xA9B4001F animParts=0
  live: spawn guid=0x800005FD name="Wide Breeches" setup=0x02000210
        pos=no-pos animParts=1
  live: spawn guid=0x800005FC name="Smock" setup=0x020000D4
        pos=no-pos animParts=1
  live: spawn guid=0x800005FE name="Shoes" setup=0x020000DE
        pos=no-pos animParts=1
  live: spawn guid=0x80000697 name="Facility Hub Portal Gem"
        setup=0x02000921 pos=no-pos animParts=0
  live: spawn guid=0x7A9B404B name="Nullified Statue of a Drudge"
        setup=0x020007DD pos=(65.3,156.8,72.8)@0xA9B40017 animParts=1

  summary recv=60 hydrated=43 drops: noPos=17 noSetup=0
                                     setupMissing=0 noMesh=0

The statue's exact data is now known and the hydration path runs
without errors. The user's "look at the Name field in the CreateObject
body" insight turned this from an unbounded visual hunt into a targeted
grep of ~60 log lines.

Tests: 77 core + 83 net = 160 passing (offline suite unchanged).
Live handshake + enter-world tests still pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 15:48:13 +02:00
docs/plans docs(plan): Phase 3c terrain blending plan 2026-04-10 23:43:04 +02:00
src feat(net+app): AnimPartChanges + Name extraction — characters clothed, 2026-04-11 15:48:13 +02:00
tests feat(net): CreateObject body parser — GUID + Position + SetupId extracted (Phase 4.7d) 2026-04-11 15:18:54 +02:00
.gitignore chore: phase 0 — skeleton + dat asset inventory 2026-04-10 09:02:56 +02:00
AcDream.slnx feat(net): AcDream.Core.Net scaffold + ISAAC keystream (Phase 4.1) 2026-04-11 14:14:28 +02:00
CLAUDE.md docs(claude): project goal + lead-developer operating instructions 2026-04-11 13:39:21 +02:00
README.md chore: phase 0 — skeleton + dat asset inventory 2026-04-10 09:02:56 +02:00

acdream

Experimental modern open-source Asheron's Call client in C# / .NET 10.

Status: pre-alpha, not playable. Phase 0 only — dat file asset inventory.

Stack: .NET 10, Chorizite.DatReaderWriter for dat parsing. Silk.NET + Avalonia planned for rendering/UI (not yet wired up).

Requires: A retail Asheron's Call install (Turbine/Microsoft property — supply your own). Set ACDREAM_DAT_DIR environment variable to the directory containing client_portal.dat, client_cell_1.dat, client_highres.dat, and client_local_English.dat, or pass it as the first CLI argument.

Layout

  • src/AcDream.Cli/ — console app that dumps asset counts from a dat directory
  • references/ — local read-only reference material (ACE, ACViewer, WorldBuilder, DatReaderWriter, holtburger, retail AC install). Gitignored.

Run

dotnet run --project src/AcDream.Cli -- "C:\path\to\Asheron's Call"

Or set ACDREAM_DAT_DIR and run without args.