PlayFab Party + Multiplayer LAN emulator (splitux co-op backend) — LMSR title 1BB03
  • C 43.6%
  • Python 34.5%
  • Shell 10.3%
  • JavaScript 5.6%
  • Nushell 3%
  • Other 3%
Find a file
gabrielgad 4b19ec29ff
Some checks failed
Build PlayFab Party Emulator / build-windows-x64 (push) Failing after -3s
ci: resolve actions from Forgejo instead of GitHub
Forgejo Actions resolves a bare owner/repo@ref against github.com, so these
jobs depended on GitHub being up even though they run on our own runner.
Now pointed at verbatim mirrors under splitux-gg, all branches and tags, so
every pinned ref resolves. Provenance: splitux/docs/CI_ACTION_MIRRORS.md
2026-07-26 20:34:05 -04:00
.github/workflows ci: resolve actions from Forgejo instead of GitHub 2026-07-26 20:34:05 -04:00
abi Initial commit: PlayFab Party + Multiplayer LAN emulator for splitux 2026-07-08 15:32:03 -04:00
auth-rebound party: xbox/mbs responders, crash triage, generator and impl updates 2026-07-26 18:24:59 -04:00
gen party: xbox/mbs responders, crash triage, generator and impl updates 2026-07-26 18:24:59 -04:00
scripts Initial commit: PlayFab Party + Multiplayer LAN emulator for splitux 2026-07-08 15:32:03 -04:00
src party: xbox/mbs responders, crash triage, generator and impl updates 2026-07-26 18:24:59 -04:00
test-bench party: xbox/mbs responders, crash triage, generator and impl updates 2026-07-26 18:24:59 -04:00
.gitignore responder: player_session/join object + TitlePlayerAccount (fix social Build NRE) 2026-07-09 15:52:56 -04:00
build.sh party: hand-roll Party data-plane state machine (network bring-up) 2026-07-08 15:45:56 -04:00
CHANGELOG.md release: 0.5.0 — in-game co-op + leave/rejoin 2026-07-10 13:26:09 -04:00
CMakeLists.txt release: 0.5.0 — in-game co-op + leave/rejoin 2026-07-10 13:26:09 -04:00
Makefile Initial commit: PlayFab Party + Multiplayer LAN emulator for splitux 2026-07-08 15:32:03 -04:00
README.md docs: refresh README + add CHANGELOG, bump to 0.4.0 2026-07-10 00:45:12 -04:00

playfab-party-emu (splitux backend)

Clean-room LAN emulator for the PlayFab Party real-time transport and PlayFab Multiplayer / Lobby control plane — the splitux co-op backend for games whose netcode rides PlayFab Party instead of Steam/EOS (first target: Lonely Mountains: Snow Riders, PlayFab title 1BB03).

Sibling to eos_sdk_emu-splitux and keen-emu-splitux; same build/CI pattern.

Status — 2-player co-op works end-to-end

Full lifecycle verified on LMSR (both Race and Team/Party modes), two instances over a shared loopback bus:

join → play → leave, with clean teardown on both sides.

  • Login / online: auth-rebound/pf-responder.py forges the PlayFab client login + Megagon MBS backend so the game authenticates offline.
  • Lobby: create / join / search-props / members, host discovers the joiner.
  • Party data plane: cross-process endpoint presence + message relay (Mirror-over-Party) over the shared bus.
  • Join reliability: the remote EndpointCreated is announced ~1s after the peer is discovered — presenting it before the Mirror server's NetworkManager is ready makes the server emit a reject control frame and the client never connects. The delay fixed the (always-failing) team mode and the flaky race.
  • Leave: departing player gets the full SDK teardown EndpointDestroyed → NetworkDestroyed → LeaveNetworkCompleted (the game hangs on "leaving" without NetworkDestroyed), and the host reconciles — it reaps the departed member from both the lobby (MemberRemoved, host 2/8→1/8) and the party network (EndpointDestroyed, stops the relay / removes the in-game player).

Open: host-initiated leave / kick (host quits) is untested.

What it replaces

Drop-in for the game's shipped native DLLs (P/Invoked by the PlayFab Unity SDK):

DLL Role Impl
PartyWin.dll / PartyWin32.dll Party real-time data plane (network / endpoints / relay) src/party_impl.c state machine (+ party_win_stubs.c)
PlayFabMultiplayerWin.dll Lobby + matchmaking control plane src/pfmp_impl.c (+ src/pfmp_win_stubs.c)

The game loads PartyWin32.dll (identical Party C-ABI to PartyWin.dll) — ship both. The exact export surface (names + ordinals) is reproduced from the shipped DLLs via .def files generated by gen/gen.py from abi/*.exports.tsv; adding a real impl means adding the name to gen.py's IMPLEMENTED set so its logging stub is dropped (make gen).

Auth rebound

auth-rebound/pf-responder.py is a loopback HTTPS responder that forges the PlayFab client login (LoginWithSteam → EntityToken) + the Megagon MBS backend (api.mbs.megagonindustries.com) so the game authenticates offline. Requires the CA from auth-rebound/gen-certs.sh in the prefix trust store and *.playfabapi.com / MBS hosts redirected to 127.0.0.1. Certs are not committed — run gen-certs.sh to regenerate.

Build

bash build.sh          # fast local MinGW cross-check (build/*.dll, no PDB, throwaway)
make sync build-vm     # canonical MSVC build on the compute node -> build/Release/
make gen               # regenerate .def + stubs after editing gen/gen.py IMPLEMENTED

CI (.github/workflows/build.yml) does the canonical MSVC build (VS 2022 x64) on the self-hosted [splitux, windows] runner, verifies export parity, and publishes a release zip on v* tags (nightly prerelease otherwise).

Co-op test bench

test-bench/coop/ — the native 2-instance co-op loop, run on the compute Windows RE box (ssh compute, nushell). See test-bench/coop/README.md.

edit src → make sync build-vm → nu bench.nu deploy → nu bench.nu clean
  → nu bench.nu launch → [drive host+join in the RDP session] → nu bench.nu verdict

send-trace.js is a frida adjunct that backtraces the Mirror 0a0b0c0d control frames to GameAssembly.dll+RVA (how the join-reject frame was localized).

The older single-instance test-bench/scripts/ (swap.sh, smoke-splitux.sh) drives the game via splitux for login/menu smoke tests.