Loading…
gathering yarn balls…
Cat Game Research
Frontier
Dev-log transmissions from the expedition

Physics Without a Server: Dynamic Authority on the PI Swarm

Protocol v6 puts a transferable, arbitrated authority stamp on every entity delta in the PI swarm — the plumbing that lets peer nodes simulate physics for entities they hold authority over while seeder arbiters keep the writes honest. The dynamic-authority model that Donnybrook, Aura Projection, and Glenn Fiedler's Oculus-sponsored VR work pointed at — plus how mirror seeders relay rooms, gossip, and signed entity state across the fabric.

Ask a room full of game-network engineers how to run physics on a peer-to-peer world and you will get two answers. The first is "make one node the server" — clean, safe, and exactly the wrong shape for a swarm where every peer is supposed to carry its share of the work. The second is "make it deterministic and lockstep the inputs" — clean in a different way, until you realize every client now simulates everything, which multiplies the compute instead of spreading it. For the past few weeks we have been building a third answer into the PI swarm, and this week its wire protocol shipped: every entity delta now carries a transferable, arbitrated authority stamp, and the seeder that owns a zone is the court that decides who may write it.

This is a dev-log on where the swarm stands — the fabric that got us here, the dynamic-authority model that went in as protocol v6, and the mirror-seeder relay machinery that carries it all.

48 B
entity delta (v6)
~13 K
lines — swarm fabric
~21 K
lines — physics engine
20 Hz
entity tick
217
swarm tests green
v6
protocol

The fabric that was already there

The PI swarm did not start as a game netcode — it started as a training swarm. pi_daemon nodes find each other over LAN discovery, authenticate with a pre-shared key, and pull model-shard work from a seeder that owns the authoritative trainer. Chat rooms, whispers, and presence came next, then player sessions, then entity replication. By the time physics became the question, the fabric already had three transports doing three different jobs:

TCP control plane
HELLO/WELCOME handshake, challenge-response HMAC, session issuance, GAME_UPDATE input, GAME_STATE delivery, chat frames. Ordered, reliable, authenticated.
UDP state plane
Entity datagrams — up to 24 deltas per packet, ≈1.2 KB (1.5 KB with a full ack trailer) — under path MTU, every datagram signed with a trailing HMAC-SHA256 of the swarm PSK. Unordered by design; per-entity sequence numbers absorb reorder and loss.
Gossip control bus
Rare, important events — node joins and parts, zone handoffs, player presence, and now authority claims and grants — flood as deduplicated SwarmEvent envelopes across seeder links.

On top of that sits the zone map: a consistent-hash (jump-hash) ring over the live seeder set that answers "which seeder owns zone Z" identically on every node. Zones are the spatial shard of the world; ownership is deterministic, so it never needs an election. The leaf client that speaks this protocol today is Clowder — our network portal GUI — which connects to any seeder directly over TCP; everything below is what happens under its socket.

How mirror seeders actually work

A single seeder is a broker. Two seeders are a fabric. The thing that stitches them is the mirror relay — and it is worth describing precisely, because it is what makes authority records meaningful across a real network.

A mirror seeder opens a long-lived TCP link upstream and presents itself with two roles at once: CLIENT — so the upstream treats it as a normal authenticated peer and sends it a full welcome — and SEEDER_LINK, which marks the connection as node-to-node relay plumbing rather than a leaf:

client Aclient Bseeder Abroker / zone ownerTCP: gossip · rooms · whispersUDP: signed entity datagramsseeder BCLIENT|SEEDER_LINKclient Cclient Dseeder B presents as CLIENT|SEEDER_LINK under its real node id, advertises its UDP port in HELLO.A and B each flood their own zones every 50 ms tick; the other merges and re-floods onward.Rooms sync by membership diff (1 s) — clients A..D can share a room without a shared seeder.
Mirror topology: a second seeder relays state, chat, and membership for its own leaf clients — no leaf needs a connection to seeder A.

Five details make the relay work:

