#116 THE REAL STEAM CAUSE: experience never rode the lobby -- joiners' pages were authored by the HOST as the "veteran" fallback
The user's question broke my first story: "did lynx host tho?" He did (Oracle joined LYNX's lobby that night) -- but more importantly the affected players' logs read mode=steam, which gets the FULL menu, so the FeJoinOnly fix in3109cfcpatched a real gap (mode=join) that is NOT the path the affected trio ran. Oracle COULD see and cycle the selector; it still didn't matter. Traced: * The steam lobby serialized exactly three member fields: nm / vh / cl. Experience never travelled. * The steam HOST rebuilds mission.pilots[] from the lobby roster (name/vehicle/color only), so every non-self page has EMPTY experience -- and BTFeMission_WriteEgg stamps the "veteran" fallback on empty. * The steam JOINER writes NO egg at all (spec->eggPath[0]=0); his mission arrives from the HOST's console feed -- i.e. the host's egg, where HIS page says veteran no matter what his own console shows. * The host's own page keeps his menu choice only as slot-0 residue of the pre-roster self fill -- why BOTH hosts (Sauron AND Lynx) were clean while all three joiners heated in "standard". FIX: experience is now published as lobby member data ("xp") beside nm/vh/cl, unpacked into BTLobbyMember, and stamped onto each member's egg page in the host's roster loop. Both exported lobby entry points + PublishSelf gain the parameter; the fe passes the local menu selection. An older client in the lobby simply has no "xp" -- the veteran fallback applies to THAT member only, exactly the pre-fix behavior, so mixed-build lobbies do not break. Per-player mixing (the original design, per the user: the sysop set each user's tier; mixed matches were legal) now works over steam end to end: each member's OWN choice -> lobby -> the host-authored egg page -> the console feed -> that node's master player. VERIFICATION BOUNDARY: builds both configs; the chain is code-traced; the lobby legs need a real Steam session -- field re-test is a JOINER selecting Standard and logging [exp] experience=1 heatModelOn=0 (plus the [fe] line on both sides). The3109cfcjoin-trim selector stays: correct for mode=join. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3109cfc49a
commit
2df1369168
@@ -1199,7 +1199,7 @@ int
|
||||
if (menu.steamAction == 1)
|
||||
{
|
||||
BTLobbyRoster roster;
|
||||
if (BTLobby_HostAndRoom(self.name, self.vehicle, self.color,
|
||||
if (BTLobby_HostAndRoom(self.name, self.vehicle, self.color, self.experience,
|
||||
&roster, spec->steamMyToken, sizeof(spec->steamMyToken),
|
||||
spec->steamMap, sizeof(spec->steamMap)) != 0 ||
|
||||
roster.memberCount == 0)
|
||||
@@ -1220,6 +1220,12 @@ int
|
||||
strncpy(pilot.color,
|
||||
member.color[0] ? member.color : kColors[0].key,
|
||||
sizeof(pilot.color) - 1);
|
||||
// #116: the member's OWN experience choice rides the lobby now;
|
||||
// absent (older build in the lobby) leaves the field empty and the
|
||||
// egg writer's "veteran" fallback applies -- the old behavior,
|
||||
// but now only for clients that never published a choice.
|
||||
strncpy(pilot.experience, member.experience,
|
||||
sizeof(pilot.experience) - 1);
|
||||
sprintf(pilot.address, "%s:%d",
|
||||
member.fakeAddress, member.gamePort);
|
||||
if (!member.isSelf)
|
||||
@@ -1238,7 +1244,7 @@ int
|
||||
}
|
||||
if (menu.steamAction == 2)
|
||||
{
|
||||
if (BTLobby_JoinAndWait(self.name, self.vehicle, self.color,
|
||||
if (BTLobby_JoinAndWait(self.name, self.vehicle, self.color, self.experience,
|
||||
spec->steamMyToken, sizeof(spec->steamMyToken),
|
||||
spec->steamMap, sizeof(spec->steamMap)) != 0)
|
||||
{
|
||||
|
||||
@@ -83,7 +83,8 @@ static int
|
||||
static CSteamID currentLobby;
|
||||
|
||||
static void
|
||||
PublishSelf(const char *pilot_name, const char *vehicle, const char *color)
|
||||
PublishSelf(const char *pilot_name, const char *vehicle, const char *color,
|
||||
const char *experience)
|
||||
{
|
||||
//
|
||||
// Identity is implicit (the member's SteamID); the roster TOKENS are
|
||||
@@ -93,6 +94,7 @@ static void
|
||||
matchmaking->SetLobbyMemberData(currentLobby, "nm", pilot_name);
|
||||
matchmaking->SetLobbyMemberData(currentLobby, "vh", vehicle);
|
||||
matchmaking->SetLobbyMemberData(currentLobby, "cl", color);
|
||||
matchmaking->SetLobbyMemberData(currentLobby, "xp", experience);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -118,6 +120,14 @@ static int
|
||||
strncpy(out->color,
|
||||
matchmaking->GetLobbyMemberData(lobby, member, "cl"),
|
||||
sizeof(out->color) - 1);
|
||||
{
|
||||
// #116: experience rides the lobby too -- absent (an older build in
|
||||
// the lobby) leaves it empty and the egg writer's "veteran" fallback
|
||||
// applies, same as before this field existed.
|
||||
const char *xp = matchmaking->GetLobbyMemberData(lobby, member, "xp");
|
||||
if (xp != NULL)
|
||||
strncpy(out->experience, xp, sizeof(out->experience) - 1);
|
||||
}
|
||||
out->isSelf = (member == SteamUser()->GetSteamID());
|
||||
if (out->name[0] == 0)
|
||||
{
|
||||
@@ -312,6 +322,7 @@ static int
|
||||
int
|
||||
BTLobby_HostAndRoom(
|
||||
const char *pilot_name, const char *vehicle, const char *color,
|
||||
const char *experience,
|
||||
BTLobbyRoster *roster_out,
|
||||
char *my_token_out, int my_token_capacity,
|
||||
char *steam_map_out, int steam_map_capacity)
|
||||
@@ -335,7 +346,7 @@ int
|
||||
}
|
||||
currentLobby = created.m_ulSteamIDLobby;
|
||||
SteamMatchmaking()->SetLobbyData(currentLobby, "btl4", "1");
|
||||
PublishSelf(pilot_name, vehicle, color);
|
||||
PublishSelf(pilot_name, vehicle, color, experience);
|
||||
LobbyLog("host: lobby up (%llu)", (unsigned long long)created.m_ulSteamIDLobby);
|
||||
|
||||
if (RunRoom(1) != 0)
|
||||
@@ -403,6 +414,7 @@ int
|
||||
int
|
||||
BTLobby_JoinAndWait(
|
||||
const char *pilot_name, const char *vehicle, const char *color,
|
||||
const char *experience,
|
||||
char *my_token_out, int my_token_capacity,
|
||||
char *steam_map_out, int steam_map_capacity)
|
||||
{
|
||||
@@ -438,7 +450,7 @@ int
|
||||
return -1;
|
||||
}
|
||||
currentLobby = lobby;
|
||||
PublishSelf(pilot_name, vehicle, color);
|
||||
PublishSelf(pilot_name, vehicle, color, experience);
|
||||
LobbyLog("join: in lobby, waiting for GO");
|
||||
|
||||
int result = RunRoom(0);
|
||||
|
||||
@@ -29,6 +29,11 @@ struct BTLobbyMember
|
||||
int gamePort; // token game port (1502)
|
||||
char vehicle[16];
|
||||
char color[16];
|
||||
char experience[16]; // #116: per-player by design (sysop-set
|
||||
// in the pod; mixed matches legal) --
|
||||
// without it on the wire, the host's
|
||||
// egg authored every JOINER as the
|
||||
// WriteEgg "veteran" fallback
|
||||
int isSelf;
|
||||
unsigned long long steamID;
|
||||
};
|
||||
@@ -46,6 +51,7 @@ struct BTLobbyRoster
|
||||
int
|
||||
BTLobby_HostAndRoom(
|
||||
const char *pilot_name, const char *vehicle, const char *color,
|
||||
const char *experience,
|
||||
BTLobbyRoster *roster_out,
|
||||
char *my_token_out, int my_token_capacity,
|
||||
char *steam_map_out, int steam_map_capacity);
|
||||
@@ -57,5 +63,6 @@ int
|
||||
int
|
||||
BTLobby_JoinAndWait(
|
||||
const char *pilot_name, const char *vehicle, const char *color,
|
||||
const char *experience,
|
||||
char *my_token_out, int my_token_capacity,
|
||||
char *steam_map_out, int steam_map_capacity);
|
||||
|
||||
Reference in New Issue
Block a user