# Request to the client-side agent (dumped game image) ## Context We have a third-party NGO/LiteNetLib server that the game connects to. The LiteNetLib transport handshake **fails in a specific, reproducible way**, and we've narrowed it to one thing: the client silently ignores our `ConnectAccept`. Evidence, so you know what's already excluded: - The client's `ConnectRequest` is byte-identical in shape to a stock LiteNetLib 1.x request: `protocolId = 13`, `HeaderSize = 18`, `addrSize = 16`, plus **3 bytes of connect data `01 00 11`**. So the client's outbound path is stock or near-stock. - Our server replies with a stock 15-byte `ConnectAccept` whose `connectionTime` matches. - The client ignores it and resends `ConnectRequest` ~11 times, all with the *same* `connectionTime` — i.e. one attempt being retried, never leaving `ConnectionState.Outgoing`. - Meanwhile our server thinks it is connected, pings, gets no Pong, and times out at 5s. - A synthetic stock-LiteNetLib-1.2.0 client, run from the *same machine and NAT* as the game, connects to the same server successfully. So this is not the network, not NAT, and not our server. It is client-side. - We reproduced the exact symptom against a stock client by making the accept the wrong length: `NetConnectAcceptPacket.FromData` begins `if (packet.Size != Size) return null`, which yields precisely this silent-ignore-and-retry behaviour. **Working hypothesis: the game's bundled LiteNetLib is forked and expects a `ConnectAccept` that differs from stock — most likely a different length.** We need the exact layout. The bundled LiteNetLib lives inside `Netcode.Transports.LiteNetLib.dll` (reported earlier as 116 types, "LiteNetLib itself bundled inside"). Third-party assemblies appear to have kept their real names, so these types should be findable by name rather than by obfuscated slug. ## Stock reference (LiteNetLib 1.2.0, MIT) — diff against this ```csharp internal sealed class NetConnectAcceptPacket { public const int Size = 15; public readonly long ConnectionTime; public readonly byte ConnectionNumber; public readonly int PeerId; public readonly bool PeerNetworkChanged; public static NetConnectAcceptPacket FromData(NetPacket packet) { if (packet.Size != Size) return null; long connectionId = BitConverter.ToInt64(packet.RawData, 1); byte connectionNumber = packet.RawData[9]; if (connectionNumber >= NetConstants.MaxConnectionNumber) return null; byte isReused = packet.RawData[10]; if (isReused > 1) return null; int peerId = BitConverter.ToInt32(packet.RawData, 11); if (peerId < 0) return null; return new NetConnectAcceptPacket(connectionId, connectionNumber, peerId, isReused == 1); } public static NetPacket Make(long connectTime, byte connectNum, int localPeerId) { var packet = new NetPacket(PacketProperty.ConnectAccept, 0); FastBitConverter.GetBytes(packet.RawData, 1, connectTime); packet.RawData[9] = connectNum; FastBitConverter.GetBytes(packet.RawData, 11, localPeerId); return packet; } } ``` Stock wire layout: `[0]` property (6) | `[1..8]` connectionTime | `[9]` connectionNumber | `[10]` isReused | `[11..14]` peerId. ## What we need **Q1 (highest value — this likely ends the investigation).** `LiteNetLib.NetConnectAcceptPacket` in the bundled copy: - the value of `Size`; - every field and its byte offset; - the full body of `FromData`, especially every early-return/rejection condition; - the full signature and body of `Make`. If `Size != 15`, tell us what the extra bytes are and where `Make` writes them. **Q2.** `LiteNetLib.NetConstants.ProtocolId`. We derived 13 from the wire; confirm from the image. Also `MaxConnectionNumber` if it differs from stock (4). **Q3.** Is `NetManager` constructed with a `PacketLayerBase` (a second constructor argument, or a non-null `_extraPacketLayer` field)? If yes, name the layer type and give `ExtraPacketSizeForLayer` plus the bodies of `ProcessInboundPacket` / `ProcessOutBoundPacket`. We ruled a layer out by inference from wire bytes; we want it confirmed from the image. **Q4.** `LiteNetLib.NetConnectRequestPacket.HeaderSize`, and **what writes the 3 connect-data bytes `01 00 11`**. Find the `NetManager.Connect(...)` call site — expected to be in the NGO voice session wrapper (`BAOFAOBLAMJ`) or in `LiteNetLibTransport` — and show how that `NetDataWriter` is populated. If those bytes are a version/handshake field, we need to know whether the server is expected to validate or echo them. **Q5 (only if cheap).** In `NetPeer`, the method handling an inbound accept (stock name `ProcessConnectAccept`): list every condition that returns false. That is the definitive list of reasons our accept could be dropped. ## Output format For each question: the decompiled source if you have it, otherwise field offsets and constant values. Give **byte offsets and exact integer values** rather than prose. Where the fork matches stock, just say "matches stock" — we only need the diff. If something genuinely isn't present in the image, say so explicitly. Do not infer the layout from stock LiteNetLib or reason about what it "should" be — we already have stock, and a plausible-but-wrong layout costs us more than a "not found".