01
It registers under its real node id. The upstream registers the link under the mirror's actual seeder identity, not an ephemeral connection handle. That means zone membership, purge-by-link on disconnect, and datagram identity all key off the same fabric-unique id the mirror uses everywhere else.
02
It advertises its UDP endpoint in HELLO. The discovery_port field in the mirror's hello is where it wants entity datagrams sent. The upstream learns the port once, caches the resolved address, and floods entity state there every tick — no per-datagram DNS, no endpoint guessing.
03
It synchronizes room membership, not traffic. A once-per-second membership diff compares the rooms the mirror's local clients occupy against the rooms it has joined upstream — a room that gains local occupancy gets a JOIN, a room that loses its last local member gets a LEAVE. The mirror only pulls broadcast traffic its own clients can actually see.
04
It relays in both directions, deduplicated. Gossip events carry a fabric-unique event_id; chat broadcasts carry a msg_id that stays constant across hops. A message making a full circle around a cyclic topology is dropped on dedup, not amplified. Whispers route by node_id across the link to whichever seeder holds the target.
05
It heals itself. The loop is connect → handshake → relay (200 ms poll, 5 s heartbeat) → teardown → 2 s backoff → reconnect. A link flap is a routine event, not a crisis: membership re-syncs, room joins re-issue, and state floods resume on the next tick.

Entity state crosses the same link on the UDP plane: every 50 ms tick, each seeder packs its zones into signed datagrams and sends them to every downstream link peer plus its upstream mirror. The receiving seeder merges them into its own replication table — and then its own tick re-floods them onward, which is how state crosses a multi-hop fabric without any node needing to know the topology. Despawn tombstones ride the same datagrams and are acknowledged back to their origin by node id, piggybacked on the ack trailer — so a NAT'd or multi-homed peer still gets credit for applying a removal. Bulk content rides the control plane separately: SWARM_ASSET_REQ pulls ranged byte windows of any file in the seeder's asset directory — the substrate for multi-source fetching once a piece-picking policy lands.

This relay design is what makes the next piece possible. If authority were a property of a connection — "whoever sent this packet may write" — it would die at the first relay hop. We needed authority to be a property of the entity itself.

The problem with client-authoritative physics

Until this week, the swarm's entity model was honest but trusting: a client sends GAME_UPDATE with a position and a sequence number, its host seeder checks the session owns the entity and the rate is under 60 updates a second, stamps it into the table, and floods it. It is the classic client-writes, server-forwards arrangement — and it has a hole you could fly a ship through. A modified client can write any position it likes (teleport), and because merges are latest-seq-wins, it can also write any sequence number it likes (win every merge forever). Session binding and rate limiting police the envelope, not the payload.

Pure server authority closes the hole but breaks the swarm: if only the zone owner may write, then every physics simulation — for every object, for every player — concentrates on seeders while clients become dumb terminals. For a network whose entire point is that peers carry the load, that is a contradiction.

"The question is not who owns the physics. It is who owns the write — per entity, provably, and changeably."

Design note · Phase 8 · pi/swarm

The literature has a name for the middle path: dynamic-authority distributed simulation. Glenn Fiedler picked exactly this model for his Oculus-sponsored networked-physics-in-VR research — players take authority over the cubes they interact with and broadcast those cubes' state; everyone else snaps that state onto their non-authority replicas. He rejected lockstep for the same reason we did: at a 90 Hz simulation rate with up to 250 ms of latency to hide, prediction would need ~25 re-simulation steps per rendered frame — a 25x compute tax on an already-CPU-heavy sim.

What shipped: authority as a field, not a role

Protocol v6 adds four bytes to every entity delta. That is the whole wire change — and it changes everything downstream:

EntityStateDelta · 48 bytes · protocol v6
entity_id      u32   fabric-unique: (node_id & 0xFFF) << 20 | counter
zone_id        u32   spatial shard the entity lives in
seq            u32   per-entity sequence — latest wins
pos[3], vel[3] f32   metres, metres/second
yaw, anim      f32+u16
flags          u16   bit0 despawn, bit1 session-bound
authority_node u32   NEW — the seeder authorized to write this entity

