LocalConsole: the in-process marshal that ends missions

Domain correction from playtest: hand-fed eggs are a developer shortcut
- a mission only ends on a console command, so the clock hits 00:00 and
counts up forever. Even single-player games need a console marshal.

RPL4CONSOLE is that console. Like the real one it lives on its own
thread: it owns the mission clock and raises the stop request at the
selected length; the app-manager per-frame hook (new gPerFrameHook seam
in APPMGR, called while the application global is live - the loop
condition NULLs it on exit, which ate the first attempt) executes the
engine-safe part, dispatching the same StopMissionMessage TeslaConsole
sent. Final scores flow in through a new RP-layer sink
(gConsoleScoreSink in RPCNSL): RPPlayer feeds it the same score it
sends a real console at mission end.

It also inherits the launcher role: the application tears down after a
stop (arcade pods were relaunched per mission by TeslaLauncher), so
WinMain respawns the process when the console ended the mission,
landing back on the race-setup screen. L4NetworkManager grows
FeedLocalEgg (the single-user egg-inject path, callable mid-session)
for the future in-process loop.

Verified end to end: menu -> 3:00 race -> stop dispatched exactly on
time -> final score collected (host 1 = 4113) -> process respawned with
the front end up. -egg runs stay unmarshaled (the dev shortcut).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-12 18:10:02 -05:00
co-authored by Claude Fable 5
parent 58eb25a792
commit 9f79508257
15 changed files with 351 additions and 0 deletions
+3
View File
@@ -3,6 +3,9 @@
#include "rpcnsl.h"
// see rpcnsl.h - the in-process console registers its intake here
void (*gConsoleScoreSink)(int player_host_ID, int final_score) = NULL;
//~~~~~~~~~~~~~~~~~~ ConsolePlayerVTVScoredMessage ~~~~~~~~~~~~~~~~~~~~~~
ConsolePlayerVTVScoredMessage::
ConsolePlayerVTVScoredMessage(HostID player_host_ID):
+7
View File
@@ -17,6 +17,13 @@ enum
ConsolePlayerVTVDamagedMessageID = 6
};
//~~~~~~~~~~~~~~~~~~~~~~~~ Local console sink ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// When an in-process console marshals the game (RP412 single player),
// this points at its final-score intake; the player's mission-ending
// path feeds it the same score it sends a real console. NULL when no
// local console is present.
extern void (*gConsoleScoreSink)(int player_host_ID, int final_score);
//~~~~~~~~~~~~~~~~~~ ConsolePlayerVTVScoredMessage ~~~~~~~~~~~~~~~~~~~
#ifdef MAC
#pragma options align=mac68k4byte
+9
View File
@@ -122,6 +122,15 @@ void
Player::MissionEndingMessageHandler(message);
Check(application);
// feed the in-process console the same final score a real console
// gets (no-op when none is registered)
if (gConsoleScoreSink != NULL &&
application->GetApplicationState() == Application::EndingMission)
{
(*gConsoleScoreSink)((int) ownerID, (int) currentScore);
}
HostManager *host_manager = application->GetHostManager();
Check(host_manager);
Host *console_host = host_manager->GetConsoleHost();