Quick navigation

Go to

Audio & communication

Chat, slash commands, and completion

Chat messages, slash commands, completion, and the built-in developer commands available to every player.

3 min read761 wordsMarkdown
On this page1 section

open77_chat provides chat, authenticated slash commands, command suggestions, history and keyboard completion. Its interface text is in English.

Source: open77_chat.

Quick usage:

T                 open chat
Enter             send or execute
Escape            close
Arrow Up/Down     select a suggestion while typing /
Tab               complete the selected command

Slash commands are resolved on the client first, then on the server. A line beginning with / is tokenised, offered to the client command registry through ExecuteCommand, and only forwarded to the server's RegisterCommand and ACL path when no client resource claims it. Either way it is not a chat message and is never broadcast to other players.

That ordering is what makes a client RegisterCommand useful: a HUD toggle answers locally in the same frame instead of making a round trip to ask the server about a setting the server does not own. unknown_command from the client half is not an error β€” it is the signal to forward β€” while any other refusal (command_restricted, an unterminated quote) is shown in chat and the line stops there rather than being sent to a server that would only answer unknown_command a second time.

Completions merge both halves. The composer asks the server for its suggestion list on open and adds every non-restricted client command from GetRegisteredCommands(); a command registered with a { help, parameters } descriptor shows that text, and one registered without shows which resource owns it.

Built-in utility commands include:

/id    show the temporary session player ID
/pos   copy the current world position as Lua
/rot   copy the current quaternion and yaw as Lua

/pos and /rot write only on the requesting client and display their result in chat and through the shared notification UI. See the clipboard guide for the API, permission, output formats, limits, and failure reasons.

Server API: Open77.chat

A server resource does not have to know the event names or the message shape to talk in chat. Open77.chat is the convenience layer over what open77_chat already listens to.

Open77.chat.send(playerId, message)          -- one player; -1 or nil = everyone
Open77.chat.broadcast(message)               -- same as send(-1, message)
Open77.chat.onMessage(function(playerId, playerName, text) end)
Open77.chat.addSuggestion(target, command, help, parameters)
Open77.chat.addSuggestions(target, list)
Open77.chat.removeSuggestion(target, command)
Open77.chat.clear(target)

message is a plain string, or the full table the chat UI renders:

Open77.chat.broadcast({
    type = "system",                -- "player" | "system"
    author = "DISPATCH",
    text = "Heat on the docks.",
    color = { 0, 229, 255 },
})

send, broadcast, addSuggestion, addSuggestions, removeSuggestion and clear return true, or false, reason. onMessage returns the handler id, so it can be passed to RemoveEventHandler; it returns nil, "invalid_chat_handler" for a non-function. Other reasons: invalid_chat_target, invalid_chat_message, invalid_chat_command, invalid_chat_suggestion, and the host bus's own resource_preparing (the call ran at the top level of the script, before the chunk had loaded -- move it into onResourceStart, a command or an event handler) and event_queue_limit.

playerId and target are numbers: an integer id, -1 for everyone (send also takes nil). A player id that arrived through a host event -- onPlayerReady, onPlayerDisconnected, onPlayerEnteredVehicle -- is a string, and the facade refuses it with invalid_chat_target: pass tonumber(playerId). source inside a RegisterNetEvent or RegisterCommand handler is already a number.

The events the chat raises for the server

Event Payload Raised
chat:ready () By the open77_chat client, through TriggerServerEvent, once its UI is up and again whenever it re-requests suggestions. Handle it with RegisterNetEvent: source is the player, there are no arguments. The usual place to addSuggestions(source, ...); players already connected when a resource starts had their chat:ready earlier, so also publish once with -1 from onResourceStart.
chat:message (playerId, playerName, text) Every message the authority accepted, after the chatMessage veto ran; what Open77.chat.onMessage subscribes to.
chatMessage (playerId, playerName, text) Cancellable, raised before a message is shown: a handler that calls CancelEvent() drops it.

It is transport, not a second authority

Every call above is one TriggerEvent on the host-wide bus, and open77_chat remains the only resource that decides anything. Three consequences worth knowing before you build on it:

  • It grants nothing. The facade never reaches a client itself, so it needs no network.events; the authority does that, with its own permissions. Any resource could already publish these events by hand β€” this only saves it from knowing their names.

  • It cannot tell you nobody listened. If no chat authority is running, the calls still return true: TriggerEvent publishes to the bus, and the bus does not know who cares.

  • To refuse a message, do not use onMessage. Observation happens after the verdict. The veto point is the existing cancellable chatMessage event:

    AddEventHandler("chatMessage", function(playerId, playerName, text)
        if isMuted(playerId) then CancelEvent() end
    end)

Ordering

Delivery is asynchronous: a send reaches clients on the next tick boundary, not inside your call.

Two sends in the same tick arrive in the opposite order. That is the resource scheduler, not the chat layer β€” __open77_tick collects ready tasks back to front, so the last one scheduled runs first, and it has always behaved this way for every callback on the server. It only becomes visible here because chat is the one surface where line order is meaningful. If you are printing a block that must read in order, put the lines in different ticks:

CreateThread(function()
    for _, line in ipairs(report) do
        Open77.chat.send(playerId, line)
        Wait(0)
    end
end)

// For agents & LLMsthis page as Markdownllms.txtllms-full.txt

// Guides Β· Lua API

Search the docs

1,905 guides and functions indexed

Try