From b99bd6574523d0b0126061a9ee94c0a4a35ebba3 Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Wed, 26 Aug 2026 22:24:35 +0000 Subject: [PATCH] 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. --- docs/re/two-entity-enumerations.md | 98 ++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 docs/re/two-entity-enumerations.md diff --git a/docs/re/two-entity-enumerations.md b/docs/re/two-entity-enumerations.md new file mode 100644 index 00000000..27113b29 --- /dev/null +++ b/docs/re/two-entity-enumerations.md @@ -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 `0xBD000000–0xBE000000`. 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 + (`pos−0x70`, 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.