NET milestone: real console->pod mission egg over the wire (+ mission-load crash)

Captured a real mission egg delivered from the SheepShaver console to the
DOSBox pod on TCP 1501, no -egg bypass: 8x ReceiveEggFileMessage chunks,
7514 bytes, reassembled byte-perfect (declared notationFileLength=7514).
The egg is the operator's own mission (BattleTech / cavern / night /
freeforall, pilot "cyd", vehicle madcat). This closes the networking
workstream end to end.

- net-tools/decode_egg.py: reassemble an egg from a captured console->pod
  TCP payload hex dump (skips interleaved StateQuery polls).
- net-tools/captured_cavern_mission.egg: the delivered egg.
- NET-NOTES.md: milestone writeup + the pod's mission-LOAD crash. The pod
  faults loading the mission (page fault, wild ptr 0xFF008B5B, exception
  000E at game 00FF:219D, NetNub Munga exit 14) -- a game-side bug in the
  mission-load path, separate from networking. Leading suspect: RIO was
  disabled for this run and the player vehicle/control setup derefs null.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-05 17:41:34 -05:00
co-authored by Claude Opus 4.8
parent bda2f7b8b3
commit e12bfe1d3a
3 changed files with 71 additions and 0 deletions
+32
View File
@@ -340,6 +340,38 @@ with a full RIO+sound boot (`net_full.conf`) so the pod presents its normal
state (applicationState may change when fully booted). Need the console UI state (applicationState may change when fully booted). Need the console UI
readout to know what state it wants before it will send the mission egg. readout to know what state it wants before it will send the mission egg.
## EGG DELIVERED OVER THE WIRE (2026-07-05) — headline goal achieved
The real SheepShaver console sent a real mission egg to the real DOSBox pod
over the emulated network, captured + reassembled byte-perfect:
- Console -> pod, TCP 1501, **8x ReceiveEggFileMessage chunks** (1040-B
NetworkPackets each = 16 hdr + 1024 msg), **7514 bytes total**, declared
notationFileLength=7514 (exact match). Reassembled with
`net-tools/decode_egg.py`; saved `net-tools/captured_cavern_mission.egg`.
- The egg is a text NOTATION file (same format as Console.ini). Contents =
the operator's actual mission: `[mission] adventure=BattleTech map=cavern
scenario=freeforall time=night weather=clear temperature=27 length=120`;
`[pilots] pilot=200.0.0.113`; `[200.0.0.113] name=cyd vehicle=madcat
experience=veteran badge=VGL dropzone=one color=Grey advancedDamage=1
role=Role::Default` + embedded pilot badge bitmaps + ordinal fonts.
- Confirms the whole decoded protocol end to end: StateQuery/StateResponse
poll, then egg chunks, pod ACKs, no `-egg` bypass. **Networking DONE.**
**BUT the pod CRASHES loading the mission** (game-side, NOT network):
`nn.log` shows `Reference to a page you don't own / PF cr2=FF008B5B /
Unhandled exception 000E at 00FF:219D ErrCode 0004 / NETNUB Munga exited
code 14` -- a page fault (wild pointer 0xFF008B5B) in the mission-load path
right after the egg arrives. Suspects: (a) RIO was DISABLED for this run
(net_pcap.conf) and the mission sets up the player's vehicle (madcat) /
controls, which may deref a null RIO/control struct; (b) missing cavern
content or a pilot-config/badge path bug. Note: cavern/night via `-egg
test.egg` with RIO ENABLED rendered fine earlier this session, so RIO-off
is the leading suspect. Real pods run RIO-on headless (no focus stealing),
so this may not reproduce there. NEXT: retry with RIO enabled + DOSBox
`priority=highest,highest` (so clicking the console can't starve the RIO's
ACK deadline during load); if it still faults, disassemble around game
code 00FF:219D. This joins the Division-renderer/crash workstream.
## Open questions / notes ## Open questions / notes
- Exact TCP listen port(s) — not in the source grep; get from NETNUB.EXE - Exact TCP listen port(s) — not in the source grep; get from NETNUB.EXE
or a capture at milestone 3. or a capture at milestone 3.
Binary file not shown.
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env python3
"""Reassemble a mission egg from a captured console->pod TCP stream.
Input: a hex dump of the concatenated console->pod TCP payloads, e.g.
tshark -r cap.pcapng -Y 'ip.src==<console> && tcp.len>0' \
-T fields -e tcp.payload | tr -d '\n' > c2p.hex
The console delivers the mission as a series of ReceiveEggFileMessage
packets (decoded from CODE/RP/MUNGA/NETWORK.HPP, wire is little-endian):
NetworkPacketHeader (16B) + Receiver::Message hdr (12B: messageLength@+16,
messageID@+20, flags@+24) + [egg body when messageLength==1024:
sequenceNumber, notationFileLength, thisMessageLength, notationData[1000]].
StateQuery packets (messageLength=16) are interleaved and skipped.
Usage: python decode_egg.py c2p.hex out.egg
"""
import sys, struct
data = bytes.fromhex(open(sys.argv[1]).read())
out = sys.argv[2] if len(sys.argv) > 2 else "reassembled.egg"
i, chunks, total = 0, {}, None
while i + 28 <= len(data):
mlen, mid = struct.unpack("<ii", data[i+16:i+24])
if mlen < 12 or mlen > 4000:
break
pkt = data[i:i+16+mlen]
if len(pkt) < 16 + mlen:
break
if mlen == 1024: # ReceiveEggFileMessage
seq, flen, tlen = struct.unpack("<iii", pkt[28:40])
chunks[seq] = pkt[40:40+tlen]
total = flen
i += 16 + mlen
egg = b"".join(chunks[k] for k in sorted(chunks))
open(out, "wb").write(egg)
print(f"chunks={len(chunks)} reassembled={len(egg)}B declared={total} "
f"complete={total is not None and len(egg) >= total} -> {out}")