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¶
- A player presses T. A small plate opens with the title you set (
Telegraphby default), the last lines and an input box. - They type
/meor/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. - They write their action, for example
/me tips his hat, and press Enter. Esc closes the box without sending. - Everyone within 10 metres sees
/me: tips his hatin a dark pill above that player's head for 6.5 seconds. A/dosits a little higher than a/me, so both can show at once. - If a player types plain text without a slash, nothing is sent. They get a short system line saying only commands are allowed.
- When the box is closed, new lines (a
/sayfrom 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¶
- Unpack the resource into
resourcesand keep the folder namehobbs_chat. - Stop the standard
chatresource. Both handle the same chat events and commands, so running both shows every line twice. -
Add it to
server.cfg. If you want the Discord log, startnss_libsfirst: -
If admins should use
/say, give them the ace permissioncommand.say, for example: -
Start the server and press T in game. The plate should open with the title from
config.lua. Type/meand 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¶
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¶
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,
/meand/doin 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_libsis 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¶
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
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.