feat(net): extract GameMessage opcodes from live fragment stream (Phase 4.6f)
Reassembles the fragments arriving from the live handshake into full
game message bodies, reads the opcode from the first 4 bytes, and
identifies them by name. On the live wire we now see exactly the
sequence ACE sends right after HandleConnectResponse:
GameMessage assembled: opcode=0xF7E5 (DDDInterrogation), body=28 bytes
GameMessage assembled: opcode=0xF658 (CharacterList), body=80 bytes
GameMessage assembled: opcode=0xF7E1 (ServerName), body=20 bytes
summary: 5 packets received, 5 decoded OK, 0 checksum failures,
3 GameMessages assembled
Every layer of the net stack is now proven live:
* NetClient send/receive on both ports 9000 and 9001
* PacketCodec.Encode building LoginRequest + ConnectResponse with
correct unencrypted CRC
* IsaacRandom byte-compatible with ACE's ISAAC (3 EncryptedChecksum
packets decoded, zero mismatches)
* PacketHeaderOptional parsing ConnectRequest, TimeSync, AckSequence
* MessageFragment.TryParse walking a body tail of back-to-back
fragments (the 152-byte packet had TWO messages: CharacterList
and ServerName packed into one datagram)
* FragmentAssembler reassembling by index
The CharacterList body has our test character +Acdream inside it but
we're not decoding its fields yet — that's Phase 4.7 where we actually
pick a character and send CharacterLogin to enter the game world.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a961d842d4
commit
0aea24c78e
1 changed files with 28 additions and 4 deletions
|
|
@ -197,12 +197,15 @@ public class LiveHandshakeTests
|
||||||
$"inbound.next=0x{new IsaacRandom(serverSeedBytes).Next():X8}, " +
|
$"inbound.next=0x{new IsaacRandom(serverSeedBytes).Next():X8}, " +
|
||||||
$"outbound.next=0x{new IsaacRandom(clientSeedBytes).Next():X8}");
|
$"outbound.next=0x{new IsaacRandom(clientSeedBytes).Next():X8}");
|
||||||
|
|
||||||
// Step 4: receive post-handshake traffic. We expect EncryptedChecksum
|
// Step 4: receive post-handshake traffic. Run fragments through a
|
||||||
// packets containing CharacterList and friends. If our ISAAC seed is
|
// FragmentAssembler so multi-packet game messages reassemble, then
|
||||||
// wrong or our CRC math is off, TryDecode will return ChecksumMismatch.
|
// read the opcode (first 4 bytes of the assembled body) to prove
|
||||||
|
// we're looking at real GameMessage opcodes.
|
||||||
|
var assembler = new FragmentAssembler();
|
||||||
int postHandshakePackets = 0;
|
int postHandshakePackets = 0;
|
||||||
int successfullyDecoded = 0;
|
int successfullyDecoded = 0;
|
||||||
int checksumFailures = 0;
|
int checksumFailures = 0;
|
||||||
|
var seenOpcodes = new List<uint>();
|
||||||
var postDeadline = DateTime.UtcNow + TimeSpan.FromSeconds(5);
|
var postDeadline = DateTime.UtcNow + TimeSpan.FromSeconds(5);
|
||||||
while (DateTime.UtcNow < postDeadline)
|
while (DateTime.UtcNow < postDeadline)
|
||||||
{
|
{
|
||||||
|
|
@ -214,13 +217,34 @@ public class LiveHandshakeTests
|
||||||
$"decode={decoded.Error}, flags={decoded.Packet?.Header.Flags}, " +
|
$"decode={decoded.Error}, flags={decoded.Packet?.Header.Flags}, " +
|
||||||
$"seq={decoded.Packet?.Header.Sequence}");
|
$"seq={decoded.Packet?.Header.Sequence}");
|
||||||
if (decoded.IsOk)
|
if (decoded.IsOk)
|
||||||
|
{
|
||||||
successfullyDecoded++;
|
successfullyDecoded++;
|
||||||
|
foreach (var frag in decoded.Packet!.Fragments)
|
||||||
|
{
|
||||||
|
var completeBody = assembler.Ingest(frag, out _);
|
||||||
|
if (completeBody is not null && completeBody.Length >= 4)
|
||||||
|
{
|
||||||
|
uint opcode = BinaryPrimitives.ReadUInt32LittleEndian(completeBody);
|
||||||
|
seenOpcodes.Add(opcode);
|
||||||
|
string name = opcode switch
|
||||||
|
{
|
||||||
|
0xF658 => "CharacterList",
|
||||||
|
0xF7E1 => "ServerName",
|
||||||
|
0xF7E5 => "DDDInterrogation",
|
||||||
|
_ => "unknown",
|
||||||
|
};
|
||||||
|
Console.WriteLine($"[live] GameMessage assembled: opcode=0x{opcode:X8} ({name}), " +
|
||||||
|
$"body={completeBody.Length} bytes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (decoded.Error == PacketCodec.DecodeError.ChecksumMismatch)
|
else if (decoded.Error == PacketCodec.DecodeError.ChecksumMismatch)
|
||||||
checksumFailures++;
|
checksumFailures++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"[live] step 4 summary: {postHandshakePackets} packets received, " +
|
Console.WriteLine($"[live] step 4 summary: {postHandshakePackets} packets received, " +
|
||||||
$"{successfullyDecoded} decoded OK, {checksumFailures} checksum failures");
|
$"{successfullyDecoded} decoded OK, {checksumFailures} checksum failures, " +
|
||||||
|
$"{seenOpcodes.Count} GameMessages assembled");
|
||||||
|
|
||||||
// The contract of Phase 4.6e is "server accepted our ConnectResponse
|
// The contract of Phase 4.6e is "server accepted our ConnectResponse
|
||||||
// and started streaming". Any post-handshake traffic at all proves
|
// and started streaming". Any post-handshake traffic at all proves
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue