From ab79e16843a9d7c83a8906e17fb9c1bc2db4cdad Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 8 Sep 2026 12:51:39 -0400 Subject: [PATCH] board: multiplayer revival - direct IP joins without GameSpy; identity constants; ranked plan --- campaign/board.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/campaign/board.md b/campaign/board.md index 076885b..8306f18 100644 --- a/campaign/board.md +++ b/campaign/board.md @@ -219,3 +219,8 @@ Status flow: `backlog → in-progress → mapped → verified` (or `blocked`). | P2 predictions: written first, then checked offline | verify | verified | high | 100% | 2026-09-08 | Three predictions written from the disassembly BEFORE checking, then run against all 11 saves: **58 waypoints, 46 flight plans, 0 failures**. nrt == Wpt on all 57 type-3 records; the nrf chain closes through `pnd` across the three-hop plans; nrp splits exactly as the branch structure says - Human (cannot bore) has only real indices, Zuul has a MIX of real indices and -1, which is what the bore path writes. AND A BONUS: turn3-state.sav's Tp=1 with nrt{-1,0,0} can only be a TARKA fleet by species elimination - a live confirmation of a SECOND table row from a save nobody was looking at. Also corrected: type 5 is a HIVER GATE THROW at a gateless system within CstR, NOT the Zuul bore; CstR now has a reader; GFlags(+0xdc) is the per-player GATE MASK (closing lane B5's open item); `pnd` is the node transit's ORIGIN id; FtTrans is a SECOND SAVED COPY of wpts[0].Tp | | P4: a ~1-minute VM test of a predicted ORIGINAL bug | verify | backlog | — | 0% | 2026-09-08 | Lane P2, for whoever holds VM140: order a fleet to the system it is ALREADY AT, then onward. The leading-destination drop happens in the solver's frame only, so types[]/routes[] end up SHIFTED BY ONE and the last slot is never written. PREDICTED SYMPTOM: the final waypoint saves as Tp 0 with an empty route. Note the framing - a CLEAN result FALSIFIES the reading rather than merely failing to confirm it | | P2 honest boundary | meta | verified | high | 100% | 2026-09-08 | Fully read: FUN_007066c0, three siblings and 20 helpers. NOT read: FUN_00705d60's tanker fold past ~0x90 bytes (the input to EVERY range decision, the largest remaining hole in the NUMBERS), the node bore, the ranking term and hash walk inside FindNodeLine, the relation scale, and MoveFleet's two classifier call sites. THREE INDIRECT EDGES ON THE MAIN LINE UNRESOLVED, the most important being `(fleet->galaxy)->vft[1]()`, the node-graph getter the whole node branch hangs off - vtable_map.py plus an image-wide E8/E9 sweep confirm the function has exactly two direct callers and no tail-call thunks, but those three are OUTBOUND edges lane V2's inversion does not reach. AND NOT ONE LEG HAS EVER BEEN OBSERVED EXECUTING UNDER AN INSTRUMENT - only its saved output has. Also caught internally: one test check was VACUOUS (the float32-squaring probe never fired) - found and fixed with an unconditional witness | +| MULTIPLAYER: GameSpy is NOT required to join | subsystems | verified | high | 95% | 2026-09-08 | Lane G2's lead answer - THIS IS A WEEKEND, NOT A PROJECT. Three GameSpy-free paths into a session, all verified from the binary and all reachable in the shipped UI: (1) MANUAL JOIN / FAVORITES - the accept handler at 0x0076ef90 calls App_StartJoin(params, address, serverBrowser = **NULL**), and a NULL browser makes StrategyJoin's ctor take its DIRECT ARM (state +0x80 = 1) straight to App_ConnectToStrategyHost 0x0089a7e0 and the game's own UDP connection - NO availability check, NO peerInitialize, NO browser, NO NatNeg; (2) COMMAND LINE `/join `; (3) LAN BROWSE - ServerBrowserNew(lanBrowse=1) with NO availability gate, broadcasting UDP 3369-3370, and LAN HOSTING takes a branch that calls qr2_init_socket(ispublic=0) and SKIPS GSIStartAvailableCheck ENTIRELY at 0x007c5088. CROSS-CHECK: NNBeginNegotiationWithSocket has exactly two call sites image-wide, NEITHER on the direct-address arm. CORROBORATED OUTSIDE THE BINARY: Kerberos staff 2012-12-08, "host needs to start their game in LAN mode, then other can join direct connect. It will not work in Internet mode" | +| GameSpy identity constants + hosts-redirect list | subsystems | verified | high | 100% | 2026-09-08 | From the .rdata constant table at 0x00a35cd4/0x00a35cd8: gamename **swordots**, secret key **Z5gR9Z**, game id **1381** - INDEPENDENTLY CONFIRMED by OpenSpy/UniSpy/333networks/gslist, which all carry row `1381 swordots Z5gR9Z`. Hosts-redirect targets: swordots.available.gamespy.com:27900/UDP, swordots.master.gamespy.com:27900/UDP, **swordots.ms5.gamespy.com:28910/TCP** (the `ms5` DERIVED by re-implementing the binary's own hash fold `h = tolower(c) - h*0x63306ce7`, % 20), natneg1/2.gamespy.com:27901/UDP, peerchat.gamespy.com:6667/TCP. Game's own ports: HostPort 3369, CombatHostPort 3370. LINKED AND CALLED: availability check, serverbrowsing SB v2, QR2, NatNeg, Peer SDK, peerchat, ghttp (MOTD only). ABSENT ENTIRELY - no string, no host, no code: GP/GPCM/GPSP login, motd/gamestats/sake/atlas, keymaster, and **CD-KEY VALIDATION OF ANY KIND** (no CDKEY substring in 49,781 strings) | +| multiplayer revival plan, ranked | subsystems | mapped | high | 90% | 2026-09-08 | TIER 0 (hours): VPN/LAN + manual join, NO SERVER AT ALL. TIER 1 (afternoon): availability responder, specified BYTE-FOR-BYTE - request `09 00 00 00 00 "swordots" 00`, reply `FE FD 09 00 00 00 00` - and LIKELY A NO-OP, because the check FAILS OPEN on DNS failure (socket == -1 returns GSIACAvailable); MATCHINGSERVICE_UNSUPPORTED was GameSpy's KILL-SWITCH, not self-detection. TIER 3 COLLAPSES TO CONFIGURATION, NOT CODE: OpenSpy and UniSpyServer both implement the exact service set and both already ship the swordots row; adding a title is one DB row plus SyncToRedis. CAVEATS, STATED: swordots is SEEDED not on OpenSpy's 132-title tested list; the public openspy.net availability responder returns "available" for ANY gamename so it PROVES NOTHING - self-host; the seeded row says queryport 6500 while SOTS reports on 3369; and its keylist omits SOTS's 16 custom QR2 keys (slot0..7, numslots, mapshape, numsys, turn, scenario, settings, slot_, ranks - ids 50-65, recovered here). 333networks RULED OUT: GameSpy v0 only, no SB v2/NatNeg/peerchat | +| two clients on ONE guest: /concurrent | meta | verified | high | 100% | 2026-09-08 | THE GAME SHIPS THE TWO-INSTANCE SWITCH. WinMain compares argv against `/concurrent` on ERROR_ALREADY_EXISTS and CONTINUES on a match - so `/concurrent /join 127.0.0.1:3369` gives two clients on one guest, NO CLONED VM NEEDED. Address parsing is DOTTED-QUAD ONLY (`localhost` is rejected). Full switch list: /join, /concurrent, /startup:, /motd_, /tell | +| CORRECTIONS from lane G2 | meta | verified | high | 100% | 2026-09-08 | ui-screen-map.md corrected in place on two counts: there are 13 NETERROR_* strings and NONE IS CD-KEY (the "21 incl. 6 CD-key" and "CD-key strings survive" claims were WRONG), and the "2017 build knows GameSpy is dead" inference is replaced with the FAIL-OPEN finding. Also: **SOTS1's backend died December 2012, not the May 2014 general shutdown** - its agreement ran through Lighthouse Interactive |