A Mac hosts a game of 8 players #32

Merged
agra merged 6 commits from task/3994-a-mac-hosts-a-game-of-8-players into master 2026-10-31 14:58:22 +02:00
Owner

Tracker: #3994

Step 3 of the multiplayer netcode plan (#3571). A hosted game now holds 8 players. Snapshots go out delta-compressed. A bots tool fills the game, and a bench holds the server's tick to 2 ms with 8 players.

  • Player limit. The server holds at most 8 players (MAX_PLAYERS). A 9th connect is refused with the new reason full, and so is an accept that arrives after the game has filled. The joining app and the bots tool both print the server at <address> holds 8 players already, as many as a game holds. The protocol version goes to 4.

  • Delta snapshots. Each client's commands now carry the tick of the newest snapshot it holds. The server keeps what it sent over the last 60 ticks, both the shared world and each connection's own part, and writes each snapshot against the newest one that client acknowledged:

    • a player or body unchanged since then is not written;
    • a changed one carries a mask and only the fields that changed, exact to the bit (-0 differs from 0);
    • players who joined or left since the base are written too, and earlier ticks are diffed against what the base carries of them.

    A client that has acknowledged nothing the server still holds gets a whole snapshot. The client keeps the snapshots it may be sent against and drops one written against a base it does not hold.

  • Bandwidth. With 8 players walking and the bodies asleep, each client within a tick's round trip, as on a LAN, receives about 15 KB a second (tests/net_bandwidth.sx, budget 20 KB/s); whole snapshots would take about 150 KB a second. Each snapshot also carries the players of the two ticks before it, and those of a tick its base does not hold are written against the base's own, so a client one to two ticks away receives about 25 KB a second and one further about 35 KB.

  • Tests. tests/net_deltas.sx pins the delta round trip: exact values, only changed fields written, joins and leaves since the base, a lost snapshot leaving the next decodable, and an unheld base refused. It also pins how the server picks a base from acknowledgements. tests/net_handshake.sx and tests/net_joining.sx pin the full refusal from both sides.

  • Bots. tools/bots.sx, built into sx-out/tools/bots, joins --count N scripted players by address (--join <address>[:port]). Each is a full client on its own UDP socket and joins at the first spawn marker. It walks its own loop through the clearing, from where one of the map's bodies starts or a dummy hangs to the next, pushing what it walks into. tests/bots.sx runs it against the dedicated server: 7 bots join, and a watching client sees them walk in the clearing and push 18 of its 20 bodies. A further bot is refused with its reason printed, and Ctrl-C makes each bot leave.

  • Server bench. tools/bench/server.sx times the first map's server with 8 scripted players walking from the spawn into the props and the dummies over a loopback, with every body and dummy part awake. The worst tick is about 0.85 ms under taskpolicy -b (2 ms budget). The gate builds it and runs it beside the physics bench. tests/server_bench_scene.sx checks that the scene does what the bench claims.

  • README. It now says how many players a game holds, how snapshots are written, and how to run the bots and the server bench.

The in-app check is for Agra: host, then run sx-out/tools/bots --join 127.0.0.1 --count 7 from the repository root. A client walking among the 7 bots predicts in about 0.5 ms a frame, 2 ms at worst, built as the app is built.

Tracker: [#3994](https://sak.swipelab.com/tasks/3994) Step 3 of the multiplayer netcode plan (#3571). A hosted game now holds 8 players. Snapshots go out delta-compressed. A bots tool fills the game, and a bench holds the server's tick to 2 ms with 8 players. - **Player limit.** The server holds at most 8 players (`MAX_PLAYERS`). A 9th connect is refused with the new reason `full`, and so is an accept that arrives after the game has filled. The joining app and the bots tool both print `the server at <address> holds 8 players already, as many as a game holds`. The protocol version goes to 4. - **Delta snapshots.** Each client's commands now carry the tick of the newest snapshot it holds. The server keeps what it sent over the last 60 ticks, both the shared world and each connection's own part, and writes each snapshot against the newest one that client acknowledged: - a player or body unchanged since then is not written; - a changed one carries a mask and only the fields that changed, exact to the bit (-0 differs from 0); - players who joined or left since the base are written too, and earlier ticks are diffed against what the base carries of them. A client that has acknowledged nothing the server still holds gets a whole snapshot. The client keeps the snapshots it may be sent against and drops one written against a base it does not hold. - **Bandwidth.** With 8 players walking and the bodies asleep, each client within a tick's round trip, as on a LAN, receives about 15 KB a second (`tests/net_bandwidth.sx`, budget 20 KB/s); whole snapshots would take about 150 KB a second. Each snapshot also carries the players of the two ticks before it, and those of a tick its base does not hold are written against the base's own, so a client one to two ticks away receives about 25 KB a second and one further about 35 KB. - **Tests.** `tests/net_deltas.sx` pins the delta round trip: exact values, only changed fields written, joins and leaves since the base, a lost snapshot leaving the next decodable, and an unheld base refused. It also pins how the server picks a base from acknowledgements. `tests/net_handshake.sx` and `tests/net_joining.sx` pin the `full` refusal from both sides. - **Bots.** `tools/bots.sx`, built into `sx-out/tools/bots`, joins `--count N` scripted players by address (`--join <address>[:port]`). Each is a full client on its own UDP socket and joins at the first spawn marker. It walks its own loop through the clearing, from where one of the map's bodies starts or a dummy hangs to the next, pushing what it walks into. `tests/bots.sx` runs it against the dedicated server: 7 bots join, and a watching client sees them walk in the clearing and push 18 of its 20 bodies. A further bot is refused with its reason printed, and Ctrl-C makes each bot leave. - **Server bench.** `tools/bench/server.sx` times the first map's server with 8 scripted players walking from the spawn into the props and the dummies over a loopback, with every body and dummy part awake. The worst tick is about 0.85 ms under `taskpolicy -b` (2 ms budget). The gate builds it and runs it beside the physics bench. `tests/server_bench_scene.sx` checks that the scene does what the bench claims. - **README.** It now says how many players a game holds, how snapshots are written, and how to run the bots and the server bench. The in-app check is for Agra: host, then run `sx-out/tools/bots --join 127.0.0.1 --count 7` from the repository root. A client walking among the 7 bots predicts in about 0.5 ms a frame, 2 ms at worst, built as the app is built.
A client's commands carry the tick of the newest snapshot it holds. The
server keeps the world it sent each of the last 60 ticks and what each
connection was sent beside it, and writes each snapshot against the newest
one sent that its client acknowledged: a player or body unchanged since is
not written, and a changed one carries a mask and the fields that changed,
exact to the bit. A client that has acknowledged none the server holds gets
a whole one. A connect or an accept while 8 play is refused as full, and the
app prints so.
Each bot is a client of its own on a UDP socket of its own, joining at the
first spawn marker, and walks a loop from where one of the map's bodies
starts or a dummy hangs to the next, those places dealt by their bearing
from the clearing's centre to one loop for each of the 8 players a game
holds. It prints each join, why one is refused, and each leave on Ctrl-C.
The tests build it beside the dedicated server and run it against it.
Its scene joins 8 scripted players over a loopback, each walking its loop
from the first spawn marker into the clearing, where every body and dummy
part wakes. The gate runs it under taskpolicy -b beside the physics bench.
agra merged commit cb32ef6c87 into master 2026-10-31 14:58:22 +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!32
No description provided.