esx_scoreboard
esx_scoreboard is the ESX Legacy player list with job counts, search and live activities like robberies. Config keys and the AddActivity export API.
esx_scoreboard is a player list for ESX Legacy that players open with a key (default Z). It shows every online player with server id, character name, job, grade and ping, a counter per job, a search bar, and a list of ongoing activities such as robberies or races that other resources push in through server exports.
Overview
| Resource | esx_scoreboard (version 1.0.0, added with ESX Legacy 1.16.0) |
| Side | Server data cache, client NUI (Svelte) |
| Dependencies | esx_lib, es_extended |
| Database | None |
| Used by | esx_pausemenu (online players button) |
The server keeps an in-memory cache of players and job counts. It updates it on esx:playerLoaded, esx:setJob, esx:jobDataRefreshed and playerDropped, and runs a full reconcile every minute as a safety net. Data is only sent to players who currently have the scoreboard open, and the list is paged, so it scales to large servers.
Installation
ensure esx_lib
ensure es_extended
ensure esx_scoreboardThe UI ships prebuilt in web/dist. To change the Svelte source, run npm install and npm run build in web (Node.js 18 or newer).
Usage
- Press
Zor run/scoreboardto toggle it. PressESCto close it. - Search filters by player name, job label or server id.
- While it is open, camera look and attack controls are disabled.
Configuration
All keys are in config/main.lua.
Display
| Key | Default | Description |
|---|---|---|
Config.OpenKey | "Z" | Default key for the scoreboard key mapping |
Config.ServerName | "ESX Server" | Name in the header |
Config.MaxPlayers | nil | Slots shown in the header; nil reads sv_maxclients |
Config.LogoUrl | "" | HTTPS logo URL; empty shows the ESX placeholder |
Config.Jobs | 7 jobs | Badge color per job, see below |
Config.ActivityTypes | 6 types | Label and icon per activity type |
Config.Debug | false | Debug logging |
Config.Jobs = {
police = { label = "Police", color = "#3B82F6" },
ambulance = { label = "EMS", color = "#EF4444" },
-- mechanic, taxi, realtor, cardealer, banker ...
}
Config.ActivityTypes = {
robbery = { label = "Robbery", icon = "💰" },
-- heist, drug, race, hostage, shootout
}Jobs that are not listed in Config.Jobs are still counted; they just use the default color.
Performance and limits
| Key | Default | Description |
|---|---|---|
Config.SummaryInterval | 10000 | ms between summary broadcasts (counts, jobs, activities) to open scoreboards |
Config.PageRefreshInterval | 15000 | ms between refreshes of the visible player page |
Config.PageRefreshBatchSize | 25 | Open scoreboards refreshed before the server thread yields |
Config.PageRefreshBatchDelay | 50 | ms between refresh batches |
Config.PingRefreshInterval | 30000 | ms between ping updates |
Config.FullReconcileInterval | 60000 | ms between full rebuilds of the player cache |
Config.OpenCooldown | 2000 | Minimum ms between open requests per player |
Config.PageRequestCooldown | 1500 | Minimum ms between page, search or sort requests per player |
Config.ActivityDebounce | 1000 | Debounce for activity broadcasts |
Config.DefaultPageSize | 50 | Rows per page |
Config.MaxPageSize | 100 | Hard cap for rows per page |
Config.MaxPageCacheEntries | 256 | Cached page payloads |
Config.MaxPlayerQueryCacheEntries | 64 | Cached search or sort results |
Config.MaxSearchLength | 48 | Max search text length accepted |
Config.MaxActivities | 64 | Activities kept; the oldest is dropped first |
Config.MaxActivityPlayers | 16 | Player entries stored per activity |
Config.UpdateInterval | Config.SummaryInterval | Legacy alias for older configs |
Server exports
Use these from your own server scripts (robberies, heists, races) to show what is going on in the city.
AddActivity
local id = exports.esx_scoreboard:AddActivity(activityType, label, location, players)| Name | Type | Description |
|---|---|---|
activityType | string | Key such as robbery; only letters, digits, _ and - are kept |
label | string? | Display text; defaults to the label from Config.ActivityTypes |
location | string? | Location text |
players | table? | Array of server ids or strings, capped at MaxActivityPlayers |
Returns the numeric activity id, or -1 if the type is invalid.
UpdateActivity
local ok = exports.esx_scoreboard:UpdateActivity(id, { label = '...', location = '...', players = { ... } })Only the fields you pass are changed. Returns true when the activity was found.
RemoveActivity
local ok = exports.esx_scoreboard:RemoveActivity(id)Returns true when the activity was found and removed.
GetActiveActivities
local list = exports.esx_scoreboard:GetActiveActivities()Returns a copy of all activities. Each entry has id, type, label, location, startTime (os.time()) and players.
Example: store robbery
local activeRobberies = {}
RegisterNetEvent('myrobbery:start', function(storeName)
local src = source
-- validate distance, cooldown and police count here
activeRobberies[src] = exports.esx_scoreboard:AddActivity('robbery', storeName, 'Grove Street', { src })
end)
local function endRobbery(src)
local id = activeRobberies[src]
if id then
exports.esx_scoreboard:RemoveActivity(id)
activeRobberies[src] = nil
end
end
AddEventHandler('playerDropped', function()
endRobbery(source)
end)Activities are not removed automatically. Always remove them when the event ends, including when the player disconnects.
Network events
| Event | Direction | Notes |
|---|---|---|
esx_scoreboard:server:open | client to server | Open and register as active viewer; rate limited by OpenCooldown |
esx_scoreboard:server:requestData | client to server | Same as open |
esx_scoreboard:server:requestPage | client to server | { page, pageSize, search, sortBy, sortAsc }; only for active viewers, rate limited |
esx_scoreboard:server:close | client to server | Stop receiving updates |
esx_scoreboard:client:receiveSummary | server to client | Counts, jobs, activities, server info |
esx_scoreboard:client:receivePage | server to client | One page of players |
esx_scoreboard:client:receiveActivities | server to client | Activity list |
The client-triggerable events only return public data (names, jobs, ping) and are rate limited, so they cannot be abused for more than reading the player list.
Common issues
- "ESX.GetJobs() unavailable after 10 attempts": es_extended was not ready. Make sure it starts before esx_scoreboard.
- Changing
OpenKeyhas no effect: FiveM saves key mappings per player. Existing players must rebind it themselves. - Players missing right after restart: the cache is rebuilt from
GetPlayers()once ESX jobs are available; wait for the first reconcile or reopen the list. - Old robberies stay on the board: your script never called
RemoveActivity.
Related
Frequently asked questions
Independent documentation
Last updated on
esx_rpchat
esx_rpchat adds roleplay chat to ESX Legacy - /me and /do proximity messages, global OOC, /twt, /anontwt and private /msg with per-command rate limits.
esx_service
esx_service is the on/off duty system for ESX Legacy jobs. It limits how many players are on duty, tracks counts in GlobalState and notifies colleagues.