sots-re/findings/subsystems/rcex-explained.md
alex 92ad44efb0 lane W2: multiplayer Tier 0 played end to end with no server; ModCount/Frame/Status watchpoints; rcex explained
Multiplayer: two clients on one guest, joined by typed IP and by LAN browse, launched a
2-player game and played two turns in lockstep. 44,319 packets captured outside the guest,
ZERO to any GameSpy port. Availability check fails open exactly as lane G2 read it; Join
Manually is enabled with every gamespy.com name dead, falsifying G2's own caveat (a). The
shipped MOTD is fetched live from kerberos-productions.com and tells players to host in LAN
mode -- the developer's own statement of the same finding.

Watchpoints: a new shim module arms DR0-DR3 from StrategyServer::ApplyAllTurnCommands.
ModCount takes exactly 12 writes per End Turn (lane A2's prediction, confirmed twice, both
predicted addresses exact); Frame takes exactly one, from BeginProcessTurn+0x2a, settling the
ModCount-vs-Frame naming in A2's favour. The Player.Status writer between tail phase 31 and
the autosave EXISTS and is StrategyNetworkClient::OnMessage+0xa15 -- correcting lane T2.
Rule 19 control: the armed run reproduced the determinism oracle byte for byte.

rcex: closed from the corpus alone. It is sixteen 4-bit per-player counters; nibble p is set
to 1 on the turn the system enters player p's AFlags and ticked to 0 the next turn. 7/7
across two different games.
2026-09-08 15:23:52 -04:00

5.5 KiB

rcex — a per-player nibble array, and the rule is one line

  • Type: subsystem (field semantics)
  • Address / RVA: ServerSystem field, streamed as int64 (struct-recovery.md row 0xf8); ticked inside ServerSystem::ProcessTurn (the "rcex tick", already in addresses.json's body-order note for 0x0074…)
  • Status: mapped — rule holds 7/7 across the corpus, from the saves alone, no VM time
  • Confidence: high on the rule, medium on the name of the event that sets it
  • Owner / date: lane W2 · 2026-09-08
  • Closes: system-visibility-prediction.md §"What the model deliberately does not write" — "rcex (6 leaves per pair). Unexplained."; system-visibility-record.md §8 row "rcex | unassigned"

The rule

rcex is a 64-bit array of sixteen 4-bit counters, one per player index. Player p's counter is nibble p, i.e. bits 4p … 4p+3.

On the turn where a system enters player p's AFlags — the per-player acquisition/observation bit — nibble p is set to 1. On the next turn the tick in ServerSystem::ProcessTurn decrements it to 0. That is the whole of the observed 0 → 1<<16 → 0 behaviour, and it is exactly the shape strategic-turn-internals.md guessed at ("Bats2 / rcex 64-bit nibble arrays: per-player 4-bit countdowns (battle / recon cooldown)") without being able to pin the index.

The evidence

Every system in the 11-save corpus with a non-zero rcex, with its AFlags beside it:

save frame system rcex set bit ⇒ nibble AFlags ⇒ set bit nibble value
turn2-state 2 Hyperion 65536 16 4 16 4 1
turn2-state 2 Koa'Vo 268435456 28 7 128 7 1
turn2-state 2 Kaa'Vaalu 65536 16 4 16 4 1
turn2-state 2 Markab 65536 16 4 16 4 1
turn2-state 2 Kea'Pono 65536 16 4 16 4 1
turn2-state 2 Ko'Rorkor 65536 16 4 16 4 1
human-turn2-orders 2 Terra 4096 12 3 8 3 1

7 for 7. nibble index == the index of the single set AFlags bit, and the nibble's value is always 1. The seventh row is the strong one: human-turn2-orders.sav is a different game with a different map, a different system and a different player index, and it obeys the same rule.

Every other save in the corpus — turn1-state, turn3-state, human-turn3-noderoute, and all six Zuul saves from frame 5 to frame 23 — has rcex == 0 on all 28 systems, which is what a counter that decays to zero in one turn looks like when nothing was acquired that turn.

That also explains the system-visibility-record.md §7 observation directly: rcex moves 0 → 65536 on turn1 → turn2 and back 65536 → 0 on turn2 → turn3 on the same six systems, and Koa'Vo takes 1 << 28 rather than 1 << 16 — because Koa'Vo was acquired by player 7 and the other five by player 4.

Why this had to be six leaves per pair and not more

The visibility model's residual was "6 leaves per pair" for rcex. It is six because six systems changed hands in the turn-1→turn-2 pair and each contributes one int64 leaf. The count was never about rcex being complicated; it was about nobody having connected the nibble index to the player index.

How this could be wrong, and the symptom of each way (rule 2)

  • The index is not the player index but something correlated with it on this corpus. Every save here has single-bit AFlags on every system — the corpus limitation system-visibility-prediction.md already flags — so "nibble index == player index" and "nibble index == index of the lowest set AFlags bit" are indistinguishable. Falsifying workload: a save where two players acquire the same system on the same turn. The rule predicts two nibbles set to 1; a lowest-bit rule predicts one.
  • The initial value is not always 1. Every observed nibble is 1. A longer cooldown (2, 3) would appear on some other event. Symptom: a nibble > 1, and a system whose rcex takes two turns to reach zero. Nothing in the corpus shows one.
  • It counts something other than acquisition. AFlags, VFlags and EFlags are all equal on all seven rows, so this corpus cannot separate them. Falsifying workload: any save where a system's VFlags and AFlags disagree.
  • The decrement is not in ServerSystem::ProcessTurn. The "rcex tick" is named in that function's body-order note in addresses.json, between the Bats2 tick and the haltv clear; the corpus is consistent with it but does not prove the site. This is the one item that wants a watchpoint — and it is now cheap: src/shim/hooks/watchpoints.cpp arms four 4-byte write watchpoints from a known this, and rcex is at a fixed offset in ServerSystem. See findings/control-flow/watchpoints-modcount-status.md §7.

Cross-refs

  • findings/objects/struct-recovery.md (the int64 typing, and the warning that "R2 int is wrong")
  • findings/subsystems/strategic-turn-internals.md (the nibble-array reading this confirms)
  • findings/subsystems/system-visibility-record.md §7-8 and findings/subsystems/system-visibility-prediction.md (the open item this closes)
  • Bats2 is the same shape and is still unassigned. It is 0 on every system of every corpus save, so it is a rule-6 hypothesis: the nibble-per-player reading is inherited from rcex and has never been exercised. The workload is a save taken on the turn after a battle.