The rule that consumes it is short enough to hold in your head. When a delta arrives at a replica:

1
Undeclared writer?authority_node == 0 — drop as malformed before anything mutates.
2
Authority recorded? The declared writer must match the table's grant exactly. Mismatch — including a tombstone — is a rogue write: dropped, counted, observable via rogue_entity_writes().
3
Never seen it? First sight accepts only if the writer holds the entity's mint tag: the entity id embeds the minting seeder's low 12 bits in its high bits, so only the minter can introduce an id. Squatting a foreign tag is a rogue write.
4
Then the normal rules apply — latest-wins sequence ordering, zone-migration re-bucketing, tombstone guards.

Because the stamp travels with the record, a relay's job stays purely mechanical: verify the datagram's HMAC, apply each delta by its own declared writer, forward verbatim. The sender of the packet is irrelevant to who may write — which is precisely what lets state cross a multi-hop fabric.

Claims, arbiters, and grants

A stamp that can never change is just a name tag. The point is that authority moves — and the movement is arbitrated:

seeder Bclaimant / relayzone owner Ajump-hash arbiterall replicasapply last-epoch-winsAUTH_CLAIM {eid, zone}AUTH_GRANT {eid, node, epoch}arbiter policy (8a)deny: claimant not a fabric member · entity never seendeny: claim's zone ≠ entity's real zone (self-arbitration closed)deny: session-bound entity (FLAG_SESSION — bound to its host)grant: everything else — first-claim-wins, epoch-ordered
Claims flood the gossip bus; the claim's zone owner evaluates and grants — or silently drops — while every replica applies grants last-epoch-wins, including the claimant and the displaced authority.

Two of the arbiter's rules deserve a sentence each because they were found the hard way — by roasting the implementation, not by design review:

And the shield: every delta a host seeder writes for a client-attached entity carries FLAG_SESSION. Session entities are never transferable — a player's own entity is bound to its host by definition, so the arbiter denies claims on them outright. Client-supplied reserved flag bits are masked at ingest, so a client can neither despawn itself through GAME_UPDATE nor peel off its own session mark.

What this buys: the peer-node physics integrator

Here is the payoff — the model the post title promises. With authority as a transferable, arbitrated entity property, the swarm's physics division of labor becomes:

ActorRole in the physics integrator
Client's host seederProxies the client's authority: stamps its own node id + SESSION on client-driven deltas. The client moves; the seeder signs.
Claim-holding seederSimulates the physics for world entities it has been granted — its own rigid-body shard of the world. The integrator work lands on N seeders' attention sets, not one.
Zone ownerArbitrates contested authority. Roadmap: simulates unclaimed entities as fallback + shadow-validates authority output.
Relays / leavesApply deltas whose declared writer holds the grant; forward verbatim; count rogue writes. No physics, no authority — just verification-shaped plumbing.

Today these roles are plumbing, not computation — the authority layer is live, but no node runs a physics integrator against it yet. The division activates as 8b lands the simulation posture; what 8a guarantees is that when the sim arrives, the write rules are already enforced at the wire.

This is the "best of both worlds" in concrete form. Like lockstep, the honest nodes all agree on who is allowed to say what. Unlike lockstep, each participant only simulates the slice of the world it holds authority over — compute spreads across the swarm the same way bandwidth already did. And unlike raw client authority, a forged write is not a merge candidate, it is a counter.

Why seeders arbitrate instead of electing a physics server
Jump-hash zone ownership was already in place for entity migration — the same deterministic "who owns this region" answer exists on every node without any leader election. Reusing it for authority arbitration means the arbiter is always the seeder standing in the right place, and arbitration survives topology changes for free: lose a seeder, the hash re-answers ownership, claims re-resolve.

