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 9e4313f3d3 feat(net): CreateObject body parser — GUID + Position + SetupId extracted (Phase 4.7d)
Decodes the CreateObject (0xF745) game message body far enough to hand
an entity off to acdream's existing IGameState/MeshRenderer pipeline.
Ported from ACE's WorldObject_Networking.cs (SerializeCreateObject,
SerializeModelData, SerializePhysicsData) and Position.cs.

Scope: the parser extracts exactly three fields —
  - GUID (u32 right after the opcode)
  - ServerPosition (landblockId + XYZ + rotation quaternion), if the
    Position bit is set in the PhysicsDescriptionFlag
  - SetupTableId (setup dat id for the visual mesh chain), if the
    CSetup bit is set

Everything else in a CreateObject body (weenie header, object description,
motion tables, palettes, texture overrides, animation frames, velocity,
acceleration, omega, scale, friction, elasticity, translucency,
default scripts, sequence timestamps, ...) is consumed-or-skipped with
just enough bytes to advance past the correct flag-gated sections.
The parser stops at the end of PhysicsData — we don't need weenie-header
fields for rendering placement.

Components parsed in order (all from ACE's serialize routines):
  1. Opcode u32 (must be 0xF745)
  2. u32 GUID
  3. ModelData header (byte 0x11 marker, byte subPaletteCount,
     byte textureChangeCount, byte animPartChangeCount), followed by
     PackedDword palette/subPalette fields, texture change records,
     anim part change records, aligned to 4 bytes at end
  4. u32 PhysicsDescriptionFlag
  5. u32 PhysicsState (skipped)
  6. Conditional Movement/AnimationFrame section
  7. Conditional Position section (LandblockId, X, Y, Z, RW, RX, RY, RZ)
  8. Conditional MTable/STable/PeTable u32 ids (all skipped)
  9. Conditional CSetup u32 (extracted as SetupTableId)

The PackedDword reader is a new helper: AC's variable-width uint format
where values ≤ 32767 encode as a u16, larger values use a marker bit in
the top of the first u16 and a continuation u16. Ported from
Extensions.WritePackedDword.

LIVE RUN AGAINST THE ACE SERVER (test account, Holtburg):

  step 4: CharacterList received account=testaccount count=2
    character: id=0x5000000A name=+Acdream
    character: id=0x50000008 name=+Wdw
  sent CharacterEnterWorldRequest
  step 6: CharacterEnterWorldServerReady received
  sent CharacterEnterWorld(guid=0x5000000A)
  step 8 summary: 83 GameMessages assembled, 68 CreateObject,
                  68 parsed, 52 w/position, 68 w/setup

  First 10 parsed CreateObjects:
    guid=0x5000000A lb=0xA9B40021 xyz=(104.89,15.05,94.01) setup=0x02000001
    guid=0x80000600 no position setup=0x02000181
    guid=0x800005FF no position setup=0x02000B77
    guid=0x80000603 no position setup=0x02000176
    guid=0x80000604 no position setup=0x02000D5C
    guid=0x80000694 no position setup=0x020005FF
    guid=0x80000697 no position setup=0x02000921
    guid=0x80000601 no position setup=0x02000179
    guid=0x80000605 no position setup=0x02000155
    guid=0x80000695 no position setup=0x020005FF

The first line is +Acdream himself — GUID matches what we picked from
CharacterList, landblock 0xA9B4 is Holtburg (the area we already render),
setup 0x02000001 is the default humanoid player mesh. The other 67 are
NPCs/weenies/scenery-weenies in the same area; the 16 without positions
are inventory items whose position is inherited from the parent.

ALL 68 CreateObjects parsed cleanly — no short reads, no format errors.
Phase 4.7d proves byte-level compatibility with ACE's outbound network
serialization format. The remaining Phase 4 work (WorldSession type +
GameWindow wiring) is glue code above a codec that now speaks the real
AC wire format.

Tests: 77 core + 83 net (+1 live test) = 161 passing, all green.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 15:18:54 +02:00
docs/plans docs(plan): Phase 3c terrain blending plan 2026-04-10 23:43:04 +02:00
src feat(net): CreateObject body parser — GUID + Position + SetupId extracted (Phase 4.7d) 2026-04-11 15:18:54 +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.