Player
On-foot movement and state sync of players, player streaming, the life cycle (spawn, death, respawn), and the server's commands to a player: teleport, appearance, animations, styles, camera, physics and HUD values.
Player model
The keys bitmask
Frame.keys (u16) has bit i set while the key of action i is pressed. The bit order is part of the protocol.
| Bit | Action |
|---|---|
| 0 | forward |
| 1 | backward |
| 2 | left |
| 3 | right |
| 4 | sprint |
| 5 | jump |
| 6 | sneak / walk |
| 7 | fire |
| 8 | aim |
Bits 9..15 are unused; the server keeps only bits 0..8 (player:keys()).
- Held keys (movement, sprint, walk, aim) are sampled as their state at the moment the frame is sent, so a release goes out at once.
- Momentary keys (jump, fire) are latched between sends and cleared on send, so a tap between two frames is not lost.
- The aim bit is set while the sender actually aims, not only while it holds the aim key: the game enters aiming by itself when the player fires without the aim key, and receivers raise the weapon and play the shot by this bit. In a vehicle the aim bit means a passenger leaning out for a drive-by, and
camis the direction of that aim; receivers hold the drive-by pose by these two fields. - Crouch is not a key: it is the
duckedpose of the frame. Crouching in the game is a toggle, so replaying the key would lose its parity. The sender reports its pose; the receiver brings the copy to that pose through the game's own crouch and stand-up transitions.
Player streaming
A player is shown to another player only when both are in the same world scope (virtual_world + interior, as for pickups and vehicles) and within distance. The scope itself is not on the wire. The server is the authority: it keeps a per-recipient set of shown players; the client only creates and removes the copy of a player. Decision: ADR 0035.
- Distance is measured from the recipient's stream center (its body, or its camera while the camera is not its own, see SetCamera) to the server's position of the player: shown within 420 m, hidden 40 m beyond that. This covers the longest hitscan ray (400 m): a shot does not go to a player the shooter is not shown to. A weapon range set by the gamemode extends the show distance to the ray of that range (range × 3) + 20 m.
- A seated player is shown exactly when its vehicle is shown (VehicleSync, 200 m; the recipient's own vehicle is always shown), not by its own distance. Shots from a vehicle do not reach past the vehicle's radius. Showing and hiding a vehicle that has other players inside, together with those players, is one transition: the players first, then the vehicle and its seats. A hide stops the relays at once, and its packets go out together once the queue has room; a show that lacks room waits as a whole and goes after the hides not yet sent.
- A spectate target (
SetCamera mode = 2) is shown at any distance. - A label or a blip attached to a player is shown only while the player is shown.
- The resource events
player_stream_in(player, for_player)andplayer_stream_out(player, for_player)fire on every change of the set, scope changes included: they report the server's decision, not the creation of a copy on the client. A disconnect does not fire them (player_disconnectdoes). - Packets about a subject go only to recipients it is shown to:
RemotePlayerSync,RemoteWeaponShot,RemoteProjectileCreate,RemoteSatchelDetonate,PlayerWasted,PlayerSpawned.RemoteStealthKillalso goes to both participants;PlayerSkillsand the animation packets also go to the subject itself.PlayerLeavegoes to everyone;ChatMessagegoes to everyone or to the recipients the gamemode picked (send_chat), regardless of streaming. - Show (on entering range, or a scope change of either side) is a snapshot on the reliable event lane:
PlayerSkills,PlayerNametag, and the currentApplyAnimationwithelapsed_ms, if one is playing. The client creates the copy on the nextRemotePlayerSync; a dead subject appears as a corpse. - Hide is PlayerStreamOut. Death and respawn of the subject while it is hidden do not go to the recipient: a new show always builds the copy anew from a living frame.
- Show and hide are droppable events: when the queue is full, the streaming reconcile (every 1 s) repeats them. Relays about the subject stop at once, whatever the queue. A show counts only once its whole snapshot is queued, and waits for a hide not yet sent: on the lane a hide always precedes the next show.
- A seated player is always in its vehicle's scope. A change of
virtual_worldor interior (TeleportPlayerto anotherarea) first frees the seat:VehicleOccupancyexit precedesTeleportPlayerin the player's stream, and the client leaves the vehicle instantly on anareachange. The vehicle stays in its scope. A server-side seating into a vehicle of another scope is refused.
Packets
PlayerSync (0x03)
Client → Server, unreliable. The owner's movement, input and observed state. The payload after seq is the Frame, a fixed 102-byte structure; RemotePlayerSync carries the same Frame layout. Inside the Frame, the input is keys plus the ducked pose. The name of a player is never in a frame: it rides PlayerNametag.
The owner sends a frame at once when any of these change, and otherwise as a heartbeat every 100 ms; changes are sampled about every 16 ms:
keysorducked;move_state, so that tiring from sprint to run while the sprint key stays held goes out at once;on_ground, because a step off a low ledge with unchanged input can start and end between heartbeats;hporarmor, so damage reaches the others within a frame;weapon,state,cliportotal, so clip use and the start and end of a reload are timely;special_action,jet_thrustorjet_stop(the jetpack controls are not inkeys).
[u8 packet_id = 0x03]
[u64 session_token] // the token from ServerHello; a mismatch drops the packet
[u32 seq] // monotonic per connection; stale and out-of-order frames are dropped
[f32 x] // ─┐ Frame (102 bytes): world position
[f32 y]
[f32 z]
[f32 yaw] // body heading, degrees
[u16 keys] // the keys bitmask, see "The keys bitmask"
[u8 ducked] // 1 crouched (a pose, not a key)
[f32 cam_x] // aim direction, unit world vector
[f32 cam_y]
[f32 cam_z]
[f32 vel_x] // body velocity, world units per physics step (50 steps per second)
[f32 vel_y]
[f32 vel_z]
[f32 shift_x] // animation-driven displacement in the body's frame: x strafe
[f32 shift_y] // y forward
[u8 move_state] // actual speed band: 0 unset, 1 still, 4 walk, 5 jog, 6 run, 7 sprint
[u8 on_ground] // 1 standing on ground, 0 in the air (jump, step off, fall)
[f32 hp] // the owner's health as its game shows it
[f32 armor] // the owner's armor as its game shows it
[u8 alive] // 1 alive, 0 dead
[u16 death_epoch] // life generation within the connection; the relay carries the server's
[u8 weapon] // held weapon type (0 = unarmed)
[u8 state] // weapon state: 0 ready, 1 firing, 2 reloading, 3 out of ammo, 4 melee hit landing
[u16 clip] // rounds in the clip
[u16 total] // total rounds of the held weapon
[f32 cam_yaw] // the sender's camera orientation for movement controls, radians
[u16 weapon_rev] // last applied inventory revision
[u8 parachute_stage] // 0 none, 1 skydive, 2 open, 3 canopy
[u8 paused] // 1 while the sender's game menu is open
[u8 on_fire] // 1 while the sender's body burns
[u8 special_action] // 0 none, 1 jetpack, 2 hands up, 3 phone, 4..7 dance styles 1..4, 8 carry
[u16 special_rev] // last taken SetSpecialAction revision
[i8 jet_thrust] // ─┐ jetpack controls after the sender's input step, 0 without a jetpack:
[u8 jet_stop] // │ thrust -1 down / 0 / 1 up, stop,
[f32 jet_strafe] // │ strafe -3..3,
[f32 jet_angle] // ─┘ thrust angle, radians
[u16 aim_target] // player id under the sender's aim, 0xFFFF none (Client → Server only)
[u16 physics_rev] // last applied SetVelocity / SetGravity revision (Client → Server only)
[u16 vital_rev] // revision of the last health / armor command taken (Client → Server only)
[u32 damage_seq] // seq of the last DamageReport sent (Client → Server only)Total: 115 bytes (1 + 8 token + 4 seq + Frame 102). The sender is identified by its address and session_token; there is no player id in the packet.
Example: player_sync.bin.
Admission
PlayerSyncis dropped until the player has been granted its connect spawn (SpawnPlayer).- The server applies and relays a frame only if
seqis newer than the last accepted one from this connection (serial number arithmetic on the wrapping u32). A duplicate or overtaken frame is dropped whole, so it cannot roll backpaused, the position or the life state. - A non-finite float fails decoding (strict decoding).
- A frame is dropped if its position is outside the world (|x|, |y| ≤ 6000, z in
-200..=4000),parachute_stage > 3,special_action > 8orjet_thrustoutside-1..=1. - The server takes the position and the pose (heading,
move_state, crouch) only from the frame of a living player (alive both on the server and in the frame). A living player's position is held to what the body could have covered since the previous frame (the movement check, seephysics_revbelow). - While the player has world bounds (SetWorldBounds), an on-foot position beyond an edge counts as on the edge, without velocity outward through that edge and without
shift, before the movement check and every other reader. A seated player's frame is not touched: the vehicle is held by its driver's bounds (VehicleFrame). A teleport or new bounds that leave the body outside start the movement check from the edge. - While the player is frozen, SetFrozen rules apply.
Fields
The owner fills every field from its own game. The relay column is what RemotePlayerSync carries.
| Field | Relay value |
|---|---|
x, y, z | The owner's, after world bounds, freeze and the movement check. While the server holds the player dead: the server's last position of the player. |
yaw, ducked, move_state, on_ground, shift, cam_yaw, parachute_stage, weapon_rev, special_rev | The owner's. |
keys | The owner's; 0 while paused or frozen; the fire bit is cleared for a weapon that uses ammo when the server's inventory has none. |
cam | Normalized by the server. |
vel | The owner's, capped to a length of 5 units per step (250 m/s). |
hp, armor | The server's ledger, not the owner's values. |
alive, death_epoch | The server's life state and generation. |
weapon, clip, total | From the server's inventory: the frame's weapon if it is in the inventory (or 0), otherwise the last confirmed one; clip and total from the inventory. |
state | The owner's; a value above 4 becomes 0. |
paused, on_fire, special_action | The server's value. |
jet_* | With the jetpack: the owner's, jet_strafe clamped to ±3 and jet_angle to ±1.5; otherwise 0. |
aim_target | Always 0xFFFF. |
physics_rev, vital_rev, damage_seq | Always 0. |
Position, velocity and on_ground. A receiver writes vel into the copy once per new packet and lets the game's physics move it. The position error is corrected gradually on top of the physics, not overwritten; a large 3D error teleports the copy. Height depends on on_ground: with on_ground = 1 the receiver corrects only x and y and leaves z to the game (jumps and gravity play natively; a near-zero vel.z on the ground would suppress the start of a fall); with on_ground = 0 it applies the full x, y, z, so a fall or a step off a ledge that started on the sender carries its real vertical speed instead of waiting for the copy's own gravity.
keys and move_state. A receiver replays keys on the copy to animate actions (jump, fire, aim, weapon pose). move_state is the sender's actual speed band, including fatigue (sprint drops to run while the sprint key is held): the receiver gates the copy's sprint by move_state, not by the sprint key, and the copy does not tire on its own.
shift. Carried but not applied by receivers. The strafe component x cannot be derived from keys while armed; on foot without a weapon x is about 0 and y is the forward motion.
cam and cam_yaw. cam is the sender's aim direction; the copy aims along it. cam_yaw is the orientation of the sender's camera that the game's on-foot movement is relative to (movement direction = stick angle − camera orientation). A receiver moves the copy relative to the sender's cam_yaw, not its own camera; otherwise the copy would walk sideways.
hp and armor. The owner reports the health and armor its game shows; damage to the owner is applied by the owner's game. The server clamps them to 0..=200, lowers its own ledger by them (Health accounting and death) and relays its ledger. A receiver shows these values on the copy every tick, and damage the receiver's own game deals to the copy is undone: a copy takes no local damage. The server never relays a lethal hp while it holds the player alive (a death commits through DeathReport or the server's ledger); otherwise a frame with alive = 1, hp <= 0 could overtake the commit and the copy would collapse without its death.
vital_rev and damage_seq. The owner captures them together with hp and armor: vital_rev is the revision of the last health / armor command it took (applied, or dropped because the player was dead), damage_seq is the seq of the last DamageReport it sent. With them the server takes the frame in the client's order (see Health accounting and death). The relay carries 0.
alive and death_epoch. These are the life state, not the death and respawn edges: the edges ride the reliable events DeathReport, PlayerWasted and PlayerSpawned (ADR 0017). alive serves late joiners and recovery (a new receiver learns that a player is already dead and shows it as a static corpse) and tells receivers to stop replaying the copy's input. A fresh death is played by PlayerWasted, a new copy is built by PlayerSpawned. death_epoch is the life generation within the connection, incremented on respawn and independent of player_token; it serves correlation, not ordering. The owner can report its own death but cannot revive itself: the server stamps its own alive and death_epoch into the relay, and revival is server-gated through RespawnPlayer (ADR 0045). Reordering is closed by seq.
weapon, state, clip, total. The owner reports the held weapon, its state and its ammo. A receiver equips the copy with that weapon when the type changes and mirrors state, clip and total every tick, so the copy reloads in step with the real player. These fields carry only the look of the weapon and its ammo: bullets and damage ride WeaponShot.
weapon_rev. The server keeps the player's weapon inventory and a revision incremented on every weapon change it makes (ADR 0024). The client echoes the revision of the last weapon command it applied successfully, in reliable lane order; the value only grows, and a failed apply does not advance it. The server reconciles its inventory with the frame's observation only when the echo equals its current revision: the armed weapon only within the inventory, ammo only downward. A frame that overtook an in-flight change is ignored, so a frame cannot lower the inventory early, and a client cannot grant itself weapons or ammo through a frame. Receivers do not use it.
Ammo of continuous weapons. The flamethrower, the spray can and the fire extinguisher with fire held spend at least 10 rounds per second of holding from the server's inventory, whatever the frame shows.
parachute_stage. The sender's parachute stage, owned by the sender (latest wins, tolerant of loss). The server relays it without interpretation. A receiver plays the same stage animations and the canopy on the copy; the pose variant (bank, steer, brake) follows the replayed keys, and the descent rides the normal velocity sync (on_ground = 0, vel.z). A receiver treats a value above 3 as 0. The death or removal of the copy ends the stage.
paused. 1 while the sender's game menu is open. The sender's world keeps running under the menu, so the motion in the frame is real while the input is neutral (keys = 0). The server keeps the last accepted value as the player's pause state (player:is_paused(), event player_pause(player, paused) on change) and relays it. A change is accepted at most once per second. A paused frame has its keys zeroed before every consumer (fire, animations, relay); an accepted transition into pause releases the keys at once. A receiver shows an AFK mark above the name tag. The vehicle of a paused driver is driven by its syncer (VehicleSyncer).
on_fire. 1 while the sender's body burns, whatever lit it (SetOnFire, a molotov, a flamethrower, touching a burning body). The server keeps it (player:is_on_fire()) and relays its own value. It is accepted only from a living body: a frame of a dead player or from the respawn window counts as 0 at once, and a respawn grant clears it. A change between 0 and 1 counts at most once per second; faster changes keep the previous value. PlayerSpawned clears the receiver's copy of the previous life until the next RemotePlayerSync. A receiver keeps the copy burning exactly while the bit is set: the fire has no author and does not spread, does not burn out early, and goes out at 0. Only the victim counts fire damage (the copy takes none). A false bit sets fire, without an author, to those right next to the copy: environmental damage, as touching a burning body in the game.
special_action, special_rev, jet_*. The special action is owned by the server and set by the gamemode (SetSpecialAction):
| Value | Action |
|---|---|
| 0 | none |
| 1 | jetpack |
| 2 | hands up (stands still) |
| 3 | phone (walks with a phone at the ear) |
| 4..7 | dance styles 1..4 (dances in place; direction keys play moves, and copies play them from the frame's keys) |
| 8 | carry (walks as if carrying a load, no sprint or jump: the owner's client keeps both out of the game and out of keys) |
The frame carries what the sender's body shows and the echo of the revision of the last SetSpecialAction taken (applied or refused: a dead or seated player does not take an action). The owner can only end an action (its game ended it: the jetpack dropped with the enter key, entering a vehicle): the server clears it on a frame of a living player with special_action = 0 and the current echo. A frame that overtook a SetSpecialAction, or a frame claiming an action, changes nothing. A receiver keeps the copy in the same action while the relay carries it and the copy is alive and on foot; it flies the jetpack by the jet_* fields and the frame's heading, not by its own input. PlayerSpawned clears the copy of the previous life until the next RemotePlayerSync.
aim_target. The sender's claim of the player under its aim. The server accepts it only if the target is alive, shown to the sender and no farther than 10 m + the target's speed × 0.5 s from the sender's camera ray, and the sender is alive, not paused and not frozen; otherwise there is no target and the frame still stands. The server remembers the target with its token. player:target() reads it, and on reading the target (same token, alive, shown) and the sender (alive, not paused, not frozen) are checked again. The relay carries 0xFFFF.
physics_rev. The revision of the last SetVelocity / SetGravity the client applied (or dropped by its own rules), in reliable lane order. The relay carries 0.
- The server accepts an echo only if it is newer than the previous one and not newer than the last revision sent, and then takes, in order, what the commands up to it change: the throw allowance and the gravity of the movement check, and
RemotePlayerSync.gravity. - A throw was applied at some moment between its sending and an upper bound: the echo, or without one, the ClientAck of its event + 1 s (the client's apply window; the ack only bounds delivery from above). A throw sets the body's velocity rather than adding to it, and the client applies throws in order, so each throw ends the earlier ones at its own upper bound.
- Beyond the normal movement limits, a frame is allowed the distance the body could have covered since the previous frame moving, at each moment, no faster than the fastest throw possible at that moment. A throw's window runs from its sending to its upper bound + 4 s (a body in the air barely slows); the horizontal and vertical excess combine as the legs of a right triangle. A frame with the echo may therefore show the path of lost frames.
- Unused allowance carries over to the next frame, at most 0.25 s at the current speed (frames arrive unevenly); after a frame with the echo, the path of an earlier, faster throw does not accumulate.
- Each throw grants at most its speed × 4 s in total; up to 8 throws are tracked at once, a newer one evicting the earliest.
- A command counts as applied without an echo 2 s after its
ClientAckor 15 s after sending (the stall limit of the reliable lane): its gravity takes effect, a throw grants its speed. - Throws not echoed by a teleport, a respawn or entering a vehicle are discarded.
- At most 32 commands may await their echo; beyond that the gamemode's call is refused with "queue full".
- Gravity below the game's own does not change the allowed rise speed and lengthens the allowed time in the air to
4 s / (g / 0.008); atg <= 0hovering is unlimited and the rise is held to 12.5 m/s.
Server reads and events
- From the frame of a living player the server keeps the velocity (
player:velocity(), m/s, at most 250 long),ducked,on_ground, the camera direction (player:camera_front(), normalized; a zero direction is not taken) andkeys(bits 0..8 only,player:keys()). - Freezing and death release the keys and the aim target on the server: the release event fires once, with the next frame or server pass. Unfreezing restores nothing until a fresh frame.
player_key_change(player, key, pressed)fires for every changed key; the next change of the same key counts after 50 ms. A held-back change arrives with the next frame, so a release is never lost and a one-frame press arrives later as a press and a release.player_weapon_reload(player, weapon)fires at the start of a reload (state= 2) of a non-melee weapon the player holds by the inventory, with ammo, in a frame of the current life and the currentweapon_rev, while the player is in control (not paused, not frozen), at most once per 250 ms.player_state_change(player, state, previous)andplayer_interior_change(player, interior, previous)are computed by the server from its own state at most 20 times per second; a change and its reversal within one pass produce no event.
RemotePlayerSync (0x04)
Server → Client, unreliable. The server relays a player's frame to the players it is shown to (Player streaming), adding the server-owned appearance and styles.
[u8 packet_id = 0x04]
[u32 player_id]
[u32 player_token] // the sender's generation: guards against a reused player_id
[u32 seq] // relayed from PlayerSync; the receiver drops stale and out-of-order frames
[f32 x] // ─┐ Frame (102 bytes), with the relay values of PlayerSync
[f32 y]
[f32 z]
[f32 yaw]
[u16 keys]
[u8 ducked]
[f32 cam_x]
[f32 cam_y]
[f32 cam_z]
[f32 vel_x]
[f32 vel_y]
[f32 vel_z]
[f32 shift_x]
[f32 shift_y]
[u8 move_state]
[u8 on_ground]
[f32 hp] // the server's ledger
[f32 armor] // the server's ledger
[u8 alive] // the server's life state
[u16 death_epoch] // the server's life generation
[u8 weapon] // from the server's inventory
[u8 state]
[u16 clip] // from the server's inventory
[u16 total] // from the server's inventory
[f32 cam_yaw]
[u16 weapon_rev] // not used by receivers
[u8 parachute_stage]
[u8 paused] // the server's value
[u8 on_fire] // the server's value
[u8 special_action] // the server's value
[u16 special_rev] // not used by receivers
[i8 jet_thrust] // ─┐ 0 without a jetpack
[u8 jet_stop] // │
[f32 jet_strafe] // │
[f32 jet_angle] // ─┘
[u16 aim_target] // always 0xFFFF
[u16 physics_rev] // always 0
[u16 vital_rev] // always 0
[u32 damage_seq] // always 0 ─┘
[u32 skin] // the player's model id, set by the server
[u16 variant] // appearance variation, see SetSkin
[u8 walk_style] // walking style, see SetWalkingStyle (0 = default)
[u8 fight_style] // fighting style, see SetFightingStyle
[u8 headless] // 1 head hidden, see SetHeadless
[u32 damage_group] // the sender's damage group
[u32 relation_rev] // revision of the relation
[u8 teammate] // 1: the sender is a teammate of THIS recipient
[f32 gravity] // the sender's body gravity, see SetGravityTotal: 137 bytes (1 + 4 + 4 + 4 + 102 + 4 + 2 + 1 + 1 + 1 + 4 + 5 + 4). The server adds the authoritative player_id, the generation player_token, the relayed seq, skin, variant and the stored walk_style, fight_style, headless, damage_group with the relation (Damage groups and relations) and gravity, so copies and late joiners converge on these without a broadcast. There is no name in the frame: it rides the PlayerNametag of the show and of every rename. A gravity outside |g| <= 0.1 or non-finite fails decoding. The receiver is generation-aware (see Sequence and generation). For what a receiver does with each Frame field, see PlayerSync.
Example: remote_player_sync.bin.
PlayerStreamOut (0x45)
Server → Client, reliable event lane. The player is no longer shown to the recipient (Player streaming); its session goes on.
[u8 packet_id = 0x45]
[u32 player_id]
[u32 player_token] // the subject's generation
[u32 event_id] // position in the per-recipient reliable stream
[u32 last_seq] // seq of the subject's last RemotePlayerSync sent to the recipient (0: none)
[u32 last_shot_order] // order of the subject's last accepted shot or ray before the hide (0: none); the next show carries only greater onesTotal: 21 bytes. The recipient removes the copy and the subject's state as on PlayerLeave, but without a tombstone:
- A
RemotePlayerSyncof the same generation withseq <= last_seq(signed comparison, survives wrap) is a late frame and is dropped; a newer one means a show and creates the copy anew. A frame of an older generation is dropped. PlayerStreamOutis ignored if a newer generation ofidhas already been accepted.- If a frame of the same generation newer than
last_seqhas already been accepted (frames of the next show overtook the hide on the reliable lane), the generation stays live, but the copy is rebuilt from a living frame: death and respawn during the hide have not reached the recipient. - In both cases the hide at once discards everything pending for the subject (death pose, animations, projectiles, stealth kill); the snapshot of the next show follows it on the lane.
- Shots and volleys are not on the lane, so their boundary is
last_shot_order: stored ones withorderup to it are discarded and theordermark is raised to it (a late shot of the previous show is dropped); newer ones belong to the next show and stay. A shot of the previous show that overtook the hide itself is applied before it, on the copy of the same show: the new copy is built only when the hide is processed. Its damage is bounded by the window and the geometry of DamageReport.
Example: player_stream_out.bin.
RespawnRequest (0x0C)
Client → Server, unreliable. A dead player asks to be respawned. It sends it on a timer, repeating while dead. This is only a request: the server decides eligibility and timing by its policy (see RespawnPlayer); the client does not revive until RespawnPlayer arrives.
[u8 packet_id = 0x0C]
[u64 session_token] // the sender's token; a mismatch drops the packetTotal: 9 bytes. The server identifies the player by address and session_token. A request from a living player, from a player that has not completed its connect spawn, or before the server's minimum time dead has passed is ignored. The request is idempotent: repeats before the grant are safe.
Example: respawn_request.bin.
RespawnPlayer (0x0D)
Server → Client, unreliable. A respawn grant with the spawn point, sent to one player when it passes the respawn policy (eligibility and timing are server configuration), as the answer to its RespawnRequest. On receipt the client revives its player at the point and sends alive = 1 again, but the server owns the life state: it stamps alive and death_epoch into the relay, and the new generation commits with the grant and the client's first confirming alive = 1 frame. A client cannot fake a respawn through PlayerSync; a death stays reported by the victim.
[u8 packet_id = 0x0D]
[f32 spawn_x] // spawn position
[f32 spawn_y]
[f32 spawn_z]
[f32 heading] // facing, radians
[i32 area] // interior of the point, 0..=255 (0 = outside)Total: 21 bytes. The client drops the packet if a number is non-finite or area is outside 0..=255. The spawn point comes from the server's spawn policy (ADR 0012); it is the same point the connect SpawnPlayer uses unless the gamemode overrides it. RespawnPlayer goes only to the respawning player. The other clients rebuild its copy from PlayerSpawned, which the server broadcasts on the same grant, not from this packet or the Frame. The client sets area, waits for the point's collision and map to load and places its player the same way as on TeleportPlayer. On the grant the server moves the player's interior scope to the point's area and reconciles streaming; the virtual world stays.
Example: respawn_player.bin.
PlayerSkills (0x10)
Server → Client, reliable event lane. A full snapshot of a player's 11 weapon skills.
[u8 packet_id = 0x10]
[u32 id] // subject player_id
[u32 player_token] // subject generation (guards against a reused id, as in RemotePlayerSync)
[u32 event_id] // position in the per-recipient reliable stream
[u16 skills[11]] // weapon skill values, 0..1000Total: 35 bytes. Weapon skills are server-authoritative: the client applies the values and never raises a skill itself. The skill of a weapon picks its tier (poor, standard, pro) in the game. Slots:
| Slot | Weapon types |
|---|---|
| 0 | 22 pistol |
| 1 | 23 silenced pistol |
| 2 | 24 desert eagle |
| 3 | 25 shotgun |
| 4 | 26 sawn-off shotgun |
| 5 | 27 combat shotgun |
| 6 | 28 micro SMG, 32 Tec-9 |
| 7 | 29 SMG |
| 8 | 30 AK-47 |
| 9 | 31 M4 |
| 10 | 33 country rifle, 34 sniper rifle |
Skills are slow server state and are not in the Frame. Recipients are the subject and the players it is shown to; a show of the subject carries its snapshot (Player streaming).
Example: player_skills.bin.
SetWanted (0x11)
Server → Client, reliable event lane.
[u8 packet_id = 0x11]
[u32 event_id] // position in the per-recipient reliable stream
[u8 level] // wanted level 0..6Total: 6 bytes. The server owns the player's wanted level and sends it only to that player: it is the recipient's own state, not a look of its copy, so there is no subject id or token. The server validates 0..6 before sending. The client applies level and re-asserts it on its player every frame, so the game's own writes do not move it; the game's own wanted-level triggers are disabled on the client, so the game never sets the level itself.
Example: set_wanted.bin.
TeleportPlayer (0x15)
Server → Client, reliable event lane. Teleports the recipient's own player; there is no subject id or token.
[u8 packet_id = 0x15]
[u32 event_id] // position in the per-recipient reliable stream
[f32 x][f32 y][f32 z] // world landing point
[f32 heading] // heading, degrees
[i32 area] // the game's area code (interior) of the destination, 0..=255
[u8 ground] // 1: the client ignores z and puts the player on the ground under x, y once its collision loadsTotal: 26 bytes.
- The client drops the packet if a position or heading value is non-finite,
areais outside0..=255(the server refuses such a teleport too) orgroundis neither 0 nor 1. - The client teleports its player, sets the area and waits for the destination's map and collision to load. Latest wins: a new teleport overrides one not yet applied.
- With
ground, the server treatszas a guess until a client frame arrives within 25 m horizontally of the point withon_ground = 1: the client sets the landing height. The server waits for the landing 30 s from the packet'sClientAck, the client 25 s from receiving it. Until the ground is found the client holds the player in the air above the point and its frames carryon_ground = 0; if it finds none it useszfrom the packet. - For the server,
areais the destination interior: a teleport also moves the player's interior scope toareaand reconciles streaming (pickups, vehicles and players of the old scope go away, those of the new one arrive; a seated player changing interior is first taken out of the vehicle, see Player streaming). On the wire this shows only as the normal streaming changes and, on a scope change, ScopeChanged.
Example: teleport_player.bin.
SetSkin (0x20)
Server → Client, reliable event lane. Sets the model and appearance of the recipient's own player; there is no subject id or token.
[u8 packet_id = 0x20]
[u32 event_id] // position in the per-recipient reliable stream
[u32 model] // player model id, from the server's catalog of allowed skins
[u16 variant] // appearance variation, >= 1Total: 11 bytes.
- The server validates
modelagainst its catalog of allowed skins (regular characters and special story characters share one numeric pool) and requiresvariant >= 1;varianthas no per-model range and is opaque. The client checksmodelagainst the same catalog before applying it. - The same
modelandvariantgive the same look on every client. Avariantof 0 would let each client pick its own random variation, so it is never sent. - Model 0 is the default player character with its default clothes; the client ignores
variantfor it (its look comes from the clothing system, which is not synced). - The other players see the new look through
RemotePlayerSync(skin,variant).
Example: set_skin.bin.
SetMoney (0x28)
Server → Client, reliable event lane. The recipient's own money; sent only to that player, there is no subject id or token.
[u8 packet_id = 0x28]
[u32 event_id] // position in the per-recipient reliable stream
[i32 amount] // absolute balance, -999_999_999..=999_999_999Total: 9 bytes. The range is a sanity bound of the protocol, not a limit of the game. The client sets its player's balance to amount and re-asserts it every frame, so the game's own money changes (taxi fares, money pickups, death, arrest, hospital) do not move it; the HUD counter rolls to the new value. Other clients never see a player's money.
The server stores the balance: it survives death and respawn; a new connection starts at 0, and a disconnect resets it. There is no baseline at connect: a fresh connection shows 0 until the first SetMoney, which carries only changes made while the player is connected. The gamemode sets the balance absolutely (range-checked) or relatively (the result clamped to the range).
Example: set_money.bin.
DeathReport (0x1B)
Client → Server, on the client's reliable event lane (the same stream as ClientChat and ClientCommand). The victim reports its own death and its death pose (ADR 0017).
[u8 packet_id = 0x1B]
[u64 token] // session_token
[u32 event_id] // position in the sender's reliable stream
[u8 weapon] // means of death, 0..=54
[u16 anim_group] // animation group of the death animation
[u16 anim] // animation id of the death animationTotal: 18 bytes. The dying client reports the death the game picked for its player. The server commits the death into its life state (only for a spawned living player, once per life) and sends PlayerWasted to the players it is shown to. A death the server has already committed from its ledger is not committed again by the report. This is the death edge; the life state (alive) stays in the Frame.
The pose must be one the game produces: weapon 0..=54, and either group 0 with anim 15..20, 22..27, 36, 39, 140 or 191, groups 33..43 with anim 219..221, or group 87 with anim 352. The server replaces any other pose with the default (0, 0, 15), and so does a receiver of PlayerWasted.
Example: death_report.bin.
PlayerWasted (0x1C)
Server → Client, reliable event lane. Broadcast of a death and its pose (ADR 0017).
[u8 packet_id = 0x1C]
[u32 id] // subject player id
[u32 player_token] // subject generation (a reused id does not apply to another player)
[u32 event_id] // position in the per-recipient reliable stream
[u8 weapon] // means of death
[u16 anim_group] // death animation group
[u16 anim] // death animation idTotal: 18 bytes. The server builds it from an accepted DeathReport or from a death it committed itself (Health accounting and death). A receiver plays this death with this pose on the subject's copy; this is the edge of a fresh death. It is not sent to the victim, which played its death locally.
Example: player_wasted.bin.
PlayerSpawned (0x1D)
Server → Client, reliable event lane. Broadcast of a respawn (ADR 0017).
[u8 packet_id = 0x1D]
[u32 id] // subject player id
[u32 player_token] // subject generation
[u32 event_id] // position in the per-recipient reliable stream
[f32 spawn[3]] // spawn position
[f32 heading] // heading, radians
[u32 sync_seq] // the subject's last accepted PlayerSync seq at the grantTotal: 33 bytes. A non-finite position or heading fails decoding. A receiver rebuilds the subject's copy at spawn / heading. The server sends it to the players the subject is shown to, once per accepted respawn grant (the same point as in RespawnPlayer); a repeated RespawnRequest in the respawn window does not duplicate it, the reliable lane retransmits it until acked. The respawning player itself gets RespawnPlayer.
sync_seq is the barrier of the previous life: the receiver raises the generation's seq mark to it (if the generation's first frame has not arrived yet, the mark waits for it), so a late RemotePlayerSync of the previous life overtaken by this event is dropped.
Example: player_spawned.bin.
RequestSpawn (0x29)
Client → Server, unreliable. A connected player that has not spawned asks for its connect spawn (ADR 0022). It sends it on a timer, repeating until SpawnPlayer arrives, as with RespawnRequest. The client's retry makes spawn delivery reliable without the reliable lane, whose receive state resets on reconnect.
[u8 packet_id = 0x29]
[u64 session_token] // the sender's token; a mismatch drops the packetTotal: 9 bytes. The server identifies the player by address and session_token. The connect spawn is gated by the gamemode (ADR 0023):
- The server answers with
SpawnPlayeronly while the connect spawn is not yet confirmed (alive, spawn pending, not respawning) and the gamemode has set the spawn info withspawn_player. - Until the gamemode spawns the player, the request grants nothing, but its
session_tokenis validated and keeps the player alive on the server, so the repeating request also serves as a keepalive while the player picks a skin or logs in. - After the gamemode sets the spawn info, a repeat before confirmation grants again idempotently (covering a lost
SpawnPlayer). - After the first living
PlayerSync, and during death and respawn, the request is dropped: it is not a second spawn path (death and respawn useRespawnRequest/RespawnPlayer). - The grant marks the player spawned; from then on
PlayerSyncis accepted.
Example: request_spawn.bin.
SpawnPlayer (0x2A)
Server → Client, unreliable. The connect spawn grant: the answer to RequestSpawn, carrying the spawn info the gamemode set with spawn_player (ADR 0023). The client creates or teleports its player here and applies the appearance.
[u8 packet_id = 0x2A]
[f32 spawn_x] // spawn position
[f32 spawn_y]
[f32 spawn_z]
[f32 heading] // facing, radians
[i32 area] // interior of the point, 0..=255 (0 = outside)
[u32 skin] // the player's model id, set by the server
[u16 variant] // appearance variation, see SetSkinTotal: 27 bytes. The client drops the packet if a position or heading value is non-finite or area is outside 0..=255, and places its player as on RespawnPlayer. On the grant the server moves the player's interior scope to the point's area and reconciles streaming. The gamemode sets the point and skin / variant in spawn_player (usually the point from the spawn policy, ADR 0012). The gamemode API takes the heading of a spawn point in degrees; SpawnPlayer, RespawnPlayer and PlayerSpawned carry it in radians. The packet goes only to the spawning player; the other clients create its copy from the normal RemotePlayerSync after its first PlayerSync.
Example: spawn_player.bin.
ApplyAnimation (0x2B)
Server → Client, reliable event lane. Plays a named animation on a player. It goes to the subject and to every player it is shown to: a recipient with id equal to its own applies it to its own player, the others to the subject's copy. The gamemode triggers it (apply_animation); there is no Client → Server packet.
[u8 packet_id = 0x2B]
[u32 id] // subject player id
[u32 player_token] // subject generation (a reused id does not apply to another player)
[u32 event_id] // position in the per-recipient reliable stream
[u8 lib[32]] // animation library name, NUL-padded ASCII (≤ 31 chars, case-insensitive on the client)
[u8 name[32]] // animation name, NUL-padded ASCII (≤ 31 chars)
[f32 delta] // blend-in rate (4.1 is the usual value); finite, > 0 (checked by the server)
[u8 flags] // bit0 loop, bit1 lock_x, bit2 lock_y, bit3 freeze, bit4 dont_interrupt, bit5 retain_state
[u32 time] // ms; 0 = to the end (or endless with loop); ≤ 600000 (checked by the server)
[f32 speed] // playback rate (1 = the authored pace); finite, 0..=10, otherwise dropped
[u32 elapsed_ms] // 0 = fresh apply; > 0 = replay on show: the time already playedTotal: 94 bytes. A non-finite delta fails decoding.
- Authority. The server owns the trigger; the client only applies. The server checks only the form (it has no catalog of animations); a client silently ignores an unknown
lib/name. The server API refusesapplyandclearfor a player that has not spawned (PlayerNotSpawned) or is dead (PlayerNotAlive). - Stored state. The server keeps the current animation per player (start time, speed, parameters). A show of the subject (Player streaming) carries a replay stamped with
elapsed_ms, and the receiver advances the animation to the equivalent progress. - Flags.
dont_interrupt: the client does not cancel the animation on movement and the game does not interrupt it; the subject's pending reactions to game events are cleared on apply.retain_state(crouch only): if the subject was crouched at apply, it crouches again when the animation ends (copies follow through the owner'sducked).freezeignorestimeentirely: the frozen pose holds until an explicit clear or death, the stored state does not expire and is replayed on show indefinitely. - What ends the stored state:
ClearAnimations, death (the death animation replaces it on every client), disconnect, the server's mirror of the movement cancel (by the subject'sFrame.keys, for an interruptable non-freeze animation), and the expiry of a timed non-freeze animation: withtime > 0and nofreezethe animation ends on clients at starttime, so after that moment the server neither reads, rewinds nor replays it. A non-loop non-freeze animation withtime = 0ends at its authored length, which the server does not know; it converges through the movement mirror or an explicit clear. The state does not survive a respawn.
- Client behavior.
- A non-freeze replay with
elapsed_ms >= time(fortime > 0) is dropped (it expired between the server building the replay and its delivery). - An animation whose data is not loaded yet waits in a per-player slot (latest wins, about 5 s to load).
- An animation for a known player whose copy does not exist yet waits in the slot until the copy appears.
- Movement cancels an interruptable non-freeze animation on every client the same way: on the subject's own client by its live input, on copies by the replayed frame keys.
freezeanddont_interrupthold untilClearAnimations.
- A non-freeze replay with
Example: apply_animation.bin.
ClearAnimations (0x2C)
Server → Client, reliable event lane. Stops a player's named animation; addressed like ApplyAnimation (the subject and the players it is shown to). The client stops only the named animation, not other actions of the player; a clear without a playing animation does nothing. It also cancels an animation still waiting in the slot.
[u8 packet_id = 0x2C]
[u32 id] // subject player id
[u32 player_token] // subject generation
[u32 event_id] // position in the per-recipient reliable streamTotal: 13 bytes.
Example: clear_animations.bin.
SetWalkingStyle (0x2D)
Server → Client, reliable event lane. The walking style of the recipient's own player; there is no subject id or token. The client sets its player's walk, run and sprint animations to style while the player has full control (unlike ApplyAnimation, which takes control away). style 0 returns to the default walk.
[u8 packet_id = 0x2D]
[u32 event_id] // position in the per-recipient reliable stream
[u8 style] // walking style id (0 = default)Total: 6 bytes. Stored state: the server keeps the value per player (it survives respawn; a new connection starts at 0) and stamps it into RemotePlayerSync.walk_style, so copies and late joiners converge without a broadcast. Valid ids: 0, 54..70 (player and weapon gaits) and 118..138 (walking styles, including 127 blind man). The server refuses others, and the client applies the same allowlist: it drops a SetWalkingStyle outside it on decode and skips such a RemotePlayerSync.walk_style on apply. A style whose animations are not loaded is silently ignored.
Example: set_walking_style.bin.
SetFightingStyle (0x56)
Server → Client, reliable event lane. The fighting style of the recipient's own player: the combo set of unarmed and brass-knuckle attacks. The same model as SetWalkingStyle: stored state (survives respawn and a skin change; a new connection starts at 15), stamped into RemotePlayerSync.fight_style. A change takes effect from the next combo.
[u8 packet_id = 0x56]
[u32 event_id] // position in the per-recipient reliable stream
[u8 style] // fighting style (4..7, 15, 16)Total: 6 bytes. Valid values: 4 normal, 5 boxing, 6 kung fu, 7 knee-head, 15 grab-kick (default), 16 elbow; 8..14 are the combo sets of melee weapons, not styles. The server refuses others, and the client applies the same allowlist: it drops a SetFightingStyle outside it on decode and skips such a RemotePlayerSync.fight_style on apply.
Example: set_fighting_style.bin.
SetHeadless (0x57)
Server → Client, reliable event lane. Hides the head of the recipient's own player. The same model as SetFightingStyle: stored state, stamped into RemotePlayerSync.headless.
[u8 packet_id = 0x57]
[u32 event_id] // position in the per-recipient reliable stream
[u8 headless] // 1 head hidden, 0 shownTotal: 6 bytes. It survives a skin change. A new respawn grant clears it: the server sends the owner SetHeadless(0) on the reliable lane (RespawnPlayer itself is unreliable, so the client does not clear the state itself), and receivers reset their copy on PlayerSpawned. A new connection starts at 0. Damage never removes a player's head in the game; only this state hides it.
Example: set_headless.bin.
SetFrozen (0x72)
Server → Client, reliable event lane. Takes away or returns control of the recipient's own player. Control is taken while the gamemode froze the player or its camera is not its own (SetCamera); the packet goes when the combined result changes. Stored server state, not sent to observers: a frozen player's frames go without input. Death does not clear it; a new respawn grant does: the server sends the owner SetFrozen(0) on the reliable lane. Set before the first spawn, it holds the player from the spawn. A new connection starts unfrozen.
[u8 packet_id = 0x72]
[u32 event_id] // position in the per-recipient reliable stream
[u8 frozen] // 1 control taken, 0 returnedTotal: 6 bytes. While the player is frozen, the server:
- zeroes
keysof itsPlayerSyncbefore every check and the relay; - holds the frame's position within 8 m horizontally and 3 m upward of where it was frozen, teleported or spawned, falling being free (seated: 25 m from the vehicle); beyond that the frame counts as on the boundary with zero velocity, the other fields are accepted;
- drops its
WeaponShot,VehicleShot,ProjectileCreate,SatchelDetonate,StealthKillRequest, refuses itsVehicleEnter, ignoresVehicleRemoteEnterandVehicleControl; it acceptsVehicleExit, and the gamemode may move and seat the player; - does not credit it with contact damage in a
DamageReport(melee, ramming, rotor): such a report of the victim is refused; - does not count it as controlling any vehicle, like a paused driver: its vehicle gets a syncer, it is never a syncer or the pilot of an RC vehicle, and its
VehicleFrame,VehicleExplode,VehicleTow,VehicleWinchare dropped; - holds the vehicle it drives with a VehicleTransform freeze (except a train) while it drives and is frozen; the gamemode's own vehicle freeze is a separate cause, and neither clears the other.
Example: set_frozen.bin.
SetCamera (0x73)
Server → Client, reliable event lane. What the recipient's own camera shows. Stored server state, not sent to observers. Death keeps it; a new respawn grant clears it: the server sends the owner mode = 0 on the lane. Set before the first spawn, it takes effect from the spawn. A new connection starts with its own camera.
[u8 packet_id = 0x73]
[u32 event_id] // position in the per-recipient reliable stream
[u8 mode] // 0 own camera, 1 fixed, 2 spectate, 3 free (map editing session only); others are dropped by the client
[f32;3 pos] // mode 1: camera position, otherwise zeros
[f32;3 look_at] // mode 1: look-at point, otherwise zeros
[u32 transition_ms] // mode 1: transition from the current view, 0 = at once, at most 60000; otherwise 0
[u8 target_present] // mode 2: 1, otherwise 0
[u32 target_id]
[u32 target_token] // generation of the target's id
[u32 prev_action] // mode 2: action_id (ActionDeclare) for the spectator panel, 0 = none
[u32 next_action] // the sameTotal: 51 bytes.
- While the camera is not its own, the player has no control (SetFrozen), and players, vehicles, pickups, blips and labels are streamed from the camera's position (
mode = 2: from the target's position), not from the body. Touching a pickup is still checked against the body. - Order on the lane: a foreign camera goes after
SetFrozen, the return of the own camera before it. - A transition (
transition_ms > 0) moves the camera from the current view toposalong the curve(1 - cos(π·t)) / 2; the stream center follows the same curve from the previous center. Server and client clocks are not synchronized: the center's time runs only while the packet is acked and the client sendsPlayerSyncframes, so the center is approximate and does not affect authority. mode = 2: the target is spawned and in the player's scope. When the target changes scope or leaves, the server sendsmode = 0and firesspectate_lost(player, target)to resources. The target's death and respawn do not end the spectating; while the client has no copy of the target, the view holds still. The panel shows the target's name and the keys the player bound to the two actions (ActionDeclare); pressing them is handled by the gamemode, there are no new Client → Server packets.mode = 3starts from the current view, with zero position fields; the client flies the camera and reports its position (EditCamera), and the world streams from it. It exists only in a map editing session (EntityEdit); the end of the session returns the own camera, and while the session lasts the gamemode cannot set another camera. The client holds the body in place without falling; freezing applies as usual.- The server allows
posandlook_atwithin ±100000 on each axis and no closer than 0.01 to each other. The client drops non-finite coordinates, coordinates beyond these bounds,posequal tolook_at, and atarget_idout of range.
Example: set_camera.bin.
SetOnFire (0x58)
Server → Client, reliable event lane. Sets the recipient's own player on fire or puts it out. This is a command, not a state: the client sets its player on fire (without an author, without spreading) or puts the fire out; the fire also goes out by the game's own rules and in water, and its damage is environmental damage without an attacker (DamageReport weapon 37). Whether a player burns is Frame.on_fire.
[u8 packet_id = 0x58]
[u32 event_id] // position in the per-recipient reliable stream
[u8 on_fire] // 1 set on fire, 0 put outTotal: 6 bytes. The server sends the command only to a living spawned player, and sets fire only to a player on foot whose new life after a respawn is confirmed; the client does not set fire to a dead or seated player. If a fire command went out in the previous life, a new respawn grant sends SetOnFire(0) after it, so a late or retransmitted command does not light the new life.
Example: set_on_fire.bin.
SetSpecialAction (0x59)
Server → Client, reliable event lane. The special action of the recipient's own player. Stored state: the server keeps the action and a revision rev (incremented on every SetSpecialAction); observers see it in the relayed Frame.special_action. The client applies it to its player and echoes rev in Frame.special_rev.
[u8 packet_id = 0x59]
[u32 event_id] // position in the per-recipient reliable stream
[u8 action] // 0 none, 1 jetpack, 2 hands up, 3 phone, 4..7 dances 1..4, 8 carry
[u16 rev] // revision; echoed in Frame.special_revTotal: 8 bytes. An action above 8 fails decoding.
- The server sends it only to a living spawned player whose new life is confirmed, and a non-zero action only to a player on foot.
- An action replaces a playing animation: the server sends
ClearAnimationsfirst, having checked in advance that the queue has room for both packets. - Death ends the action. If an action went out in the previous life, a new respawn grant sends
SetSpecialAction(0)after it. - A new connection starts at 0 with
rev0: the client ends any action left over from a previous connection.
Example: set_special_action.bin.
SetVelocity (0x90)
Server → Client, reliable event lane. Throws the recipient's own player. A command, not a state: the client writes the velocity into its player once (with z > 0 lifting it off the ground, like a jump), then the game moves the body, and observers see the result in its frames.
[u8 packet_id = 0x90]
[u32 event_id] // position in the per-recipient reliable stream
[f32 vel_x] // ─┐ units per step (m/s / 50); |vel| <= 2.0 (100 m/s), finite,
[f32 vel_y] // │ otherwise the packet fails decoding
[f32 vel_z] // ─┘
[u16 life] // the life generation (the frames' death_epoch) the throw is for
[u16 rev] // the player's physics revision; the client returns it in Frame.physics_revTotal: 21 bytes. The server sends it only to a living spawned player on foot whose new life is confirmed. The client drops it if its player is dead or seated; if life differs from its current death_epoch (a retransmission that caught up with a respawn); if a TeleportPlayer was delivered after it while it was not yet applied (the teleport stops the body; the cancel takes effect before the teleport); and if it has not applied it within 1 s of receipt (after that the server may already count it as applied, see physics_rev in PlayerSync). The game limits the rise of a player in the air to 0.25 units per step (12.5 m/s).
Example: set_velocity.bin.
SetGravity (0x91)
Server → Client, reliable event lane. The body gravity of the recipient's own player. Stored state, stamped into RemotePlayerSync.gravity: the copy falls with the same gravity on every observer. It applies only to the player's body; vehicles and other physics use the game's gravity. It survives death and respawn; a new connection starts at 0.008 (the game's gravity).
[u8 packet_id = 0x91]
[u32 event_id] // position in the per-recipient reliable stream
[f32 gravity] // units per step²; 0.008 = the game's; -0.1..0.1, finite, otherwise the packet fails decoding; < 0 pulls up
[u16 rev] // physics revision shared with SetVelocity; echoed in Frame.physics_revTotal: 11 bytes. The client applies it in one queue with SetVelocity, in reliable lane order.
Example: set_gravity.bin.
SetDrunkLevel (0x92)
Server → Client, reliable event lane. The drunkenness of the recipient's own player, on the game's scale. Stored state, not sent to observers: only the owner sees the effect. It holds until changed and does not wear off by itself; a new respawn grant sends SetDrunkLevel(0) on the lane if the level is not 0. A new connection starts at 0; the end of a session sobers the client.
[u8 packet_id = 0x92]
[u32 event_id] // position in the per-recipient reliable stream
[u8 level] // 0 sober .. 255Total: 6 bytes.
Example: set_drunk_level.bin.
SetPlayerStats (0x93)
Server → Client, reliable event lane. Stamina and lung capacity of the recipient's own player. Stored state that observers do not need: fatigue rides move_state, running out of air is the victim's damage. It survives death and respawn; a new connection starts at 1000 / 1000 without a packet, and a client outside a session keeps 1000.
[u8 packet_id = 0x93]
[u32 event_id] // position in the per-recipient reliable stream
[u16 stamina] // 0..1000, otherwise the packet fails decoding
[u16 lung_capacity] // 0..1000, otherwise the packet fails decodingTotal: 9 bytes.
Example: set_player_stats.bin.
SetWorldBounds (0x94)
Server → Client, reliable event lane. A rectangle in which the recipient's own player and the vehicle it drives stay. Stored state: the client returns the body or the vehicle to the edge once per frame and removes velocity outward; the server holds the player's frames (PlayerSync) and its vehicle's VehicleFrame inside it while it drives. It survives death and respawn; a new connection starts with the whole world.
[u8 packet_id = 0x94]
[u32 event_id] // position in the per-recipient reliable stream
[u8 enabled] // 1 rectangle, 0 the whole world; > 1 fails decoding
[f32 min_x] // ─┐ with enabled = 1: finite, |v| <= 6000, min < max;
[f32 min_y] // │ with enabled = 0: zeros; otherwise the packet fails decoding
[f32 max_x] // │
[f32 max_y] // ─┘Total: 22 bytes.
Example: set_world_bounds.bin, set_world_bounds_off.bin.
ScopeChanged (0x5D)
Server → Client, reliable event lane, sent as a critical event (never refused for a full queue). The recipient's own virtual_world or interior changed. The server sends it on every scope change of the player: a virtual world change, a teleport into another interior (after TeleportPlayer), and a new respawn grant into another interior; what the previous life left behind stays in the scope where it died. The connect spawn does not send it.
[u8 packet_id = 0x5D]
[u32 event_id] // position in the per-recipient reliable stream; also the barrier for throwsTotal: 5 bytes. The client has one world while world scope exists only on the server, so the client itself removes what it had in the scope it left, in order with the other projectile events:
- projectiles in flight that have not exploded: all except its own thrown with
seen_event≥event_id(the server accepts those in the new scope, see ProjectileCreate); its own satchels are among the removed ones, and the server clears its satchel latch; - world fire, except burning bodies (a player burning is SetOnFire /
Frame.on_fire).
Fire within one scope diverges between clients: each client runs spreading and burning out itself.
Example: scope_changed.bin.
SetAnimationProgress (0x2E) / SetAnimationSpeed (0x2F)
Server → Client, reliable event lane. Seeks / changes the rate of the player's current named animation; addressed like ApplyAnimation (the subject and the players it is shown to). The current animation is implicit: the server owns the stored animation state and the server API refuses the call when nothing plays (PlayerNotAnimating), so there is no animation name on the wire.
[u8 packet_id = 0x2E / 0x2F]
[u32 id] // subject player id
[u32 player_token] // subject generation
[u32 event_id] // position in the per-recipient reliable stream
[f32 progress|speed] // 0x2E: 0..=1, finite; 0x2F: 0..=10, finite (0 = pause); otherwise the packet fails decodingTotal: 17 bytes each.
- A receiver whose animation data is still loading keeps the value in the slot and applies it when the animation starts.
progressseeks the animation to that share of its length, wrapping for a looped one; exactly 1.0 lands just before the end, as in the game.speedsets the playback rate; it also enters the stored state and rides the replay on show.progressis stored only for reading through the server API; the replay does not use it (it useselapsed_ms).
Example: set_animation_progress.bin, set_animation_speed.bin.
