W3: NVO.TShn's writer trapped live, its gate named, and 158/158 on the corpus
TShn is refreshed to Frame for every (system, player) that satisfies ServerSystem::IsKnownTo 0x00746390 -- IsVisibleTo OR (2-bit sensor contact at ServerSystem+0x24 AND CCC_AdvSens). Not AFlags alone, which is why lane E3 could not fit it. The writer chain was confirmed live, frame by frame, from a hardware watchpoint on Spica's NVO node; the refresh runs twice per End Turn (driver phase 24 and combat-done phase 25). Also: the trade and spy containers read out of a live game for the first time (both empty, zero traps -- the workload confirmation two lanes lacked); Player.Status's predicate named as ReqCL != 0 && Elim == 0, with Species != 4 shown to be a corpus coincidence; and all ten command ModCount handlers named, two of them only reachable on a turn-1 workload. Corrects objects/layouts.md's ServerSystem PID@0x274 row and closes system-visibility-record.md 7. Runs: build w3tshn-bb81d3d-20260908T2007Z, configs shim.cfg.w3tshn / w3mod / w3control. Rule 19 control passed -- the armed run reproduced the determinism oracle byte for byte.
This commit is contained in:
parent
4ce7a1e0a7
commit
471d6cb973
8 changed files with 811 additions and 0 deletions
197
findings/control-flow/w3-containers-status-handlers.md
Normal file
197
findings/control-flow/w3-containers-status-handlers.md
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
# The trade/spy containers measured, the `Player.Status` predicate named, and the last four `ModCount` handlers
|
||||
|
||||
- **Type:** control-flow (live measurement + instruction stream)
|
||||
- **Status:** **verified**, with one part explicitly labelled as an absence rather than a result
|
||||
- **Confidence:** high. The container counts are a live reading; the three predicates are
|
||||
byte-decoded, not inferred.
|
||||
- **Owner / date:** lane W3 · 2026-09-08
|
||||
- **Companion to:** `nvo-tshn-visible-owner.md` (same run, same build) and
|
||||
`watchpoints-modcount-status.md` (lane W2, whose §5 and §8 this closes)
|
||||
|
||||
---
|
||||
|
||||
## 1. The trade-route and spy-program containers — measured, and both empty
|
||||
|
||||
Two lanes have failed to build a trade/spy workload, and lane W2 left the right instrument for the
|
||||
next attempt: both containers are one add from the arming point. This lane armed them.
|
||||
|
||||
**At `ApplyAllTurnCommands` entry on `ref-turn2.sav`, live:**
|
||||
|
||||
```
|
||||
watch: trade routes -- manager=0x0e4cdd38 (S+4+0x154) vector@mgr+0x3c
|
||||
first=0x00000000 last=0x00000000 end=0x00000000 count=0
|
||||
watch: spy programs -- manager=0x0e512f68 (S+4+0x158) vector@mgr+0x10
|
||||
first=0x00000000 last=0x00000000 end=0x00000000 count=0
|
||||
```
|
||||
|
||||
Both managers exist and are non-null; both vectors are **entirely unallocated** — `_Myfirst`,
|
||||
`_Mylast` and `_Myend` are all zero, which is a default-constructed `std::vector` that has never
|
||||
held an element, not an emptied one. Watchpoints on both `_Mylast` words took **zero traps** across
|
||||
the whole End Turn.
|
||||
|
||||
**Why that is worth writing down rather than a shrug.** The reason two lanes could not build this
|
||||
workload was never that the click path is long; it is that nobody could tell whether an attempt had
|
||||
*taken*. `ServerTradeManagerImpl` and `ServerSpyManager` are both reachable from the `S` frame the
|
||||
existing detour already holds, so the confirmation costs two lines of arithmetic and one log line.
|
||||
The next lane to spend twenty turns on trade stations can now check after every turn whether the
|
||||
container grew, instead of playing blind and reading the save afterwards.
|
||||
|
||||
**What this does not say (rule 20).** It does not say the containers are never filled. It says: on
|
||||
this save, in this turn, they were empty and nothing grew them. That is the *absence of the
|
||||
workload*, measured — which is exactly what a workload confirmation is for, and it is the first
|
||||
time either container has been read out of a live game.
|
||||
|
||||
**What is still missing for spies specifically.** The plan was that if the spy vector ever grew, the
|
||||
trapped return addresses would name the UI that creates a spy program — the question no lane has
|
||||
answered. It did not grow, so that question is **still open**, and the instrument that would answer
|
||||
it is now written and proven to arm. The next step is not more reading: it is one game in which a
|
||||
spy program is created by any means, with `watch.mode=tshn` armed.
|
||||
|
||||
This also bears on the RNG ledger. Lane V proved four tail callees can draw RNG and lane H's
|
||||
predecessor showed they are entered every turn and gated inside on empty containers (rule 20's
|
||||
worked example). This run confirms the containers are empty at the level of the vector's own
|
||||
pointers, so the gating is not a mis-read of a partially-initialised object. If either container
|
||||
ever fills, the ledger and the standalone's generator model both need extending — and the trigger
|
||||
for that work is now detectable.
|
||||
|
||||
## 2. `Player.Status` — the predicate is `ReqCL`, and `Species != 4` is a corpus coincidence
|
||||
|
||||
Lane W2 found the missing writer (`OnMessage + 0xa15`). What was open was *which players get
|
||||
which value*. Only two corpus saves carry a non-zero `Status`, and there `Species != 4`,
|
||||
`!(NPC && !RebAI)` and "has a badge string" all fit the same 8 observations — a 1-bit predicate
|
||||
against nine saves that never exercise it (rule 20). The code separates them.
|
||||
|
||||
**Answer:** at the moment the post-turn autosave is written,
|
||||
|
||||
> `Status == 4` ⟺ `ReqCL(+0xfd) != 0 && Elim(+0xf8) == 0`.
|
||||
> Every other player still carries the `0` that the previous `ResumePlaying` wrote.
|
||||
|
||||
**No species field is read on any path that writes or reads `Status`.** Neither is `NPC(+0xfb)` or
|
||||
`RebAI(+0xfc)`. The four writers, decoded:
|
||||
|
||||
| writer | value | gate |
|
||||
|---|---|---|
|
||||
| `MarkPlayerTurnEnded 0x00821a40`, store `0x00821a6b` | 4 | **none** — one call per `SNMPlayerEndTurn` message, the handle taken straight off the wire by `OnPlayerEndTurn 0x007d9af0` at `0x007d9b25`. The field tests at `0x007d9b5d`/`0x007d9ba2`/`0x007d9bb0` all follow the store and only decide whether to raise a "waiting on X" event |
|
||||
| `ProcessTurn + 0x5ca`, store `0x007dcc8a` | 1 | `if (p->IsAI(+0xf9) == 0 \|\| p->+0xfa != 0)`, decoded at `0x007dcc78`, over the encounter records at `S+0x1e8` (0x74-byte outer records, 0x44-byte inner entries whose first dword is the `ServerPlayer*`) |
|
||||
| `OnMessage + 0xa15`, store ends `0x00785055` | 4 | **none** — the `Game::SNMSetPlayerStatus` handler (vftable `0x00a2d718`, `{vptr, +4 player INDEX, +8 status}`) writes the value verbatim off the wire. The *sender* is `StrategyNetworkServer::Update 0x007cda40`, which computes `2*(flag != 0) + 2` at `0x007cdcf0` — 4 when the player has nothing pending, 2 when it does — after gating on `Status == 1` at `0x007cdc0b` |
|
||||
| `ResumePlaying + 0xb1`, store `0x007ddd41` | 0 | a loop over **all** players, `0x007ddd18..0x007ddd55`, gated only on `Elim(+0xf8) == 0` |
|
||||
|
||||
So `Status == 4` marks "this player's End Turn arrived", and the population is "players that own a
|
||||
`StrategyClient`". `ReqCL` is the field that decides that, in two places:
|
||||
|
||||
- `StrategyNetworkServer::SetupAIPlayers 0x007bc1c0`, gate at `0x007bc25c`: a player with
|
||||
`ReqCL == 0` is skipped before the AI list is built, so it never gets a client and can never be
|
||||
the subject of `MarkPlayerTurnEnded`.
|
||||
- **The game's own definition of "everybody has ended their turn"**,
|
||||
`StrategyNetworkServer::AllPlayersTurnEnded 0x007cfd00` (vftable slot 3), loop
|
||||
`0x007cffb7..0x007d0002` with the literal `4` loaded at `0x007cffa3`:
|
||||
```c
|
||||
for (p : players) {
|
||||
if (!p->ReqCL(+0xfd)) continue;
|
||||
if ( p->Elim (+0xf8)) continue;
|
||||
if ( p->Status(+0x164) != 4) return false;
|
||||
}
|
||||
```
|
||||
That is the predicate, written by the game, in one place.
|
||||
|
||||
**Why the four special players have `ReqCL == 0`:** `FUN_00797b80`, the script binding that creates
|
||||
them, calls the script `CreatePlayer 0x0057b6c0` with the literal name `"_NPC"` (`0x009f94e0`) and
|
||||
`NPC = 1`, then clears `[esi+0xfd]` explicitly at `0x00797bcb`.
|
||||
|
||||
**Why the corpus cannot see this:** `ReqCL` is serialised (tag `"ReqCL"`), and `ReqCL == (Species != 4)`
|
||||
holds in all 88 player records of all 11 saves. Two independent-looking checks with one hidden
|
||||
shared assumption is one check (rule 8) — here the assumption is that this game's four NPC slots are
|
||||
the only players with `ReqCL == 0`, which is true of the corpus and is not the rule.
|
||||
|
||||
This also **corrects `treaty-turn-stamp.md` §3 further than lane W2 did**: `MarkPlayerTurnEnded` is
|
||||
not "the only writer of `Player.Status = 4` in the image" by three more sites, all client-side and
|
||||
all acting on `client->+0x150`: `0x007773be` (writes 4 at player/client bind time), `CancelEndTurn +
|
||||
0x4a` `0x0078573a` and `0x007774ee` (both write 0).
|
||||
|
||||
## 3. The four unnamed `ModCount` handler EIPs — all four named
|
||||
|
||||
Lane W2 measured exactly 12 `ModCount` bumps per End Turn on `ref-turn2` and could attribute eight
|
||||
of the ten command handlers. The four it recorded as
|
||||
`StrategySim_ModCountBump_unresolved_*` are:
|
||||
|
||||
| trap EIP | function | name | evidence |
|
||||
|---|---|---|---|
|
||||
| `0x00821a87` (**×4/turn**) | `0x00821a80` | **`StrategyServer::OnCommand_SetResearchRate`** | `"OnCommand: Cannot set research rate for player %d(id). Player not found"` (`0xa2fbb0`), `"OnCommand: Research rate %f constrained to %f for %s."` (`0xa2fb78`); body sets `player->ResRate(+0xbc)` = the argument clamped to `[0,1]` by two `fcomp`s against `0x9e1ef0` / `0x9e1e68` |
|
||||
| `0x0086c3e9` | `0x0086c3e0` | **`StrategyServer::OnCommand_SetSystemRates`** | `"OnCommand: Unable to set system rates for %d(id). System not found."` (`0xa32448`), `"OnCommand: Player setting system rates for system he does not own."` (`0xa32400`) |
|
||||
| `0x0088bf01` | `0x0088bed0` | **`StrategyServer::OnCommand_TransferShips`** | `"OnCommand: Cannot transfer ships. Player %d(id) not found."` (`0xa33248`), `"TransferShips: Fleet flagged as empty, but still has ships."` |
|
||||
| `0x0084946e` | `0x00849460` | **`StrategyServer::OnCommand_AddBuildOrder`** | no string of its own; identified from its callers — the Build Screen `0x00656540` → `0x00762fd0` (`"BuildQueue::DoAddOrder(): Requested design %s was not found…"`, `"Build Screen: Cannot build %s."`) and the command replay at `0x0089009f`. Body: pick `target->+0xa4` or `+0x98` by `cmd[0]`, validate with `0x0083ce50`, then `BuildQueue::AddOrder 0x00845260` push_backs onto the list at `queue+0x10` |
|
||||
|
||||
**Why `SetResearchRate` fires four times.** The reference game has four players with a client
|
||||
(indices 0–3 — the two humans and the two rebel-AI empires) and four `_NPC` pseudo-players without
|
||||
one. Each client sends one research-rate command per turn. That is the same count, and the same
|
||||
population, as §2's `ReqCL` predicate — the two results are the same fact seen from two sides, and
|
||||
neither was derived from the other.
|
||||
|
||||
**W2's caveat survives and should be repeated: 12 is not a constant.** It is this save's command
|
||||
count. `turn1→turn2→turn3` moves `ModCount` 0 → 12 → 24 in this game; `human-turn2-orders.sav` sits
|
||||
at 25 on frame 2; the Zuul line runs 63 @ f5 → 210 @ f15 → 412 @ f23, i.e. ~15–25 per turn. What
|
||||
generalises is the structure — two driver bumps plus one per applied command — and now, with all ten
|
||||
handlers named, the per-command half of that is enumerable rather than approximate.
|
||||
|
||||
### 3.1 A second turn, a different command set, and two more handlers (lane AI4's probe)
|
||||
|
||||
Lane AI4 committed a prediction for `turn1-state.sav` before this run: again exactly 12 bumps, but
|
||||
out of a *different* command set, whose trap multiset **contains EIPs never seen on turn 2 and omits
|
||||
three that were**. That is a much sharper test than "does the number match 12" — a wrong model fails
|
||||
visibly instead of landing on 12 by coincidence. The same build, one config key changed
|
||||
(`watch.mode=tshn` → `modcount`), one save swapped.
|
||||
|
||||
**Result: 12 traps on `S+0x8`, values contiguous 1 → 12** (this save starts at `ModCount = 0`):
|
||||
|
||||
| Ghidra VA | × | handler | on turn 2? |
|
||||
|---|---|---|---|
|
||||
| `0x0086c3e9` | 1 | `OnCommand_SetSystemRates` | yes, ×1 |
|
||||
| `0x00821a87` | **4** | `OnCommand_SetResearchRate` | yes, ×4 |
|
||||
| **`0x0088fe0d`** | **3** | **`OnCommand_SetResearchProject`** — a *second* bump **inlined** into `ApplyTurnCommandBatch` at `+0x45a`, distinct from lane W2's inlined site `0x0089046c`. String `"OnCommand: Cannot set research project for player %d(id). Player not found."` (`0xa30ed0`) | **never seen** |
|
||||
| **`0x0088291d`** | **1** | **`StrategyServer::OnCommand_CreateDesign`** `0x00882910`. Strings `"OnCommand: Cannot create design for player %d(id), player does not exist."` (`0xa32c40`), `"OnCommand: Unable to create design. ID not specified."` (`0xa32c04`) | **never seen** |
|
||||
| `0x0084946e` | 1 | `OnCommand_AddBuildOrder` | yes, ×1 |
|
||||
| `0x007dc6f3` | 1 | `ProcessTurn`, first instruction (driver) | yes |
|
||||
| `0x007d92cd` | 1 | `OnAllCombatDone_Tail + 0x2a` (driver) | yes |
|
||||
|
||||
**Absent, and predicted absent:** `0x0089046f` (×2 on turn 2, the AI fleet order), `0x008657ad`
|
||||
(fleet route) and `0x0088bf01` (transfer ships). No fleet moved on turn 1.
|
||||
|
||||
**AI4's named falsifier is answered: `0x0088291a` DOES fire, once.** So command list 1 emits a
|
||||
design **command**, not merely a design object that the build order names — the engine has to
|
||||
implement the command.
|
||||
|
||||
**A cross-check that neither model was fitted to.** Four rate-gate bumps and **three** research-
|
||||
project bumps. The three are the AI empires; the human player set a rate but picked no project,
|
||||
which is exactly why the reference game's map banner reads *"No Research Project Assigned"* in the
|
||||
turn-2 screenshot. And the four-vs-three split is the same population §2 derives from `ReqCL`:
|
||||
four players have a client and submit a command block, the four Species-4 pseudo-players submit
|
||||
none. Lane AI4 reached that population by counting bumps; §2 reached it by decoding
|
||||
`AllPlayersTurnEnded`. Neither was derived from the other.
|
||||
|
||||
**Also confirmed independently this lane:** `ref-turn2.sav` on the VM and
|
||||
`verify/results/saves/turn2-state.sav` in the repo are the same 66,739 bytes with sha256
|
||||
`ab4ac2d7e2977260…`. Lane AI4's correction stands, and it means the `TShn` measurement in
|
||||
`nvo-tshn-visible-owner.md` was taken on the standalone's own reference pair.
|
||||
|
||||
**Coverage caveat for this second run.** It has **no oracle control of its own** — the autosaves it
|
||||
produced are a turn-1 workload for which no historical hash exists. What it rests on is that the
|
||||
binary is bit-identical to run 1 (`w3tshn-bb81d3d-20260908T2007Z`), whose armed run *did* reproduce
|
||||
the oracle byte for byte, and that the two configs differ only in `watch.mode`. The canary self-test
|
||||
passed in both.
|
||||
|
||||
**Not determined:** which build queue `cmd[0] == 1` selects. `Game::ServerSystem` has exactly one
|
||||
`BQ` pointer (`+0xa4`), which fits `cmd[0] == 0`, but the handle map's value type is a base
|
||||
subobject whose offset differs per class (the design handle gets a `-0xa0` adjust at `0x008494ca`),
|
||||
so the frame those two offsets are measured in was not pinned down.
|
||||
|
||||
## 4. Coverage — what this run did not establish (rule 15)
|
||||
|
||||
- **One save, one game, one turn.** Everything above is `ref-turn2.sav` plus the instruction stream.
|
||||
- **The containers are empty, so nothing about their behaviour when full was observed.** Not one
|
||||
element of either has ever been seen in this campaign, live or on the wire.
|
||||
- **`Status` was not watched in this run at all** — §2 is entirely instruction-stream work, resting
|
||||
on lane W2's live ordering. The one thing a run could still add is *membership*: whether players
|
||||
4–7 ever appear in the `S+0x1e8` encounter records that §2's phase-31 loop walks. That is
|
||||
data-dependent and cannot be read.
|
||||
- **`ReqCL` was verified as the gate in two functions**; it was not traced through every path that
|
||||
could create a client.
|
||||
250
findings/subsystems/nvo-tshn-visible-owner.md
Normal file
250
findings/subsystems/nvo-tshn-visible-owner.md
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
# `NVO.TShn` — the visible-owner record: one writer, one gate, and the gate is not `AFlags`
|
||||
|
||||
- **Type:** subsystem (live watchpoint + instruction stream + whole-corpus falsification)
|
||||
- **Status:** **verified** — the writer was trapped on the running game; the gate was byte-decoded;
|
||||
the resulting model was then tested against every `NVO` record in all 11 saves
|
||||
- **Confidence:** high for the writer and the gate (both instruction-verified, one of them live).
|
||||
Medium for *which conjunct* fails at the corpus's single frozen record, because the field it
|
||||
turns on is not serialised — see §6.
|
||||
- **Owner / date:** lane W3 · 2026-09-08
|
||||
- **Instrument:** `src/shim/hooks/watchpoints.{h,cpp}` `watch.mode=tshn`, build
|
||||
`w3tshn-bb81d3d-20260908T2007Z`, configs `shim.cfg.w3tshn` / `shim.cfg.w3control`
|
||||
- **Predictions, committed before the build:** `sots-engine/docs/W3-predictions.md`
|
||||
- **Supersedes:** `system-visibility-record.md` §7 ("`TShn`'s writer remains unnamed"), and one row
|
||||
of `objects/layouts.md` (§2 below)
|
||||
|
||||
---
|
||||
|
||||
## 0. The result in one line
|
||||
|
||||
> `NVO[p].TShn` is set to the current `Frame` for every `(system, player)` pair that satisfies
|
||||
> **`ServerSystem::IsKnownTo(sys, p)`**, once in driver phase 24 and again in combat-done phase 25.
|
||||
> `IsKnownTo` is `IsVisibleTo` **or** a second branch — partial sensor contact plus the
|
||||
> `CCC_AdvSens` tech — and that second branch is the whole reason lane E3 could not make `AFlags`
|
||||
> fit.
|
||||
|
||||
`IsKnownTo`, whole body, byte-decoded at `0x00746390` (`0x007463ae`, `0x007463c0`, `0x007463cb`,
|
||||
`0x007463da`) and again inlined at `0x0075bd70` (`0x0075bd88`, `0x0075bd93`, `0x0075bd9e`,
|
||||
`0x0075bdb2`):
|
||||
|
||||
```c
|
||||
bool ServerSystem::IsKnownTo(ServerPlayer* p) {
|
||||
if (!p) return false;
|
||||
if ((this->AFlags(+0xd4) >> p->PlyrIdx(+0x28)) & 1) return true; // == IsVisibleTo
|
||||
if (p->PlyrIdx >= 15) return false;
|
||||
if (((this->SnLv(+0x24) >> (2 * p->PlyrIdx)) & 3) == 0) return false; // sensor contact level
|
||||
return TechTree::HasResearched(p->TechTree(+0xf4), 10000); // CCC_AdvSens
|
||||
}
|
||||
```
|
||||
|
||||
`tech-effects.md` line 89 already carried the sentence *"a player in 'partial contact' state (2-bit
|
||||
field, `+0x24`) counts as known/visible when AdvSens is researched"* against exactly these three
|
||||
addresses. What was missing was never the identification — it was the connection to `NVO`. That is
|
||||
method rule 18's corollary, for the second time this campaign.
|
||||
|
||||
## 1. What the run measured
|
||||
|
||||
Workload: `ref-turn2.sav` (Frame 2, 8 players), one End Turn, four hardware data-write watchpoints
|
||||
armed from the single `ApplyAllTurnCommands` detour.
|
||||
|
||||
**Target selection was done by predicate at arm time, not by a hard-coded address**, and the log
|
||||
prints all 28 systems so the choice is auditable:
|
||||
|
||||
```
|
||||
watch: systems vector @0x0e4f7c9c first=0x1ee82490 last=0x1ee82500 count=28 (expect 28 …)
|
||||
watch: NVO header probe -- ServerSystem+0x274 validated on 9 systems, +0x26c on 0; using +0x274
|
||||
watch: sys[11] @0x0e4e51a8 'Spica' AFlags=0x0 NVO=1 NVE=0 root=0x1eea8c40 key=4 ok=1
|
||||
watch: SELFTEST canary writes=1 traps=1 dr7=0xdddd0055 PASS
|
||||
watch: slot 0 -> 'Spica' NVO root+0x10 {touched:i16,TShn:i16} key=player 4 = 0x1eea8c50
|
||||
watch: slot 1 -> 'Spica' NVO._Mysize (=1 at arm) = 0x0e4e5420
|
||||
```
|
||||
|
||||
Prediction P1.0 was that the predicate "AFlags == 0 and NVO non-empty" has exactly one solution on
|
||||
this save and that solution is Spica. It named Spica.
|
||||
|
||||
**Four traps, two store sites, two calls** (Ghidra VAs; runtime base was `0x00e80000`, so
|
||||
`VA = EIP - 0xa80000`):
|
||||
|
||||
| seq | trap EIP | store | value after | reading |
|
||||
|---|---|---|---|---|
|
||||
| 0 | `0x0075b95a` | `0x0075b957` `mov WORD PTR [eax],cx` | `0x00020003` | `touched` 2 → **3**, `TShn` still 2 |
|
||||
| 1 | `0x0075b965` | `0x0075b961` `mov WORD PTR [eax+0x2],dx` | `0x00030003` | **`TShn` 2 → 3** |
|
||||
| 2 | `0x0075b95a` | same | `0x00030003` | second call, same values |
|
||||
| 3 | `0x0075b965` | same | `0x00030003` | second call, same values |
|
||||
|
||||
Every trap carried `[ebp+4] = 0x0075bd4a` and `[[ebp]+4] = 0x0075bdc3`. Those are the returns of
|
||||
the `call 0x0075b880` at `0x0075bd45` and the `call 0x0075bca0` at `0x0075bdbe` — so the chain
|
||||
|
||||
```
|
||||
RefreshVisibleOwnerIfKnown 0x0075bd70 → RecordVisibleOwner 0x0075bca0
|
||||
→ SetVisibleOwner 0x0075b880
|
||||
→ NVO::operator[] 0x0075a890
|
||||
→ the TShn store 0x0075b961
|
||||
```
|
||||
|
||||
is confirmed **live, frame by frame**, not inferred. The stack scan also caught `0x0086a806`, a
|
||||
stale return inside `StrategyServer::UpdateSensors`'s sibling — the sensor phase, which is where
|
||||
the loop lives.
|
||||
|
||||
Against the predictions: P1.1 (`TShn` ends at 3) held; P1.2 (not `ltis`'s writer `FUN_00743ec0`,
|
||||
not `RecordObservation 0x00756300`) held; **P1.3 held — slot 1, `NVO._Mysize`, took zero traps**,
|
||||
which is what makes slot 0's four traps mean "this node was written" rather than "this address is
|
||||
stale". The prediction said "at least one" trap and got four; the refinement is §3.
|
||||
|
||||
## 2. Two corrections to the record (rule 11)
|
||||
|
||||
**`objects/layouts.md`, `Game::ServerSystem`.** The rows `PID`@0x274 (handle) and `NVO`@0x278 (int)
|
||||
are wrong as field rows. `+0x274` is `NVO._Myhead` and `+0x278` is `NVO._Mysize`; the `"PID"` tag on
|
||||
the wire belongs to the map node's **key**, written by `WriteHandleId` at `0x0074a0c3` after
|
||||
`node+0xc` is mapped through `server->players[idx]` — exactly as `EPid` does for `NVE`. Same for the
|
||||
`NVE`@0x288 and `NVs`@0x298 rows: those offsets are the `_Mysize` count words.
|
||||
|
||||
The MSVC `_Tree` here is 0x10 bytes — allocator `+0x0`, `_Myhead` `+0x4`, `_Mysize` `+0x8`,
|
||||
comparator `+0xc` — and the three maps sit at `+0x270`, `+0x280`, `+0x290`.
|
||||
|
||||
**The `+0x8` that made three lanes disagree.** `ServerSystem::Write` `0x00749630` is entered on the
|
||||
**IStreamable subobject at `ServerSystem+0x8`** (the RTTI COL offset for vftable `0x00a2043c` is
|
||||
`+0x8`), so every displacement in its body is 8 less than the object-relative offset. That is the
|
||||
whole explanation for `+0x26c` vs `+0x274`, and for `AFlags` reading as `[edi+0xcc]` there and
|
||||
`+0xd4` everywhere else. Lane E3's `+0x284/+0x288` for `NVE` and its `AFlags(+0xd4)` were both
|
||||
right. The arming code does not depend on the reconciliation being right: it probes both bases and
|
||||
logs the count each validates on (`+0x274` on 9 systems, `+0x26c` on 0).
|
||||
|
||||
**`NVO` node layout** (instruction-verified from `Write`'s walk `0x0074a0b0..0x0074a195`):
|
||||
`_Left +0x0`, `_Parent +0x4`, `_Right +0x8`, key = **player index** `+0xc`, then
|
||||
`touched:int16 +0x10`, `TShn:int16 +0x12`, `OID:int32 +0x14`, `isind:bool +0x18`,
|
||||
`indi` (inline `IndependenceInfo`) `+0x1c`, `_Color +0x8c`, **`_Isnil +0x8d`**. An `NVO` node is
|
||||
about `0x8e` bytes; `NVE`'s is `0x20`, with `_Isnil` at `+0x19`. They are not the same shape.
|
||||
|
||||
## 3. Why four traps and not two: the two-phase refresh
|
||||
|
||||
`SensorPhase_RefreshAllVisibleOwners 0x00813700` is called from
|
||||
`StrategyServer::UpdateSensors 0x0086a8d0` at `0x0086a91d`, and `UpdateSensors` is called from
|
||||
three places:
|
||||
|
||||
| caller | when |
|
||||
|---|---|
|
||||
| `StrategyServer::ProcessTurn` `0x007dcb9a` | **driver phase 24** (`turn-driver.md`) |
|
||||
| `OnAllCombatDone_Tail` `0x007d981b` | **combat-done phase 25** (`combat-done-tail.md`) |
|
||||
| `LoadGame` `0x007ddba8` | on load |
|
||||
|
||||
The watchpoints were armed during the End Turn, after the load, so the load call is outside the
|
||||
window and **exactly two** of the three should fire. Two did. That is a live confirmation of both
|
||||
phase attributions, taken for free from a watchpoint that was pointed at something else.
|
||||
|
||||
The inner loops are `for p in players: for s in systems:` and the player loop is **unfiltered** —
|
||||
there is no NPC or species test anywhere on the write path. The only NPC test in the whole family is
|
||||
in `SVSOIndependentSystems::Update 0x0075cb20`, and it *excludes* NPCs (`p->NPC(+0xfb) != 0 →
|
||||
skip`). So the corpus split described below is not a special case for monsters; it is the ordinary
|
||||
gate resolving differently.
|
||||
|
||||
## 4. Every writer of `NVO`, and why the list is complete
|
||||
|
||||
`NVO`'s `operator[]` is `0x0075a890` and it has **exactly one caller in the image**:
|
||||
`ServerSystem::SetVisibleOwner 0x0075b880` at `0x0075b94b`. (`NVE`'s `operator[]` `0x00752730` has
|
||||
three.) Every caller of `FindVisibleOwner 0x0074d2e0` only reads the record. So `SetVisibleOwner` is
|
||||
the only function that can write a `TShn`, and its three record producers are:
|
||||
|
||||
| producer | `TShn` it supplies |
|
||||
|---|---|
|
||||
| `RecordVisibleOwner 0x0075bca0` | `(int16)server->Frame(+0x8)` — the per-turn refresh |
|
||||
| `RecordVisibleOwnerNone 0x0075bc00` | `(int16)Frame`, with `OID = 0` |
|
||||
| `ShareVisibleOwner 0x0075b9b0` | the *source player's* `TShn`, unrefreshed (alliance intel; no corpus save has an alliance, so this has never run — rule 6) |
|
||||
|
||||
plus `ServerSystem::Read 0x0075f0bd` reconstructing from the wire.
|
||||
|
||||
`SetVisibleOwner` always stamps `touched` with the current `Frame` and copies `TShn` from the
|
||||
source, so **`touched` is "refreshed this turn" and `TShn` is "the turn this ownership view was
|
||||
taken"**. `SynchronizePlayer 0x007c6220` corroborates from the reading side: it decides whether to
|
||||
ship an `NVO` update to a client by comparing `touched` against the frame (`0x007c761d`) and puts
|
||||
`(SnLv >> 2*idx) & 3` in the same packet (`0x007c76a4`).
|
||||
|
||||
`SetVisibleOwner`'s own gate is a sanity check, not a rule: skip the write when the caller asks to
|
||||
record player *p* as owner of a system *p* does not own, logging *"VisibleOwner: %s sees their self
|
||||
as owner of %s, but they are not."*. Its converge point is `0x0075b983`, the inlined
|
||||
`IndependenceInfo` destructor that both arms run — checked, per method rule 4, before calling it a
|
||||
branch.
|
||||
|
||||
## 5. The model, and its falsification against the whole corpus
|
||||
|
||||
Model, as it would be implemented:
|
||||
|
||||
```c
|
||||
// once per (player, system) in the sensor phase, twice per turn
|
||||
if (sys->IsKnownTo(p)) {
|
||||
OwnRec* r = sys->NVO[p->PlyrIdx];
|
||||
r->touched = (int16)server->Frame;
|
||||
r->TShn = (int16)server->Frame;
|
||||
r->OID = sys->PID ? sys->PID->id : 0;
|
||||
r->isind = sys->indi != NULL; r->indi = *sys->indi;
|
||||
}
|
||||
```
|
||||
|
||||
Test: for every `NVO` record in all 11 saves, predict *frozen* vs *refreshed* from the record's own
|
||||
player index, the system's `AFlags`, and whether that player has researched `CCC_AdvSens`, then
|
||||
compare against `TShn == Frame`.
|
||||
|
||||
| predicted | observed | n |
|
||||
|---|---|---|
|
||||
| refresh — `AFlags` bit set | `TShn == Frame` | 103 |
|
||||
| refresh — sensor branch (player has AdvSens) | `TShn == Frame` | 54 |
|
||||
| **frozen** — `AFlags` bit clear, player lacks AdvSens | **`TShn == 22` at `Frame == 23`** | 1 |
|
||||
|
||||
**158 records, 0 mismatches.** The single frozen record is Bismol in `zuul-turn23-fleet23.sav` —
|
||||
lane E3's named counter-example, and the model predicts it rather than excusing it. Script:
|
||||
`scripts/…` is not needed; the whole test is 40 lines over `verify/save-reader/save_reader.py` and
|
||||
is reproduced in §8.
|
||||
|
||||
**The discriminator, measured.** `CCC_AdvSens` (`TNm == "CCC_AdvSens"`, tech id 10000) in every
|
||||
save:
|
||||
|
||||
| players | `St` | `TAcq` | `TiAcq` |
|
||||
|---|---|---|---|
|
||||
| every Species-4 pseudo-player (`NPC=true, RebAI=false`: Alien Menace, Peacekeeper Enforcer, Von Neumann, Independent Colony) | **4** | **1** | 37 or 43 |
|
||||
| every real empire, in every save, at every turn | 0 | −1 | −1 |
|
||||
|
||||
That is why the seven `AFlags == 0` systems whose `TShn` tracks `Frame` — Spica in the reference
|
||||
game, Altair / Galifrey / Kithrup / Alversi / Dosadi / Hyrakius in the Zuul game — all key their
|
||||
single `NVO` entry to a Species-4 player, and why Bismol, keyed to real empire index 1, freezes the
|
||||
moment its `AFlags` bit clears. It is not a species rule. It is that the monsters start the game
|
||||
with the entire tech tree.
|
||||
|
||||
## 6. What is still a hypothesis, and why (rules 6 and 15)
|
||||
|
||||
- **Which conjunct fails at Bismol is not decidable from any save.** `SnLv` (`ServerSystem+0x24`) is
|
||||
**not serialised** — it is recomputed every turn in the sensor phase from live positions and
|
||||
sensor ranges. Bismol's record is consistent with *either* "player 1 was outside the sensor
|
||||
envelope" or "player 1 lacks AdvSens". The corpus proves the second is true; it cannot show the
|
||||
first is false. Both reduce to the same one-line model, so nothing downstream is blocked, but the
|
||||
claim "the sensor branch is what refreshes Spica" is **[H]**, not [V]: no measurement of `SnLv`
|
||||
exists. One arming line (`sys+0x24` in slot 1 instead of `_Mysize`) settles it on the next VM run.
|
||||
- **The 54 "sensor branch" rows are unobserved in one conjunct each.** They are consistent, not
|
||||
proven. The honest statement of the corpus result is: *no record contradicts the model, and the
|
||||
one record that contradicted `AFlags` alone is explained.*
|
||||
- **`ShareVisibleOwner` has never executed** — no corpus save has an alliance.
|
||||
- **`p->PlyrIdx < 15`** is required for the sensor branch, and `RefreshVisibleOwnerIfKnown` uses
|
||||
`1 << PlyrIdx` while the sensor phase indexes `SnLv` by *loop position*. Those agree only if the
|
||||
player vector is ordered by `PlyrIdx`. It is, on every corpus save, but that was not verified in
|
||||
code.
|
||||
- **One system watched, not 28.** Spica's writes were trapped; the other 27 systems' `TShn` writes
|
||||
are corpus evidence, not watchpoint evidence.
|
||||
- **Per-thread blind spot.** Debug registers are per-thread; the arm was on the turn thread only.
|
||||
|
||||
## 7. Files
|
||||
|
||||
- Hit log: `verify/results/shim/watchpoints/w3-tshn-containers.txt`
|
||||
- Arming log (all 28 systems, the base probe, the canary, the container counts):
|
||||
`verify/results/shim/watchpoints/w3-shim-log-excerpt.txt`
|
||||
- Addresses: `ghidra/addresses.d/lane-w3.json`
|
||||
- Predictions: `sots-engine/docs/W3-predictions.md`; instrument notes `sots-engine/docs/W3-watchpoints.md`
|
||||
|
||||
## 8. Reproducing the corpus test
|
||||
|
||||
```python
|
||||
# for each save: Frame; per player (PlyrIdx, CCC_AdvSens St/TAcq); per system (Name, AFlags, PID,
|
||||
# and each NVO entry's PID key + TShn). Map the wire's handle key back to a PlyrIdx using systems
|
||||
# whose AFlags has exactly one bit, then:
|
||||
bit = (AFlags >> idx) & 1
|
||||
pred = 'refresh' if (bit or advsens[idx]) else 'frozen'
|
||||
assert (pred == 'frozen') == (TShn != Frame)
|
||||
```
|
||||
|
|
@ -136,6 +136,17 @@ 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
|
||||
|
|
|
|||
256
ghidra/addresses.d/lane-w3.json
Normal file
256
ghidra/addresses.d/lane-w3.json
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
{
|
||||
"entries": [
|
||||
{
|
||||
"name": "ServerSystem_off_AFlags",
|
||||
"offset": "0xd4",
|
||||
"convention": "offset",
|
||||
"status": "verified",
|
||||
"prototype": "int AFlags -- the per-player 'has something here' mask, bit = PlyrIdx. Confirmed from ServerSystem::Write 0x00749630 at 0x00749aec (`mov ecx,[edi+0xcc]` with the \"AFlags\" tag at 0xa1f3b0; Write runs on the IStreamable subobject at +0x8, so 0xcc + 8 = 0xd4). Lane E3's +0xd4 is right"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_off_SnLv",
|
||||
"offset": "0x24",
|
||||
"convention": "offset",
|
||||
"status": "verified",
|
||||
"prototype": "int SnLv -- 2 bits per player (level = 0 none / 1 partial / 3 full), NOT SERIALISED. Written once per turn in the sensor phase at 0x0086a789 as `s->SnLv = (s->SnLv & ~(3<<2i)) | (level<<2i)`; read by ServerSystem::IsKnownTo 0x00746390 and RefreshVisibleOwnerIfKnown 0x0075bd70, and shipped to clients by SynchronizePlayer at 0x007c76a4. Because it is absent from every save, any NVO/TShn claim that depends on it is a hypothesis on the corpus alone (rule 6)"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_off_NVO_Myhead",
|
||||
"offset": "0x274",
|
||||
"convention": "offset",
|
||||
"status": "verified",
|
||||
"prototype": "std::map<int PlyrIdx, OwnRec>::_Myhead for NVO (the visible-owner record). The map object starts at +0x270 and is 0x10 bytes: allocator +0x0, _Myhead +0x4, _Mysize +0x8, comparator +0xc. From ServerSystem::Write 0x0074a097/0x0074a195 (`[edi+0x26c]`, +8 for the IStreamable subobject) and from the live arming probe, which validated +0x274 on 9 of 28 systems and +0x26c on 0"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_off_NVO_Mysize",
|
||||
"offset": "0x278",
|
||||
"convention": "offset",
|
||||
"status": "verified",
|
||||
"prototype": "std::map _Mysize for NVO. Write 0x0074a07d (`[edi+0x270]`) carries the \"NVO\" tag 0xa1f2c0, so the wire's NVO count is this word. CORRECTS objects/layouts.md, which lists NVO@0x278 as an int field and PID@0x274 as a handle: +0x274 is _Myhead and the \"PID\" tag belongs to the node KEY, not to the object"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_off_NVE_Myhead",
|
||||
"offset": "0x284",
|
||||
"convention": "offset",
|
||||
"status": "verified",
|
||||
"prototype": "std::map _Myhead for NVE (the sighting record). Write 0x0074a1bb. Confirms lane E3's +0x284/+0x288"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_off_NVE_Mysize",
|
||||
"offset": "0x288",
|
||||
"convention": "offset",
|
||||
"status": "verified",
|
||||
"prototype": "std::map _Mysize for NVE. Write 0x0074a1a1, tag \"NVE\""
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_off_NVs_Myhead",
|
||||
"offset": "0x294",
|
||||
"convention": "offset",
|
||||
"status": "verified",
|
||||
"prototype": "std::map _Myhead for NVs (the per-player PlayerView record). Write 0x0074a27d"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_off_NVs_Mysize",
|
||||
"offset": "0x298",
|
||||
"convention": "offset",
|
||||
"status": "verified",
|
||||
"prototype": "std::map _Mysize for NVs. Write 0x0074a263, tag \"NVs\""
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_NVO_node_off_TShn",
|
||||
"offset": "0x12",
|
||||
"convention": "offset",
|
||||
"status": "verified",
|
||||
"prototype": "int16 TShn, offset within the NVO map NODE (not the value). Node is _Left +0x0, _Parent +0x4, _Right +0x8, key(PlyrIdx) +0xc, value +0x10 = {int16 touched +0x10, int16 TShn +0x12, int32 OID +0x14, bool isind +0x18, IndependenceInfo indi +0x1c}, _Color +0x8c, _Isnil +0x8d -- so an NVO node is ~0x8e bytes, unlike NVE's 0x20. From Write 0x0074a0ca (`lea ecx,[ebx+0x12]`, tag \"TShn\") and confirmed live: a 4-byte watchpoint on value+0x0 trapped both halves separately"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_NVO_operator_index",
|
||||
"offset": "0x0075a890",
|
||||
"convention": "thiscall",
|
||||
"status": "verified",
|
||||
"prototype": "OwnRec* (NVO_map* this /* ServerSystem+0x270 */, const int* plyrIdx) // std::map::operator[]; returns node+0x10. Exactly ONE caller in the image: ServerSystem::SetVisibleOwner 0x0075b94b (NVE's operator[] 0x00752730 has three). That single-caller fact is what makes the writer inventory below complete"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_SetVisibleOwner",
|
||||
"offset": "0x0075b880",
|
||||
"convention": "thiscall",
|
||||
"status": "verified",
|
||||
"prototype": "void (ServerSystem* this, ServerPlayer* p, const OwnRec* src) // ret 8. Stamps value.touched = (int16)this->owner(+0x10)->Frame(+0x8) and copies src's TShn/OID/isind/indi verbatim into NVO[p->PlyrIdx]. THE ONLY WRITER OF NVO.TShn IN THE IMAGE. Local gate (converge point 0x0075b983, the inlined IndependenceInfo dtor, so it is a real branch and not a rule-4 artifact): skip the whole write when src->OID == p->id but this->PID(+0x100)->id != p->id, logging \"VisibleOwner: %s sees their self as owner of %s, but they are not.\""
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_SetVisibleOwner_TShnStore",
|
||||
"offset": "0x0075b961",
|
||||
"convention": "offset",
|
||||
"status": "verified",
|
||||
"prototype": "site `mov WORD PTR [eax+0x2],dx` -- the TShn store. Trapped live by a DR watchpoint on Spica's NVO root node: EIP 0x0075b965 (the trap is taken after the store retires), twice per End Turn. The touched-word store is the instruction before it at 0x0075b957 (trap EIP 0x0075b95a)"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_RecordVisibleOwner",
|
||||
"offset": "0x0075bca0",
|
||||
"convention": "thiscall",
|
||||
"status": "verified",
|
||||
"prototype": "void (ServerSystem* this, ServerPlayer* p) // ret 4. Builds an OwnRec with TShn = (int16)server->Frame, OID = this->PID(+0x100)->id, isind/indi from this->indi(+0x1c8), then calls SetVisibleOwner at 0x0075bd45. THIS is where TShn = Frame is decided. Confirmed live: the trapped frame's [ebp+4] was 0x0075bd4a"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_RecordVisibleOwnerNone",
|
||||
"offset": "0x0075bc00",
|
||||
"convention": "thiscall",
|
||||
"status": "mapped",
|
||||
"prototype": "void (ServerSystem* this, ServerPlayer* p) // TShn = Frame, OID = 0 -- 'you now see this system as unowned'. Frame read at 0x0075bc5f"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_ShareVisibleOwner",
|
||||
"offset": "0x0075b9b0",
|
||||
"convention": "thiscall",
|
||||
"status": "mapped",
|
||||
"prototype": "void (ServerSystem* this, ServerPlayer* from, ServerPlayer* to) // the NVO analogue of ShareObservation 0x00754d90: passes the source player's record verbatim, so TShn is NOT refreshed. No corpus save has an alliance, so this path has never executed (rule 6)"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_FindVisibleOwner",
|
||||
"offset": "0x0074d2e0",
|
||||
"convention": "thiscall",
|
||||
"status": "verified",
|
||||
"prototype": "OwnRec* (ServerSystem* this, ServerPlayer* p) // map find on NVO (0x0074d2f2 `lea ecx,[esi+0x270]`, 0x0074d300 `cmp edx,[esi+0x274]`); returns node+0x10 or NULL. Every caller only READS the record"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_GetVisibleOwnerId",
|
||||
"offset": "0x0074f710",
|
||||
"convention": "thiscall",
|
||||
"status": "mapped",
|
||||
"prototype": "int (ServerSystem* this, ServerPlayer* p) // FindVisibleOwner then rec->OID"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_IsKnownTo",
|
||||
"offset": "0x00746390",
|
||||
"convention": "thiscall",
|
||||
"status": "verified",
|
||||
"prototype": "bool (ServerSystem* this, ServerPlayer* p) // THE NVO GATE, and it is looser than IsVisibleTo 0x00743fb0. Whole body: `p && ( (this->AFlags(+0xd4) >> p->PlyrIdx(+0x28)) & 1 || ( p->PlyrIdx < 15 && ((this->SnLv(+0x24) >> 2*p->PlyrIdx) & 3) != 0 && TechTree::HasResearched(p->TechTree(+0xf4), 10000 /* CCC_AdvSens */) ) )`. Byte-decoded at 0x007463ae / 0x007463c0 / 0x007463cb / 0x007463da"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_RefreshVisibleOwnerIfKnown",
|
||||
"offset": "0x0075bd70",
|
||||
"convention": "thiscall",
|
||||
"status": "verified",
|
||||
"prototype": "bool (ServerSystem* this, ServerPlayer* p) // ret 4. IsKnownTo inlined byte-for-byte (0x0075bd88 AFlags, 0x0075bd93 idx<15, 0x0075bd9e SnLv, 0x0075bdb2 HasResearched(0x2710)), then RecordVisibleOwner at 0x0075bdbe. Confirmed live: the trapped frame's [[ebp]+4] was 0x0075bdc3, the return of that call"
|
||||
},
|
||||
{
|
||||
"name": "SensorPhase_RefreshAllVisibleOwners",
|
||||
"offset": "0x00813700",
|
||||
"convention": "fastcall",
|
||||
"status": "verified",
|
||||
"prototype": "void (StrategyServer_S4* server) // for p in players(+0x50..+0x54) for s in systems(+0x40..+0x44): s->RefreshVisibleOwnerIfKnown(p) at 0x00813737. The player loop is UNFILTERED -- no NPC/species test anywhere on this path. Called from StrategyServer::UpdateSensors 0x0086a8d0 at 0x0086a91d"
|
||||
},
|
||||
{
|
||||
"name": "SensorPhase_ComputeContactLevel",
|
||||
"offset": "0x00850cf0",
|
||||
"convention": "thiscall",
|
||||
"status": "mapped",
|
||||
"prototype": "int (Sensors* this, ServerPlayer* p, Entity* e) // 3 if IsVisibleTo (AFlags), else 1 if inside p's sensor envelope, else 0 (0x00850d18 / 0x00850e1b). Its result is packed into ServerSystem::SnLv(+0x24) at 0x0086a789"
|
||||
},
|
||||
{
|
||||
"name": "ServerSystem_ChangeOwner",
|
||||
"offset": "0x0075bde0",
|
||||
"convention": "thiscall",
|
||||
"status": "mapped",
|
||||
"prototype": "void (ServerSystem* this, ServerPlayer* oldOwner, ServerPlayer* newOwner) // Game::ServerSystem vftable 0x00a2044c slot 6; records a visible-owner entry for both parties at 0x0075be77 / 0x0075bf2b"
|
||||
},
|
||||
{
|
||||
"name": "StrategyServer_OnCommand_SetResearchRate",
|
||||
"offset": "0x00821a80",
|
||||
"convention": "thiscall",
|
||||
"status": "verified",
|
||||
"prototype": "bool (StrategyServer_S4* this, int playerId, const float* rate) // ret 8. Bumps ModCount at 0x00821a84 then sets player->ResRate(+0xbc) = clamp(*rate, 0.0f, 1.0f), logging \"OnCommand: Research rate %f constrained to %f for %s.\" when it clamps. NAMES lane W2's `StrategySim_ModCountBump_unresolved_00821a84`, the handler that fires 4x per End Turn on ref-turn2 -- once per player that has a client"
|
||||
},
|
||||
{
|
||||
"name": "StrategyServer_OnCommand_SetSystemRates",
|
||||
"offset": "0x0086c3e0",
|
||||
"convention": "thiscall",
|
||||
"status": "verified",
|
||||
"prototype": "bool (StrategyServer_S4* this, int playerId, const SysRatesCmd* cmd) // ret 8. Bumps ModCount at 0x0086c3e6. Strings \"OnCommand: Unable to set system rates for %d(id). System not found.\" (0xa32448) and \"OnCommand: Player setting system rates for system he does not own.\" (0xa32400). NAMES lane W2's `..._unresolved_0086c3e6`"
|
||||
},
|
||||
{
|
||||
"name": "StrategyServer_OnCommand_TransferShips",
|
||||
"offset": "0x0088bed0",
|
||||
"convention": "thiscall",
|
||||
"status": "verified",
|
||||
"prototype": "int (StrategyServer_S4* this, int playerId, const TransferCmd* cmd, ...) // ret 0xc. Bumps ModCount at 0x0088befe. Strings \"OnCommand: Cannot transfer ships. Player %d(id) not found.\" (0xa33248) and \"TransferShips: Fleet flagged as empty, but still has ships.\". NAMES lane W2's `..._unresolved_0088befe`"
|
||||
},
|
||||
{
|
||||
"name": "StrategyServer_OnCommand_AddBuildOrder",
|
||||
"offset": "0x00849460",
|
||||
"convention": "thiscall",
|
||||
"status": "mapped",
|
||||
"prototype": "bool (StrategyServer_S4* this, int requesterId, const AddOrderCmd* cmd, int* outReason) // ret 0xc. Bumps ModCount at 0x0084946b. cmd = {int which, id target, id design, int arg}; picks target->+0xa4 (which==0) or +0x98 (which==1) as the BuildQueue, validates with 0x0083ce50, then BuildQueue::AddOrder 0x00845260 push_backs onto the list at queue+0x10. Reached from the Build Screen 0x00656540 via 0x00762fd0, and from the command replay at 0x0089009f. NAMES lane W2's `..._unresolved_0084946b`"
|
||||
},
|
||||
{
|
||||
"name": "StrategyServer_OnCommand_SetResearchProject",
|
||||
"offset": "0x0088fe0a",
|
||||
"convention": "offset",
|
||||
"status": "verified",
|
||||
"prototype": "site `inc DWORD PTR [ebx+0x4]` -- a SECOND ModCount bump INLINED into ApplyTurnCommandBatch 0x0088f9b0 (at +0x45a), distinct from the one lane W2 found at 0x0089046c. String \"OnCommand: Cannot set research project for player %d(id). Player not found.\" (0xa30ed0). Fires 3x on turn1-state and 0x on turn2-state -- once per AI player that picks a research target on turn 1; the human player set a rate but no project, which is why the reference game's map banner reads \"No Research Project Assigned\""
|
||||
},
|
||||
{
|
||||
"name": "StrategyServer_OnCommand_CreateDesign",
|
||||
"offset": "0x00882910",
|
||||
"convention": "thiscall",
|
||||
"status": "verified",
|
||||
"prototype": "bool (StrategyServer_S4* this, int playerId, const DesignCmd* cmd) // ret 8. Bumps ModCount at 0x0088291a. Strings \"OnCommand: Cannot create design for player %d(id), player does not exist.\" (0xa32c40) and \"OnCommand: Unable to create design. ID not specified.\" (0xa32c04). Fires 1x on turn1-state, 0x on turn2-state. ANSWERS LANE AI4'S FALSIFIER: command list 1 emits a design COMMAND, not merely a design object that the build order names"
|
||||
},
|
||||
{
|
||||
"name": "BuildQueue_AddOrder",
|
||||
"offset": "0x00845260",
|
||||
"convention": "thiscall",
|
||||
"status": "mapped",
|
||||
"prototype": "void (BuildQueue* this, int arg, ShipDesign* design) // builds an order record with five fields pre-set to -1 and push_backs it onto the std::list at this+0x10/+0x14 (\"list<T> too long\" guard at 0x008452e4)"
|
||||
},
|
||||
{
|
||||
"name": "StrategyServer_CanAddBuildOrder",
|
||||
"offset": "0x0083ce50",
|
||||
"convention": "cdecl",
|
||||
"status": "mapped",
|
||||
"prototype": "bool (ServerPlayer* who, BuildQueue* q, ShipDesign* d, int* outReason) // the 'can this be built' validator; writes a numbered reason (0,1,2,8,...) through arg 4 and calls BankruptcyLevel 0x0080db10"
|
||||
},
|
||||
{
|
||||
"name": "ServerPlayer_off_ReqCL",
|
||||
"offset": "0xfd",
|
||||
"convention": "offset",
|
||||
"status": "verified",
|
||||
"prototype": "bool ReqCL -- 'this player requires a client'. THE PREDICATE BEHIND Player.Status: a player with ReqCL == 0 never reaches SetupAIPlayers' AI list (gate at 0x007bc25c), so never gets a StrategyClient, so is never the subject of MarkPlayerTurnEnded and keeps the 0 that ResumePlaying wrote. The script binding that creates the four '_NPC' pseudo-players clears it explicitly at 0x00797bcb"
|
||||
},
|
||||
{
|
||||
"name": "StrategyNetworkServer_AllPlayersTurnEnded",
|
||||
"offset": "0x007cfd00",
|
||||
"convention": "thiscall",
|
||||
"status": "verified",
|
||||
"prototype": "bool (StrategyNetworkServer* this) // vftable slot 3. The game's OWN statement of which players must carry Status == 4: `for p in players: if (!p->ReqCL(+0xfd)) continue; if (p->Elim(+0xf8)) continue; if (p->Status(+0x164) != 4) return false;` -- loop 0x007cffb7..0x007d0002 with edi = 4 loaded at 0x007cffa3"
|
||||
},
|
||||
{
|
||||
"name": "StrategyNetworkServer_SetupAIPlayers",
|
||||
"offset": "0x007bc1c0",
|
||||
"convention": "thiscall",
|
||||
"status": "verified",
|
||||
"prototype": "void (StrategyNetworkServer* this) // the ReqCL gate at 0x007bc25c decides which players get an AI client at all; AddAIPlayer 0x007bc333 -> 0x007b8ab0"
|
||||
},
|
||||
{
|
||||
"name": "StrategyServer_OnPlayerEndTurn",
|
||||
"offset": "0x007d9af0",
|
||||
"convention": "thiscall",
|
||||
"status": "verified",
|
||||
"prototype": "void (StrategyServer* this, const SNMPlayerEndTurn* msg) // ONE call per network message, not a loop: handle = msg->+0x4 straight off the wire, MarkPlayerTurnEnded(this+4, handle) at 0x007d9b25 with NO gate before the store. Reached from StrategyNetworkClient::OnMessage 0x00784816. The field tests at 0x007d9b5d/0x007d9ba2/0x007d9bb0 all follow the store and only decide whether to raise a 'waiting on X' event"
|
||||
},
|
||||
{
|
||||
"name": "StrategyServer_ProcessTurn_StatusOneStore",
|
||||
"offset": "0x007dcc8a",
|
||||
"convention": "offset",
|
||||
"status": "verified",
|
||||
"prototype": "site `mov DWORD PTR [eax+0x164],1` -- the phase-31 Status write lane T2 located. Its gate, decoded at 0x007dcc78, is `if (p->IsAI(+0xf9) == 0 || p->+0xfa != 0)`, over the encounter records at S+0x1e8 (0x74-byte outer, 0x44-byte inner with the ServerPlayer* first). It reads NO species field"
|
||||
},
|
||||
{
|
||||
"name": "Game_SNMSetPlayerStatus_vftable",
|
||||
"offset": "0x00a2d718",
|
||||
"convention": "offset",
|
||||
"status": "verified",
|
||||
"prototype": "vftable for Game::SNMSetPlayerStatus (factory 0x0080f990, sizeof 0xc: vptr / +4 player INDEX / +8 status). Its handler is the OnMessage+0xa15 store lane W2 found: no gate, no field test -- the value comes verbatim off the wire. The sender is StrategyNetworkServer::Update 0x007cda40 at 0x007cdcf0, which computes `2*(flag!=0) + 2`, i.e. 4 when the player has nothing pending and 2 when it does"
|
||||
}
|
||||
]
|
||||
}
|
||||
16
verify/results/shim/watchpoints/w3-modcount-turn1.txt
Normal file
16
verify/results/shim/watchpoints/w3-modcount-turn1.txt
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
watchhit seq=0 mark=1 slot=0 dr6=0xffff0ff1 eip=0x012ec3e9 value=1(0x00000001) tid=948 ebpret=0x0130fce2 ebpret2=0x0120f6e6 scan=0x0130fce2,0x013dad98,0x0120f6e6,0x01204909
|
||||
watchhit seq=1 mark=1 slot=0 dr6=0xffff0ff1 eip=0x012a1a87 value=2(0x00000002) tid=948 ebpret=0x0130ffcb ebpret2=0x0120f6e6 scan=0x0130ffcb,0x013dad98,0x0120f6e6,0x01204909
|
||||
watchhit seq=2 mark=1 slot=0 dr6=0xffff0ff1 eip=0x0130fe0d value=3(0x00000003) tid=948 ebpret=0x0120f6e6 ebpret2=0x0139dfa1 scan=0x013dad98,0x0120f6e6,0x01204909,0x00000000
|
||||
watchhit seq=3 mark=1 slot=0 dr6=0xffff0ff1 eip=0x012a1a87 value=4(0x00000004) tid=948 ebpret=0x0130ffcb ebpret2=0x0120f6e6 scan=0x0130ffcb,0x013dad98,0x0120f6e6,0x01204909
|
||||
watchhit seq=4 mark=1 slot=0 dr6=0xffff0ff1 eip=0x0130fe0d value=5(0x00000005) tid=948 ebpret=0x0120f6e6 ebpret2=0x0139dfa1 scan=0x013dad98,0x0120f6e6,0x01204909,0x00000000
|
||||
watchhit seq=5 mark=1 slot=0 dr6=0xffff0ff1 eip=0x012a1a87 value=6(0x00000006) tid=948 ebpret=0x0130ffcb ebpret2=0x0120f6e6 scan=0x0130ffcb,0x013dad98,0x0120f6e6,0x01204909
|
||||
watchhit seq=6 mark=1 slot=0 dr6=0xffff0ff1 eip=0x0130fe0d value=7(0x00000007) tid=948 ebpret=0x0120f6e6 ebpret2=0x0139dfa1 scan=0x013dad98,0x0120f6e6,0x01204909,0x00000000
|
||||
watchhit seq=7 mark=1 slot=0 dr6=0xffff0ff1 eip=0x012a1a87 value=8(0x00000008) tid=948 ebpret=0x0130ffcb ebpret2=0x0120f6e6 scan=0x0130ffcb,0x013dad98,0x0120f6e6,0x01204909
|
||||
watchhit seq=8 mark=1 slot=0 dr6=0xffff0ff1 eip=0x0130291d value=9(0x00000009) tid=948 ebpret=0x01310007 ebpret2=0x0120f6e6 scan=0x01310007,0x013dad98,0x0120f6e6,0x01204909
|
||||
watchhit seq=9 mark=1 slot=0 dr6=0xffff0ff1 eip=0x012c946e value=10(0x0000000a) tid=948 ebpret=0x013100a4 ebpret2=0x0120f6e6 scan=0x013100a4,0x013dad98,0x0120f6e6,0x01204909
|
||||
watchhit seq=10 mark=1 slot=0 dr6=0xffff0ff1 eip=0x0125c6f3 value=11(0x0000000b) tid=948 ebpret=0x723b7324 ebpret2=0x723b6ff4 scan=0x0141005c,0x012c946e,0x00000000,0x00000000
|
||||
watchhit seq=11 mark=1 slot=2 dr6=0xffff0ff4 eip=0x0125cc94 value=1(0x00000001) tid=948 ebpret=0x723b7324 ebpret2=0x723b6ff4 scan=0x0141005c,0x012c946e,0x00000000,0x00000000
|
||||
watchhit seq=12 mark=1 slot=2 dr6=0xffff0ff4 eip=0x01205055 value=4(0x00000004) tid=948 ebpret=0x0139dfa1 ebpret2=0x013a5651 scan=0x012317aa,0x01178f0f,0x0140c080,0x0124cac1
|
||||
watchhit seq=13 mark=1 slot=0 dr6=0xffff0ff1 eip=0x012592cd value=12(0x0000000c) tid=948 ebpret=0x72389974 ebpret2=0x72389644 scan=0x0140fca0,0x01026c32,0x00e81000,0x01205055
|
||||
watchhit seq=14 mark=1 slot=2 dr6=0xffff0ff4 eip=0x0125dd49 value=0(0x00000000) tid=948 ebpret=0x01204fe4 ebpret2=0x0139dfa1 scan=0x01410138,0x01204fe4,0x00eb4814,0x01214827
|
||||
watchhit seq=15 mark=1 slot=3 dr6=0xffff0ff8 eip=0x0125dd49 value=0(0x00000000) tid=948 ebpret=0x01204fe4 ebpret2=0x0139dfa1 scan=0x01410138,0x01204fe4,0x00eb4814,0x01214827
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
19:16:30:24.311 [tid 948] config: watch=on
|
||||
20:16:30:24.311 [tid 948] config: watch.players=2
|
||||
21:16:30:24.311 [tid 948] config: watch.mode=modcount
|
||||
22:16:30:24.311 [tid 948] config: watch.out=C:\SOTS\shim.watch.txt
|
||||
89:16:30:24.921 [tid 948] watch: VEH=01b37d58 text=[0x00e81000,0x0145ca00) out=C:\SOTS\shim.watch.txt
|
||||
90:16:30:24.936 [tid 948] watch: arm hook StrategyServer::ApplyAllTurnCommands rva=0x0038f6a0 va=0120f6a0 create=MH_OK enable=MH_OK
|
||||
108:16:33:53.478 [tid 948] watch: players vector @0e997424 begin=0e9b2940 end=0e9b2960 count=8
|
||||
109:16:33:53.478 [tid 948] watch: SELFTEST canary writes=1 traps=1 dr7=0xdddd0055 PASS
|
||||
110:16:33:53.478 [tid 948] watch: slot 0 -> S+0x8 (A2:ModCount / T:PhaseCounter) = 0x0e9973d8
|
||||
111:16:33:53.478 [tid 948] watch: slot 1 -> S+0xc (A2:Frame / addresses.json:ModCount) = 0x0e9973dc
|
||||
112:16:33:53.478 [tid 948] watch: slot 2 -> player[0]+0x164 Status = 0x0e9175f4
|
||||
113:16:33:53.478 [tid 948] watch: slot 3 -> player[1]+0x164 Status = 0x0e916a3c
|
||||
114:16:33:53.478 [tid 948] watch: ARMED on tid 948 dr7=0xdddd0055, S=0e9973d0 (ApplyAllTurnCommands this)
|
||||
115:16:33:54.368 [tid 9192] watchhit seq=0 mark=1 slot=0 dr6=0xffff0ff1 eip=0x012ec3e9 value=1(0x00000001) tid=948 ebpret=0x0130fce2 ebpret2=0x0120f6e6 scan=0x0130fce2,0x013dad98,0x0120f6e6,0x01204909
|
||||
116:16:33:54.368 [tid 9192] watchhit seq=1 mark=1 slot=0 dr6=0xffff0ff1 eip=0x012a1a87 value=2(0x00000002) tid=948 ebpret=0x0130ffcb ebpret2=0x0120f6e6 scan=0x0130ffcb,0x013dad98,0x0120f6e6,0x01204909
|
||||
117:16:33:54.368 [tid 9192] watchhit seq=2 mark=1 slot=0 dr6=0xffff0ff1 eip=0x0130fe0d value=3(0x00000003) tid=948 ebpret=0x0120f6e6 ebpret2=0x0139dfa1 scan=0x013dad98,0x0120f6e6,0x01204909,0x00000000
|
||||
118:16:33:54.368 [tid 9192] watchhit seq=3 mark=1 slot=0 dr6=0xffff0ff1 eip=0x012a1a87 value=4(0x00000004) tid=948 ebpret=0x0130ffcb ebpret2=0x0120f6e6 scan=0x0130ffcb,0x013dad98,0x0120f6e6,0x01204909
|
||||
119:16:33:54.368 [tid 9192] watchhit seq=4 mark=1 slot=0 dr6=0xffff0ff1 eip=0x0130fe0d value=5(0x00000005) tid=948 ebpret=0x0120f6e6 ebpret2=0x0139dfa1 scan=0x013dad98,0x0120f6e6,0x01204909,0x00000000
|
||||
120:16:33:54.368 [tid 9192] watchhit seq=5 mark=1 slot=0 dr6=0xffff0ff1 eip=0x012a1a87 value=6(0x00000006) tid=948 ebpret=0x0130ffcb ebpret2=0x0120f6e6 scan=0x0130ffcb,0x013dad98,0x0120f6e6,0x01204909
|
||||
121:16:33:54.368 [tid 9192] watchhit seq=6 mark=1 slot=0 dr6=0xffff0ff1 eip=0x0130fe0d value=7(0x00000007) tid=948 ebpret=0x0120f6e6 ebpret2=0x0139dfa1 scan=0x013dad98,0x0120f6e6,0x01204909,0x00000000
|
||||
122:16:33:54.368 [tid 9192] watchhit seq=7 mark=1 slot=0 dr6=0xffff0ff1 eip=0x012a1a87 value=8(0x00000008) tid=948 ebpret=0x0130ffcb ebpret2=0x0120f6e6 scan=0x0130ffcb,0x013dad98,0x0120f6e6,0x01204909
|
||||
123:16:33:54.368 [tid 9192] watchhit seq=8 mark=1 slot=0 dr6=0xffff0ff1 eip=0x0130291d value=9(0x00000009) tid=948 ebpret=0x01310007 ebpret2=0x0120f6e6 scan=0x01310007,0x013dad98,0x0120f6e6,0x01204909
|
||||
124:16:33:54.368 [tid 9192] watchhit seq=9 mark=1 slot=0 dr6=0xffff0ff1 eip=0x012c946e value=10(0x0000000a) tid=948 ebpret=0x013100a4 ebpret2=0x0120f6e6 scan=0x013100a4,0x013dad98,0x0120f6e6,0x01204909
|
||||
125:16:33:54.368 [tid 9192] watchhit seq=10 mark=1 slot=0 dr6=0xffff0ff1 eip=0x0125c6f3 value=11(0x0000000b) tid=948 ebpret=0x723b7324 ebpret2=0x723b6ff4 scan=0x0141005c,0x012c946e,0x00000000,0x00000000
|
||||
126:16:33:54.368 [tid 9192] watchhit seq=11 mark=1 slot=2 dr6=0xffff0ff4 eip=0x0125cc94 value=1(0x00000001) tid=948 ebpret=0x723b7324 ebpret2=0x723b6ff4 scan=0x0141005c,0x012c946e,0x00000000,0x00000000
|
||||
127:16:33:54.368 [tid 9192] watchhit seq=12 mark=1 slot=2 dr6=0xffff0ff4 eip=0x01205055 value=4(0x00000004) tid=948 ebpret=0x0139dfa1 ebpret2=0x013a5651 scan=0x012317aa,0x01178f0f,0x0140c080,0x0124cac1
|
||||
128:16:33:54.368 [tid 9192] watchhit seq=13 mark=1 slot=0 dr6=0xffff0ff1 eip=0x012592cd value=12(0x0000000c) tid=948 ebpret=0x72389974 ebpret2=0x72389644 scan=0x0140fca0,0x01026c32,0x00e81000,0x01205055
|
||||
129:16:33:54.368 [tid 9192] watchhit seq=14 mark=1 slot=2 dr6=0xffff0ff4 eip=0x0125dd49 value=0(0x00000000) tid=948 ebpret=0x01204fe4 ebpret2=0x0139dfa1 scan=0x01410138,0x01204fe4,0x00eb4814,0x01214827
|
||||
130:16:33:54.368 [tid 9192] watchhit seq=15 mark=1 slot=3 dr6=0xffff0ff8 eip=0x0125dd49 value=0(0x00000000) tid=948 ebpret=0x01204fe4 ebpret2=0x0139dfa1 scan=0x01410138,0x01204fe4,0x00eb4814,0x01214827
|
||||
48
verify/results/shim/watchpoints/w3-shim-log-excerpt.txt
Normal file
48
verify/results/shim/watchpoints/w3-shim-log-excerpt.txt
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
19:16:11:26.261 [tid 2420] config: watch=on
|
||||
20:16:11:26.261 [tid 2420] config: watch.players=2
|
||||
21:16:11:26.261 [tid 2420] config: watch.mode=tshn
|
||||
22:16:11:26.261 [tid 2420] config: watch.out=C:\SOTS\shim.watch.txt
|
||||
89:16:11:26.886 [tid 2420] watch: VEH=00b79158 text=[0x00e81000,0x0145ca00) out=C:\SOTS\shim.watch.txt
|
||||
90:16:11:26.902 [tid 2420] watch: arm hook StrategyServer::ApplyAllTurnCommands rva=0x0038f6a0 va=0120f6a0 create=MH_OK enable=MH_OK
|
||||
108:16:15:22.976 [tid 2420] watch: systems vector @0x0e4f7c9c first=0x1ee82490 last=0x1ee82500 end=0x1ee82500 count=28 (expect 28 on the reference game)
|
||||
109:16:15:22.976 [tid 2420] watch: NVO header probe -- ServerSystem+0x274 validated on 9 systems, +0x26c on 0; using +0x274 (pointer delta 0)
|
||||
110:16:15:22.976 [tid 2420] watch: sys[0] @0x0e4d2008 'Caladan' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
111:16:15:22.976 [tid 2420] watch: sys[1] @0x0e4d22e8 'Hyperion' AFlags=0x10 NVO=1 NVE=1 root=0x0e4e34f8 key=4 ok=1
|
||||
112:16:15:22.976 [tid 2420] watch: sys[2] @0x0e4d25c8 'Tully' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
113:16:15:22.976 [tid 2420] watch: sys[3] @0x0e4d28a8 'Foo' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
114:16:15:22.976 [tid 2420] watch: sys[4] @0x0e4d2b88 'Gamma Cephei' AFlags=0x1 NVO=1 NVE=1 root=0x1eea9490 key=0 ok=1
|
||||
115:16:15:22.976 [tid 2420] watch: sys[5] @0x0e4d2e68 'Biter' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
116:16:15:22.976 [tid 2420] watch: sys[6] @0x0e4d3148 'Flinx' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
117:16:15:22.976 [tid 2420] watch: sys[7] @0x0e4d3428 'Procyon' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
118:16:15:22.976 [tid 2420] watch: sys[8] @0x0e4d3708 'Kor?Voth' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
119:16:15:22.976 [tid 2420] watch: sys[9] @0x0e4e4be8 'Ke'Rassak' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
120:16:15:22.976 [tid 2420] watch: sys[10] @0x0e4e4ec8 'Koa?Tuun' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
121:16:15:22.976 [tid 2420] watch: sys[11] @0x0e4e51a8 'Spica' AFlags=0x0 NVO=1 NVE=0 root=0x1eea8c40 key=4 ok=1
|
||||
122:16:15:22.976 [tid 2420] watch: sys[12] @0x0e4e5488 'Junction' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
123:16:15:22.976 [tid 2420] watch: sys[13] @0x0e4e5768 'Nemesis' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
124:16:15:22.976 [tid 2420] watch: sys[14] @0x0e4e5a48 'Keu?Loko' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
125:16:15:22.976 [tid 2420] watch: sys[15] @0x0e4e5d28 'Ke'Dolarra' AFlags=0x2 NVO=1 NVE=1 root=0x1eea9360 key=1 ok=1
|
||||
126:16:15:22.976 [tid 2420] watch: sys[16] @0x0e4e6008 'Koa?Vo' AFlags=0x80 NVO=1 NVE=1 root=0x1eea9100 key=7 ok=1
|
||||
127:16:15:22.976 [tid 2420] watch: sys[17] @0x0e52f838 'Delphi' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
128:16:15:22.976 [tid 2420] watch: sys[18] @0x0e52ef80 'Kaa?Vaalu' AFlags=0x10 NVO=1 NVE=1 root=0x1eea9230 key=4 ok=1
|
||||
129:16:15:22.976 [tid 2420] watch: sys[19] @0x0e5300f0 'Mu Bootis' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
130:16:15:22.976 [tid 2420] watch: sys[20] @0x0e52f550 'Kor?Hakaan' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
131:16:15:22.976 [tid 2420] watch: sys[21] @0x0e5306c0 'Serpens' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
132:16:15:22.976 [tid 2420] watch: sys[22] @0x0e52ec98 'Markab' AFlags=0x10 NVO=1 NVE=1 root=0x1eea96f0 key=4 ok=1
|
||||
133:16:15:22.976 [tid 2420] watch: sys[23] @0x0e52f268 'Pascal' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
134:16:15:22.976 [tid 2420] watch: sys[24] @0x0e52fb20 'Kao?Lolto' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
135:16:15:22.976 [tid 2420] watch: sys[25] @0x0e52fe08 'Kea?Pono' AFlags=0x10 NVO=2 NVE=1 root=0x1eea8780 key=4 ok=1
|
||||
136:16:15:22.976 [tid 2420] watch: sys[26] @0x0e5303d8 'Gienah' AFlags=0x0 NVO=0 NVE=0 root=0x00000000 key=0 ok=0
|
||||
137:16:15:22.976 [tid 2420] watch: sys[27] @0x0e534ca0 'Ko'Rorkor' AFlags=0x10 NVO=1 NVE=1 root=0x1eea8f38 key=4 ok=1
|
||||
138:16:15:22.976 [tid 2420] watch: trade routes -- manager=0x0e4cdd38 (S+4+0x154) vector@mgr+0x3c first=0x00000000 last=0x00000000 end=0x00000000 count=0
|
||||
139:16:15:22.976 [tid 2420] watch: spy programs -- manager=0x0e512f68 (S+4+0x158) vector@mgr+0x10 first=0x00000000 last=0x00000000 end=0x00000000 count=0
|
||||
140:16:15:22.976 [tid 2420] watch: SELFTEST canary writes=1 traps=1 dr7=0xdddd0055 PASS
|
||||
141:16:15:22.976 [tid 2420] watch: slot 0 -> 'Spica' NVO root+0x10 {touched:i16,TShn:i16} key=player 4 = 0x1eea8c50
|
||||
142:16:15:22.976 [tid 2420] watch: slot 1 -> 'Spica' NVO._Mysize (=1 at arm) = 0x0e4e5420
|
||||
143:16:15:22.976 [tid 2420] watch: slot 2 -> trade routes vector _Mylast (mgr+0x40, count 0 at arm) = 0x0e4cdd78
|
||||
144:16:15:22.976 [tid 2420] watch: slot 3 -> spy programs vector _Mylast (mgr+0x14, count 0 at arm) = 0x0e512f7c
|
||||
145:16:15:22.976 [tid 2420] watch: ARMED on tid 2420 dr7=0xdddd0055, S=0e4f7c58 (ApplyAllTurnCommands this)
|
||||
146:16:15:24.585 [tid 1892] watchhit seq=0 mark=1 slot=0 dr6=0xffff0ff1 eip=0x011db95a value=131075(0x00020003) tid=2420 ebpret=0x011dbd4a ebpret2=0x011dbdc3 scan=0x011d29f6,0x014056db,0x011dbd4a,0x012ea806
|
||||
147:16:15:24.585 [tid 1892] watchhit seq=1 mark=1 slot=0 dr6=0xffff0ff1 eip=0x011db965 value=196611(0x00030003) tid=2420 ebpret=0x011dbd4a ebpret2=0x011dbdc3 scan=0x011d29f6,0x014056db,0x011dbd4a,0x012ea806
|
||||
148:16:15:24.585 [tid 1892] watchhit seq=2 mark=1 slot=0 dr6=0xffff0ff1 eip=0x011db95a value=196611(0x00030003) tid=2420 ebpret=0x011dbd4a ebpret2=0x011dbdc3 scan=0x011d29f6,0x014056db,0x011dbd4a,0x012ea806
|
||||
149:16:15:24.585 [tid 1892] watchhit seq=3 mark=1 slot=0 dr6=0xffff0ff1 eip=0x011db965 value=196611(0x00030003) tid=2420 ebpret=0x011dbd4a ebpret2=0x011dbdc3 scan=0x011d29f6,0x014056db,0x011dbd4a,0x012ea806
|
||||
4
verify/results/shim/watchpoints/w3-tshn-containers.txt
Normal file
4
verify/results/shim/watchpoints/w3-tshn-containers.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
watchhit seq=0 mark=1 slot=0 dr6=0xffff0ff1 eip=0x011db95a value=131075(0x00020003) tid=2420 ebpret=0x011dbd4a ebpret2=0x011dbdc3 scan=0x011d29f6,0x014056db,0x011dbd4a,0x012ea806
|
||||
watchhit seq=1 mark=1 slot=0 dr6=0xffff0ff1 eip=0x011db965 value=196611(0x00030003) tid=2420 ebpret=0x011dbd4a ebpret2=0x011dbdc3 scan=0x011d29f6,0x014056db,0x011dbd4a,0x012ea806
|
||||
watchhit seq=2 mark=1 slot=0 dr6=0xffff0ff1 eip=0x011db95a value=196611(0x00030003) tid=2420 ebpret=0x011dbd4a ebpret2=0x011dbdc3 scan=0x011d29f6,0x014056db,0x011dbd4a,0x012ea806
|
||||
watchhit seq=3 mark=1 slot=0 dr6=0xffff0ff1 eip=0x011db965 value=196611(0x00030003) tid=2420 ebpret=0x011dbd4a ebpret2=0x011dbdc3 scan=0x011d29f6,0x014056db,0x011dbd4a,0x012ea806
|
||||
Loading…
Add table
Reference in a new issue