re:psn in action, v0.6.0, pre-release

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.

TCP 36100TCP 36100game traffic, directre-psn server192.0.2.10player A198.51.100.7player B203.0.113.9
  1. player A → server HELLO Player A says who it is and which UDP port its game listens on.
  2. server → player A HELLO_ACK The server answers with a user id, the address it sees, a token and a flags byte.
  3. player A → server CREATE_ROOM Player A opens a room.
  4. server → player A ROOM_JOINED The room exists, and A is its first member.
  5. player B → server HELLO Player B connects the same way.
  6. server → player B HELLO_ACK The server answers B.
  7. player B → server SEARCH_ROOMS B asks for rooms of its title that are not full.
  8. server → player B ROOM_LIST A's room is in the list.
  9. player B → server JOIN_ROOM B joins it.
  10. server → player B ROOM_JOINED The reply carries every member's address and game port, so B knows where A is.
  11. 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.

TCP 36100TCP 36100direct after the punchre-psn server192.0.2.10player A198.51.100.7player B203.0.113.9
  1. player A → server HELLO Player A connects and gives its game port.
  2. server → player A HELLO_ACK The answer includes the token that A's probes will carry.
  3. player A → server CREATE_ROOM A opens a room.
  4. server → player A ROOM_JOINED A is its first member.
  5. player A → server PROBE From its game socket, A sends a UDP probe, so the server sees A's router's public address and port.
  6. server → player A PROBE_REPLY The server tells A what it saw.
  7. player B → server HELLO Player B connects.
  8. server → player B HELLO_ACK The server answers B.
  9. player B → server PROBE B probes from its game socket too.
  10. server → player B PROBE_REPLY The server tells B what it saw.
  11. player B → server JOIN_ROOM B joins A's room.
  12. server → player B ROOM_JOINED The member entry for A carries the router address and port from A's probe.
  13. player A → player B PUNCH Both players send PUNCH packets to each other's router address, which opens each router for the other.
  14. 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.

TCP 36100TCP 36100direct path: blockedrelayedrelayedre-psn server192.0.2.10player A198.51.100.7player B203.0.113.9
  1. player A → server HELLO Player A connects.
  2. server → player A HELLO_ACK The flags byte says the server relays.
  3. player B → server HELLO Player B connects.
  4. server → player B HELLO_ACK The server answers B.
  5. player A → server PROBE A probes; its router keeps one port, so the server sees a usable address.
  6. player B → server PROBE B probes; its router maps a new port for each destination.
  7. player A → player B PUNCH A punches towards the address the server saw for B. It does not reach B.
  8. server → player A ROUTE The server tells A that streams to B go through the relay.
  9. player A → server STREAM_CONNECT A opens a new TCP connection to the server and asks for a stream to B.
  10. server → player B STREAM_OFFER The server offers B the stream, on B's control connection.
  11. player B → server STREAM_ACCEPT B opens its own new TCP connection to the server and accepts, within 10 seconds.
  12. server → player A STREAM_READY The stream is ready on A's side.
  13. server → player B STREAM_READY And on B's side.
  14. 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.

TCP 36100TCP 36100direct path: blockedre-psn server192.0.2.10player A198.51.100.7player B203.0.113.9
  1. player A → server HELLO Player A connects.
  2. server → player A HELLO_ACK The flags byte says the server does not relay.
  3. player B → server HELLO Player B connects.
  4. server → player B HELLO_ACK The server answers B.
  5. player A → server PROBE A probes.
  6. player B → server PROBE B probes; its router maps a new port for each destination.
  7. player A → player B PUNCH The punch towards B's seen address does not reach B.
  8. 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.
Top