Reads the live world out of guest RAM and drives the pad from it. Working: loop-rate memory reads, whole-RAM float scanning with numpy (1270 orthonormal 3x3 blocks in 6.2 s), entity enumeration by unit type (116 live instances in Stage 02), pad control written straight into the vgamepad FIFO (the CLI spawns a process per command and its tap/hold sleep inside the server, so neither is usable in a control loop), unattended mission entry, and the Hangar loadout -- the "Recommended" control is AUTO SELECT, which at 5 % progress is a no-op because only two weapons are developed and both are already mounted. Not working, and the reason the craft is not yet flown: the class 0x820af030 is NOT the live entity. It has one object per spawned thing and carries the unit-ID string, which is why it looked like the entity list, but every one of its 384 words is constant across a 29 s in-flight capture. No transform lives in it or one pointer hop from it. Input correlation (hard left yaw vs hard right, looking for a turn axis that reverses) does find self-like objects at cos = -0.99, but they cluster in what looks like a camera volume rather than the craft, and with no definition pointer near them the trick of learning one entity's layout and applying it to the rest has nothing to anchor on -- so the 33418 moving triples in a firefight cannot be split into enemies, friendlies and bullets, and there is nothing to aim at. Two dead ends are recorded so they are not repeated: RT is not the throttle (the two-state speed scan therefore found nothing), and comparing orientation matrices 2 s apart is outside the small-angle regime, which is what produced "angular velocities" of 30000. Also corrects the claim in unit-struct-runtime.md that 0x820af030 holds live state. The definition class 0x820af844 and every value derived from it are unaffected. autopilot2.py (a PD controller using body angular velocity from consecutive rotation matrices) is committed but has never had a valid config to run against, and is marked as untested.
6.5 KiB
Memory-driven autopilot — build log and current state
Status: 🟡 PARTIAL — infrastructure works, the craft is not yet flown. Started 2026-07-29. This is the honest state, not a plan: what is proven, what is not, and the one thing that most cheaply unblocks the rest.
Goal
Fly and fight a mission by reading the game's own world state out of guest RAM and driving the pad from it — the RE payoff being an oracle for the reimplementation's flight model and AI, and a way to reach missions the save cannot otherwise reach (unit coverage for unit-struct-runtime is capped at 21/110 because definitions load per stage).
What works (verified)
| Piece | Tool | Evidence |
|---|---|---|
| Live guest-RAM reads at loop rate | gworld.py |
/dev/shm file opened once, pread per tick; a whole-RAM scan is ~6 s, a targeted read is microseconds |
| Whole-RAM float scanning | numpy over SEEK_DATA extents |
1 270 orthonormal 3×3 blocks located in 6.2 s |
| Entity enumeration by unit type | gworld.py entities |
116 live instances in Stage 02, typed by unit ID, incl. exactly one …_Player |
| Pad control at loop rate | flight_probe.Pad |
writes command lines straight into the vgamepad FIFO; the vgamepad CLI spawns a process per command and its tap/hold sleep inside the server, so neither is usable in a control loop |
| Unattended mission entry | launch_mission.sh |
title → LOAD GAME → slot 01 → READY ROOM → TAKE OFF → in flight, repeatable |
| Hangar loadout | launch_mission.sh --hangar |
the "Recommended" control is AUTO SELECT — "Mount most suitable weapons", already the default cursor position. At 5 % progress it is a no-op: only two weapons are developed, and they are already mounted |
| Finding moving objects | findplayer.py |
position triples recovered from motion alone — straight-line, constant-speed filter over K whole-RAM samples |
What is NOT solved
Identifying our own craft, and typing the other entities. Both remain open, and the autopilot cannot work without them.
- The
0x820af030class is not the live entity. It has one object per spawned thing and carries the unit-ID string, so it looked like the entity list — but over a 29 s in-flight capture all 384 words of it are constant (whatchanges.py). It is a static spawn record. The earlier claim inunit-struct-runtime.mdthat this class is "the spawned entity instance — live state" is wrong in the second half: it is per-spawn, but it is not live state. The parts of that document that depend on the definition class0x820af844are unaffected. - No transform in or one hop from that object: no orthonormal 3×3, and no
unit quaternion, within
0x2000of it or behind any of its 121 pointers. - Input correlation finds a self-object, but not obviously the craft.
Holding hard-left then hard-right yaw and looking for a position whose turn
axis reverses (
findself.py,selfstate.py) gives clean hits (cos ≈ −0.99), but they cluster at0x40009xxxin what looks like an 8-corner box with ±45 000 coordinates — a camera/skybox volume that follows the player, not the craft. Its speed (≈359/s) is suspiciously close to the HUD's 350, which supports "follows the player" but is not proof of identity. - Entity typing is unavailable: no definition pointer within ±0x800 of the self-position, so the trick of learning one object's layout and applying it to all the others has nothing to anchor on. Without typing, the 33 418 moving triples in a firefight cannot be separated into enemies, friendlies and bullets, so there is nothing to aim at.
Dead ends, recorded so they are not re-run
- Speed-scan for the player object (
findspeed.py, the classic two-state value scan: coast → boost → coast). Sound method, butRTis not the throttle — 5 864 floats matched the cruise speed and none rose. The control actually bound to acceleration was never established, and the run that would have established it ended in GAME OVER. - Comparing orientation matrices 2 s apart. At a real turn rate that is far
outside the small-angle regime, so the skew part of
A·Bᵀis not the rotation vector and the "angular velocity" comes out as ~30 000. Sample incrementally (6 Hz) and re-check orthonormality on every read — blocks found by a scan get overwritten between the scan and the read.
The next step that unblocks the most
Find the game's own target pointer instead of typing entities ourselves.
The HUD has a lock-on system (a TARGET marker and a target-cycle button), so
a global almost certainly holds a pointer to the currently-targeted entity.
Reading that gives an enemy's live object address directly — which yields both
target selection and the entity layout (position offset within it), i.e. it
collapses problems 3 and 4 into one. It is also cheap to find: cycle the target
with the pad and watch which pointer-shaped global changes in step.
Operational notes
- An unattended craft dies — the ship flies straight into a firefight, and two long scans were invalidated by a GAME OVER mid-run. Any scan longer than ~30 s needs either a survivable holding pattern or a fresh mission.
Xvfband the emulator die on their own every few minutes here, cleanly (exit 0), cause unidentified. Everything that must not be interrupted is run as one background task that starts the display, the emulator, the navigation and the measurement together, so nothing has to survive between tool calls.pgrepcannot be used for liveness in this container: PID 1 issleep infinityand never reaps, so dead processes linger as<defunct>and still match by name. Useps -o stat=and skipZ, orxdpyinfofor X.- numpy is not installed system-wide;
pip install --break-system-packages numpyputs it in/sylph-home/.local, which is only onsys.pathwhenHOME=/sylph-home. Scripts run withHOME=/sylph-home/reneedPYTHONPATH=/sylph-home/.local/lib/python3.12/site-packages.
Files
gworld.py (live reader + entity list) · flight_probe.py (scripted inputs +
sampling, and the Pad FIFO client) · flight_analyze.py · whatchanges.py
(encoding-agnostic "which words are live") · findplayer.py · findself.py ·
findrot_global.py · findspeed.py · liveents.py · selfstate.py (the
whole chain → JSON) · autopilot2.py (PD controller; untested — it has never
had a valid config to run against) · launch_mission.sh.