Players are shown only to players in the same world scope and within distance
Status: Accepted (2026-10-26)
Context
A world scope (virtual_world + interior) already separated pickups and vehicles. Players were not separated: their frames, shots, projectiles, deaths and animations went to every connection. A player in another virtual world was visible as a copy, and its shots hit through the worlds. Sending everything to everyone also does not scale with distance: a player far away needs no frames at all.
The packet formats, radii and recipients are in Player streaming and PlayerStreamOut.
Decision
- The server keeps a shown set per recipient. A player is shown to a recipient when both are in the same scope and the player is within distance of the recipient's stream center, with hysteresis. A seated player is shown exactly when its vehicle is; a spectate target always is. This set is the only filter for every relay about a subject, and it changes at once, independent of the send queue.
- Showing is a snapshot. On show the recipient gets the subject's current state (skills, nametag, the playing animation) on the reliable lane, and the copy appears with the next frame; a dead subject appears as a corpse.
- Hiding is an explicit event, not a leave.
PlayerStreamOutcarries the subject's last relayed frameseq: older frames are dropped, a newer one means the next show. A hide not yet sent goes before the next show; death and respawn that happen while the subject is hidden are not sent, and a new show always builds the copy from a living frame. - A seated player is always in its vehicle's scope. A scope change of the player first takes it out of the seat (the vehicle stays in its scope); a server-side seating into a vehicle of another scope is refused.
- Spawn and respawn set the interior of the spawn point (0, outside, by default); the virtual world is kept.
- Damage respects the scope. An instant-hit shot counts only in the scope it was fired in, a melee blow only in the victim's current scope. Area damage and vehicle kills check the scope of their own cause.
- Chat stays global. Chat per world is gamemode policy (
player_chat,send_chatrecipients), not a platform rule. - Resources see the decision.
player_stream_inandplayer_stream_outreport changes of the shown set.
Consequences
- The player radius (420 m) is larger than the vehicle radius (200 m): seated players disappear before players on foot, and shots from a vehicle do not reach past the vehicle radius. The player radius covers the longest instant-hit ray, so a shot never goes to a player the shooter is not shown to; a gamemode weapon range extends it.
- The player list does not depend on streaming: the gamemode decides who is listed.
Alternatives considered
- Filter relays by scope without hiding on the client. Rejected: copies from another world would freeze in place forever.
- Hide with
PlayerLeave. Rejected: a leave retires the subject's generation, so the same player could never be shown again. - A driver's scope change moves the vehicle with every passenger. Rejected: other players would change worlds without a gamemode decision, and it needs new API. The gamemode moves a vehicle explicitly.
- One scope check before damage is classified. Rejected: a grenade thrown before a world change would not count. Area damage and vehicles check the scope of the cause instead.
