This is the Phase 4 protocol-compatibility proof. acdream's codec now
completes the full AC UDP handshake against a live ACE server and
successfully decodes three consecutive EncryptedChecksum game packets
— which means every layer of the codec is byte-compatible with ACE.
Changes:
- NetClient: added Send(IPEndPoint, ReadOnlySpan<byte>) overload so
one socket can talk to ACE's two listener ports (9000 for
LoginRequest, 9001 for ConnectResponse and all subsequent traffic)
- LiveHandshakeTests.Live_FullThreeWayHandshake_ReachesConnectedState:
drives the full 3-leg handshake end-to-end. Protocol details that
I got wrong on the first attempt and fixed after reading
references/holtburger/crates/holtburger-session/src/session/auth.rs:
* ConnectResponse header.Sequence = 1 (LoginRequest is seq 0)
* ConnectResponse header.Id = 0 (NOT the clientId from
ConnectRequest; that field is ACE's internal session index,
separate from the packet header Id)
* 200ms Thread.Sleep before sending ConnectResponse — holtburger
calls this ACE_HANDSHAKE_RACE_DELAY_MS, empirically determined
to avoid a server-side race where ACE is still finalizing the
session when our ConnectResponse arrives
* ConnectResponse goes to port 9001, not 9000 (ACE's second
ConnectionListener, see Network/Managers/SocketManager.cs)
LIVE RUN OUTPUT:
[live] step 1: sending 84-byte LoginRequest to 127.0.0.1:9000
[live] step 2: got 52-byte datagram from 127.0.0.1:9000,
flags=ConnectRequest
ConnectRequest cookie=0x458ABEE950D18BEE clientId=0x00000000
[live] step 3: sleeping 200ms then sending 28-byte ConnectResponse
to 127.0.0.1:9001
ISAAC seeds primed
[live] step 4: got 28-byte datagram from :9001,
flags=EncryptedChecksum,TimeSync, seq=2 OK
[live] step 4: got 64-byte datagram from :9001,
flags=EncryptedChecksum,BlobFragments, seq=3 OK
[live] step 4: got 152-byte datagram from :9001,
flags=EncryptedChecksum,BlobFragments, seq=4 OK
[live] step 4: got 24-byte datagram from :9001,
flags=AckSequence, seq=4 OK
[live] step 4: got 24-byte datagram from :9001,
flags=AckSequence, seq=4 OK
[live] step 4 summary: 5 packets received, 5 decoded OK,
0 checksum failures
What each "OK" proves, reading left to right:
* TimeSync (seq=2): our IsaacRandom is byte-compatible with ACE's
ISAAC.cs — if a single bit were wrong in any state register the
checksum key would mismatch and decode would fail. Our inbound
ISAAC consumed one word for this packet.
* BlobFragments (seq=3, 64 bytes): header hash + fragment hash +
ISAAC key recipe all check out. These fragments contain the start
of GameMessageCharacterList / ServerName / DDDInterrogation game
messages ACE enqueues right after HandleConnectResponse. We don't
parse game message bodies yet (Phase 4.7) but the fragments are
fully retrievable from Packet.Fragments.
* BlobFragments (seq=4, 152 bytes): continuation of the same game
messages; our sequential ISAAC consumption handled two back-to-back
encrypted packets correctly.
* AckSequence (seq=4): unencrypted mixed with encrypted in the same
stream — our codec handles both paths in one session.
Everything in AcDream.Core.Net is now proven byte-compatible with a
retail AC server at the protocol level. The remaining Phase 4 work
(4.6f, 4.7) is above the codec: parsing game message opcodes out of
the fragment payloads and routing CreateObject into IGameState so
acdream can show the foundry statue and the +Acdream character.
Test counts: 77 core + 73 net (+1 new live test) = 150 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>