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:
@@ -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
|
||||
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
|
||||
- Exact TCP listen port(s) — not in the source grep; get from NETNUB.EXE
|
||||
or a capture at milestone 3.
|
||||
|
||||
Binary file not shown.
@@ -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}")
|
||||
Reference in New Issue
Block a user