7.3 KiB
W2 — predictions, written before any run
Lane W2, 2026-09-08. Committed before the VM runs and before the shim build (method rule 2).
Part 1 — multiplayer Tier 0
Restating lane G2's multiplayer-gamespy.md §7 as the things this lane will actually observe, and
where my expectation differs from G2's.
Setup: one Win10 guest (VM140), GOG 1.8.1, hooks=off (rule 19 — the instrument is removed for a
test whose whole point is "does the shipped game do this"). Capture is tcpdump on spicy's
tap140i0, i.e. outside the guest, so it cannot perturb the guest and it sees every packet that
leaves the VM. It does not see loopback traffic between the two instances — that is a known and
accepted blind spot, and it is the right trade: the predictions that matter (P1's "no GameSpy
traffic", P2's DNS query) are all about traffic that leaves.
Pre-run fact established before the test, and it matters for P2: from the guest,
swordots.available.gamespy.com returns NXDOMAIN ([System.Net.Dns]::GetHostAddresses throws
"No such host is known") while www.google.com resolves. So there is no stale wildcard record, and
the availability check will take exactly the gethostbyname == NULL arm G2 read at 0x0040a0f0.
That makes P2 a test of the fail-open reading specifically.
W2-P5 (G2's P5) — /concurrent reaches the main menu. A second Sword of the Stars.exe
started with /concurrent while the first is running exists as its own process with its own window,
rather than foregrounding the first and exiting. Falsified if the second process exits within
~20 s and only one PID remains.
Confidence: high. The CreateMutexA / ERROR_ALREADY_EXISTS / argv-compare chain is read out of
the binary and there is nothing conditional about it.
W2-P1 (G2's P1) — a direct join succeeds with no GameSpy traffic. With instance A hosting a LAN
multiplayer game and instance B started as
"Sword of the Stars.exe" /concurrent /join 127.0.0.1:3369, B reaches the multiplayer lobby and
appears in a slot on A. Over the whole run the tap capture shows zero packets to UDP 27900, UDP
27901, TCP 28910, TCP 6667 and zero DNS queries for any *.gamespy.com name after /join is
parsed. Falsified if any of those appear on the join path, or the join fails with a network error
while both instances are up.
Confidence: medium-high on the "no GameSpy traffic" half (static reading is clear), medium on the
"join succeeds" half — see G2's own (a)/(b)/(c) caveats, to which I add a fourth: the two
instances share C:\SOTS, one Profiles directory, one sots.ini and one SavedGames. Nothing
in G2's reading covers what the host does when a second process opens the same profile. If the join
fails, that is the first thing to rule out before concluding anything about the protocol.
W2-P2 (G2's P2) — the availability check fails open. On entering Join Multi-Player, at most
one DNS query for swordots.available.gamespy.com leaves the guest, it is answered NXDOMAIN, and
the UI does not show MATCHINGSERVICE_UNSUPPORTED / "Online support … is no longer available".
Falsified if that dialog appears.
I differ from G2 here in one respect: G2 predicts "exactly one DNS query". The Windows resolver
negative-caches, and the SDK's own retry is a socket retry not a DNS retry, so I expect one or
two queries and will not read two as a falsification of anything.
W2-P3 (G2's P3) — LAN discovery. B's LAN page lists A's game after a refresh. I rate this
lower than G2 does, for a reason G2 could not have known: the two instances are on the same
host, so the broadcast to 255.255.255.255:3369 and the QR2 socket are contending for one UDP
port on one machine. SO_REUSEADDR-less binds mean the second instance may fail to bind 3369 at
all. Falsified if the LAN list stays empty while a manual join to the same address succeeds — and
I expect that outcome to be more likely than not, and to be an artifact of the single-guest
setup rather than evidence about the mechanism.
W2-P4 (G2's P4) — self-hosted master server. Not attempted this lane. Tier 3 is a container deployment plus a DNS redirect; it is a full lane's work, it is not on the critical path to playing, and spending VM time on it before Tier 0 is proved would be the wrong order. Reported as not-run, not as unknown.
New — W2-P6: the dedicated server. sots_server.exe and Dedicated Server Launchpad.exe are
both present in C:\SOTS. The forum report G2 quotes says the dedicated server does not work. If
Tier 0 via two clients is obstructed by the shared-install problem, this is the fallback host; if
Tier 0 works, running it is a cheap bonus data point on a reproducible community bug. No prediction
is offered — I have not read it, and G2 did not either.
Part 2 — ModCount watchpoint
Lane A2's prediction is adopted unchanged (alliance-mask-and-modcount.md §3): exactly 12 hits
on a 4-byte write watchpoint at (char*)S + 8, two at 0x007dc6f0 and 0x007d92ca, ten with a
return address in StrategySim::ApplyTurnCommandBatch 0x0088f9b0 or its callers, none at
0x007b9e20, and all before ProcessTurn is entered. Falsifiers (a)-(d) as A2 wrote them.
My own addition, and it is a rule-19 addition. A hardware watchpoint is an instrument too, and a
more invasive one than a MinHook detour: it single-steps nothing but it does take an exception on
every write, and the handler runs in the game's thread. So the run is only trustworthy if the
autosave it produces is byte-identical to the hooks=off autosave from the same input save. I
will take that control. If the watchpoint perturbs the turn, the hit list is still useful as an
enumeration of writers but the count is not a measurement of the un-instrumented game, and I
will say so rather than reporting 12-or-not as if it were.
Prediction on the control itself: the watchpoint is byte-neutral. A #DB on a data write is
delivered after the store retires, the handler restores state exactly, and there is no code
modification anywhere — unlike a detour, which is what §2 of tail-probes.md caught. If this
control fails, that is the more interesting result and it says something about the perturbation
mechanism lane H left undetermined.
Part 3 — Player.Status and TShn
Same instrument, different addresses; both are write watchpoints with a per-hit EIP + return-address record.
Player.Status. Prediction: at least one writer after tail phase 31 and before the autosave, at an EIP outside the tail-phase code, writing 4. The regression is that the phase writes 1 and the file carries 4. Falsified if the only write of 4 happens inside phase 31, which would mean the phase catalog's attribution of phase 31 is wrong rather than a later writer existing.TShn(player+0x274map). E3 named Spica vs Bismol as the discriminating pair and proved the gate is notAFlags. Prediction: the writer is a single site reached once per qualifying system, and the discriminant is a property of the system rather than of the player — because a player-level gate would have movedTShnfor Spica too. Falsified if the writes come from two or more EIPs, or if the EIP is inside a per-player loop with no system in scope.
Both of these are lower priority than ModCount because ModCount's prediction is numeric and
theirs is not.