Modern open-source C# .NET 10 Asheron's Call client. Faithful port of retail client behaviour to Silk.NET with a plugin API.
Brings the codec to end-to-end: a raw UDP datagram goes in, a parsed
Packet comes out with verified CRC (both plain and ISAAC-encrypted
variants). Synthetic packets built inside tests round-trip through
TryDecode cleanly.
Added:
- Packets/PacketHeaderOptional.cs: parses every flag-gated section
that lives between the 20-byte header and the body fragments —
AckSequence, RequestRetransmit (with count + array), RejectRetransmit,
ServerSwitch, LoginRequest (tail slurp), WorldLoginRequest,
ConnectResponse, CICMDCommand, TimeSync (double), EchoRequest (float),
Flow (FlowBytes + FlowInterval). Records the raw consumed bytes into
RawBytes so CalculateHash32 can hash them verbatim — AC's CRC requires
hashing the optional section separately from the main header and the
fragments.
- Packets/Packet.cs: a record type bundling Header, Optional, Fragments,
and the raw body bytes. Produced by the decoder, consumed by downstream
handlers in Phase 4.5.
- Packets/PacketCodec.cs: TryDecode(datagram, isaac?) that
1. Unpacks the header,
2. Bounds-checks DataSize against the buffer,
3. Parses the optional section,
4. If BlobFragments is set, walks the body tail as back-to-back
MessageFragment.TryParse calls,
5. Computes headerHash + optionalHash + fragmentHash,
6. Verifies CRC:
- Unencrypted: sum equals header.Checksum
- Encrypted: (header.Checksum - headerHash) XOR payloadHash must
equal the next ISAAC keystream word (which is consumed on match)
Returns a PacketDecodeResult(Packet?, DecodeError) so callers can log
and drop malformed packets instead of throwing.
- Public helper PacketCodec.CalculateFragmentHash32 so tests (and later
the encode path) can reuse the fragment-hash math.
Tests (7 new, 44 total in net project, 121 across both test projects):
- Minimal valid packet with AckSequence optional, no fragments, plain
checksum — verifies optional parse + CRC accept
- Wrong checksum rejected
- Buffer shorter than header → TooShort
- Header DataSize > buffer → HeaderSizeExceedsBuffer
- Packet with BlobFragments flag + one fragment: parses fragment and
validates the full headerHash + fragmentHash equals wire checksum
- Encrypted checksum ROUND TRIP: two ISAAC instances with same seed,
one encodes the checksum key, one decodes — validates the
(Header.Checksum - headerHash) XOR payloadHash == isaacNext contract
byte-for-byte
- Encrypted checksum with wrong key on the wire → rejected
Known limitation: the parser advances past WorldLoginRequest and
ConnectResponse their full 8 bytes whereas ACE "peeks" them (seek/reset).
The on-wire byte count is the same, only the read-position behavior
differs; any consumer that wanted to re-read those sections can do so
from Packet.BodyBytes.
Phase 4.5 (NetClient UDP pump + handshake state machine) next.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| docs/plans | ||
| src | ||
| tests | ||
| .gitignore | ||
| AcDream.slnx | ||
| CLAUDE.md | ||
| README.md | ||
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 directoryreferences/— 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.