Files
Campus-25/static/Servers/Tachyon/docs/client-agent-request-3.md
T
2026-10-14 06:59:51 +02:00

4.0 KiB
Raw Blame History

Request #3: the forked NGO batch/message framing and ConnectionApproved

The LiteNetLib layer is solved and the client now completes the transport handshake against our server and sends its NGO ConnectionRequest. We decoded it and confirmed the Tachyon JSON (AI/AT/VB/CKA/CIA/CPK). But the fork changed NGO's batch and message framing, so we cannot send a ConnectionApproved the client will accept without the exact serialization.

What we already worked out from the wire (for your cross-check)

Decoded from a real 2041-byte ConnectionRequest batch:

[0:2]    magic  u16 = 0x1160
[2:4]    count  u16 = 0                     (stock BatchCount, sent as 0)
[4:8]    size   u32 = 2041                  (total datagram length)
[8:16]   hash   u64 = 0x3faa53cee8c5e338    (= XXHash64 over bytes [24:2041], VERIFIED)
[16:24]  u64        = 1                      (fork field; equals message count here)
[24]     bitpacked  = 1                      (message type: ConnectionRequest, client-local idx)
[25:27]  bitpacked  = 2014                   (message size)
[27]     0x81                                (version-header preamble byte)
[28:63]  7 × { u32 hash ; 1 byte version=0 }
[63:68]  00 00 00 00 01                      (5 bytes, not in stock NGO)
[68:76]  u64 ConfigHash
[76:80]  u32 = 1961  (ConnData length)
[80:2041] Tachyon JSON

So the batch hash covers [24:end] (message stream), NOT stock's [16:alignedLength], and the batch is NOT padded to 8 bytes. Two message-body bytes are unexplained: the 0x81 preamble, and the 00 00 00 00 01.

Questions (in priority order)

Q1 — forked batch header & hash, in NetworkMessageManager. Confirm the header layout above. Specifically:

  • Is the u64 at [16:24] the message count, a tick, or something else? What writes it?
  • Confirm BatchHash = XXHash64(buffer + 24, size - 24) (or give the real range/seed).
  • Does the RECEIVER validate BatchHash and reject on mismatch, or log-and-continue? This decides whether our outbound hash must be exact.
  • Does the receiver iterate messages by the u64 count, by BatchCount, or by position?

Q2 — the version-header preamble. In the forked ConnectionRequestMessage.Serialize (and the shared version-header code), what is written before the version table? Stock writes BytePacker.WriteValueBitPacked(N) (N=7 -> 0x39). We see a single 0x81. And what is the 00 00 00 00 01 written AFTER the table, before ConfigHash? Give the exact field sequence of the forbidden segment as this build serializes it.

Q3 — ConnectionApprovedMessage serialization (the thing we must send). Full Serialize (server→client) for this build. We need every field and its encoding, in order: the version header (same preamble as Q2?), OwnerClientId, NetworkTick, any ConnectedClientIds, sceneObjectCount, and anything the fork added. Also the message type index the client expects for ConnectionApproved in the pre-approval (local) space, and how Deserialize reads it — because the client's Deserialize is what accepts or rejects our packet.

Q4 — how the approval reaches Tachyon. Earlier finding: NetworkManager.RecRoom_ConnectionInfo is a fork addition, hypothesised to carry the approval JSON (PMFAAFNFMAL, the "Carrot" / SessionKeyB blob). Confirm whether the approval JSON rides inside ConnectionApprovedMessage's ConnectionData, in RecRoom_ConnectionInfo, or elsewhere — i.e. where in the server→client bytes we must place {"DR":"Carrot",...}.

Q5 — key-null path for the session. Does the client require a non-null SessionKeyB to proceed, or will it accept an approval with "Carrot" and no key and stay plaintext (marker 0x11)? This decides whether we can defer the RSA keypair patch and run voice unencrypted.

Output

Decompiled Serialize/Deserialize bodies where possible, else exact field order + encodings

  • byte offsets. Distinguish read from inferred. If a field genuinely is not present, say so — do not fill it from stock NGO; the fork has already diverged in three places above.