diff --git a/docs/netcode-revival.md b/docs/netcode-revival.md new file mode 100644 index 0000000..2ef47bc --- /dev/null +++ b/docs/netcode-revival.md @@ -0,0 +1,64 @@ +# Netcode revival (branch `netcode-revival`) + +Goal: revive Skate 3's **original** Xbox 360 multiplayer — the game's own +RakNet-based peer-to-peer netcode — instead of only the ghost co-presence +layer. `main` keeps the working ghost multiplayer + releases; this branch +carries the netcode work and stays clearly separate. + +The transport is **RakNet over XNet/XSocket** (confirmed: rexglue's XNet +comments reference RakNet, and the game's `socket → setsockopt → bind → +sendto` sequence matches `RakPeer::Startup`). The game speaks its own +protocol; we only need the **platform netcode layer to work** (implement +rexglue's stubbed XNet session APIs + route the game's UDP through the +relay). We do not decode the game protocol. + +## Architecture +- **Virtual addressing**: each machine gets a stable `XNADDR` (virtual IP + `10.80.0.x` + unique 6-byte `abEnet` → RakNet GUID). `XNetXnAddrToInAddr` / + `XNetServerToInAddr` resolve those into the socket layer's address space. +- **Relay = P2P stabilizer**: session UDP is forwarded outbound through the + relay (tagged `game` vs `ghost`) to the destination member's real socket; + on a LAN the transport can go direct. The server stabilizes NAT/remote + play but is not required on a LAN. +- **Session registry in the relay**: `XNetQosListen` advertises a session; + `XNetQosLookup` returns discovered sessions; the lobby/room-code UI stays + as the manual join path. +- **Ghost layer is kept** as the guaranteed fallback (co-presence that works + for everyone even if the netcode path fails). + +## Phase 0 — XNet session/address layer (done, rexglue `767bd6e`) +Implemented in `rexglue-sdk/src/kernel/xam/xam_net.cpp`: +- `XNetGetTitleXnAddr`: stable virtual identity (was loopback + `0xCC` MAC). +- `XNetXnAddrToInAddr` / `XNetInAddrToXnAddr` / `XNetServerToInAddr` / + `XNetXnAddrToMachineId` / `XNetInAddrToString`: resolve virtual addresses + (were failure / `666.666.666.666`). +- `XNetQosListen`: accept advertisement (was `X_ERROR_FUNCTION_FAILED`). +- `XNetQosLookup`: success + empty `XNQOS` + completion signal (Phase 1 + fills it from the relay registry). +- `XNetCreateKey` / `XNetRegisterKey` / `XNetUnregisterKey`: session keys. +- `XNetConnect` / `XNetGetConnectStatus`: report established. +- `getsockopt`: real basic reads off the native socket. +- `XNetSetSystemLinkPort`: accept. + +## Phase 1 — relay game-transport + session registry (next) +- Extend `ghost_relay` to forward game UDP between room members + expose the + session list that `XNetQosLookup` reads. +- Give rexglue `XSocket` a relay-transport mode (recognize `10.80.0.x` + virtual destinations; route via the relay outbound). +- Test: two game instances on one machine (loopback through the relay) reach + a joined session. + +## Phase 2 — invite UI +- `XamShowGameInviteUI` / `XamShowFriendsUI` as ImGui `XamDialog`s backed by + the relay lobby (real player list). Receive side (`XamNotifyCreateListener` + / `XamSessionRefObjByHandle`) already works. + +## Phase 3 — iterate to playable +Whatever the game needs past discovery (RakNet handshake, ports, privilege +fakes). Ghost stays as fallback throughout. + +## Notes +- Testing is done with **two instances on one machine** (no second device); + the relay loops them via `127.0.0.1`. A headless harness may be added. +- Build: `clang-22` override, preset `linux-relwithdebinfo`, per + `docs/build.md` — same as `main`. \ No newline at end of file