skate3recomp-multiplayer/docs/netcode-revival.md

3.3 KiB

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 XamDialogs 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.