Multiplayer with the engine
Owners, replication, rpc and claims, the host as a page, shared time, rooms of 30.
- An entity has an owner.
world.spawn([...], { owner: me.id })makes a player's page simulate it and write its components;{ owner: 'host' }is for the host's rules (bots, pickups, a ball). A component withnet: { owner: 'host' }is written by the host even on a player's entity (health, score): clients can't award themselves anything, not even its starting value (the host starts it from the schema; set it innet.onSpawnon the host when it depends on the entity). - Replicate what others must see, nothing else.
defineComponent(name, schema, { net: { replicate: true } })sends only changed fields, quantized by each field'sstep, batched inside the room's budget, drawn about 100 ms behind between two updates. Usepredict: trueon the owner's movement for instant feel; the host corrects it smoothly. Don't replicate what is derived. - Actions are calls.
net.rpc('fire', { args: { angle: t.f32(...) }, to: 'host', rate: 10, validate, run }): arguments are checked on arrival, a bad call is refused and never run, a player can only call so fast, and during a match only its players can call (spectators: truelets watchers in). Contested things ("two players reach one chair", a pickup) arenet.claims: the host settles them by the room's clock. Never trust a client's word about damage, score, position of someone else, or time. - The host is a page. It can reload, drop or leave. Rules behind
authority: 'host'follow the role at once; entities the host owns are kept in the room and come back with a new host. The engine tests exactly this (below): runcheck. - Time and randomness. Rules use
time.dtandworld.rng('stream')(seeded from the match), shared deadlines use the match clock (world.time.match, paused with the match), neverDate.now()orMath.random()in rules. - 30 players. Per-entity update rates of 10 to 20 Hz, interest by distance (
Net({ focus })),priorityon what matters.