Skip to content

Chat

hobbs_chat, current version 1.0.1

Overview

A replacement for the standard chat. It is built for roleplay servers where nobody should type into a global chat box: free text is off, players only use commands. /me and /do do not land in a chat window at all, they float above the player's head for the people standing around. Admins and the console still reach everyone with /say, and every /me and /do can be logged to Discord.

The chat window itself sits in the top left corner and only pops up when a player opens it. Other resources that post to the chat or register command suggestions keep working, because the script listens to the same events as the standard chat.

How it plays

  1. A player presses T. A small plate opens with the title you set (Telegraph by default), the last lines and an input box.
  2. They type /me or /do. While typing, a list of matching commands appears under the box, with a short description and the argument they are on. Tab fills in the highlighted entry, Ctrl+Up and Ctrl+Down move through the list.
  3. They write their action, for example /me tips his hat, and press Enter. Esc closes the box without sending.
  4. Everyone within 10 metres sees /me: tips his hat in a dark pill above that player's head for 6.5 seconds. A /do sits a little higher than a /me, so both can show at once.
  5. If a player types plain text without a slash, nothing is sent. They get a short system line saying only commands are allowed.
  6. When the box is closed, new lines (a /say from an admin, a server message) fade in at the top left for a few seconds and hide again. The window keeps the last 50 lines, and Up and Down bring back the last 50 things the player typed.

Requirements

Resource Note
nss_libs (optional) Only for the Discord log. The webhook is sent through its Discord API. Without it the chat runs fine, the log just stays off.

There is no dependency in the manifest on purpose: the chat has to start no matter what else is missing. No framework, database or inventory is used.

Installation

  1. Unpack the resource into resources and keep the folder name hobbs_chat.
  2. Stop the standard chat resource. Both handle the same chat events and commands, so running both shows every line twice.
  3. Add it to server.cfg. If you want the Discord log, start nss_libs first:

    ensure nss_libs
    ensure hobbs_chat
    
  4. If admins should use /say, give them the ace permission command.say, for example:

    add_ace group.admin command.say allow
    
  5. Start the server and press T in game. The plate should open with the title from config.lua. Type /me and check that the suggestion appears.

There is no SQL file, no items and no jobs to create. Only config.lua, server_config.lua and locales.lua are open files, everything else including the UI is encrypted.

Configuration

Language and header

Config.Locale = "en"        -- "en" or "de" (locales.lua)

Config.Title = "Telegraph"  -- header of the open chat
Config.Subtitle = ""        -- "" = none

The title is shown in the head of the open plate, the subtitle to its right. All texts, including the two command descriptions in the suggestion list, live in locales.lua. To add a language, copy a block, translate it and set Config.Locale to its key.

Open key

Config.OpenKey = 0x9720FCEE -- T

A RedM control hash. The chat opens when the key is released, once the window has finished loading.

/me and /do

Config.Rp = {
    duration    = 6500,   -- ms the text stays
    distance    = 10.0,   -- m, who sees it
    maxLength   = 50,     -- characters
    rateLimitMs = 800,    -- min. time between two messages of one player
    headOffset  = 0.3,    -- m above the head bone
    doStack     = 0.12,   -- /do sits this much above /me
    font        = 1,      -- game font id
    scale       = 0.3,
}
Key Meaning
duration How long the text floats above the head
distance Players further away than this do not see the text. The server already sends it only to players within distance plus 5 metres, so nobody downloads text they cannot see
maxLength Longer messages are cut, counted in characters, not bytes, so umlauts and accents do not eat extra room
rateLimitMs Minimum gap between two /me of the same player. /me and /do count separately. A player who is too fast gets the "Not so fast" line, at most once every 3 seconds
headOffset Height of the /me above the head bone
doStack Extra height of the /do above the /me
font, scale Game font and text size of the floating text

I kept maxLength at 50 on purpose. A /me is one short action, and a 50 character pill still fits above a head without covering half the screen. If your players write longer scenes, raise it, but try 80 before you try 200.

Example: on a busy server where crowds stand close together, drop distance to 6.0 so a /me at the bar does not show up for people at the door.

Line breaks and double spaces are flattened before the text is sent, and broken characters are stripped.

/say

Config.SayMaxLength = 256

Maximum length of a broadcast from the console or from an admin. The input box itself also stops at 256 characters.

Discord log (server_config.lua)

This file stays on the server. config.lua is downloaded by every client, so the webhook never goes in there.

Config.Webhook = {
    enabled  = false,
    username = "hobbs_chat",
    avatar   = "",
    url      = "",
    urls = {
        me     = "",
        ["do"] = "",
    },
    colors = {
        me     = "#c8a84b",
        ["do"] = "#c41e1e",
    },
}
Key Meaning
enabled Turn the log on. Needs nss_libs running
username, avatar Name and avatar of the webhook post
url One webhook for everything
urls.me, urls["do"] Separate webhooks per command. "" falls back to url
colors Embed colour per command

