A second Mac joins a hosted game by address and sees the host's player walk #29

Merged
agra merged 2 commits from task/3992-a-second-mac-joins-a-hosted-game-by-addr into master 2026-10-31 11:09:36 +02:00
Owner

Tracker: #3992

Step 1 of the multiplayer netcode plan (#3571): the app hosts a game over UDP, or joins one by address, and every player sees the others' skeletons walk, turn and jump where the server has them.

  • Transport. One non-blocking UDP socket a side (src/net/udp.sx). A message longer than a 1200-byte datagram crosses in fragments the receiver reassembles, and one missing a fragment is dropped whole (src/net/fragments.sx); the first map's whole snapshot crosses in two. The loopback (src/net/loopback.sx) is now an in-process datagram network behind the same send and receive, the tests' transport.
  • Handshake. connect (protocol version, the world file's hash, a client salt), challenge (a server salt), accept (both salts), welcome (player id, first snapshot). Nothing larger than the connect answers an address before the challenge round trip; a differing version or world is refused. 10 s of silence or a leave ends a connection and removes its player; a datagram that does not decode, or comes from an address with no connection, is dropped and the server carries on.
  • Many players. The game (src/sim/game.sx) is the server's rules and state apart from any transport; the server (src/sim/server.sx) runs it for every connection, each its own player joining at the map's first spawn marker, while a respawn takes the next marker in turn. Commands stay newest-wins per player; players pass through each other.
  • Hosting. The app's server ticks on its own thread over its own world read from the world file (src/sim/host.sx), and the host's own client joins it over UDP at 127.0.0.1 like any other. Launching hosts on UDP port 27960, --port sets another, --join <address>[:port] joins instead (src/net/launch.sx). A joined app whose server falls silent for 10 s closes, printing why. The in-frame lockstep of server and client is gone.
  • View. The client draws its eye, the bodies and the other players at a view tick that runs at the clock's pace two ticks behind the newest snapshot, leaning toward that delay, so the view moves forward every frame of a steady walk whatever phase of the client's ticks the snapshots arrive at (tests/sim_view.sx, which fails on the two-newest lerp by the client's own fraction).
  • Remote players draw as the training dummies' skeleton standing in the template's pose at their feet, turned to their yaw, and not while dead (tests/figure_draws.sx, with its golden).
  • Tests. Over UDP on 127.0.0.1 a server and two clients on the first map: each welcomed with its own player at the first spawn marker, each client's snapshot putting the other's player where the server's tick did (tests/net_udp.sx); a threaded host and two UDP clients (tests/net_host.sx); the handshake from both sides (tests/net_handshake.sx, tests/net_joining.sx); fragments (tests/net_fragments.sx). tests/sim_headless.sx covers the transport, server and host modules.
  • README, Playing: how to host and join.
Tracker: [#3992](https://sak.swipelab.com/tasks/3992) Step 1 of the multiplayer netcode plan (#3571): the app hosts a game over UDP, or joins one by address, and every player sees the others' skeletons walk, turn and jump where the server has them. - **Transport.** One non-blocking UDP socket a side (`src/net/udp.sx`). A message longer than a 1200-byte datagram crosses in fragments the receiver reassembles, and one missing a fragment is dropped whole (`src/net/fragments.sx`); the first map's whole snapshot crosses in two. The loopback (`src/net/loopback.sx`) is now an in-process datagram network behind the same send and receive, the tests' transport. - **Handshake.** connect (protocol version, the world file's hash, a client salt), challenge (a server salt), accept (both salts), welcome (player id, first snapshot). Nothing larger than the connect answers an address before the challenge round trip; a differing version or world is refused. 10 s of silence or a leave ends a connection and removes its player; a datagram that does not decode, or comes from an address with no connection, is dropped and the server carries on. - **Many players.** The game (`src/sim/game.sx`) is the server's rules and state apart from any transport; the server (`src/sim/server.sx`) runs it for every connection, each its own player joining at the map's first spawn marker, while a respawn takes the next marker in turn. Commands stay newest-wins per player; players pass through each other. - **Hosting.** The app's server ticks on its own thread over its own world read from the world file (`src/sim/host.sx`), and the host's own client joins it over UDP at 127.0.0.1 like any other. Launching hosts on UDP port 27960, `--port` sets another, `--join <address>[:port]` joins instead (`src/net/launch.sx`). A joined app whose server falls silent for 10 s closes, printing why. The in-frame lockstep of server and client is gone. - **View.** The client draws its eye, the bodies and the other players at a view tick that runs at the clock's pace two ticks behind the newest snapshot, leaning toward that delay, so the view moves forward every frame of a steady walk whatever phase of the client's ticks the snapshots arrive at (`tests/sim_view.sx`, which fails on the two-newest lerp by the client's own fraction). - **Remote players** draw as the training dummies' skeleton standing in the template's pose at their feet, turned to their yaw, and not while dead (`tests/figure_draws.sx`, with its golden). - **Tests.** Over UDP on 127.0.0.1 a server and two clients on the first map: each welcomed with its own player at the first spawn marker, each client's snapshot putting the other's player where the server's tick did (`tests/net_udp.sx`); a threaded host and two UDP clients (`tests/net_host.sx`); the handshake from both sides (`tests/net_handshake.sx`, `tests/net_joining.sx`); fragments (`tests/net_fragments.sx`). `tests/sim_headless.sx` covers the transport, server and host modules. - README, Playing: how to host and join.
The app hosts a game on its own server thread, or joins one with --join.
Messages cross in fragments of at most 1200-byte datagrams, a client is
challenged before it is welcomed, the server serves a player for each
connection, and the client draws its eye, the bodies and the other players'
skeletons at a view that runs a steady delay behind the newest snapshot.
agra merged commit 83f85faa34 into master 2026-10-31 11:09:36 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sx/hulken!29
No description provided.