Skip to content

Refusable events fail closed; chat is one of them ​

Status: Accepted (2026-10-28)

Context ​

Some events let the gamemode refuse a player's action: a handler returns false and the action does not happen. If an error or a timeout in a handler counted as "not refused", a bug in a mode that forbids, for example, entering team vehicles would silently lift the ban. The same contract decides what happens to an ordinary chat line, which a resource may filter, answer or replace, and which must not escape a filter that crashed.

Events reach resources through the server's event stream (ADR 0048); the reliable event lane (ADR 0007) carries chat in both directions.

Decision ​

  • The refusable events are vehicle_start_enter, vehicle_jack, vehicle_control, trailer_attach, winch_attach, player_remote_enter, player_detonate_satchels, player_stealth_kill, player_chat and player_connecting. The return value of every other event is ignored.

  • The event is refused if any handler returns false, raises an error or runs past the callback time limit, including a handler that catches the time-limit error with its own pcall. nil, true and any other value let the action through. All handlers run in any case, so several resources (a filter and a logger, say) coexist. An event sourced at a vehicle that an earlier handler already destroyed is not raised, and a refusable one is refused.

  • player_connecting can give a reason. A handler refuses with return false, "reason"; the first reason given wins, and the player sees it. A failed or timed-out handler gives no reason, and its error text never reaches the player.

  • Chat goes through player_chat. The flow for a ClientChat line:

    • the server acknowledges it on the reliable lane at once; only the next in-order line, within the per-connection flood limit, goes further;
    • it sanitizes the text once, before Lua or any broadcast sees it: UTF-8 up to the first NUL with invalid sequences replaced, control and format characters removed (so embedded line breaks or direction overrides cannot spoof chat lines), surrounding whitespace trimmed. An empty result is dropped: no event, no broadcast;
    • it raises player_chat(player, text), sourced at the sender, so dlmp.root handlers receive it;
    • unless the event is refused, it broadcasts the line to everyone as a ChatMessage attributed to the sender, in the sender's current color.

    A returned string does not rewrite the line. A resource that wants other text, or other recipients, refuses the event and sends its own line with player:send_chat(recipients, text) or replies with player:send_message(text). player_chat is not tied to the player's life cycle: it fires before the first spawn and while the player is dead; the resource decides its own policy.

  • Commands keep their own contract. A command handler returns true to consume the command; a handler error neither consumes the command nor blocks lower-priority handlers.

Consequences ​

  • A bug in a handler can wrongly forbid an action, but it can never lift a ban the mode depends on. A chat line swallowed by a crashed filter is visible in the server log.
  • The polarity of chat matches every other refusable event: a handler that forgets to return lets the chat through, which is the ordinary behavior.
  • Chat filtering, replies and redirection need no wire change; ClientChat and ChatMessage are enough.

Alternatives considered ​

  • Fail open: an error or timeout does not refuse. Rejected: a handler bug removes the mode's restriction.
  • Fail open for chat only. Rejected: one rule for all refusable events is simpler, and a lost chat line is visible in the log.
  • true suppresses the chat broadcast, as true consumes a command. Rejected: a chat filter refuses a line rather than consuming it, and every refusable event uses false for that.
  • Stopping at the first handler that refuses. Rejected: chat is an event, not a named lookup like a command; every subscriber should see it.
  • Rejecting a chat line with invalid UTF-8 instead of replacing the bad bytes. Rejected: a lossy decode keeps the readable part of the message; removing control characters is needed in any case.

Documentation licensed under CC BY 4.0. DarqlineMP is not affiliated with Rockstar Games or Take-Two Interactive. Grand Theft Auto is a trademark of Take-Two Interactive Software, Inc.