re: the two enumerations are different structures; only the vtable scan is complete

Same moment, same stage: the INST_VTABLE scan sees 116 objects across 14 types;
the moving+0x130 method sees 30 across 4.  Every capital ship, station, missile and
the objective-critical Acropolis reads ZERO in the +0x130 method.

The absence is structural, not a filter artifact -- both obvious explanations were
tested and failed.  Dropping the speed floor to 0 raised the count 30 -> 59 and
recovered the _Player but still only 4 types; scanning the WHOLE map with no floor
gives 181824 movers and still 4 types.  And 0/116 vtable instances lie inside the
window where the +0x130 blocks are found (instances 0xbc372cc0-0xbc9bc720, window
0xbd000000-0xbe000000), independently confirming these are separate allocations.

Consequence: an autopilot that must protect the Acropolis cannot find it via the
+0x130 method at all.

Also found: LOAD GAME -> slot 01 no longer restores the S01 training area but a
Stage-02-style escort mission (Acropolis, SchlosBase, cruisers, frigates).  Slot 01
is the AUTO-SAVE, so the restored mission moves as the save is written -- which
invalidates the earlier "101 vs 42" comparison outright, since those came from
different stages.

Corrects the previous note: entities2 prints its count AFTER dedup, so the 101 was
already deduplicated; the gap was the stage change, not duplication.
This commit is contained in:
Sylpheed RE agent
2026-08-26 22:24:35 +00:00
parent dddff2007a
commit a21927ab01
2 changed files with 98 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 KiB

View File

@@ -0,0 +1,98 @@
# ✅ The two enumerations are two different structures — and only one is complete
Measured 2026-08-26, live flight HUD, movers precondition checked (18 002).
## The stage changed between runs — earlier cross-run comparisons were invalid
`LOAD GAME → slot 01` did **not** land on the same mission it did earlier today.
This run's definition table has **14** entries and no asteroids at all:
```
UN_bf001_TCAF_SchlosBase UN_f101_TCAF_Acropolis UN_f201_TCAF_Tanker
UN_e105_ADAN_Cruiser UN_e106_ADAN_Destroyer UN_e108_ADAN_ASFrigate
UN_e201_ADAN_ISCMissile UN_f003_TCAF_ArrowHead UN_f105/f106 TCAF ...
```
That is the **Stage 02 escort mission** (the `ACROPOLIS` of
[`autopilot-memory-driven.md`](autopilot-memory-driven.md)), not the S01 training
area with `UN_S01_Asteroid_cmesh_*` that earlier runs loaded. Slot 01 is the
**auto-save**, so the mission it restores moves as the save is written.
**This invalidates the earlier "101 vs 42" comparison outright** — those two
numbers came from *different stages*, not merely different moments. And it means
no runtime roster is reproducible across runs unless the stage is verified first.
## Same moment, same stage, both methods
| type | `INST_VTABLE` scan | moving + `+0x130` |
|---|---|---|
| `UN_e007_ADAN_Turret` | 21 | 14 |
| `UN_e010_ADAN_Attacker_S` | 9 | 8 |
| `UN_f001_TCAF_DeltaSaber_T` | 7 | 7 |
| `UN_f001_TCAF_DeltaSaber_T_Player` | 1 | 1 |
| `UN_e106_ADAN_Destroyer` | 19 | **0** |
| `UN_f106_TCAF_Destroyer` | 14 | **0** |
| `UN_f105_TCAF_Cruiser` | 11 | **0** |
| `UN_e105_ADAN_Cruiser` | 7 | **0** |
| `UN_e108_ADAN_ASFrigate` | 9 | **0** |
| `UN_e201_ADAN_ISCMissile` | 9 | **0** |
| `UN_f003_TCAF_ArrowHead` | 6 | **0** |
| `UN_f101_TCAF_Acropolis` / `bf001_SchlosBase` / `f201_Tanker` | 1 each | **0** |
| **total** | **116** | **30** |
## ✅ The absence is structural, not a filter artifact
The obvious explanations were tested and **both fail**:
* **Speed band.** `moving()` filters displacement to `lo=1.0 … hi=4000.0` per
0.5 s, which would exclude a slow capital ship. Dropping the floor to **0** and
the ceiling to 1e12 raised the count 30 → 59 and recovered the `_Player` — but
still **only 4 types**. Capital ships never appear.
* **Address window.** `moving()` defaults to `0xBD0000000xBE000000`. Scanning
the **whole map** with no speed floor gives 181 824 movers and **still 4 types**.
So the ten missing types do not have a definition pointer at `position + 0x130`
anywhere in memory that a moving-triple scan can reach. **The `+0x130` block is
not the universal entity record — it belongs to a subset.**
🟡 The subset is *nearly* "small craft": every type it does cover is a fighter,
attacker or turret. But `UN_f003_TCAF_ArrowHead` is a TCAF fighter and reads
**0**, so "small craft" is not the rule. ❔ What actually distinguishes them is
not established — plausibly an active-flight-model flag, with parked wingmen and
capital ships handled by a different update path.
## ✅ They live in different regions, which is why they are different structures
```
vtable instances : 0xbc372cc0 .. 0xbc9bc720 (116)
ENT_VA window : 0xbd000000 .. 0xbe000000
instances inside that window : 0/116
```
**Zero** of the 116 vtable objects lie in the window where the `+0x130` position
blocks are found. This is independent confirmation of
[the earlier refutation](entity-position-anchor-refuted.md): the vtable object and
the position block are separate allocations in separate regions, not two views of
one record.
## Which list to use
* **Completeness** — the `INST_VTABLE` scan. It sees all 14 types including
stations, capital ships and the objective-critical `Acropolis`.
* **Motion state** — the `+0x130` blocks. They carry position, speed, orientation
(`pos0x70`, stride 16) and hull (`pos+0x154`), but only for ~4 types.
* An autopilot that must protect the `Acropolis` **cannot** locate it through the
`+0x130` method at all. That is a concrete limit on the existing approach.
## Also confirmed
The exact **2× duplication** reappeared: def-pointer hits come in pairs `0x1000`
apart with byte-identical positions (`0x011db3bd80` / `0x011db3cd80`, both
`(-1654.7, -3992.7, 458.2)`, both moving 424.482). ❌ It is **not** page aliasing
in `gmem.MAP` terms — aliased VAs share one *file offset*, and these differ by
`0x1000` in the offset itself, so they are genuinely two copies.
**Correction to [the previous note](entity-count-discrepancy-resolved.md):** it
said "at least one of the counts was un-deduplicated". That is wrong —
`entities2.py` prints its count *after* `ents = list(uniq.values())`, so the 101
was already deduplicated. The gap was the stage change, not duplication.