The physics engine itself is ready for this division — that part was already true, and it is what made the model plausible rather than aspirational. The V Engine's ~21 K-line rigid-body system runs a fixed 60 Hz timestep with spatial-grid broadphase, warm-started manifolds, contact islands, and a PGS solver — and, critically, it is ordering-deterministic end to end: islands and manifolds are sorted deterministically, job-graph merges follow a fixed order, and pseudo-random directions are entity-seeded. Ordering determinism is the hard half of distributed simulation — the half ODE famously lacked when Fiedler had to re-seed its RNG per frame to make lockstep work at all.

The research the model stands on

None of this is novel — which is the point. The architecture is a deliberate composition of proven ideas:

WorkWhat it contributes to our model
Fiedler — Networked Physics in VR (Oculus-sponsored)The core posture: distributed simulation where players take authority over objects they interact with and broadcast state; chosen over lockstep specifically to avoid rollback re-simulation cost on a CPU-heavy sim.
Walsh, Ward & McLoone — physics-aware dead reckoning (ISSC 2012 + Walsh's MSc thesis, NUI Maynooth)The formal version: a dynamic authority scheme for shared physics objects is shown to reduce overall inconsistency in peer-to-peer distributed interactive applications; physics-aware dead reckoning reduces instantaneous divergence between handoffs.
Bharambe et al. — Donnybrook (SIGCOMM 2008)Attention-set-based update prioritization + multi-source dissemination; simulations demonstrated viability up to ~900 players (user study on a Quake III prototype). Their attention sets are focus-computed per player; our spatial AoI radius is a simpler instance of the same interest-management family.
Brown, Ushaw & Morgan — Aura Projection (ACM I3D 2019)Physics sharded by spatial region across servers with predictive object migration — our zone_id → jump-hash owner map plus ZONE_HANDOFF is the same region→authority machinery, adapted to P2P.
GGPO / deterministic lockstepThe road not taken: perfect consistency at minimum bandwidth, but every peer simulates everything and the whole model demands bit-exact simulation — the gate we deliberately defer to Phase 8c.
Photon QuantumThe production reference for where this ends: deterministic ECS with verified vs predicted frames — our "arbiter grants + shadow validation" is a coarser-grained version of the same verified/predicted split.
Fiedler — Floating Point DeterminismThe honest gate for Phase 8c: FP determinism is achievable — a Gas Powered Games engineer's first-hand account reports SupCom 1+2 and Demigod relying on IEEE754 across 1M+ customers — but the actual recipe is narrow: same compiler, same instruction set, enforced FPU precision/rounding mode after external-library calls. Not a posture you get by accident.

Shipped vs roadmap — read this before quoting us

implementedauthority_node on every delta; the mint-tag first-sight gate; rogue-write rejection + counting; PHYS_AUTH_CLAIM/GRANT gossip with zone-owner arbitration; epoch-ordered grants; session-entity shields; entity_spawn/entity_write/claim_entity_authority APIs; seeder-side world entities that are claimable by construction. Verified over real mirror links: a claim crosses the gossip bus, the grant lands on claimant and displaced authority, post-grant writes replicate, and the old owner's write is rejected as rogue. Session-entity claims are denied on both nodes of a link.

roadmap Client-authority physics execution (8b): the plumbing assumes a seeder writes for its client — letting a client compute physics for objects it is granted, with the seeder shadow-validating output (dt-bounded speed, continuity, static-geometry sanity) and revoking chronic violators, is the next phase. Verifier re-simulation (8c): bit-exactness discipline plus per-frame state hashes so designated peers can re-simulate a shard and prove desync — that is when the strict-FP question gets answered, and not before there is a real sim to validate against.

Caveats and limits

Cross-references and further reading

Final thought

Server authority asks "which machine is in charge?" Distributed simulation asks a better question: "which machine is in charge of this thing, right now, and who agreed?" Answering it inside the state record — rather than in a connection table — is what lets a swarm replicate physics the same way it already replicates chat: hop by hop, verified at each step, honest about who wrote what. Phase 8a is four bytes and an arbitration loop. Everything it enables is much bigger.

← All transmissions