Player commands are a packet of their own, handled by resources and answered with server messages
Status: Accepted (2026-06-25)
Context
Players type commands (/where, /kick 3) in the same input box as chat, and game modes act on them. The server needs a path from "a player typed a command" to "a resource reacted", and a way to answer one player with a line of text that is not player chat. This builds on the reliable event lane and the server facade of ADR 0009.
Decision
- A command travels as ClientCommand, not as chat. When the input starts with
/, the client sends the line without the slash asClientCommandinstead of ClientChat. Detecting the slash is only framing: the client does not parse or execute commands itself. The server checks the token and the reliable order as for chat, sanitizes the line, drops an empty one and rate-limits commands per connection. It never relays a command to other players. - Resources register command handlers.
dlmp.add_command_handler(name, fn, opts)binds a command name to a callback that receives the player and the whitespace-split arguments as strings. Names match case-insensitively unless the handler asks otherwise; handlers run in priority order (high,normal,low, then registration order), across resources. The first handler that returnstrueconsumes the command and stops the dispatch. A handler that fails or times out neither consumes the command nor blocks the handlers after it. A handler is removed withdlmp.remove_command_handleror goes away with its resource. - An unconsumed command gets an answer from the core. When no handler consumes it, the player receives
Unknown command: /<name>. A bare/is ignored silently. - Answers are ServerMessage, not chat.
Player:send_messagesends one line of text to one player, with up to 16 colored runs. The client shows it in the chat log without a player attribution. Commands that only the server operator may run are console commands (dlmp.add_console_command), not player commands; see ADR 0026.
The player only triggers a command. The server owns its reception and dispatch; the resource that consumes it owns the reaction, expressed through the server facade.
Consequences
- A typo in a command can never leak into public chat, and a chat line can never be mistaken for a command: the two are different packets.
- Permissions are the handler's business: the core has no notion of an admin or a command ACL.
- Client-side autocomplete and history are not part of this decision.
Alternatives considered
- Detect the leading
/inClientChaton the server. One packet with two meanings: a branching mistake broadcasts a command as chat. - Answer through
ChatMessagewith a system sender. Mixes server notices with player chat and leaves an overloaded packet that a real server-message primitive would have to split later.
