Members get the score sheet: owner publishes results through the lobby

Round seven was the first complete Steam race: three machines, five
minutes, wall deaths respawning, the console ending the race on time,
and rematches launched from the same lobby. The one gap the run showed:
only the owner saw scores - members went straight back to the room.

The owner now publishes its console results (host, score, name, keyed
by the race nonce) into lobby data right after teardown; members pull
the sheet (waiting up to 8s for it to land), inject it into the local
results intake, and the same RACE RESULTS screen shows on every
machine before the room.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-13 00:34:34 -05:00
co-authored by Claude Fable 5
parent a1b524da29
commit 18c6f05cf1
5 changed files with 158 additions and 0 deletions
+6
View File
@@ -23,6 +23,7 @@
#include "rpl4pb.h"
#include "rpl4fe.h"
#include "rpl4console.h"
#include "rpl4lobby.h"
#include "..\munga_l4\l4steamtransport.h"
#include "..\munga_l4\l4splr.h"
#include "rpl4ver.h"
@@ -279,6 +280,11 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
{
}
// lobby races: the owner publishes the score sheet, members
// pull it - the same results screen shows on every machine
RPL4Lobby_PushRaceResults();
RPL4Lobby_PullRaceResults();
// last race's final scores first (no-op on first boot)
if (!RPL4FrontEnd_ShowResults(hInstance, hWnd))
{
+30
View File
@@ -703,3 +703,33 @@ const char *
}
return gPilotNames[index];
}
void
RPL4LocalConsole_ClearResults()
{
gResultCount = 0;
gPilotNameCount = 0;
}
void
RPL4LocalConsole_InjectResult(int host_ID, int score, const char *name)
{
if (gResultCount >= maxResults)
{
return;
}
gResults[gResultCount].hostID = host_ID;
gResults[gResultCount].score = score;
++gResultCount;
int index = host_ID - firstPilotHostID;
if (name != NULL && index >= 0 && index < maxRemotePods + 1)
{
strncpy(gPilotNames[index], name, sizeof(gPilotNames[index]) - 1);
gPilotNames[index][sizeof(gPilotNames[index]) - 1] = '\0';
if (index >= gPilotNameCount)
{
gPilotNameCount = index + 1;
}
}
}
+8
View File
@@ -55,3 +55,11 @@ Logical
// NULL when unknown - the results screen falls back to pod numbers.
const char *
RPL4LocalConsole_GetResultName(int host_ID);
// Lobby members do not marshal races - the owner publishes the score
// sheet through lobby data and it is injected here so the same results
// screen shows on every machine.
void
RPL4LocalConsole_ClearResults();
void
RPL4LocalConsole_InjectResult(int host_ID, int score, const char *name);
+105
View File
@@ -15,10 +15,13 @@ Logical RPL4Lobby_InRoom() { return False; }
int RPL4Lobby_Host(HINSTANCE, HWND) { return LobbyRoomLeft; }
int RPL4Lobby_Join(HINSTANCE, HWND) { return LobbyRoomLeft; }
int RPL4Lobby_Room(HINSTANCE, HWND) { return LobbyRoomLeft; }
void RPL4Lobby_PushRaceResults() { }
void RPL4Lobby_PullRaceResults() { }
#else
#include "rpl4fe.h"
#include "rpl4console.h"
#include "..\munga_l4\l4steamtransport.h"
#pragma pack(push, 8)
@@ -40,6 +43,8 @@ namespace
CSteamID gLobby;
Logical gInLobby = False;
int gLastGoNonce = 0; // launches we already answered
int gShownResultsNonce = 0; // score sheets we already displayed
const char kResultsKey[] = "res";
// async call-result plumbing
Logical gCallDone = False;
@@ -696,4 +701,104 @@ int
return RunRoom(instance, main_window);
}
void
RPL4Lobby_PushRaceResults()
{
if (!gInLobby || !IsOwner() || RPL4LocalConsole_ResultCount() == 0)
{
return;
}
SteamAPI_RunCallbacks();
char sheet[600];
sprintf(sheet, "%d:", gLastGoNonce);
for (int i = 0; i < RPL4LocalConsole_ResultCount(); ++i)
{
int host_ID, score;
if (!RPL4LocalConsole_GetResult(i, &host_ID, &score))
{
continue;
}
const char *name = RPL4LocalConsole_GetResultName(host_ID);
char entry[80];
sprintf(entry, "%d|%d|%.30s;", host_ID, score,
(name != NULL) ? name : "");
if (strlen(sheet) + strlen(entry) < sizeof(sheet))
{
strcat(sheet, entry);
}
}
SteamMatchmaking()->SetLobbyData(gLobby, kResultsKey, sheet);
gShownResultsNonce = gLastGoNonce; // the owner sees its own screen
DEBUG_STREAM << "Lobby: results published (" << sheet << ")\n" << std::flush;
}
void
RPL4Lobby_PullRaceResults()
{
if (!gInLobby || IsOwner() || gLastGoNonce == gShownResultsNonce)
{
return;
}
//
// The owner publishes right after its race teardown - ours may
// finish first, so give the sheet a moment to arrive
//
const char *sheet = NULL;
DWORD deadline = GetTickCount() + 8 * 1000;
for (;;)
{
SteamAPI_RunCallbacks();
sheet = SteamMatchmaking()->GetLobbyData(gLobby, kResultsKey);
if (sheet != NULL && sheet[0] != '\0' && atoi(sheet) == gLastGoNonce)
{
break;
}
if ((LONG)(GetTickCount() - deadline) >= 0)
{
return; // no sheet for this race - skip the screen
}
Sleep(100);
}
gShownResultsNonce = gLastGoNonce;
RPL4LocalConsole_ClearResults();
const char *cursor = strchr(sheet, ':');
cursor = (cursor != NULL) ? cursor + 1 : sheet;
while (*cursor != '\0')
{
int host_ID = atoi(cursor);
int score = 0;
char name[32] = "";
while (*cursor != '\0' && *cursor != '|' && *cursor != ';') ++cursor;
if (*cursor == '|')
{
score = atoi(++cursor);
while (*cursor != '\0' && *cursor != '|' && *cursor != ';') ++cursor;
}
if (*cursor == '|')
{
++cursor;
int n = 0;
while (cursor[n] != '\0' && cursor[n] != ';' &&
n < (int) sizeof(name) - 1)
{
name[n] = cursor[n];
++n;
}
name[n] = '\0';
cursor += n;
}
while (*cursor != '\0' && *cursor != ';') ++cursor;
if (*cursor == ';') ++cursor;
if (host_ID > 0)
{
RPL4LocalConsole_InjectResult(host_ID, score, name);
}
}
DEBUG_STREAM << "Lobby: results pulled for race " << gLastGoNonce << "\n" << std::flush;
}
#endif // RP412_STEAM
+9
View File
@@ -46,3 +46,12 @@ int
// Re-enter the room of the lobby we are already in (post-race).
int
RPL4Lobby_Room(HINSTANCE instance, HWND main_window);
// Post-race score sheet: the owner publishes its console's results
// into lobby data; members pull them into the local results intake so
// the same results screen shows everywhere. Both are no-ops when not
// in a lobby (or not the respective role).
void
RPL4Lobby_PushRaceResults();
void
RPL4Lobby_PullRaceResults();