# 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 (done, built) - v5 relay: `kTypeGame` (opaque game UDP, unicast or room-broadcast) + `kTypeSessionQuery`/`Reply` (discovery). JoinPayload carries the virtual IP. - rexglue `XNetTransport`: joins the same relay + room as the ghost client, routes `sendto` to `10.80.0.x` peers through the relay, injects inbound game datagrams into the game's sockets, and serves session discovery to `XNetQosLookup`. - Wire structs byte-verified against the game's proto (sizes 7/31/1410/25/801) with `static_assert`s on both sides. ## Test recipe (two instances on one machine, local relay) 1. Build: `cmake --build out/build/linux-relwithdebinfo --target ghost_relay skate3 -j$(nproc)` 2. Start the local relay: `./out/build/linux-relwithdebinfo/ghost_relay --port 40001` 3. Launch instance A: `./out/build/linux-relwithdebinfo/skate3` Launch instance B (same machine; the relay loops them via 127.0.0.1). 4. In each instance's M menu: set `skate3_ghost_server = 127.0.0.1`, enable multiplayer, join the SAME room code. The relay then routes both the ghost poses and the game's own netcode traffic. 5. Verify in the log (stdout): `xnet transport: connecting to relay ...`, `xnet transport: relay joined`, and on session search `xnet transport: session reply with N session(s)`. Enable the game's online flow (invite/join) and check whether RakNet establishes a session. 6. Two instances bind the same UDP data port → the second may fail to bind. If so, the game's socket bind must become relay-aware (bind to an ephemeral port in relay mode) — a known Phase 3 item. ## 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`.