How the log behaves, so you know what to expect in the channel:

  • Messages are collected and posted in batches, the first one 5 seconds after a message comes in, then every 600 ms until the queue is empty. One post carries up to 50 lines, /me and /do in separate embeds with their own colour.
  • Each line reads `HH:MM:SS` **player name** (server id) message. Markdown characters in the message are escaped and @ is defused, so a player cannot ping your Discord roles from in game.
  • The queue holds 200 messages per webhook. If Discord is slow and the queue fills, the oldest message is dropped and the server console says so.
  • When the resource stops, the queue is flushed first, so a restart does not swallow the last lines.
  • If nss_libs is missing or the webhook fails, the messages are dropped with a line in the server console. Players are never affected.

Commands and keys

Command Who What it does
/me <action> Everyone Shows /me: <action> above your head for players nearby
/do <description> Everyone Shows /do: <description> above your head, a little higher than /me
/say <text> Console, and admins with the ace command.say Broadcast to every player. From the console it shows with a Console tag, from an admin with a System tag and the admin's name
/clear Everyone Clears your own chat window
Key Where Action
T In game Open the chat
Enter Chat open Send
Esc Chat open Close without sending
Tab Suggestion list showing Fill in the highlighted command
Ctrl+Up / Ctrl+Down Suggestion list showing Move through the list
Up / Down Chat open Browse the last 50 things you typed

The key names in the window follow locales.lua, the keys themselves are fixed.

Exports and events

Everything below runs on the client. Other resources talk to the chat the same way they talk to the standard chat, so most of them need no change.

Show a line

-- from the server, to one player or to everyone (-1)
TriggerClientEvent('chat:addMessage', source, {
    templateId = 'system',          -- 'console', 'system', 'user' or anything else for a plain line
    args = { 'Sheriff', 'The bank closes at dusk.' },  -- { author, message } or { message }
    color = { 255, 160, 0 },        -- optional, colours the author
    multiline = false,              -- optional, keeps line breaks
})

-- from another client resource
exports.hobbs_chat:addMessage({ args = { 'Hello' } })

chatMessage(author, color, message) is accepted as well. Server print output shows up as a System line. Colour codes ^0 to ^9 inside a message are rendered: ^1 red, ^2 green, ^3 yellow, ^4 blue, ^5 cyan, ^6 purple, ^7 white, ^8 orange, ^9 pink, ^0 back to normal.

Suggestions

-- one command
TriggerClientEvent('chat:addSuggestion', source, '/sheriff', 'Call the sheriff', {
    { name = 'reason' },
})

-- several at once
TriggerClientEvent('chat:addSuggestions', source, {
    { name = '/sheriff', help = 'Call the sheriff', params = { { name = 'reason' } } },
})

-- remove one again
TriggerClientEvent('chat:removeSuggestion', source, '/sheriff')

-- from another client resource
exports.hobbs_chat:addSuggestion('/sheriff', 'Call the sheriff', { { name = 'reason' } })

Clear

TriggerClientEvent('chat:clear', source)

FAQ

Players say they cannot write in the chat. That is the design. Plain text is refused and the player sees "Only commands are allowed, e.g. /me ." If you want a talk channel, another resource has to register the command, the chat only carries commands.

Nothing happens when I press T. Wait for the window to load, the key only works once the UI has reported in. If another resource holds the NUI focus or binds the same key, change Config.OpenKey. Also make sure the standard chat resource is not running next to this one.

/say does nothing for my admins. The command is restricted. The player needs the ace command.say, for example through add_ace group.admin command.say allow and add_principal identifier.fivem:123456 group.admin in server.cfg. From the server console it always works.

I see "Not so fast" although I only sent one message. /me and /do are each limited to one message per rateLimitMs (800 ms by default). Double pressing Enter triggers it. Raise the value if you want a bigger gap, lower it if your players type fast and complain.

Can I change how the pill above the head looks? Font and size through font and scale in Config.Rp. The dark background, the white text and the /me: prefix are fixed in the encrypted part.

Other resources post to chat:addMessage, do they still work? Yes. chat:addMessage, chatMessage, chat:addSuggestion, chat:addSuggestions, chat:removeSuggestion and chat:clear are all handled, and templateId values console and system get their tags.

The Discord log stays empty. Check in this order: enabled = true in server_config.lua, a webhook in url or in urls, and nss_libs started before hobbs_chat. Then send a /me and wait 5 seconds, the first batch is delayed on purpose. A failed send prints [hobbs_chat] Discord webhook failed in the server console.

What happens on a resource restart? The chat closes and the NUI focus is released, so nobody is left with a stuck cursor. Pending Discord messages are posted before the resource stops. The window and the typed history start empty again.

Does the floating text cost performance? The draw loop only runs every frame while a pill is actually visible on your screen. With no /me or /do active it checks twice a second. The chat key is polled every 5 ms while the window is closed and every 150 ms while it is open.

Does the script touch money, jobs or the database? No. It has no SQL, no items and no job checks. The only outside call is the optional Discord webhook through nss_libs.

Changelog

1.0.1

First release.