Steam lobby: host, join, room, and launch into a marshaled race
RPL4LOBBY implements the multiplayer front door on ISteamMatchmaking. The setup menu grows HOST STEAM RACE / JOIN STEAM RACE buttons when the Steam wire is live; hosting creates a tagged public lobby, joining finds one. Every member publishes FakeIP + fake ports + persona + loadout as member data; the room screen lists members (host marked) and gives the owner a launch button. Launching writes a nonced go-roster into lobby data. Each pod registers every peer with the Steam transport (two-port peer table: engine console/game ports map to Steam fake ports on connect) and enters the race: the owner through the hosted-race path - it builds the multi-pilot egg from real personas and loadouts and its console marshals everyone - and members as network pods that boot straight into WaitingForEgg for the owner to feed over the wire. The lobby outlives races: members loop back through WinMain into the room (no local console needed - MissionCompleted is waived for member races), and the owner returns to the room after its results screen. Leaving the lobby clears the hosted-race priming. Verified on this box: menu buttons appear under RP412STEAM=1, hosting creates a lobby on the Steam backend, the room runs and leaves back to the menu; single-player cycling and the LAN hosted race both still pass. Full three-account mesh test is next, on real hardware. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+309
-125
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "rpl4fe.h"
|
||||
#include "rpl4console.h"
|
||||
#include "rpl4lobby.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
@@ -125,6 +126,8 @@ namespace
|
||||
GroupWeather,
|
||||
GroupLength,
|
||||
GroupLaunch,
|
||||
GroupSteamHost, // buttons, not selections (Steam builds only)
|
||||
GroupSteamJoin,
|
||||
GroupCount
|
||||
};
|
||||
|
||||
@@ -150,6 +153,7 @@ namespace
|
||||
HBRUSH editBrush;
|
||||
Logical launched;
|
||||
Logical closed;
|
||||
int steamAction; // 1 = host lobby, 2 = join lobby
|
||||
};
|
||||
|
||||
FEState *gFE = NULL;
|
||||
@@ -178,12 +182,36 @@ namespace
|
||||
{
|
||||
char address[64]; // mesh (game port) address for [pilots]
|
||||
char name[32];
|
||||
const char *vehicle;
|
||||
const char *color;
|
||||
char vehicle[24];
|
||||
char color[16];
|
||||
char badge[24];
|
||||
};
|
||||
|
||||
// lobby-fed override (real personas + loadouts); empty = env parsing
|
||||
FEHostedPilot gHostedPilots[maxExtraPilots];
|
||||
int gHostedPilotCount = 0;
|
||||
char gHostedOwnerAddress[64] = "";
|
||||
|
||||
int CollectExtraPilots(const FEState *fe, ExtraPilot *extras, char *owner_address)
|
||||
{
|
||||
if (gHostedPilotCount > 0)
|
||||
{
|
||||
sprintf(owner_address, "%s:1502", gHostedOwnerAddress);
|
||||
for (int i = 0; i < gHostedPilotCount; ++i)
|
||||
{
|
||||
strcpy(extras[i].address, gHostedPilots[i].address);
|
||||
strcpy(extras[i].name,
|
||||
gHostedPilots[i].name[0] ? gHostedPilots[i].name : "Pilot");
|
||||
strcpy(extras[i].vehicle,
|
||||
gHostedPilots[i].vehicle[0] ? gHostedPilots[i].vehicle : "speck");
|
||||
strcpy(extras[i].color,
|
||||
gHostedPilots[i].color[0] ? gHostedPilots[i].color : "Red");
|
||||
strcpy(extras[i].badge,
|
||||
gHostedPilots[i].badge[0] ? gHostedPilots[i].badge : "None");
|
||||
}
|
||||
return gHostedPilotCount;
|
||||
}
|
||||
|
||||
const char *host_pods = getenv("RP412HOSTPODS");
|
||||
if (host_pods == NULL || host_pods[0] == '\0')
|
||||
{
|
||||
@@ -233,10 +261,11 @@ namespace
|
||||
ExtraPilot *pilot = &extras[extra_count];
|
||||
sprintf(pilot->address, "%s:%d", entry, console_port + 1);
|
||||
sprintf(pilot->name, "PILOT %d", extra_count + 2);
|
||||
pilot->vehicle = kVehicles[
|
||||
(fe->selection[GroupVehicle] + extra_count + 1) % FE_COUNT(kVehicles)].key;
|
||||
pilot->color = kColors[
|
||||
(fe->selection[GroupColor] + extra_count + 1) % FE_COUNT(kColors)].key;
|
||||
strcpy(pilot->vehicle, kVehicles[
|
||||
(fe->selection[GroupVehicle] + extra_count + 1) % FE_COUNT(kVehicles)].key);
|
||||
strcpy(pilot->color, kColors[
|
||||
(fe->selection[GroupColor] + extra_count + 1) % FE_COUNT(kColors)].key);
|
||||
strcpy(pilot->badge, "None");
|
||||
++extra_count;
|
||||
}
|
||||
return extra_count;
|
||||
@@ -304,6 +333,26 @@ namespace
|
||||
launch->rect.right = col3 + col_w;
|
||||
launch->rect.bottom = client_h - row_h;
|
||||
|
||||
// Steam lobby buttons (only when the Steam wire is live)
|
||||
if (RPL4Lobby_Available())
|
||||
{
|
||||
FEItem *host = &fe->items[fe->itemCount++];
|
||||
host->group = GroupSteamHost;
|
||||
host->index = 0;
|
||||
host->rect.left = col3;
|
||||
host->rect.top = client_h - 6 * row_h;
|
||||
host->rect.right = col3 + col_w;
|
||||
host->rect.bottom = client_h - 5 * row_h;
|
||||
|
||||
FEItem *join = &fe->items[fe->itemCount++];
|
||||
join->group = GroupSteamJoin;
|
||||
join->index = 0;
|
||||
join->rect.left = col3;
|
||||
join->rect.top = client_h - (9 * row_h) / 2;
|
||||
join->rect.right = col3 + col_w;
|
||||
join->rect.bottom = client_h - (7 * row_h) / 2;
|
||||
}
|
||||
|
||||
// pilot name edit sits at the top of column 3
|
||||
if (fe->nameEdit != NULL)
|
||||
{
|
||||
@@ -339,6 +388,8 @@ namespace
|
||||
case GroupWeather: return kWeather[index].name;
|
||||
case GroupLength: return kLengths[index].name;
|
||||
case GroupLaunch: return "L A U N C H R A C E";
|
||||
case GroupSteamHost: return "HOST STEAM RACE";
|
||||
case GroupSteamJoin: return "JOIN STEAM RACE";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -377,7 +428,7 @@ namespace
|
||||
for (int i = 0; i < fe->itemCount; ++i)
|
||||
{
|
||||
const FEItem *item = &fe->items[i];
|
||||
if (item->group != previous_group && item->group != GroupLaunch)
|
||||
if (item->group != previous_group && item->group < GroupLaunch)
|
||||
{
|
||||
previous_group = item->group;
|
||||
RECT header = item->rect;
|
||||
@@ -389,11 +440,11 @@ namespace
|
||||
}
|
||||
|
||||
Logical selected =
|
||||
(item->group == GroupLaunch) ||
|
||||
(item->group >= GroupLaunch) ||
|
||||
(fe->selection[item->group] == item->index);
|
||||
|
||||
RECT row = item->rect;
|
||||
if (item->group == GroupLaunch)
|
||||
if (item->group >= GroupLaunch)
|
||||
{
|
||||
HBRUSH launch_brush = CreateSolidBrush(kGreenBright);
|
||||
FrameRect(mem, &row, launch_brush);
|
||||
@@ -462,6 +513,11 @@ namespace
|
||||
// SendMessage never passes through the queue)
|
||||
PostMessageA(fe->menuWindow, WM_NULL, 0, 0);
|
||||
}
|
||||
else if (item->group == GroupSteamHost || item->group == GroupSteamJoin)
|
||||
{
|
||||
fe->steamAction = (item->group == GroupSteamHost) ? 1 : 2;
|
||||
PostMessageA(fe->menuWindow, WM_NULL, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
fe->selection[item->group] = item->index;
|
||||
@@ -673,7 +729,8 @@ namespace
|
||||
egg += line;
|
||||
sprintf(line, "color=%s\n", extras[p].color);
|
||||
egg += line;
|
||||
egg += "badge=None\n";
|
||||
sprintf(line, "badge=%s\n", extras[p].badge);
|
||||
egg += line;
|
||||
}
|
||||
|
||||
egg += "[largebitmap]\n";
|
||||
@@ -734,13 +791,14 @@ namespace
|
||||
//############################ RPL4FrontEnd_Run ##########################
|
||||
//########################################################################
|
||||
|
||||
Logical
|
||||
RPL4FrontEnd_Run(
|
||||
HINSTANCE instance,
|
||||
HWND main_window,
|
||||
char *egg_path_out,
|
||||
int egg_path_size
|
||||
)
|
||||
static int gLastLaunchMode = FELaunchSingle;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Build frontend.egg from the persisted loadout (the menu persists
|
||||
// on every exit; lobby launches can happen without a live menu)
|
||||
//---------------------------------------------------------------
|
||||
static Logical
|
||||
BuildEggFromPersisted(char *egg_path_out, int egg_path_size)
|
||||
{
|
||||
FEState fe;
|
||||
memset(&fe, 0, sizeof(fe));
|
||||
@@ -753,138 +811,225 @@ Logical
|
||||
{
|
||||
fe.selection[GroupLength] = 2; // 5:00
|
||||
}
|
||||
gFE = &fe;
|
||||
gLastMissionSeconds = kLengths[fe.selection[GroupLength]].seconds;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Window class + fonts
|
||||
//---------------------------------------------------------------
|
||||
static Logical class_registered = False;
|
||||
if (!class_registered)
|
||||
std::string egg;
|
||||
egg.reserve(16384);
|
||||
BuildEggText(&fe, egg);
|
||||
|
||||
strncpy(egg_path_out, "frontend.egg", egg_path_size - 1);
|
||||
egg_path_out[egg_path_size - 1] = '\0';
|
||||
|
||||
FILE *file = fopen(egg_path_out, "wb");
|
||||
if (file == NULL)
|
||||
{
|
||||
class_registered = True;
|
||||
WNDCLASSA window_class;
|
||||
memset(&window_class, 0, sizeof(window_class));
|
||||
window_class.style = CS_HREDRAW | CS_VREDRAW;
|
||||
window_class.lpfnWndProc = FrontEndWndProc;
|
||||
window_class.hInstance = instance;
|
||||
window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
window_class.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
|
||||
window_class.lpszClassName = "RPFrontEnd";
|
||||
RegisterClassA(&window_class);
|
||||
}
|
||||
|
||||
RECT client;
|
||||
GetClientRect(main_window, &client);
|
||||
|
||||
fe.menuWindow = CreateWindowExA(
|
||||
0, "RPFrontEnd", "",
|
||||
WS_CHILD | WS_VISIBLE,
|
||||
0, 0, client.right, client.bottom,
|
||||
main_window, NULL, instance, NULL);
|
||||
if (fe.menuWindow == NULL)
|
||||
{
|
||||
gFE = NULL;
|
||||
DEBUG_STREAM << "FrontEnd: could not write " << egg_path_out
|
||||
<< "\n" << std::flush;
|
||||
return False;
|
||||
}
|
||||
fwrite(egg.data(), 1, egg.size(), file);
|
||||
fclose(file);
|
||||
DEBUG_STREAM << "FrontEnd: wrote " << egg_path_out << " ("
|
||||
<< (int) egg.size() << " bytes)\n" << std::flush;
|
||||
return True;
|
||||
}
|
||||
|
||||
int text_h = client.bottom / 40;
|
||||
if (text_h < 16) text_h = 16;
|
||||
if (text_h > 24) text_h = 24;
|
||||
fe.textFont = CreateFontA(-text_h, 0, 0, 0, FW_NORMAL,
|
||||
FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
|
||||
CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_MODERN,
|
||||
"Consolas");
|
||||
fe.titleFont = CreateFontA(-(text_h * 2), 0, 0, 0, FW_BOLD,
|
||||
FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
|
||||
CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_MODERN,
|
||||
"Consolas");
|
||||
fe.editBrush = CreateSolidBrush(kBlack);
|
||||
|
||||
fe.nameEdit = CreateWindowExA(
|
||||
0, "EDIT", fe.pilotName,
|
||||
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
|
||||
0, 0, 10, 10,
|
||||
fe.menuWindow, NULL, instance, NULL);
|
||||
SendMessageA(fe.nameEdit, WM_SETFONT, (WPARAM) fe.textFont, TRUE);
|
||||
SendMessageA(fe.nameEdit, EM_SETLIMITTEXT, sizeof(fe.pilotName) - 2, 0);
|
||||
|
||||
LayoutMenu(&fe, client.right, client.bottom);
|
||||
InvalidateRect(fe.menuWindow, NULL, TRUE);
|
||||
Logical
|
||||
RPL4FrontEnd_Run(
|
||||
HINSTANCE instance,
|
||||
HWND main_window,
|
||||
char *egg_path_out,
|
||||
int egg_path_size
|
||||
)
|
||||
{
|
||||
gLastLaunchMode = FELaunchSingle;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Modal loop until the player launches or closes the window
|
||||
// Coming back from a race while still in a lobby: straight to
|
||||
// the room (the lobby outlives races - single binary payoff)
|
||||
//---------------------------------------------------------------
|
||||
MSG msg;
|
||||
while (!fe.launched && !fe.closed)
|
||||
if (RPL4Lobby_InRoom())
|
||||
{
|
||||
BOOL result = GetMessageA(&msg, NULL, 0, 0);
|
||||
if (result <= 0)
|
||||
int outcome = RPL4Lobby_Room(instance, main_window);
|
||||
if (outcome == LobbyRoomClosed)
|
||||
{
|
||||
fe.closed = True;
|
||||
break;
|
||||
return False;
|
||||
}
|
||||
if (msg.message == WM_QUIT ||
|
||||
(msg.message == WM_SYSCOMMAND && msg.wParam == SC_CLOSE))
|
||||
if (outcome == LobbyLaunchMember)
|
||||
{
|
||||
fe.closed = True;
|
||||
gLastLaunchMode = FELaunchMember;
|
||||
egg_path_out[0] = '\0';
|
||||
return True;
|
||||
}
|
||||
// closing the main window ends the front end too
|
||||
if (msg.message == WM_CLOSE && msg.hwnd == main_window)
|
||||
if (outcome == LobbyLaunchHost)
|
||||
{
|
||||
fe.closed = True;
|
||||
}
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageA(&msg);
|
||||
if (!IsWindow(main_window))
|
||||
{
|
||||
fe.closed = True;
|
||||
gLastLaunchMode = FELaunchHost;
|
||||
return BuildEggFromPersisted(egg_path_out, egg_path_size);
|
||||
}
|
||||
// LobbyRoomLeft: fall through to the setup menu
|
||||
}
|
||||
|
||||
Logical launched = fe.launched;
|
||||
if (launched)
|
||||
for (;;)
|
||||
{
|
||||
gLastMissionSeconds = kLengths[fe.selection[GroupLength]].seconds;
|
||||
|
||||
GetWindowTextA(fe.nameEdit, fe.pilotName, sizeof(fe.pilotName) - 1);
|
||||
if (fe.pilotName[0] == '\0')
|
||||
FEState fe;
|
||||
memset(&fe, 0, sizeof(fe));
|
||||
strcpy(fe.pilotName, gLastPilotName);
|
||||
if (gHavePersist)
|
||||
{
|
||||
strcpy(fe.pilotName, "Pilot");
|
||||
}
|
||||
strcpy(gLastPilotName, fe.pilotName);
|
||||
memcpy(gPersistSelection, fe.selection, sizeof(gPersistSelection));
|
||||
gHavePersist = True;
|
||||
|
||||
std::string egg;
|
||||
egg.reserve(16384);
|
||||
BuildEggText(&fe, egg);
|
||||
|
||||
strncpy(egg_path_out, "frontend.egg", egg_path_size - 1);
|
||||
egg_path_out[egg_path_size - 1] = '\0';
|
||||
|
||||
FILE *file = fopen(egg_path_out, "wb");
|
||||
if (file != NULL)
|
||||
{
|
||||
fwrite(egg.data(), 1, egg.size(), file);
|
||||
fclose(file);
|
||||
DEBUG_STREAM << "FrontEnd: wrote " << egg_path_out << " ("
|
||||
<< (int) egg.size() << " bytes)\n" << std::flush;
|
||||
memcpy(fe.selection, gPersistSelection, sizeof(fe.selection));
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_STREAM << "FrontEnd: could not write " << egg_path_out
|
||||
<< "\n" << std::flush;
|
||||
launched = False;
|
||||
fe.selection[GroupLength] = 2; // 5:00
|
||||
}
|
||||
gFE = &fe;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Window class + fonts
|
||||
//---------------------------------------------------------------
|
||||
static Logical class_registered = False;
|
||||
if (!class_registered)
|
||||
{
|
||||
class_registered = True;
|
||||
WNDCLASSA window_class;
|
||||
memset(&window_class, 0, sizeof(window_class));
|
||||
window_class.style = CS_HREDRAW | CS_VREDRAW;
|
||||
window_class.lpfnWndProc = FrontEndWndProc;
|
||||
window_class.hInstance = instance;
|
||||
window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
window_class.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
|
||||
window_class.lpszClassName = "RPFrontEnd";
|
||||
RegisterClassA(&window_class);
|
||||
}
|
||||
|
||||
RECT client;
|
||||
GetClientRect(main_window, &client);
|
||||
|
||||
fe.menuWindow = CreateWindowExA(
|
||||
0, "RPFrontEnd", "",
|
||||
WS_CHILD | WS_VISIBLE,
|
||||
0, 0, client.right, client.bottom,
|
||||
main_window, NULL, instance, NULL);
|
||||
if (fe.menuWindow == NULL)
|
||||
{
|
||||
gFE = NULL;
|
||||
return False;
|
||||
}
|
||||
|
||||
int text_h = client.bottom / 40;
|
||||
if (text_h < 16) text_h = 16;
|
||||
if (text_h > 24) text_h = 24;
|
||||
fe.textFont = CreateFontA(-text_h, 0, 0, 0, FW_NORMAL,
|
||||
FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
|
||||
CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_MODERN,
|
||||
"Consolas");
|
||||
fe.titleFont = CreateFontA(-(text_h * 2), 0, 0, 0, FW_BOLD,
|
||||
FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
|
||||
CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_MODERN,
|
||||
"Consolas");
|
||||
fe.editBrush = CreateSolidBrush(kBlack);
|
||||
|
||||
fe.nameEdit = CreateWindowExA(
|
||||
0, "EDIT", fe.pilotName,
|
||||
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
|
||||
0, 0, 10, 10,
|
||||
fe.menuWindow, NULL, instance, NULL);
|
||||
SendMessageA(fe.nameEdit, WM_SETFONT, (WPARAM) fe.textFont, TRUE);
|
||||
SendMessageA(fe.nameEdit, EM_SETLIMITTEXT, sizeof(fe.pilotName) - 2, 0);
|
||||
|
||||
LayoutMenu(&fe, client.right, client.bottom);
|
||||
InvalidateRect(fe.menuWindow, NULL, TRUE);
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Modal loop until launch, lobby button, or close
|
||||
//---------------------------------------------------------------
|
||||
MSG msg;
|
||||
while (!fe.launched && !fe.closed && fe.steamAction == 0)
|
||||
{
|
||||
BOOL result = GetMessageA(&msg, NULL, 0, 0);
|
||||
if (result <= 0)
|
||||
{
|
||||
fe.closed = True;
|
||||
break;
|
||||
}
|
||||
if (msg.message == WM_QUIT ||
|
||||
(msg.message == WM_SYSCOMMAND && msg.wParam == SC_CLOSE))
|
||||
{
|
||||
fe.closed = True;
|
||||
}
|
||||
// closing the main window ends the front end too
|
||||
if (msg.message == WM_CLOSE && msg.hwnd == main_window)
|
||||
{
|
||||
fe.closed = True;
|
||||
}
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageA(&msg);
|
||||
if (!IsWindow(main_window))
|
||||
{
|
||||
fe.closed = True;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Harvest the loadout whichever way we leave the menu - the
|
||||
// lobby publishes it as member data, launches build from it
|
||||
//---------------------------------------------------------------
|
||||
if (!fe.closed)
|
||||
{
|
||||
GetWindowTextA(fe.nameEdit, fe.pilotName, sizeof(fe.pilotName) - 1);
|
||||
if (fe.pilotName[0] == '\0')
|
||||
{
|
||||
strcpy(fe.pilotName, "Pilot");
|
||||
}
|
||||
strcpy(gLastPilotName, fe.pilotName);
|
||||
memcpy(gPersistSelection, fe.selection, sizeof(gPersistSelection));
|
||||
gHavePersist = True;
|
||||
}
|
||||
|
||||
Logical launched = fe.launched;
|
||||
Logical closed = fe.closed;
|
||||
int steam_action = fe.steamAction;
|
||||
|
||||
DestroyWindow(fe.menuWindow);
|
||||
DeleteObject(fe.textFont);
|
||||
DeleteObject(fe.titleFont);
|
||||
DeleteObject(fe.editBrush);
|
||||
gFE = NULL;
|
||||
|
||||
if (closed)
|
||||
{
|
||||
return False;
|
||||
}
|
||||
if (launched)
|
||||
{
|
||||
// plain launch: single player, or a LAN-hosted race when
|
||||
// RP412HOSTPODS is set in the environment
|
||||
return BuildEggFromPersisted(egg_path_out, egg_path_size);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Steam lobby (host or join), then back here if they leave
|
||||
//---------------------------------------------------------------
|
||||
int outcome = (steam_action == 1)
|
||||
? RPL4Lobby_Host(instance, main_window)
|
||||
: RPL4Lobby_Join(instance, main_window);
|
||||
if (outcome == LobbyRoomClosed)
|
||||
{
|
||||
return False;
|
||||
}
|
||||
if (outcome == LobbyLaunchMember)
|
||||
{
|
||||
gLastLaunchMode = FELaunchMember;
|
||||
egg_path_out[0] = '\0';
|
||||
return True;
|
||||
}
|
||||
if (outcome == LobbyLaunchHost)
|
||||
{
|
||||
gLastLaunchMode = FELaunchHost;
|
||||
return BuildEggFromPersisted(egg_path_out, egg_path_size);
|
||||
}
|
||||
// LobbyRoomLeft: show the menu again
|
||||
}
|
||||
|
||||
DestroyWindow(fe.menuWindow);
|
||||
DeleteObject(fe.textFont);
|
||||
DeleteObject(fe.titleFont);
|
||||
DeleteObject(fe.editBrush);
|
||||
gFE = NULL;
|
||||
|
||||
return launched;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -899,6 +1044,45 @@ const char *
|
||||
return gLastPilotNamesCsv;
|
||||
}
|
||||
|
||||
int
|
||||
RPL4FrontEnd_LastLaunchMode()
|
||||
{
|
||||
return gLastLaunchMode;
|
||||
}
|
||||
|
||||
void
|
||||
RPL4FrontEnd_GetLoadout(char *vehicle, char *color, char *badge)
|
||||
{
|
||||
int v = gHavePersist ? gPersistSelection[GroupVehicle] : 0;
|
||||
int c = gHavePersist ? gPersistSelection[GroupColor] : 0;
|
||||
int b = gHavePersist ? gPersistSelection[GroupBadge] : 0;
|
||||
strcpy(vehicle, kVehicles[v].key);
|
||||
strcpy(color, kColors[c].key);
|
||||
strcpy(badge, kBadges[b].key);
|
||||
}
|
||||
|
||||
void
|
||||
RPL4FrontEnd_SetHostedPilots(
|
||||
const char *owner_address,
|
||||
const FEHostedPilot *pilots,
|
||||
int count
|
||||
)
|
||||
{
|
||||
if (count > maxExtraPilots)
|
||||
{
|
||||
count = maxExtraPilots;
|
||||
}
|
||||
gHostedPilotCount = (pilots != NULL) ? count : 0;
|
||||
for (int i = 0; i < gHostedPilotCount; ++i)
|
||||
{
|
||||
gHostedPilots[i] = pilots[i];
|
||||
}
|
||||
strncpy(gHostedOwnerAddress,
|
||||
(owner_address != NULL) ? owner_address : "",
|
||||
sizeof(gHostedOwnerAddress) - 1);
|
||||
gHostedOwnerAddress[sizeof(gHostedOwnerAddress) - 1] = '\0';
|
||||
}
|
||||
|
||||
//########################################################################
|
||||
//######################## RPL4FrontEnd_ShowResults ######################
|
||||
//########################################################################
|
||||
|
||||
Reference in New Issue
Block a user