How the pieces talk
A re:psn server keeps rooms and leaderboards and tells each player where the others are. These four scenarios follow two players from first contact to game traffic. Each is a fixed script, not live traffic: the addresses are the ones reserved for documentation, and every message name is one of the server's own, described in docs/api.md. The way game traffic is routed is explained in docs/architecture.md.
TCP control connection direct game traffic a direct path that fails game traffic through the server
Direct: players on one LAN or VPN
Both players can reach each other, so the server only introduces them. Game traffic never touches it.
- player A → server
HELLOPlayer A says who it is and which UDP port its game listens on. - server → player A
HELLO_ACKThe server answers with a user id, the address it sees, a token and a flags byte. - player A → server
CREATE_ROOMPlayer A opens a room. - server → player A
ROOM_JOINEDThe room exists, and A is its first member. - player B → server
HELLOPlayer B connects the same way. - server → player B
HELLO_ACKThe server answers B. - player B → server
SEARCH_ROOMSB asks for rooms of its title that are not full. - server → player B
ROOM_LISTA's room is in the list. - player B → server
JOIN_ROOMB joins it. - server → player B
ROOM_JOINEDThe reply carries every member's address and game port, so B knows where A is. - player B → player A B sends game datagrams straight to A. The server is not on the path.
Hole punch: two ordinary home routers
Each player is behind a router. The server learns each router's public address from a probe, the players punch through, and then talk directly.
- player A → server
HELLOPlayer A connects and gives its game port. - server → player A
HELLO_ACKThe answer includes the token that A's probes will carry. - player A → server
CREATE_ROOMA opens a room. - server → player A
ROOM_JOINEDA is its first member. - player A → server
PROBEFrom its game socket, A sends a UDP probe, so the server sees A's router's public address and port. - server → player A
PROBE_REPLYThe server tells A what it saw. - player B → server
HELLOPlayer B connects. - server → player B
HELLO_ACKThe server answers B. - player B → server
PROBEB probes from its game socket too. - server → player B
PROBE_REPLYThe server tells B what it saw. - player B → server
JOIN_ROOMB joins A's room. - server → player B
ROOM_JOINEDThe member entry for A carries the router address and port from A's probe. - player A → player B
PUNCHBoth players send PUNCH packets to each other's router address, which opens each router for the other. - player B → player A Game datagrams now go directly between the players.
Relayed: one player behind a symmetric NAT
B's router picks a new port for every destination, so the punch cannot work. A server run with -relay carries the game traffic instead. The room steps are as in the scenarios above and are left out here; the server sends ROUTE to both players, and only A's copy is shown.
- player A → server
HELLOPlayer A connects. - server → player A
HELLO_ACKThe flags byte says the server relays. - player B → server
HELLOPlayer B connects. - server → player B
HELLO_ACKThe server answers B. - player A → server
PROBEA probes; its router keeps one port, so the server sees a usable address. - player B → server
PROBEB probes; its router maps a new port for each destination. - player A → player B
PUNCHA punches towards the address the server saw for B. It does not reach B. - server → player A
ROUTEThe server tells A that streams to B go through the relay. - player A → server
STREAM_CONNECTA opens a new TCP connection to the server and asks for a stream to B. - server → player B
STREAM_OFFERThe server offers B the stream, on B's control connection. - player B → server
STREAM_ACCEPTB opens its own new TCP connection to the server and accepts, within 10 seconds. - server → player A
STREAM_READYThe stream is ready on A's side. - server → player B
STREAM_READYAnd on B's side. - player A → player B From here the server copies the stream's bytes between the two connections. UDP game datagrams go through the relay as well.
Blocked: the relay is off
The same failed punch as above, but the server was not started with -relay, so it carries nothing. The room steps are as in the scenarios above and are left out here.
- player A → server
HELLOPlayer A connects. - server → player A
HELLO_ACKThe flags byte says the server does not relay. - player B → server
HELLOPlayer B connects. - server → player B
HELLO_ACKThe server answers B. - player A → server
PROBEA probes. - player B → server
PROBEB probes; its router maps a new port for each destination. - player A → player B
PUNCHThe punch towards B's seen address does not reach B. - server → player A There is no ROUTE and no stream: the server has nothing to offer. The players need a port forward, or a shared VPN such as Tailscale.