From 22f732fcfb59520b0a3d3e6561ae867eba2f0995 Mon Sep 17 00:00:00 2001 From: Joe DiPrima Date: Tue, 4 Aug 2026 11:24:05 -0500 Subject: [PATCH] Steam BUILD GATE: same-zip lobbies only (#108 confound killer) Mixed-build lobbies silently corrupt raw-struct replication (night-9: one stale-zip player, one desynced stream, ghost mechs + K/D doubt). Three additive edits in btl4lobby.cpp: 1. HOST stamps the lobby with its exact build (btl4ver = BT_VERSION_STRING). 2. JOINER's lobby search filters on build equality -- a stale-zip player simply finds no lobby, and the "no lobby found" log line NAMES the local build so the report is self-diagnosing (#68's silent-exit lesson). 3. Post-entry verify (covers invites/direct joins + unstamped older hosts): mismatch -> log both versions loudly, LeaveLobby, fail the join. LAN/relay (join.bat) handshake remains a separate 717 item -- this covers the Steam path the operator asked about. Co-Authored-By: Claude Fable 5 --- game/glass/btl4lobby.cpp | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/game/glass/btl4lobby.cpp b/game/glass/btl4lobby.cpp index 480ea91..2f80f90 100644 --- a/game/glass/btl4lobby.cpp +++ b/game/glass/btl4lobby.cpp @@ -13,6 +13,7 @@ #pragma pack(push, 8) #include "steam/steam_api.h" #include "steam/isteammatchmaking.h" +#include // BUILD GATE (#108 confound): exact-build lobbies #pragma pack(pop) // @@ -346,6 +347,12 @@ int } currentLobby = created.m_ulSteamIDLobby; SteamMatchmaking()->SetLobbyData(currentLobby, "btl4", "1"); + // BUILD GATE: stamp the host's exact build on the lobby. Joiners filter + // on equality (below) and verify after entry -- a mixed-build lobby + // silently corrupts raw-struct replication (the night-9 ghost confound: + // one stale-zip player desyncs one connection's stream). Same build or + // no entry. + SteamMatchmaking()->SetLobbyData(currentLobby, "btl4ver", BT_VERSION_STRING); PublishSelf(pilot_name, vehicle, color, experience); LobbyLog("host: lobby up (%llu)", (unsigned long long)created.m_ulSteamIDLobby); @@ -427,6 +434,11 @@ int ISteamMatchmaking *matchmaking = SteamMatchmaking(); matchmaking->AddRequestLobbyListStringFilter( "btl4", "1", k_ELobbyComparisonEqual); + // BUILD GATE: only lobbies stamped with OUR exact build are visible. A + // stale-zip player finds nothing -- and the log names the build so the + // "no lobby found" report is self-diagnosing (#68's silent-exit lesson). + matchmaking->AddRequestLobbyListStringFilter( + "btl4ver", BT_VERSION_STRING, k_ELobbyComparisonEqual); SteamAPICall_t call = matchmaking->RequestLobbyList(); LobbyMatchList_t match_list; memset(&match_list, 0, sizeof(match_list)); @@ -434,7 +446,9 @@ int LobbyMatchList_t::k_iCallback, 15000) != 0 || match_list.m_nLobbiesMatching == 0) { - LobbyLog("join: no btl4 lobby found"); + LobbyLog("join: no btl4 lobby found for build %s " + "(a lobby on a DIFFERENT build is invisible by design -- " + "host and joiners must run the same zip)", BT_VERSION_STRING); return -1; } @@ -450,6 +464,22 @@ int return -1; } currentLobby = lobby; + // BUILD GATE belt-and-suspenders: the list filter covers discovery, but + // verify the entered lobby too (covers invite/direct joins and any + // filter drift). Mismatch = leave loudly, never play version-skewed. + { + const char *host_ver = matchmaking->GetLobbyData(currentLobby, "btl4ver"); + if (host_ver == NULL || strcmp(host_ver, BT_VERSION_STRING) != 0) + { + LobbyLog("join: BUILD MISMATCH -- host runs %s, this machine runs %s; " + "leaving (both sides must run the same zip)", + (host_ver && *host_ver) ? host_ver : "", + BT_VERSION_STRING); + matchmaking->LeaveLobby(currentLobby); + currentLobby = CSteamID(); + return -1; + } + } PublishSelf(pilot_name, vehicle, color, experience); LobbyLog("join: in lobby, waiting for GO");