Map editing
An edit session lets a resource give a player a free camera and a selection tool for moving objects, vehicles, pickups, gang zones and colshapes. The player only proposes; the server checks every proposal and the session's resource applies it. Decisions and limits are in ADR 0043.
Packets
EntityEdit (0x8D)
A player's proposal in an edit session. Client → Server, reliable event lane. A resource opens the session with dlmp.begin_edit: the camera flies free and the player's controls are off. The session ends with dlmp.end_edit, the resource stopping, the player disconnecting or the player's death (event edit_end).
[u8 packet_id = 0x8D]
[u64 token]
[u32 event_id] // position in the Client → Server reliable stream
[u8 action] // 0 select, 1 deselect, 2 edit, 3 parent
[u8 kind] // 0 object, 1 vehicle, 2 pickup, 3 gang zone, 4 colshape
[u32 id] // the entity of this kind; 0 on deselect
[u32 rev] // edit: object — the revision from the last ObjectState, vehicle — pose_rev from the last
// VehicleTransform, pickup — rev from the last PickupSync, zone — rev from the last GangZone,
// colshape — rev from the last ColshapeShape; otherwise 0
[f32 x, y, z] // edit: object — as in ObjectState (the offset from the parent when attached), vehicle and
// pickup — world position, zone — the min corner (x, y) and 0, colshape — `a` of its form;
// otherwise zeros
[f32 rx, ry, rz] // object — as in ObjectState; vehicle — pitch, roll, heading in degrees, as
// `dlmp.set_vehicle_rotation`; pickup — zeros; zone — the max corner (x, y) and 0; colshape — `b`Total: 47 bytes. Dropped silently (but acknowledged) when there is no session, kind is unknown, the entity is not shown to this player or not in their scope, action is unknown, the edit is of an entity that is not selected or is not at its current revision, values are not finite, the position is outside the world, an offset is above 1000 on any axis, a rotation's absolute value is above 3600, the parent is not an object or the resource did not ask for a parent, or the per-connection limit of 20 at once and 10 per second is exceeded; rejected proposals spend the limit as well. An entity of another resource is refused the same way.
Which entities can be edited:
- Vehicle: not a train, not a wreck, nobody inside, and not in use without a seat: not driven by remote control, not towed and not carried by a winch.
- Pickup: streamed to the player (and so active and in their scope). An edit is refused with a nonzero rotation and with a position outside the pickup bounds (PickupSync).
- Gang zone: shown to this player (visible to them and in their scope). An edit is refused with a nonzero third coordinate on either corner, with min not below max on any axis and with a corner outside world bounds (|x|, |y| ≤ 6000). A zone is never a parent.
- Colshape: its outline is shown to this player (ColshapeShape). An edit is read in the terms of its current form (an edit does not change the kind of form) and is refused by the same rules as the
ColshapeShapedecoder. A colshape is never a parent.
After a refused selection the client receives EditState with the previous selection. The client keeps the proposed pose of its copy until the next state of the entity:
- object: ObjectState (0x8A); the server sends it for every edit, accepted or refused: with a new revision when the resource applied it, with the previous one when not;
- vehicle: VehicleTransform (0x54); on a refused or unapplied edit the server sends the previous pose with the previous
pose_revonly to the proposer and only if the vehicle is streamed to them; other copies and the revision do not change; - pickup: PickupSync (0x12) the same way: the previous pose with the previous
rev, only to the proposer; - zone: GangZone (0x71) with the previous
rev, only to the proposer and only if it is still shown to them, on the next streaming pass (not a separate pass for each refusal); - colshape: ColshapeShape (0x95), the same as a zone.
The selection is visible only to this player; other players' copies show only the pose the server applied.
The session's resource applies an edit: entity_edit(player, entity, x, y, z, rx, ry, rz), then set_position and set_rotation of an object, dlmp.set_vehicle_position and dlmp.set_vehicle_rotation of a vehicle, Pickup:set_position of a pickup; for a zone entity_edit(player, zone, min_x, min_y, max_x, max_y) and GangZone:set_area; for a colshape entity_edit(player, colshape, form) with a form table as in dlmp.create_colshape, and Colshape:set_form. A selection raises entity_select(player, entity|nil, mode), with mode "select" or "parent".
Example: entity_edit.bin.
EditCamera (0x8E)
Where the session camera is. Client → Server, unreliable, latest wins: sent on a move or turn, at most 5 times a second, only in an open session.
[u8 packet_id = 0x8E]
[u64 token]
[u32 seq] // grows from report to report, wrapping through 0
[f32 x, y, z]
[f32 front_x, front_y, front_z] // view direction; the server normalizes itTotal: 37 bytes. Dropped when there is no session, past the limit of 10 at once and 6 per second, when the position is not finite or above 100000 in absolute value on any axis, when the direction is not finite or shorter than 0.5, or when seq is not newer than the last accepted one (newer means seq - last, taken as a signed 32-bit value, is positive; every session starts over). The value is this player's stream center and readable by the gamemode; it is not authority.
Example: edit_camera.bin.
EditState (0x8F)
The state of the recipient's edit session. Server → Client, reliable event lane, sent on every change of its fields; critical on open and close. A fresh connection has no session.
[u8 packet_id = 0x8F]
[u32 event_id]
[u8 active] // 1 open, 0 closed; with 0 the other fields are zeros
[f32 grid] // grid step, meters, 0..1000; 0 = no grid
[f32 angle] // angle step, degrees, 0..360; 0 = no step
[u8 pick_parent] // 1: the client sends the next click as a parent pick
[u8 selected_kind] // kind of the selected entity, as EntityEdit.kind
[u32 selected] // its id, 0 = noneTotal: 20 bytes. The decoder rejects steps out of range, non-finite values, an unknown selected_kind, and active or pick_parent other than 0 / 1. The steps are only a hint for the tool: an edit is checked by the rules of EntityEdit, not against the grid.
Example: edit_state.bin.
