Chat, HUD and dialogs
Chat, player commands and server messages; nametags, chat bubbles and the player list; the server-wide UI theme; input actions; dialogs, tables, forms, menus and offers; objective markers and HUD widgets. Every packet on this page travels on the reliable event lane.
Delivery
Server → Client packets on this page are either critical or droppable:
- critical — never dropped: the packet stays queued until acknowledged, and a recipient that stops acknowledging is disconnected (Disconnect);
- droppable — enqueued only while the recipient's reliable queue has room; otherwise the packet is not sent. What happens then (a later resync, a refusal to the resource) is stated per packet.
Client → Server packets on this page share the sender's single reliable stream. Every packet the server accepts in order is acknowledged with ServerAck, including packets it then drops silently by the rules below.
Text fields are UTF-8, padded with zeros to the field size. "One line" means no control characters (Unicode Cc: C0, DEL and C1). Where the server "sanitizes like chat", it takes the text up to the first NUL, replaces invalid UTF-8, removes control and format (Unicode Cf) characters and trims surrounding whitespace.
HUD widgets
HUD widgets (HudAnnouncement, HudCountdown, HudStatusLine, HudKillFeedAdd, HudProgress, HudNotificationAdd, HudGauge, HudSpeedometer and their clear packets) do not depend on scope and are allowed in any connection state, including PreSpawn (before the first spawn). A new connection starts with an empty HUD and on joining receives a snapshot of what resources wrote to dlmp.root. A write to dlmp.root changes the widget for everyone; a write to one player changes it only for that player, until the next write to dlmp.root. Timed widgets are counted down by the client: the server sends the remaining time when it sets the widget and in the snapshot, never once per second.
Icons
The icon byte of ObjectiveMarker and HudGauge indexes a built-in icon. In Lua the icon is given by name:
| Index | Name | Index | Name | Index | Name |
|---|---|---|---|---|---|
| 0 | pin | 6 | car | 12 | water |
| 1 | target | 7 | house | 13 | energy |
| 2 | flag | 8 | money | 14 | fuel |
| 3 | star | 9 | heart | 15 | warning |
| 4 | skull | 10 | shield | 16 | lock |
| 5 | person | 11 | food | 17 | unlock |
0xFF means no icon and is allowed only on a gauge. The client rejects any other index.
Packets
ClientChat (0x07)
Client → Server, reliable event lane.
[u8 packet_id = 0x07]
[u64 session_token] // the token from ServerHello; a mismatch drops the packet
[u32 event_id] // position in the sender's Client → Server reliable stream
[u8 text[1024]] // UTF-8, null-paddedTotal: 1037 bytes. The server binds the message to the sender by its socket address; the client never sends a player_id. event_id is the monotonic 1-based position in this connection's Client → Server stream; the server delivers strictly in order and answers with ServerAck.
The server sanitizes the text (see Delivery) and drops an empty result. Above 5 lines at once and 2 per second per connection, a line is acknowledged but dropped. An accepted line is relayed as ChatMessage.
Example: client_chat.bin.
ChatMessage (0x08)
Server → Client, reliable event lane.
[u8 packet_id = 0x08]
[u32 player_id] // the author
[u32 event_id] // position in the server's stream to this recipient
[u8 name[64]] // the author's name
[u32 color] // the author's color when the line was sent: 0xRRGGBB, or 0x01000000 for the theme's text color
[u8 text[1024]]Total: 1101 bytes. color colors the name. A line already in the chat is not recolored when the player's color changes. The client rejects any other color value.
event_id is the monotonic 1-based position in the server's stream to this recipient; every recipient has its own stream and numbering. The client delivers strictly in order and answers with ClientAck; the server resends what is unacknowledged after its retransmission timeout.
Example: chat_message.bin.
ClientCommand (0x19)
Client → Server, reliable event lane. The client sends this instead of ClientChat when the input starts with /.
[u8 packet_id = 0x19]
[u64 session_token] // the token from ServerHello; a mismatch drops the packet
[u32 event_id] // position in the sender's Client → Server reliable stream, shared with ClientChat and the other C → S events
[u8 text[1024]] // UTF-8 command line WITHOUT the leading `/` (the client strips it), null-paddedTotal: 1037 bytes (the same layout as ClientChat). The server checks the token and the reliable order as for chat and answers with ServerAck. The command line is sanitized like chat and an empty one is dropped; above 10 commands at once and 4 per second per connection, a command is acknowledged but dropped.
The server never relays a command. It dispatches the command to the resources' command handlers (dlmp.add_command_handler), which parse text and act. Any reply comes back as a separate ServerMessage; an unknown command gets Unknown command: /<name>.
Example: client_command.bin.
ServerMessage (0x1A)
Server → Client, reliable event lane: a text line from the server to one player (a command reply, Unknown command… and the like). It shares the per-recipient stream with ChatMessage; the client shows text in the chat log without a player attribution.
[u8 packet_id = 0x1A]
[u32 event_id] // position in the server's stream to this recipient
[u8 text[1024]] // UTF-8, null-padded
[u8 segment_count] // 0..=16; 0 = the whole line in the theme's text color
[16 x { u16 end, u32 color }] // colored runs; the first segment_count are used, the sender zeroes the restTotal: 1126 bytes. Color travels in these fields; the text has no markup. A run starts where the previous one ended (the first at byte 0) and ends at byte end of the text; color is 0xRRGGBB or 0x01000000 (the theme's text color).
The client rejects the packet when segment_count > 16, the ends do not strictly increase, an end lies beyond the text or inside a UTF-8 character, the last end is not the text length, or a color is invalid. The server builds the runs itself: empty runs are not counted, and more than 16 runs or an invalid color fail send_message with nil, err. When the text is cut to 1024 bytes, runs past the cut are dropped.
Example: server_message.bin.
PlayerNametag (0x74)
Server → Client, reliable event lane, droppable: how the subject's nametag looks to this recipient. Sent only to recipients the subject is streamed to (Player streaming), never to the subject itself. The packet carries the whole state.
[u8 packet_id = 0x74]
[u32 id] // the subject
[u32 player_token] // the subject's generation; the client does not show a nametag of another generation
[u32 event_id]
[u8 shown] // 1 = the nametag is visible to THIS recipient, 0 = hidden
[f32 distance] // meters from the recipient's camera, 0 < distance <= 300
[u8 text[64]] // zero-padded, NUL-terminated; empty = the player's name
[u32 color] // 0xRRGGBB; 0x01000000 = no color, the theme's text color
[u8 name[64]] // the subject's name, as in ClientHelloTotal: 150 bytes.
- Sent on every stream-in, whenever what the recipient sees changes, and when the subject is renamed. A change that did not fit the queue goes out as the current state on the next streaming pass.
nameis the recipient's only source of the remote player's name (nametag, spectator panel):RemotePlayerSynccarries no name.shownis the resource's decision for this recipient if it made one, otherwise its decision for everyone. Default:shown=1,distance=70, emptytext.- The server owns the state: it survives death and respawn and is cleared by the subject's disconnect; a per-recipient decision is also cleared by the recipient's disconnect.
coloris the player's color, the same for every recipient: the color set by the resource, otherwise the team color, otherwise0x01000000. A change is sent at once, including on a change of team or of the team's color.- The client drops a packet with a non-finite or out-of-range
distanceor any othercolorvalue. Until a packet for the subject's current generation arrives, there is no nametag: the subject's movement can arrive before the reliable snapshot.PlayerStreamOutandPlayerLeaveclear the state. - The client checks the distance and line of sight from its own camera; health and armor on the nametag come from
RemotePlayerSync.
Example: player_nametag.bin.
PlayerChatBubble (0x89)
Server → Client, reliable event lane, droppable: text above the subject for a time. Sent only to recipients the subject is streamed to (Player streaming), never to the subject itself.
[u8 packet_id = 0x89]
[u32 id] // the subject
[u32 player_token] // the subject's generation; the client does not show text of another generation
[u32 event_id]
[u32 time_ms] // time left, 0..3600000; 0 = remove the text
[f32 distance] // meters from the recipient's camera, 0 < distance <= 300
[u32 color] // 0xRRGGBB; 0x01000000 = no color, the theme's text color
[u8 text[256]] // UTF-8, zero-padded, NUL-terminated; all zeros when time_ms=0Total: 281 bytes.
- New text replaces the previous one. The client counts the time from receipt; the packet carries no end time. A change that did not fit the queue goes out as the current state on the next streaming pass.
- The server owns the state: a stream-in carries the live text with the time left; death and respawn do not remove it, disconnect does.
- Independent of the nametag: visible while the nametag is hidden and while the nametag widget is off. It is drawn above the nametag; the client checks distance and line of sight from its own camera, as for the nametag.
PlayerStreamOutandPlayerLeaveremove the text. - The client drops a packet with
time_ms,distanceorcolorout of range. The values only affect drawing.
Example: player_chat_bubble.bin.
PlayerListEntry (0x75)
Server → Client, reliable event lane, droppable: one row of the recipient's player list. A packet with the recipient's own id does not become a list row: it carries the recipient's own name and color and is sent when they change; until the first change, the name is the one the client joined with.
[u8 packet_id = 0x75]
[u32 id]
[u32 event_id]
[u8 listed] // 1 = add or replace the row, 0 = remove it
[u8 name[64]] // UTF-8, null-padded; all zeros when listed=0
[u32 color] // 0xRRGGBB; 0x01000000 = no color (see PlayerNametag)Total: 78 bytes.
- A player's rename or color change replaces their row for everyone it is shown to; a change that did not fit the queue goes out on the next reconciliation. The client drops a packet with any other
colorvalue. - The resource decides whom the list names, with
dlmp.set_player_list_scope:"all"(everyone on the server, the default),"world"(the samevirtual_world, in any interior) or"stream"(players streamed to the recipient, see Player streaming). The server filters: the client receives nothing about other players in this packet. - The server keeps a set of rows per recipient and reconciles it in the same pass as streaming (the 1 s tick, a change of world, area or visibility); a player who leaves loses their row on the next reconciliation.
- The packet carries no generation: lane order separates the previous and the new owner of an id, and a new row replaces the previous one.
- A new row is never queued into the last half of the reliable queue, so the full list of a large server arrives over several reconciliations; a row that did not fit goes out on the next one. A removal goes before a new row with the same id.
- A new connection starts with an empty list and receives it anew. The values only affect drawing.
Example: player_list_entry.bin.
UiTheme (0x61)
Server → Client, reliable event lane, critical: the look of the built-in UI, server-wide. Sent to every connection on a change, and to a new connection on joining if the theme is not the default. The client starts every new connection with the default theme.
[u8 packet_id = 0x61]
[u32 event_id]
[u16 widgets] // visibility bits: 0 chat, 1 nametags, 2 corner label, 3 player list, 4 announcement, 5 countdown,
// 6 status lines, 7 kill feed, 8 gauges, 9 speedometer; a hidden widget still receives its data
[u8 font] // 0 sans, 1 mono (the client's built-in fonts)
[u8 font_size] // 10..=48, pixels at a screen height of 1080, scaled with the height
[u8 chat_rows] // 1..=40, chat height in lines, until the player sets their own
[u8 style_kill_feed] // kill feed look: 0 rail, 1 chip (slanted plate), 2 board
[u8 style_status] // status lines look, same values
[u8 style_countdown] // countdown look, same values
[u8 speed_unit] // speedometer unit: 0 km/h, 1 mph
[f32 chat_width] // 0.1..=1, share of the screen width, until the player sets their own
[f32 opacity] // 0..=1, density of the dark bars and panels
[f32 glow] // 0..=1, strength of the line's glow, 0 = none; the player's own setting can only lower it
[u32 color_text] // the colors below are 0xRRGGBB: text
[u32 color_name] // name in chat
[u32 color_time] // timestamp, id in the player list
[u32 color_line] // start of the line
[u32 color_accent] // end of the line, its node, group headers
[u32 color_panel] // panel background
[u32 color_icon] // cause icon in the kill feed
[u32 color_health] // health bar on the nametag
[u8 label_len] // then the three texts of the corner label, each a length 0..=31 and that many bytes:
[.. label] // the name, in color_text
[u8 label_accent_len]
[.. label_accent] // the continuation of the name, in color_accent
[u8 label_note_len]
[.. label_note] // what stands after the name; empty = the player's nameTotal: 61..154 bytes; no bytes follow the texts. When both name texts are empty, the label shows the word DarqlineMP. Each text is one line of UTF-8 without NUL; the client rejects any other text and cuts what it shows to the screen width.
Default: every widget except the speedometer (widgets = 0x01FF), km/h, empty texts, sans, 18, 10 lines, rail for the kill feed and the status lines, chip for the countdown, width 0.42, opacity 0.72, glow 1, colors F2F4FF, 7DB4FF, A4A9C4, 009DFF, B548FF, 03040A, F2F4FF, B548FF (in field order).
The client rejects a value out of range (including NaN), an unknown font or look, and a color above 0xFFFFFF. The chat always sits in the top-left corner; the client moves it only when it would otherwise not fit on the screen. The server refuses a theme with a value out of range (dlmp.set_ui_theme returns nil, err).
Example: ui_theme.bin.
PlatformKeys (0x62)
Server → Client, reliable event lane, critical: the default keys of the platform actions, server-wide (ADR 0037). Sent to every connection on a change, and to a new connection on joining if they are not the stock keys. The client starts every new connection with the stock keys K, L, G.
[u8 packet_id = 0x62]
[u32 event_id]
[u8 engine] // key code (see ActionDeclare), 0 = the action is off
[u8 lights]
[u8 passenger]Total: 8 bytes. An action that is off does not fire, even for a player who chose their own key for it. The client rejects a code not in the key list.
The server refuses (dlmp.set_platform_action_key returns nil, err) a core key, a stock key of the game's controls, the stock key of another platform action, another platform action's current key and the default key of a declared action. The stock keys K, L, G stay reserved for their actions even while those are moved or off, so moving an action back to its stock key is always allowed. Setting the current value again succeeds and sends nothing.
Example: platform_keys.bin.
ActionDeclare (0x63)
Server → Client, reliable event lane, critical: a script action a resource declared. Sent to every connection on declaration and to a new connection on joining. The player chooses the key; the server never learns it and receives only ActionInput.
[u8 packet_id = 0x63]
[u32 event_id]
[u32 action_id] // never reused: a press in flight cannot reach a new action
[u8 default_key] // key code, 0 = no default key
[u8 default_mods] // held with it: bit0 Ctrl, bit1 Shift, bit2 Alt; not with key 0 and not with a modifier key
[u8;32 name] // a-z, 0-9, _; not empty, zero-padded; the name the player's saved choice is stored under
[u8;64 description] // one line of UTF-8, zero-padded; empty = the name is shownTotal: 107 bytes. Key codes are Windows virtual-key codes:
| Keys | Codes |
|---|---|
A..Z | 0x41..0x5A |
0..9 | 0x30..0x39 |
| F1..F12 | 0x70..0x7B |
| Backspace, Tab, Enter, CapsLock, Space | 0x08, 0x09, 0x0D, 0x14, 0x20 |
| PageUp, PageDown, End, Home | 0x21, 0x22, 0x23, 0x24 |
| Left, Up, Right, Down | 0x25, 0x26, 0x27, 0x28 |
| Insert, Delete | 0x2D, 0x2E |
| LeftShift, RightShift, LeftCtrl, RightCtrl, LeftAlt, RightAlt | 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 |
Esc cannot be bound. A server declares at most 64 actions.
The client rejects a packet with a code not in the list, invalid default_mods, a name outside its alphabet or a control character in the description. The server refuses (dlmp.register_action returns nil, err) a name in use and, without modifiers, a core key (T, Y, Tab, F1, F5, F7, F8, F11, F12, Backspace), a stock key of the game's controls (Q, W, E, R, A, S, D, F, G, H, Z, X, C, V, N, M, 1, 2, 3, Enter, Space, LeftShift, LeftCtrl, LeftAlt, the arrow keys) or a platform action's stock or current key (K, L, G by default); it also refuses the same key and modifiers as another action's default. The client does not assign a default key that is taken on that client (the player rebound a control to it): the action stays without a key until the player chooses one.
Example: action_declare.bin.
ActionRemove (0x64)
Server → Client, reliable event lane, critical, to every connection: the action is withdrawn, by dlmp.unregister_action or by the stop of its resource. Its prompt (ActionPrompt) is removed before this packet.
[u8 packet_id = 0x64]
[u32 event_id]
[u32 action_id]Total: 9 bytes.
Example: action_remove.bin.
ActionInput (0x65)
Client → Server, reliable event lane: the key of a declared action went down or up. The client does not send it while the chat or dialog input is open, a menu is open or the window is not focused; an action held at that moment is released.
[u8 packet_id = 0x65]
[u64 token]
[u32 event_id] // position in the Client → Server reliable stream
[u32 action_id]
[u8 pressed] // 1 down, 0 up; any other value rejects the packetTotal: 18 bytes. Accepted from a player in any connection state, including PreSpawn; the server raises the resource event player_action(player, name, pressed). Dropped silently (but acknowledged): the action is not declared or was withdrawn, the same edge again (down and up alternate), a press above 20 at once and 10 per second per connection. The release of a held action does not spend the limit. When a held action is withdrawn, the server itself finishes it with a release for the resources; the held actions of a player who left get no release.
Example: action_input.bin.
ActionPrompt (0x79)
Server → Client, reliable event lane, critical: the prompt of a declared action for one player. One slot on screen; a new prompt replaces the previous one. It is removed by dlmp.hide_action_prompt, by the action's withdrawal (active=0 before ActionRemove) and by the stop of the resource that wrote it last; death does not remove it, and it is not part of a new connection's snapshot. A write with the same action and text is not sent.
The server does not know the key: the client fills it in from the player's bindings, and a press arrives as the usual ActionInput. When the player has no key for the action, the client shows No key. The prompt is hidden while a menu, the chat input or a dialog input is open, since actions do not fire then. The theme does not control its visibility: the resource decides.
[u8 packet_id = 0x79]
[u32 event_id]
[u8 active]
[u32 action_id] // see ActionDeclare; not 0 when active=1
[u8 text[64]] // UTF-8, NUL-terminated, one line; not empty when active=1Total: 74 bytes. The client rejects active other than 0 or 1 and, with active=1, a zero action or an empty text. The server refuses (nil, err) an undeclared action, an empty text and a text longer than the field; it sanitizes the text like chat.
Example: action_prompt.bin.
DialogShow (0x69)
Server → Client, reliable event lane, critical: opens a modal dialog, replacing the open one. Allowed in any connection state, including PreSpawn.
[u8 packet_id = 0x69]
[u32 event_id]
[u32 dialog_id] // monotonic: a response to a replaced dialog never matches the open one
[u8 kind] // 0 message, 1 list, 2 input
[u8 title[64]] // UTF-8, NUL-terminated, one line
[u8 button1[32]] // not empty
[u8 button2[32]] // empty = no second button
[u8 body[1024]] // the text; for a list, its rows separated by `\n`, 1..64 rowsTotal: 1162 bytes. Esc acts as the second button; without a second button Esc does not close the dialog. While the game's pause menu is open the client holds the dialog back, and it waits for the chat input to close before showing it.
Example: dialog_show.bin.
DialogHide (0x6A)
Server → Client, reliable event lane, critical: closes the dialog or the passive table with this id if it is still open.
[u8 packet_id = 0x6A]
[u32 event_id]
[u32 dialog_id]Total: 9 bytes.
Example: dialog_hide.bin.
DialogTableRows (0x77)
Server → Client, reliable event lane, not critical: rows of the next version of a table. The server enqueues a version's rows and its commit (DialogTable) together and only when the queue has room for all of them; otherwise the resource call fails and the version is not sent. The client holds the rows until the DialogTable of that version, so a version appears on screen as a whole.
[u8 packet_id = 0x77]
[u32 event_id]
[u32 dialog_id]
[u32 rev] // version, >= 1
[u16 first_row] // first_row + row_count <= 256
[u8 row_count] // 1..255; the rows are consecutive
[u8 columns] // 1..8
[u8 body[1072]] // row_count * columns cells back to back, zeros after themTotal: 1089 bytes. A cell is [u8 head]; when bit 0x80 of head is set, a color [u8 b, g, r] follows (the low three bytes of 0xRRGGBB, little-endian); then the UTF-8 text of head & 0x7F bytes, at most 63 and without control characters. A cell without the color bit takes the theme's text color.
A row is never split between packets. body holds two rows of eight cells of the maximum length, so the largest table takes 128 packets plus the commit. The client rejects a packet whose cells do not add up to row_count * columns, run past body or are followed by non-zero bytes.
Example: dialog_table_rows.bin.
DialogTable (0x78)
Server → Client, reliable event lane, not critical (see DialogTableRows): shows version rev of a table, after its rows. A player has two table slots, each with its own dialog_id from the shared monotonic dialog counter:
- a modal table (
passive=0) takes the dialog's place: it replaces the open dialog, captures input and is answered withDialogResponse; - a passive table (
passive=1) is independent of the dialog and lies under it: it has no buttons, no response and does not capture input; a new passive table replaces the previous one.
[u8 packet_id = 0x78]
[u32 event_id]
[u32 dialog_id]
[u32 rev]
[u8 passive] // 0 or 1
[u8 columns] // 1..8
[u16 rows] // 0..256
[u8 title[64]] // UTF-8, NUL-terminated, one line
[u8 button1[32]] // not empty for a modal table, empty for a passive one
[u8 button2[32]] // empty = no second button; empty for a passive table
[u8 align[8]] // per column: 0 left, 1 center, 2 right
[u8 column_titles[8][32]] // UTF-8, NUL-terminated, one lineTotal: 409 bytes. A new dialog_id starts with no rows. The same dialog_id is an update: the server sends only the rows that differ from the previous version (all of them when the column count changes); the client keeps the others and drops rows past the new count. Selection and scroll position are kept; a selection past the end moves to the last row. The same content is not a new version: the server sends nothing, and a response in flight stays valid.
The client computes column widths from the content; text longer than its column is cut. The client shows all 256 rows of a modal table by scrolling; a passive table does not scroll, and the rows that do not fit the screen height are counted in its title. Longer tables are paged by the resource.
A passive table is removed by DialogHide, by the player's death (the server sends DialogHide), by the stop of the resource that showed it and by the end of the connection.
Example: dialog_table.bin.
DialogResponse (0x6B)
Client → Server, reliable event lane: the response to the open dialog.
[u8 packet_id = 0x6B]
[u64 token]
[u32 event_id] // position in the Client → Server reliable stream
[u32 dialog_id]
[u32 rev] // the table version the player saw; 0 for the other kinds
[u8 button] // 0 first, 1 second
[u16 item] // row index for a list and for a table with rows, otherwise 0xFFFF
[u8 text[256]] // for input: UTF-8, NUL-terminated; otherwise emptyTotal: 280 bytes. Dropped silently (but acknowledged): no dialog is open on this connection or this is another dialog, rev is not the current version, the button does not exist, the index is outside the list or the rows of this version or is present where none is expected, text on a non-input dialog, a text field without a NUL or not UTF-8, above 5 at once and 2 per second per connection. An accepted response closes the dialog: one response per dialog. The text is sanitized like chat. The resource that showed the dialog receives the response in its callback.
The client does not close a table after responding; the server's DialogHide on the accepted response closes it. A response to a version that was just replaced is dropped; the table stays with the new content and the player responds again.
An open form does not accept this packet: its response is DialogFormResponse.
Example: dialog_response.bin.
DialogForm (0x7D)
Server → Client, reliable event lane, critical: opens a form — several fields in one window, a modal dialog that replaces the open one. Allowed in any connection state, including PreSpawn. Closed by DialogHide.
[u8 packet_id = 0x7D]
[u32 event_id]
[u32 dialog_id] // the shared dialog counter
[u8 count] // 1..16
[u8 title[64]] // UTF-8, NUL-terminated, one line
[u8 button1[32]] // not empty
[u8 button2[32]] // empty = no second button
[u8 body[1024]] // count fields back to back, zeros after themTotal: 1162 bytes. A field is [u8 type], [u8 label_len], a UTF-8 label of 1..63 bytes without control characters, then by type:
| type | Field | Then |
|---|---|---|
| 0 | Text | [u8 max_len] 1..255, [u8 value_len] at most max_len, the value |
| 1 | Number | [f64 min, max, step, value] |
| 2 | Slider | As a number, with step > 0 |
| 3 | Checkbox | [u8 value] 0 or 1 |
| 4 | Select | [u8 option_count] 1..32, [u8 value] an index, then the options: [u8 len], a text of 1..63 bytes |
Number: the bounds are finite and within 1e12 in absolute value, min < max, 0 <= step <= max - min, value lies in the range. With step = 0 any value of the range is allowed; otherwise the value is min + k * step, where k may deviate from an integer by up to 1e-3, and with a fractional step the result is rounded to 9 decimal places. The server requires step >= 1e-6 and at least 1e-9 of the larger bound in absolute value, so that rounding stays within this tolerance. The server snaps the value to the grid and reports -0 as 0.
The client rejects a packet whose fields do not add up to count, run past body, break these constraints or are followed by non-zero bytes, and a form whose longest possible answer does not fit in values of DialogFormResponse (2 + max_len bytes for a text, 9 for a number, 2 for a checkbox or a select). The client chooses the layout; colors come from the theme. The first button is disabled while the text of a number field fails these checks. Field names stay on the server: the wire carries only the field order.
Example: dialog_form.bin.
DialogFormResponse (0x7E)
Client → Server, reliable event lane: the response to the open form.
[u8 packet_id = 0x7E]
[u64 token]
[u32 event_id] // position in the Client → Server reliable stream
[u32 dialog_id]
[u8 button] // 0 first, 1 second
[u8 count] // with the first button: the form's field count; with the second: 0
[u8 values[1024]] // count values in field order, zeros after themTotal: 1043 bytes. A value is [u8 type] of its field, then: text [u8 len] + bytes; number and slider [f64]; checkbox [u8] 0 or 1; select [u8] index. The second button carries no values.
Dropped silently (but acknowledged): no form is open on this connection or this is another dialog, a dialog of another kind is open, the button does not exist, count differs from the field count, more values or bytes than the fields take, a wrong type, a text longer than max_len, containing NUL or not UTF-8, a number that is NaN, out of range or off the step, a checkbox other than 0 or 1, an index past the options, above the limit shared with DialogResponse of 5 at once and 2 per second per connection. An accepted response closes the form: one response per form. Text is sanitized like chat. The resource that showed the form receives the values in its callback, keyed by field name. The client closes the form itself as soon as the response is sent.
Example: dialog_form_response.bin.
MenuItems (0x7F)
Server → Client, reliable event lane, not critical: the items of a menu level. The server enqueues the items and the level's MenuShow together and only when the queue has room for all of them; otherwise the resource call fails, a player's step is dropped and the level stays as it was. The client holds the items until the MenuShow of that level.
[u8 packet_id = 0x7F]
[u32 event_id]
[u32 menu_id] // the level, see MenuShow
[u16 first_item] // first_item + item_count <= 256
[u8 item_count] // 1..255; the items are consecutive
[u8 body[1072]] // item_count items back to back, zeros after themTotal: 1084 bytes. An item is [u8 flags] (1 disabled, 2 opens a submenu), then three strings, each [u8 len] + UTF-8 without control characters: the label of 1..63 bytes, the value on the right of 0..31 bytes and the description of 0..127 bytes. An item is never split between packets; the largest level takes 64 packets plus the commit. The client rejects a packet whose items do not add up to item_count, run past body, break the lengths, carry other flags bits or are followed by non-zero bytes.
Example: menu_items.bin.
MenuShow (0x80)
Server → Client, reliable event lane, not critical (see MenuItems): shows a menu level, replacing the one on screen. Allowed in any connection state, including PreSpawn.
[u8 packet_id = 0x80]
[u32 event_id]
[u32 menu_id] // monotonic, new for every level shown: input for a level that is gone never matches
[u16 items] // 1..256
[u16 selected] // < items
[u8 nested] // 0 or 1: a step back returns to the parent level instead of closing the menu
[u8 title[64]] // UTF-8, NUL-terminated, one line; may be emptyTotal: 78 bytes. A player has one menu, independent of the dialog; it does not capture input: the player keeps moving and scrolls the menu with the core keys (ADR 0037). A dialog and the open chat input stand above the menu and take the keys; the game's pause menu hides it. The submenu tree stays on the server: the client knows only the level on screen and receives a step down or up as a new MenuShow. The client does not show a level for which at least one item did not arrive. The client chooses the layout; colors come from the theme.
Example: menu_show.bin.
MenuHide (0x81)
Server → Client, reliable event lane, critical: closes the menu if this level is on screen. The menu is closed by a step back from the root, by hide_menu and by the stop of the resource that showed it; the client handles the end of the connection itself. The player's death does not close the menu: the resource decides.
[u8 packet_id = 0x81]
[u32 event_id]
[u32 menu_id]Total: 9 bytes.
Example: menu_hide.bin.
MenuInput (0x82)
Client → Server, reliable event lane: input to the menu level on screen.
[u8 packet_id = 0x82]
[u64 token]
[u32 event_id] // position in the Client → Server reliable stream
[u32 menu_id]
[u8 action] // 0 hover, 1 select, 2 back
[u16 item] // item index; 0xFFFF for backTotal: 20 bytes. Dropped silently (but acknowledged): no menu is open on this connection, menu_id is not the level on screen, another action value, an index outside the level's items, item other than 0xFFFF for back, a select of a disabled item, a hover on the item already hovered, a step between levels before the client has acknowledged the MenuShow of the level on screen (back from the root does not wait for that acknowledgment), above the per-connection limit: hover 20 at once and 12 per second, select and back 10 at once and 5 per second. Rejected inputs also spend the limit. A hover on a disabled item is accepted, so its description can be read.
The resource that showed the menu receives hover and select by item name, and a close on back from the root. Selecting an item that opens a submenu produces only the next level's MenuShow; back produces the parent level's MenuShow with the item it was entered from selected. On a new level the client at once reports the selected item with a hover. While the list is being scrolled, the client sends a hover at most once per 100 ms and always sends the item it stopped on.
Example: menu_input.bin.
OfferShow (0x83)
Server → Client, reliable event lane, to one player: a card to answer within a time — an invitation, a duel, a deal. It does not capture input: the player keeps moving and answers with the core keys (Y and N by default, ADR 0037). A player has one card: while one is open, the next one is refused (nil, err); there is no queue, the resource decides. Death does not remove it.
[u8 packet_id = 0x83]
[u32 event_id]
[u32 offer_id] // monotonic: a response to a previous card never matches the open one
[u32 remaining_ms] // 1..=300000; the server keeps the time, the client only draws it and accepts no keys after its own end
[u8 title[64]] // may be empty
[u8 text[256]] // not empty, one line; the client wraps it to its width and shows 3 lines
[u8 accept[32]] // the accept label, not empty
[u8 decline[32]] // the decline label, not emptyTotal: 397 bytes. The texts are UTF-8, NUL-terminated within the field, without control characters; the client rejects others. The server creates offers of 1000..=300000 ms. When the time runs out, the server sends OfferHide and calls the resource that showed the offer with timeout; dlmp.hide_offer, the stop of the resource and the player leaving remove the offer without a call.
Example: offer_show.bin.
OfferHide (0x84)
Server → Client, reliable event lane, critical: removes the card if offer_id matches the one shown.
[u8 packet_id = 0x84]
[u32 event_id]
[u32 offer_id]Total: 9 bytes.
Example: offer_hide.bin.
OfferResponse (0x85)
Client → Server, reliable event lane: the player's answer. The client removes the card itself; the packet gets no reply other than the acknowledgment.
[u8 packet_id = 0x85]
[u64 token]
[u32 event_id] // position in the Client → Server reliable stream
[u32 offer_id]
[u8 accept] // 1 accept, 0 declineTotal: 18 bytes. Dropped silently (but acknowledged): no offer is open on this connection, another offer_id, the time has run out by the server's clock, accept other than 0 or 1, above the per-connection limit of 5 at once and 2 per second; rejected answers also spend the limit. An accepted answer closes the offer: one answer per card.
Example: offer_response.bin.
ObjectiveMarker (0x86)
Server → Client, reliable event lane: an objective marker drawn over the world at any distance. The server decides showing and hiding as for Blip: by the scope of the recipient and of the target, at most the 32 nearest per player. A player never receives a marker on themselves; a marker on a player arrives once both have spawned. A player leaving or a vehicle being deleted removes the marker; a change of their world hides it from those left in the old one.
[u8 packet_id = 0x86]
[u32 event_id]
[u32 marker_id]
[u8 active] // 0 removes the marker; the other fields then carry no meaning
[u8 icon] // a built-in icon index, see Icons
[u32 color] // 0xRRGGBB, or 0xFFFFFFFF for the theme's accent color
[f32 pos[3]] // inside the world; for a marker on an entity, where the entity was
[u8 attach_kind] // 0 point, 1 player, 2 vehicle
[u32 attach_id]
[u8 text[64]] // may be empty; UTF-8, NUL-terminated within the field, without control charactersTotal: 96 bytes. With active=1 the client rejects an unknown icon, an invalid color, a non-finite pos and an unknown attach_kind. The client places the marker on the entity while that entity is streamed in, otherwise at pos; the server updates pos of a marker on an entity at most once per second and only when it moved more than 5 m. The distance shown is measured from the local player. Off screen and behind the camera, the marker sits at the screen edge in the direction of the target, with an arrow. See Icons.
Example: objective_marker.bin.
HudGauge (0x87)
Server → Client, reliable event lane, critical: one gauge of the block next to the radar. The block holds up to 6 gauges, ordered by slot. Targets and lifetime follow HUD widgets and HudStatusLine: a write to dlmp.root goes to everyone and to new connections, a write to one player holds until the next write to dlmp.root, an unchanged write is not sent, and the stop of the writing resource removes the gauge.
[u8 packet_id = 0x87]
[u32 event_id]
[u8 slot] // 0..=5
[u8 active] // 0 removes the gauge; the other fields then carry no meaning
[u8 icon] // see Icons, or 0xFF
[u16 value] // 0..=1000, thousandths of a full gauge
[u32 color] // 0xRRGGBB, or 0xFFFFFFFF for the theme's line
[u8 label[32]] // may be empty; UTF-8, NUL-terminated within the field, without control charactersTotal: 46 bytes. The client rejects slot above 5, active other than 0 or 1 and, with active=1, an unknown icon, a value above 1000 or an invalid color. The server refuses a value outside 0..1 and NaN (dlmp.set_gauge returns nil, err). Visibility is bit 8 of UiTheme.widgets.
Example: hud_gauge.bin.
HudSpeedometer (0x88)
Server → Client, reliable event lane, critical: the resource's fields on the speedometer, replacing the previous ones. Targets and removal as for HudGauge. The client reads the speed from its own vehicle and only displays it; the speed never goes to the server. The widget is visible to the driver and passengers when bit 9 of UiTheme.widgets is set, in the unit of UiTheme.speed_unit. The widget does not show the vehicle's health: a resource shows it with a gauge.
[u8 packet_id = 0x88]
[u32 event_id]
[u16 fuel] // 0..=1000, 0xFFFF = no field
[u8 lock] // 0 no field, 1 unlocked, 2 lockedTotal: 8 bytes. Both fields absent remove them from the widget; the speed stays. The client rejects other values.
Example: hud_speedometer.bin.
HudAnnouncement (0x6C)
Server → Client, reliable event lane, critical: the central announcement, replacing the previous one. See HUD widgets. active=0 removes the widget; the other fields then carry no meaning.
[u8 packet_id = 0x6C]
[u32 event_id]
[u8 active]
[u32 remaining_ms] // not 0 when active=1; the client hides the widget when it runs out
[u32 color] // 0xRRGGBB, or 0xFFFFFFFF for the theme's text color
[u8 title[64]] // UTF-8, NUL-terminated, one line; not empty when active=1
[u8 subtitle[128]] // UTF-8, NUL-terminated, one line; empty = noneTotal: 206 bytes. The client rejects active other than 0 or 1 and, with active=1, a zero remaining time, an empty title or a color out of range. The server refuses (nil, err) an empty title, a text longer than its field and a duration outside 1..=300000 ms; it sanitizes the text like chat.
Example: hud_announcement.bin.
HudCountdown (0x6D)
Server → Client, reliable event lane, critical: a countdown, replacing the previous one. Reaching zero raises no event: the client hides the widget, and the resource decides what follows with its own timer.
[u8 packet_id = 0x6D]
[u32 event_id]
[u8 active]
[u32 remaining_ms] // 1..=total_ms when active=1
[u32 total_ms] // the full duration: the line shows the share left
[u8 label[64]] // UTF-8, NUL-terminated, one line; empty = no labelTotal: 78 bytes. The client rejects active other than 0 or 1 and, with active=1, remaining_ms outside 1..=total_ms. The server refuses a duration outside 1..=86400000 ms and a text longer than its field.
Example: hud_countdown.bin.
HudStatusLine (0x6E)
Server → Client, reliable event lane, critical: one status line. A write with unchanged values is not sent.
[u8 packet_id = 0x6E]
[u32 event_id]
[u8 slot] // 0..=7, top to bottom; 1..8 in Lua
[u8 active]
[u32 color] // the value's color: 0xRRGGBB, or 0xFFFFFFFF for the theme's text color
[u8 label[32]] // UTF-8, NUL-terminated, one line; empty = none
[u8 text[64]] // the value; UTF-8, NUL-terminated, one line; not empty when active=1Total: 107 bytes. The client rejects slot above 7, active other than 0 or 1 and, with active=1, an invalid color or an empty text.
Example: hud_status_line.bin.
HudKillFeedAdd (0x6F)
Server → Client, reliable event lane, not critical: a kill feed row. Losing a new row is harmless and flooding does not disconnect; a resend after a clear is critical (see HudKillFeedClear). The feed keeps the last 5 entries, a new one pushing out the oldest; the client shows an entry for 15 s. The server never publishes deaths to the feed by itself.
[u8 packet_id = 0x6F]
[u32 event_id]
[u32 age_ms] // the entry's age; not 0 in the snapshot and in a resend after a clear
[u8 killer[64]] // name at the time of adding; empty = none
[u8 victim[64]] // name at the time of adding, not empty
[u8 cause] // weapon or cause of death
[u32 killer_color] // the player's color at the time of adding: 0xRRGGBB, or 0x01000000 (the theme's color; also with no killer)
[u32 victim_color] // the sameTotal: 146 bytes. An entry stores its colors together with the names and is never recolored; the snapshot and resends carry the same colors. The client rejects any other color value and an empty victim. Any cause is valid: the client draws its own icon for a known cause and a skull for an unknown one; the value only selects the icon. The snapshot carries no entry older than 15 s.
Example: hud_kill_feed_add.bin.
HudKillFeedClear (0x70)
Server → Client, reliable event lane, critical: clears the feed. The stop of a resource removes only its entries: the server sends a clear and a critical resend of the remaining entries with their ages.
[u8 packet_id = 0x70]
[u32 event_id]Total: 5 bytes.
Example: hud_kill_feed_clear.bin.
HudProgress (0x7A)
Server → Client, reliable event lane, critical: a progress bar, replacing the previous one. One packet describes the whole run: the client moves the bar from from to to over remaining_ms by itself, and the resource sends a new packet only when the run changes (a capture stopped or went back). The end of the run raises no event and does not remove the bar: it stays at to, and the resource decides what follows with its own timer. The snapshot carries the current position and the time left. A standing bar (remaining_ms=0) with unchanged values is not sent. The theme does not control its visibility: the resource decides.
[u8 packet_id = 0x7A]
[u32 event_id]
[u8 active]
[u16 from] // 0..=1000, thousandths of the bar
[u16 to] // 0..=1000; may be less than from
[u32 remaining_ms] // 0..=3600000; 0 = standing still, then from = to
[u32 color] // the line: 0xRRGGBB, or 0xFFFFFFFF for the theme's colors
[u8 label[64]] // UTF-8, NUL-terminated, one line; empty = no labelTotal: 82 bytes. The client rejects active other than 0 or 1 and, with active=1, values above 1000, a remaining time above the maximum, movement without time (remaining_ms=0 with from != to) and a color out of range. The server refuses (nil, err) a value outside 0..1 (and NaN), a duration outside 1..=3600000 ms and a text longer than its field; it sanitizes the text like chat.
Example: hud_progress.bin.
HudNotificationAdd (0x7B)
Server → Client, reliable event lane, not critical: a notification line at the side of the screen. Losing a new line is harmless and flooding does not disconnect; a resend after a clear is critical (see HudNotificationClear). The last 4 are shown: an expired line frees its place, otherwise a new line pushes out the oldest. The client removes a line itself when it expires; the snapshot carries no expired lines. The theme does not control visibility: the resource decides.
[u8 packet_id = 0x7B]
[u32 event_id]
[u32 age_ms] // the line's age, less than duration_ms; not 0 in the snapshot and in a resend after a clear
[u32 duration_ms] // 1..=30000, the full display time
[u32 color] // the text: 0xRRGGBB, or 0xFFFFFFFF for the theme's text color
[u8 text[96]] // UTF-8, NUL-terminated, one line, not emptyTotal: 113 bytes. The client rejects a duration out of range, an age not less than the duration, a color out of range and an empty text. The server refuses (nil, err) a duration out of range, an empty text and a text longer than its field; it sanitizes the text like chat.
Example: hud_notification_add.bin.
HudNotificationClear (0x7C)
Server → Client, reliable event lane, critical: removes all notifications. The stop of a resource removes only its lines: the server sends a clear and a critical resend of the remaining lines with their ages. The clear is sent even when the lines have already expired on the server, since their packet may still be on the lane. An empty queue sends no packet.
[u8 packet_id = 0x7C]
[u32 event_id]Total: 5 bytes.
Example: hud_notification_clear.bin.
