# The per-system visibility record: `NVE`, `ltis`, and the explored mask Lane E3, 2026-09-08. Target: the 32 diverging `nve` leaves on the reference pair. Status of each claim is marked **[V] instruction-verified**, **[D] decompiler-only**, or **[H] hypothesis** (rule 6). --- ## 1. What the record is `Sys.NVE` is a `std::map` at `ServerSystem+0x284/+0x288`. It is **"what this player last saw at this system"**: the turn of the sighting and the random-encounter type that was there. Its sibling `NVO` is the same idea for *ownership* (`TShn` = turn, `OID` = owner as last seen), and `NVs`/`PlayerView` is the same idea for the *colony's numbers*. Three maps, one concept, one key. Node layout, reconciling `struct-recovery.md` §1.2 with the accessors below: MSVC `_Tree` nodes put `_Myval` at `+0xc`, so the key (player index) is `node+0xc` and the **8-byte value is `node+0x10`**: | value offset | wire name | type | meaning | |---|---|---|---| | +0x0 | *(not serialised)* | int16 | the turn the record was last **touched** | | +0x2 | `ETS` | int16 | the turn the **sighting** was made | | +0x4 | `Eid` | int32 | the random-encounter type id present, `-1` for none | The key is written to the wire as `EPid`, the player **handle id** (16, 32, 528 …), not the index. That reconciles the isnil byte at `+0x19` with an 8-byte value. **[V]** (arithmetic over the accessors in §2, each of which reads a named offset). ## 2. Every writer and reader of the map | addr | what it is | body | |---|---|---| | `0x00756300` | **`ServerSystem::RecordObservation(p, encId)`** — the primary writer | `f = (int16)this->owner(+0x10)->Frame(+0x8); rec = NVE[p->PlyrIdx(+0x28)]; rec[0] = (f<<16)|f; rec[1] = encId;` **[D]** | | `0x007536a0` | `ServerSystem::CopyObservationTo(p, srcRec)` — intel sharing | `rec = NVE[p->PlyrIdx]; rec.touched = (int16)Frame; rec.ETS = src.ETS; rec.Eid = src.Eid;` — the *sighting* stamp is copied, only the touch stamp is refreshed **[D]** | | `0x00754d90` | `ShareObservation(systhis, from, to)` | `a = Find(from); b = Find(to); if (a && (!b || b->ETS < a->ETS)) CopyObservationTo(to, a);` — newer sighting wins **[D]** | | `0x0074d360` | `ServerSystem::FindObservation(p)` | map find on `p->PlyrIdx`, returns `node+0x10` or null **[D]** | | `0x0074f830` | `ServerSystem::LastSeenEncounterId(p)` | map find, returns `node+0x14` (`Eid`) or `-1` **[D]** | | `0x00752730` | the map's `operator[]` | 3 callers only: the two writers above and `ServerSystem::Read` **[V]** (cross-reference query) | **There is no eraser.** `operator[]` has exactly three callers and none of them removes an entry, so once a player has an entry at a system it keeps it for the rest of the game. That is what makes the record a *memory* rather than a *state*. ## 3. Where the primary writer is called from Tail phase 17, `FUN_007cf560` (the "rebuild every `PlayerView`" phase). Disassembled at `0x007cf7a7..0x007cf7ce` — **[V]**, byte-decoded, not from the decompiler: ``` mov edx,[esi+0x44] ; systems.begin mov edi,[edx+ebx*4] ; edi = sys mov eax,[ebp-0x14] ; player push eax mov ecx,edi ; this = sys call 0x00743fb0 ; ServerSystem::IsVisibleTo(sys, player) test al,al jz skip mov eax,[edi+0x184] ; the encounter type id parked on the system mov ecx,[ebp-0x14] ; player push eax ; arg: encId push ecx ; arg: player mov ecx,edi ; this = sys call 0x00756300 ; RecordObservation ``` Outer loop is over **players**, inner over **systems**. The gate is one instruction: * `FUN_00743fb0(sys, p)` = `(sys->AFlags(+0xd4) >> p->PlyrIdx(+0x28)) & 1` **[V]**. `AFlags` — not `VFlags`, not `EFlags`. `combat-retreat-pipeline.md` §5 already had `AFlags` as the *derived, non-sticky* union `FFlags | GFlags | isOwner`, recomputed on every fleet arrival and departure. So the record is refreshed exactly while the player still has something at the system, and freezes the moment the last fleet leaves and the player does not own it. ## 4. `Eid`'s source `sys->+0x184` is a `StarSystem` member initialised to `-1` in the constructor (`FUN_00752ea0`, `param_1[0x61] = 0xffffffff`) **[D]**, and set by `FUN_007887c0(sys, encId)` — the encounter-placement routine, which refuses unless the system is unowned, planetless and still `-1` **[D]**. **It is not on the wire.** It is fixed for the life of the game, so the only observable it can be recovered from is the encounter fleet sitting at the system, whose `FtEnc` carries the same id. Across all 11 saves, `Eid == FtEnc` of the fleet at the system, for every one of the six encounter fleets in the corpus, and `-1` everywhere else. The `Defenses` fleet at Koa'Vo has `FtEnc == 0` and `FtFlg == 0x400`, and its system's `Eid` is `-1`; every real encounter fleet has `FtEnc > 0` and `FtFlg & 0x10`. **Nothing in the corpus separates "`FtEnc > 0`" from "`FtFlg & 0x10`" as the test, and nothing separates either from the true rule, which reads a field that is not saved at all.** Labelled **[H]**; §8 names the workload. ## 5. `ltis` — a second writer, in the other driver Driver phase 29, `FUN_00743ec0` (41 B, whole body) **[D]**: ```c if (this->AFlags(+0xd4) != 0) this->ltis(+0x2c8) = this->owner(+0x10)->Frame(+0x8); ``` `ltis` is the system-level twin of `ETS`: same clock, same gate, but the gate is `AFlags != 0` for **any** player rather than for a particular one, and the value is a full int rather than an int16. `board.md` records `TShn`/`ltis` as *"NOTHING NAMES THEIR WRITER"* — this names `ltis`'s. `TShn` is still unnamed and is **not** the same rule (§7). ## 6. The explored mask, and the one thing `OnExploredChanged` does Tail phase 21, `FUN_007a3c60`, per system × per player **[D]**: ```c if (IsVisibleTo(sys, p) && !IsExploredBy(sys, p)) { SetExploredBy(sys, p, true); // 0x007438b0: EFlags |= 1 << PlyrIdx NodeGraphRegister(...); // 0x006e4620 PostEvent("EVENT_FLEET_EXPLORED", ...); } ``` so **`EFlags |= AFlags`**, once per turn, in the tail. `SetExploredBy`'s trailing `vft[0x1c](p, wasSet, on)` — the indirect edge `lane-b5.json` flagged as unresolved — resolves through `Game::ServerSystem`'s primary vftable `0x00a2044c` slot 7 to **`0x007480b0`** (`tools/vtable_map.py`). Its whole body **[D]**: ```c if (!wasSet && on && server->Frame(+0x8) > 1) { FUN_00747a20(p->PlyrIdx, 1); if (p->PlyrIdx < 15) this->+0x2a4 |= 1 << p->PlyrIdx; } ``` `+0x2a4` is past `NVs` and is not in the serialised table: a runtime "newly explored this turn" mask, not save state. **The edge is resolved and it writes nothing that reaches the wire.** Note the `Frame > 1` guard — turn 1 is special-cased throughout this subsystem. ## 7. What this does NOT explain > **CLOSED by lane W3, 2026-09-08 — see `nvo-tshn-visible-owner.md`.** `NVO.TShn`'s writer is > `ServerSystem::SetVisibleOwner 0x0075b880` (trapped live on Spica), reached from the sensor phase > via `RefreshVisibleOwnerIfKnown 0x0075bd70`. The looser gate this section correctly predicted is > `ServerSystem::IsKnownTo 0x00746390` = `IsVisibleTo` **or** (2-bit sensor contact at > `ServerSystem+0x24`, which is *not serialised*, **and** the player has researched `CCC_AdvSens`). > Spica's entry belongs to a Species-4 NPC pseudo-player and every one of those starts the game with > `CCC_AdvSens`; Bismol's belongs to a real empire and none of those has it in any corpus save. > The resulting model predicts all 158 `NVO` records across the 11 saves, including Bismol. > The section below stands as written — it is what made the case checkable. `NVO.TShn` moves on **more** systems than `NVE.ETS` does. On `turn1 -> turn2`, Spica has `AFlags == 0`, no `NVE` entry and no `ltis` move — and its `TShn` still goes 1 -> 2. So `NVO` has a looser gate than `AFlags`, and it is **not** `VFlags`, `EFlags` or `FFlags` either (all zero at Spica). `TShn`'s writer remains unnamed. Conversely `zuul-turn23-fleet23.sav`'s Bismol has `AFlags == 0` and freezes `TShn`, `ETS` **and** `ltis` at 22 together — so on that save the two rules agree. Whatever `NVO`'s gate is, it admits Spica and excludes Bismol. `rcex` (int64) moves `0 -> 65536` on the same six systems on `turn1 -> turn2` and back `65536 -> 0` on `turn2 -> turn3`. `65536 = 1 << 16`, and Koa'Vo takes `1 << 28`. It is a per-system word that toggles with the encounter/visibility cluster and it is **not** modelled here. ## 8. Workloads that would settle the open items | open item | workload | |---|---| | whether `Eid` is `sys->+0x184` (fixed at generation) or re-read from the fleet each turn | a save where an encounter fleet is **destroyed** at a system that stays visible: `+0x184` keeps the id, a fleet-derived rule drops to `-1` | | whether the `Eid` source test is `FtEnc > 0` or `FtFlg & 0x10` | any save with a fleet carrying `FtEnc > 0` and `FtFlg & 0x10` clear, or the converse | | `NVO.TShn`'s gate | a watchpoint on `ServerSystem+0x274`'s map during one turn — rule 18; Spica vs Bismol is already the discriminating pair | | more than one player observing one system | every save in the corpus has **single-bit** `AFlags`. A two-empire contact save would exercise the map ordering (ascending `PlyrIdx`) and the `EPid` handle lookup for a second entry | | `ShareObservation` (alliance intel) | no save has two players in an alliance; the newer-sighting-wins rule has never executed | | `rcex` | unassigned |