Carries the worldwide lobby-search filter for cross-region internet
testing (the only change since 4.12.1 besides the README rewrite).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
README.md rewritten around the lineage: Red Planet 4.10 on the
Tesla 1 pod platform, 4.11 the Win32 port, 4.12 the Steamification
of 4.11 - with the arcade-to-consumer replacement table, current
status (v4.12.1 verified on three machines), playing/building/docs
sections brought up to date.
And ahead of internet-wide beta testing: lobby search now applies the
worldwide distance filter - Steam defaults to roughly same-region
results, which would have hidden lobbies from cross-region testers
even though joining them works fine.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The environ.ini reader now skips comments (# or ;), blank lines, and
anything that is not KEY=VALUE, so the shipped file documents the
whole configuration surface: the core settings as-shipped (controls,
renderer, gauge canvas, plasma, single-window cockpit, frame rate,
Steam networking), the optional toggles (keyboard lighting, stick
flip, AA, particles, plasma scale/position, fixed seed), LAN hosting
without Steam, the developer/testing switches (RP412DEVKEYS,
L4CONSOLELEN, the Steam self-test), and the arcade multi-monitor
heritage variables. Stale L4MFDSCALE reference dropped from the
README.
Verified: the game boots on the commented file with values applied
(controls line honored, Steam transport up from the in-file switch).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per the user: the game never reads the pilot keypad, so the numpad
becomes the flight cluster - 8/2/4/6 stick, 7/9 pedals, 0 trigger -
with Shift/Ctrl as throttle up/down and Alt as reverse thrust. That
frees the entire letter board: W/A/S/D/Q/E return to their printed
MFD bank positions and B goes back to being the gap key, so the
default profile now carries the complete unmodified vRIO bank layout.
The keypad addresses stay bindable (arcade key events) but ship
unbound.
Alt as a held flight control meant every release popped the window
menu and stole focus - WndProc now eats SC_KEYMENU. Shift/Ctrl/Alt
key-name aliases added to the parser alongside the .NET names.
Profile parses clean (59 key buttons, 8 key axes); single-player
cycle and key-bomb tests green (Alt+Q abort unaffected).
Machines with an existing bindings.txt keep their old map - delete
the file to take the new defaults.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
Round six raced all three machines (staging fix confirmed) and then
exposed what happens when a pod leaves mid-mission - which arcade pods
never did.
The B crash dump named it exactly: VTV::TakeDamageMessageHandler
resolved message->inflictingEntity to NULL (the entity belonged to the
departed owner) and dereferenced it - Verify is compiled out in
release. Collision damage from an entity that no longer exists is now
ignored.
And the race B and C were left in was a zombie: the owner (console)
had aborted, so the mission clock would count up forever and the
death/respawn flow hung with nobody to arbitrate. Lobby-member races
now set gConsoleLossEndsMission: losing the console mid-mission posts
StopMission locally, the pod tears down, and lands back in the lobby
room. Arcade -net pods keep the re-listen-and-wait behavior.
Loopback hosted race still green.
For the drivers: the ampersand key is the arcade mission-abort - that
was every crash-on-keypress so far; and a sleeping Bluetooth pad wakes
on the Xbox button and hot-connects within 3 seconds (PadRIO
re-probes).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round five reached the race - full mesh on all three machines, eggs,
ACKs, mission running - but the owner raced ALONE. Cause: the engine
self-runs a pod at WaitingForLaunch when its console host is not
online (the arcade no-console fallback), and the owner''s in-process
console never connects to its own pod. On fast-loading owners the
self-run beat the console''s staging gate, so RunMission was never
sent and the members sat staged at black screens until someone hit
the & emergency-abort key.
gConsoleMarshalsLaunch (APPMGR) now tells the engine an in-process
console owns the launch: the network-race install sets it and the
owner holds at WaitingForLaunch with everyone else; plain single
player leaves it False and auto-runs as always. Verified on loopback:
all pods staged - RUN is back in the hosted-race log and both the
hosted race and the single-player cycle pass.
Also: unhandled-exception minidumps (rpl4crash.dmp beside the exe,
dbghelp loaded lazily) so test-machine crashes hand back stacks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round four reached one step from the race: eggs delivered, both
members ACKed with complete meshes, one member entered LoadingMission.
Two failures remained, both now fixed.
One: Steam reports incoming callers under locally-allocated ALIAS
FakeIPs, not their global ones - the owner accepted both mesh legs
but its identity check compared the alias against the egg address and
never counted the connections (no Connected to GameMachineHost on the
owner). The peer table now carries each member SteamID (lobby go
roster gained a field) and Accept resolves the caller identity back
to the global FakeIP the egg promised.
Two: mission load stalls the game thread for 10-30s with nothing
pumping, and Steam default 10s connected-timeout sheared every
connection mid-load (end reason 4001, rx ages 11.5-20.5s - right at
load duration). Connected timeout is now 90s; TCP never timed out an
idle arcade link and races pump every frame once running.
Self-test still green (ping, survives listener close).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
The lobby-owner-as-console architecture, in-engine. RPL4CONSOLE gains
RPL4LocalConsole_InstallNetworkRace: the owner pod switches to network
mode and meshes like any pod (egg fed locally via FeedLocalEgg, which
now opens the ConsoleOnly state gate), while the console tick also
marshals REMOTE pods over NetTransport speaking the exact arcade
protocol - egg chunks with 5s-retry-until-ACK, 1Hz state polling,
RunMission once every pod stages at WaitingForLaunch, StopMission at
expiry (remotes first, local pod holds until their EndMission scores
land), score intake labeled with [pilots]-order names on the results
screen.
The front end builds the multi-pilot egg: RP412HOSTPODS lists member
console channels (lobby stand-in; the Steam lobby feeds the same
path), RP412HOSTPORT/RP412HOSTADDR set the owner side.
Winsock Connect now redials with a fresh socket per attempt (a refused
TCP socket is dead; the old loop reused it) bounded at 120s - needed
whenever a peer boots after the caller, which is the normal Steam
lobby launch order.
Verified on loopback: member pod in -net, owner hosting from its menu;
mesh completed both sides, 30s race, remote score collected over the
wire (host 3), local stop after the drain, results screen shows both
pilots by name in one process that returns to the menu.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Steamworks SDK 1.64 vendored at extern/steamworks_sdk_164 (headers +
win32 redistributables only; .gitignore trims the rest). Both projects
build with RP412_STEAM; activation stays behind the RP412STEAM=1
environment switch, so plain desktop runs never touch Steam.
L4STEAMTRANSPORT.cpp implements NetTransport on ISteamNetworkingSockets
with FakeIP: SteamNetTransport_Install brings up SteamAPI, relay
network access, and a two-port FakeIP identity (fake port 0 = console
channel, 1 = game mesh), then swaps the process wire; any failure logs
the reason and the game carries on over TCP. Addressing keeps the
engine untouched: all pods share the -net port convention, eggs carry
fakeip:engineport, and the transport alone translates engine ports to
Steam fake ports via the lobby-fed peer table (RegisterPeer). Connect
mirrors the TCP retry-while-refused loop; Receive normalizes message
lanes back into the stream semantics CheckBuffers expects.
Runtime verified on this box: RP412STEAM=1 under AppID 480 came up as
169.254.59.52 (fake ports 32256/32257); without Steam credentials it
falls back to TCP cleanly; default boot logs no Steam lines at all.
steam_api.dll ships in the dist.
Next: the lobby layer (ISteamMatchmaking member data -> RegisterPeer +
egg build + RPL4CONSOLE marshal), which needs a second account to test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
After a console-marshaled race ends, the race loop now shows a RACE
RESULTS screen (place / pilot / final score, sorted descending, with a
CONTINUE button) before returning to the setup menu. Scores come from
the local console's intake; single-player rows carry the pilot's own
name, additional pods show their host number until the Steam roster
maps IDs to personas.
The setup menu also keeps the player's selections and pilot name across
races now instead of resetting to defaults each cycle.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
WinMain now wraps the engine block in a loop: when a front-end-launched
mission ends under the local console, the setup screen comes back in the
same process instead of exiting (the arcade relaunch-per-mission model).
Replaces the CreateProcess self-respawn - required for Steam, where the
lobby and sockets must survive across races.
Second-cycle re-init crash fixed: d3d_OBJECT kept a static texture cache
keyed by filename, so race 2 got IDirect3DTexture9 pointers created on
race 1 destroyed device and died at first draw (DrawMesh AV). The cache
is now flushed in ~DPLRenderer before the device is released, and
ParticleEngine::Initialize drops particles left over from the previous
mission. Verified: three consecutive 30s races in one PID, each stopped
on time by the console with final scores collected.
Also: L4CONSOLELEN env override for test-length races, and the console
exposes MissionCompleted() for the loop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
Starting without -egg, -net, or -mr now boots a race-setup screen
(RP_L4/RPL4FE.cpp) instead of aborting: track / vehicle / color / badge
/ time-of-day / weather / race length plus pilot name, populated from
TeslaConsole''s RPConfig.xml catalog (Death Race scenario). LAUNCH
builds the egg exactly as the console did - the RPMission.ToEggString
port, including the pilot name pre-rendered to 1bpp plasma bitmaps
(128x32 + 64x16) via GDI with the console''s auto-shrink font logic and
the verbatim ordinal graphics - writes frontend.egg, and injects it
into the standard egg-load path (new L4Application::
SetEggNotationFileName).
The menu is a GDI child of the main window (pod green-on-black, double
buffered, mouse driven, EDIT control for the name) running a modal loop
before engine init; closing the window exits cleanly. Found and fixed
along the way: the empty egg CString holds a NULL representation
(operator! is the safe emptiness test), and the modal loop needed a
queue nudge for launch clicks delivered via SendMessage.
Verified end to end: boot -> menu -> LAUNCH -> generated egg (7.5KB) ->
racing in the 1080p cockpit with score and mission clock running.
start-windowed.bat now boots into the front end.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The main game window becomes the cockpit shell (enlarged, clipping
children); every display folds in as a chrome-less child pane in the
pod interior arrangement:
[ MFD UL ] [ MFD UC ] [ MFD UR ]
[ plasma (reduced) ][ viewscreen (centered) ]
[ MFD LL ] [ Map ] [ MFD LR ]
The 3D scene presents into a black STATIC viewscreen child via
Present's hDestWindowOverride (new gMainPresentWindow global) - no
swap-chain changes, and STATIC's transparent hit-testing keeps mouse
input over the 3D view flowing to the game window. MFDSplitView gains a
parent/child mode; PlasmaScreen::Position reparents the glass into the
shell. Main window class background goes black for the cockpit gaps.
Verified by screenshot: live green gauges (LIFT CUT / BOOST / CHUTE /
trigger-program screens) with their red button strips, the 3D canyon in
the centered viewscreen, plasma score glass at its left, map with lit
amber preset lamps - one window, 976x1132 client at 50% scale.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The arcade startup hid the cursor unconditionally in release builds -
correct for a pod, but desktop windowed play needs the mouse for the
on-screen cockpit buttons and the cursor vanished over every display
window. Hide (and restore) it only when running fullscreen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hand-converted the four .vcproj projects to .vcxproj (Win32, v143,
Windows 11 SDK + DXSDK June 2010 for d3dx9/dxerr only). WinTesla.sln now
builds the v143 projects; the legacy solution is kept as WinTesla_vc9.sln.
Kept: /Zp1 in Munga_L4+RP_L4, Unicode, x86, /DYNAMICBASE:NO,
/FORCE:MULTIPLE (header-defined globals still duplicated across TUs).
Changed: CRT unified to /MD(d); import libs linked by the exes instead of
merged into Munga_L4.lib; WINDOWS_IGNORE_PACKING_MISMATCH and
_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS defined;
legacy_stdio_definitions.lib for the June-2010 dxerr.lib.
Source fixes, all behavior-preserving: Time gains standard (non-volatile)
copy-ctor/assignment overloads (rvalues cannot bind to volatile& in
standard C++); operator==(SOCKADDR_IN&,...) made inline; L4DINPUT's
Enum*Callback pair renamed DIEnum* (collided with L4CTRL's under LTCG);
std::ios.in -> std::ios::in in CAMMGR.cpp.
Verified: VC9 baseline rebuilt from this tree first, then the v143 build
compared against it in a sandboxed game working copy - identical logs and
behavior through RIO init (against vRIO) and mission load, including the
same pre-existing AV in d3d_OBJECT::LoadTexture (L4D3D.cpp:262) that both
toolchains hit; documented in BUILD.md 4 as the next debugging target.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verified full build with VC++ 2008 Express SP1 + Windows SDK v6.0A + DirectX
SDK (June 2010): 4 Projects succeeded, 0 failed (Release|Win32). Outputs:
Release\rpl4opt.exe, Release\RPL4TOOL.exe, lib\Munga_L4.lib, lib\DivLoader.lib.
Two projects referenced DirectX but were never repointed at $(DXSDK_DIR)
(they had no hardcoded path to replace earlier):
- DivLoader.vcproj: add "$(DXSDK_DIR)Include" to both compiler configs
(was failing on D3DX9.h).
- RPL4TOOL.vcproj / RPL4TOOL VS2008.vcproj: add "$(DXSDK_DIR)Lib\x86" to the
linker search path (was failing with LNK1181 on dinput8.lib).
.gitignore: ignore the build-output static libs that land in lib/
(Munga_L4.lib, DivLoader.lib); the dependency libs OpenAL32.lib and
libsndfile-1.lib stay tracked.
BUILD.md / docs/BUILD-NOTES.md: record the verified build, the CLI recipe,
and the DXSDK_DIR stale-environment gotcha.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Express editions of Visual C++ ship without ATL. The only ATL usage was
in MUNGA_L4/L4APP.cpp (atlbase.h/atlconv.h + USES_CONVERSION/W2A macros, all
in that one file) for wide-to-ANSI conversion of command-line arguments.
- Replace the ATL includes with a self-contained L4WideToAnsi helper (a
WideCharToMultiByte wrapper) and local USES_CONVERSION/W2A macros that
reproduce ATL's W2A semantics. All call sites consume the result immediately
(stricmp / CString assignment / atoi / atol), so behaviour is unchanged.
- Set UseOfATL="0" in Munga_L4.vcproj, RP_L4.vcproj and their VS2008 variants.
- Document the Express build path and the confirmed June 2010 DirectX SDK in
BUILD.md (new section 6) and docs/BUILD-NOTES.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Imports the current Win32 source for the pod-racing game 'Red Planet',
built on the MUNGA engine and its L4 (Win32/DirectX) platform layer:
- MUNGA / MUNGA_L4: cross-platform engine core and Win32 backend
- RP / RP_L4: Red Planet game logic and Win32 application
- DivLoader, Setup1: asset loader and installer project
- lib, MUNGA_L4/openal, MUNGA_L4/sos: third-party audio dependencies
Removed stale Subversion metadata and added .gitignore/.gitattributes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>