From 3109cfc49a672018fa7004c7f6ee1f7896ec5be6 Mon Sep 17 00:00:00 2001 From: Joe DiPrima Date: Sun, 2 Aug 2026 10:40:51 -0500 Subject: [PATCH] #116: steam JOINERS were silently locked to VETERAN -- restore their experience selector Oracle isolated it: "standard mode still having heat and leaks only occurred in steam not in solo. Sauron and Lynx did not have the issue but Conn Man, Rajel and myself did." The night-9 logs close the case: affected (Oracle/ConnMan/Rajel): [exp] experience=2 heatModelOn=1 clean (Sauron's std sessions): [exp] experience=1 heatModelOn=0 CAUSE: the glass front end's JOIN layout trimmed the menu to "the mech list + the JOIN button; everything else is the operator's call" -- and the experience selector's hidden default is 2 (veteran). So HOSTS got whatever they picked (Sauron hosted the lobbies all night -> his standard landed), while every JOINER launched as veteran regardless of anything they did -- which is why Oracle's cycling-the-settings experiment changed nothing, and why solo (full menu) never showed the problem. heat >= veteran, hence "standard but heating". DESIGN CONFIRMED BY THE USER before fixing: experience is PER-PLAYER by the original design -- the sysop set each user's tier and mixed-experience matches were legal. Architecture already supports it end to end: each node's master player reads its OWN egg's experience (btMission+0xE4 -> BTPlayer @004c0bc8), so per-node choice IS per-player choice. FIX: the join layout gains the GroupExperience selector (the selection->egg write path is the pre-existing host path, field-proven). Also: the front end now prints "[fe] pilot experience= (join|host/solo)" at launch, so every future field log answers this class of report without asking anyone. VERIFICATION BOUNDARY, stated plainly: builds both configs; the write path is shared with the host flow which the field already exercises; the visible selector + the [fe] line need one join-mode launch to eyeball, and the field re-test is Oracle running a standard steam match as a JOINER and seeing no heat. Tooling note recorded in test-harness.md: bash-heredoc python collapses one backslash level even single-quoted -- a "\n" arrives as a real newline and replaces silently no-op. Build backslashes from bytes([92]); verify replaces by length delta, not by the script saying "fixed". (This burned four edit rounds tonight and several earlier C2001 hunts.) Co-Authored-By: Claude Opus 5 (1M context) --- context/test-harness.md | 5 +++++ game/glass/btl4fe.cpp | 16 ++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/context/test-harness.md b/context/test-harness.md index 01d1762..61f27d8 100644 --- a/context/test-harness.md +++ b/context/test-harness.md @@ -117,6 +117,11 @@ sleep ; kill $relay; bt_kill_ours - **Stale exe:** `LNK1104` / a bench ignoring new diagnostics = the previous process still holds `btl4.exe`, or the build silently didn't run — check the `btl4.vcxproj ->` line printed, then rerun. +- **Editing source through bash-heredoc python collapses `\` one level** even + single-quoted -- a `"\n"` you meant as backslash-n arrives as a real newline, + the "fix" silently no-ops (`good == bad`), and the C2001 hunt repeats. Build + backslashes as `bytes([92])`/`chr(92)`, and verify a replace by LENGTH DELTA + or a re-read, never by the script printing "fixed". - Bench artifacts (`*.EGG` copies, `*_NNN.png`, bench logs) must be deleted from `content/` before any dist cut; field logs are never committed. diff --git a/game/glass/btl4fe.cpp b/game/glass/btl4fe.cpp index 5aeafb5..5723b14 100644 --- a/game/glass/btl4fe.cpp +++ b/game/glass/btl4fe.cpp @@ -742,8 +742,18 @@ static void { // JOIN trim: the mech list + the JOIN button; callsign is the edit // control (repositioned in BTFrontEnd_Run). Everything else is the - // operator's call. - y = MenuTopY; AddGroup(GroupVehicle, MenuCol2X, &y); + // operator's call -- EXCEPT experience, which is PER-PLAYER by the + // original design (the sysop set each user's tier; mixed-experience + // matches were legal), and each node's master reads its OWN egg's + // value, so mixing already works end to end. + // + // #116: this group was missing here, so joiners silently launched with + // the hidden menu default (veteran) no matter what they believed they + // selected -- "standard mode still having heat and leaks only occurred + // in steam": hosts got their pick, all three affected players were + // JOINERS, and the one clean player (Sauron) was the one HOSTING. + y = MenuTopY; AddGroup(GroupVehicle, MenuCol2X, &y); + y = MenuTopY; AddGroup(GroupExperience, MenuCol3X, &y); AddButton(GroupLaunch, MenuCol5X, MenuClientH - 190, 220, 52); return; } @@ -1173,6 +1183,8 @@ int strcpy(self.vehicle, kVehicles[menu.selection[GroupVehicle]].key); strcpy(self.color, kColors[menu.selection[GroupColor]].key); strcpy(self.experience, kExperience[menu.selection[GroupExperience]].key); + printf("[fe] pilot experience=%s (%s)\n", self.experience, + FeJoinOnly() ? "join" : "host/solo"); strcpy(self.badge, kBadges[menu.selection[GroupBadge]].key); strcpy(self.patch, kPatches[menu.selection[GroupPatch]].key); strcpy(self.dropzone, kDropZones[menu.selection[GroupDropZone]].key);