esx_society
esx_society adds shared job accounts, the boss menu, hiring, salaries, grade labels, uniforms and money washing to ESX Legacy. Full API and config reference.
esx_society turns an ESX job into a company. It links a job to a shared bank account, adds a boss menu for hiring, firing, promotions, salaries, grade labels and uniforms, and lets bosses launder black money into the company account. Job resources such as esx_policejob, esx_taxijob and esx_mechanicjob register their society with it and open its boss menu.
Overview
| Fact | Value |
|---|---|
| Resource | esx_society |
| Side | Server (API, validation, money) and client (boss menu) |
| Dependencies | esx_lib, es_extended, cron, esx_addonaccount |
| Uses at runtime | oxmysql, esx_datastore (society garage), skinchanger (uniform saving) |
| SQL | esx_society.sql (society_moneywash table) |
A society is a plain record kept in memory:
| Field | Type | Description |
|---|---|---|
name | string | Job name, for example taxi. Must match jobs.name. |
label | string | Display name. |
account | string | Shared esx_addonaccount account, usually society_<job>. |
datastore | string | Shared esx_datastore store, usually society_<job>. Used for the society garage. |
inventory | string | Shared esx_addoninventory inventory, usually society_<job>. |
data | table | Free-form options. The official jobs pass { type = 'public' } or { type = 'private' }. esx_society stores this table but does not read it. |
Societies are not saved in the database. Every job resource must register its society again on every start.
Installation
Install the dependencies
Make sure oxmysql, es_extended, esx_lib, cron and esx_addonaccount are installed. Install esx_datastore and esx_addoninventory too, because the official job resources create society datastores and inventories.
Import the SQL
Import esx_society.sql. It creates the society_moneywash table. On start, esx_society also creates society_moneywash_operations and an index esx_society_employees on users (job, job_grade, identifier) by itself.
CREATE TABLE `society_moneywash` (
`id` int NOT NULL AUTO_INCREMENT,
`identifier` varchar(60) NOT NULL,
`society` varchar(60) NOT NULL,
`amount` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;Start it before every job resource
ensure cron
ensure esx_addonaccount
ensure esx_datastore
ensure esx_addoninventory
ensure esx_society
# job resources after this line
ensure esx_policejob
ensure esx_taxijobInnoDB required for money washing
On start the money wash module checks that users, society_moneywash and addon_account_data use the InnoDB engine. If one does not, it prints [esx_society] Moneywash disabled: ... and every wash request fails with "invalid amount". The rest of the resource keeps working.
Configuration
All keys live in config.lua.
| Key | Default | Description |
|---|---|---|
Config.Locale | GetConvar('esx:locale', 'en') | Language of the menus and notifications. |
Config.EnableESXIdentity | true | Show firstname lastname from the users table in the employee list. Set to false if you do not use esx_identity. |
Config.MaxSalary | 3500 | Highest salary a boss can set for a grade. Must be a whole number from 0 up to this value. |
Config.MaxJobGradeLabelLength | 40 | Grade labels set by bosses are trimmed to this length. |
Config.JobGradeUpdateCooldown | 1500 | Minimum delay in ms between two salary or label changes by the same boss for the same job. 0 disables the limit. |
Config.SocietyGarageDistance | 12.0 | Max distance in meters from a garage zone for the society garage events. |
Config.SocietyGarageZones | mechanic and taxi zones | Per-society list of vector3 points where the society garage may be used. |
Config.BossGrades | { ['boss'] = true } | Grade names (job_grades.name) that get boss access. Add more names to give access to several grades. |
Config.EnableUniformManagement | false | Show uniform management in every boss menu. You can also enable it per job with the uniforms option. |
Config.UniformSaveCooldown | 30000 | Minimum delay in ms between two uniform saves for the same job. |
Config.UniformDrawableMax | 2000 | Highest accepted drawable ID for clothing components. Keep it high for custom clothing packs. |
Config.UniformTextureMax | 255 | Highest accepted texture ID (components ending in _2). |
Config.UniformPropMax | 255 | Highest accepted prop ID for helmet_1, glasses_1, watches_1, bracelets_1 and ears_1. Props may also be -1 (none). |
Config.UniformComponentLimits | {} | Per-component overrides, for example helmet_1 = { min = -1, max = 250 }. |
Config.UniformComponents | 30 skin keys | The skinchanger components that make up a uniform: tshirt_1 … ears_2. A saved uniform must contain every one of them. |
Config.BossGrades = {
['boss'] = true,
['chef'] = true, -- give a second grade access to the boss menu
}How it works
- Boss check: a player has boss access to a society when
xPlayer.getJob().name == society.nameand the grade name is inConfig.BossGrades. Every server event and callback repeats this check. The client menu is only a view. - Account: balance, deposits and withdrawals go through the shared esx_addonaccount account named in
society.account. - Salaries and labels are written to
job_gradesand then reloaded withESX.RefreshJob(job)(orESX.RefreshJobs()as fallback), so changes apply without a restart. - Employees are listed in pages of 50: online players first, then offline players from the
userstable. - Money washing is queued in
society_moneywash_operationsand paid by a cron task every day at 03:00 (TriggerEvent('cron:runAt', 3, 0, WashMoneyCRON)). - Society payouts: when es_extended's
Config.EnableSocietyPayoutsistrue, the paycheck module callsesx_society:getSocietyand pays salaries from the society account. See Money and accounts.
Server API
registerSociety (export and event)
Registers a job as a society. Call it once when your job resource starts.
exports.esx_society:registerSociety(name, label, account, datastore, inventory, data)
TriggerEvent('esx_society:registerSociety', name, label, account, datastore, inventory, data)| Name | Type | Description |
|---|---|---|
name | string | Job name. |
label | string | Display label. |
account | string | Shared addon account name. |
datastore | string | Shared datastore name. |
inventory | string | Shared addon inventory name. |
data | table | Free options, for example { type = 'private' }. |
Returns nothing. Registering the same name twice prints a warning and keeps the first entry.
exports.esx_society:registerSociety('taxi', 'Taxi', 'society_taxi', 'society_taxi', 'society_taxi', { type = 'public' })The event is a local server event (AddEventHandler). Clients cannot trigger it.
GetSociety (export)
local society = exports.esx_society:GetSociety(name)| Name | Type | Description |
|---|---|---|
name | string | Society (job) name. |
Returns the society table or nil.
local society = exports.esx_society:GetSociety('police')
if society then
TriggerEvent('esx_addonaccount:getSharedAccount', society.account, function(account)
print(('%s has $%s'):format(society.label, account.money))
end)
endesx_society:getSociety (event)
Callback style lookup, used by es_extended's paycheck module.
TriggerEvent('esx_society:getSociety', name, cb)| Name | Type | Description |
|---|---|---|
name | string | Society name. |
cb | function | Receives the society table or nil. |
esx_society:getSocieties (event)
TriggerEvent('esx_society:getSocieties', function(societies)
for _, society in ipairs(societies) do
print(society.name, society.account)
end
end)Returns the array of all registered societies.
Client API
esx_society:openBossMenu
Opens the boss menu (ESX context menu). Use it from a marker or target zone in your job resource.
TriggerEvent('esx_society:openBossMenu', society, close, options)| Name | Type | Description |
|---|---|---|
society | string | Society name. Must equal the player's job name. |
close | function or nil | Passed to ESX.OpenContext as the close handler of the main menu. Called when the player closes it. |
options | table or nil | Menu entries to show. Missing keys use the defaults below. |
| Option | Default | Entry |
|---|---|---|
checkBal | true | Check society balance |
withdraw | true | Withdraw money |
deposit | true | Deposit money |
wash | true | Wash black money |
employees | true | Employee list and recruiting |
salary | true | Salary per grade |
grades | true | Rename grade labels |
uniforms | Config.EnableUniformManagement | Save the worn outfit as a grade uniform |
TriggerEvent('esx_society:openBossMenu', 'taxi', function()
ESX.CloseContext()
end, { wash = false, uniforms = true })The menu first asks the server through the esx_society:isBoss callback. Non-bosses see nothing.
Network events (client to server)
These are RegisterServerEvent handlers, so any client can send them. All of them validate on the server.
Security
Each event below is reachable from a modified client. The checks listed are what protects you. If you fork esx_society, keep them.
| Event | Parameters | Server checks |
|---|---|---|
esx_society:checkSocietyBalance | society | Boss access to that society. Shows the balance as a notification. |
esx_society:withdrawMoney | society, amount | Society exists, amount is a positive number (rounded), boss access, account has enough money. |
esx_society:depositMoney | society, amount | Society exists, positive amount, player's job equals the society (any grade), player has the cash. |
esx_society:washMoney | society, amount | Positive amount, boss access, enough black_money, wash module ready. |
esx_society:putVehicleInGarage | society, vehicle | Same job, within Config.SocietyGarageDistance of a Config.SocietyGarageZones point, valid plate, the player is in or next to (10 m) a vehicle with that plate and model. Deletes the entity and stores it in the society datastore key garage. |
esx_society:removeVehicleFromGarage | society, vehicle | Same job and garage zone checks, valid plate. Removes the entry from garage. |
TriggerServerEvent('esx_society:depositMoney', 'taxi', 500)depositMoney only requires the job, not a boss grade. Every employee can pay into the company account, even though the boss menu shows the button only to bosses.
Server callbacks
Registered with xLib.callback.registerCompat. Call them with ESX.TriggerServerCallback, ESX.AwaitServerCallback or xLib.callback. See Callbacks.
| Callback | Arguments | Returns | Access |
|---|---|---|---|
esx_society:isBoss | job | boolean | Caller's own job and boss grade. |
esx_society:getSocietyMoney | society | number (0 when denied) | Boss of that society. |
esx_society:getEmployees | society, cursor? | employees, nextCursor | Boss of that society. One request per player at a time. |
esx_society:getOnlinePlayers | society | array of { source, identifier, name, job = { name, grade, grade_name } } | Boss of that society. Cached for 1 second. |
esx_society:getJob | job | job table with grades sorted by grade, or false | No check. Returns public job data. |
esx_society:setJob | identifier, job, grade, actionType | boolean | Boss. See below. |
esx_society:setJobSalary | job, grade, salary | boolean | Boss of that job, valid grade, salary 0 to Config.MaxSalary, cooldown. |
esx_society:setJobLabel | job, grade, label | boolean | Boss of that job, valid grade, label trimmed to Config.MaxJobGradeLabelLength, cooldown. |
esx_society:setJobUniform | job, grade, skin | boolean | Boss of that job, complete and in-range skin, sex matches identity, Config.UniformSaveCooldown. grade = -1 saves for all grades. |
esx_society:getVehiclesInGarage | society | array of stored vehicles | Same job, inside a garage zone. |
esx_society:setJob rules
actionType must be 'hire', 'promote' or 'fire'.
- hire / promote:
jobmust be the boss's own job andgrademust exist and be lower than or equal to the boss's grade. Hiring needs the target online. - fire:
jobmust be'unemployed'andgrademust be0. - Offline targets (promote or fire) are updated in the
userstable only if they currently work for the boss's job.
local ok = ESX.AwaitServerCallback('esx_society:setJob', targetIdentifier, 'taxi', 2, 'promote')
if ok then
ESX.ShowNotification('Promoted')
endReading the balance in a HUD
local job = ESX.PlayerData.job
ESX.TriggerServerCallback('esx_society:getSocietyMoney', function(money)
print(('Company balance: $%s'):format(money))
end, job.name)Money washing in detail
- The boss enters an amount. The server checks boss access and the player's
black_money. - Inside
WithPlayerPersistence, the amount is removed fromblack_moneyand a row is written tosociety_moneywash_operationswith statequeuedin one transaction. If that fails, the money is refunded. - Every day at 03:00 the cron task adds each queued amount to the society account and marks it
paid.
There is no washing fee: 1,000 black money becomes 1,000 in the society account. Old rows in society_moneywash are migrated into the new table on start.
Integration tips
- Create the SQL rows first. A society needs
society_<job>inaddon_account(shared = 1). The official job SQL files also add the same name todatastoreandaddon_inventory. - Register the society at the top of your server file, after
esx_societystarted. Withensureorder this is guaranteed. - To give a second grade boss rights, add its
job_grades.nametoConfig.BossGrades. The check uses the name, not the grade number. - Uniforms are stored in
job_grades.skin_maleandskin_femaleand are read by job scripts that dress players from the grade skin.
Common issues
- Menu shows nothing: the grade name is not in
Config.BossGrades, or the job name passed toopenBossMenudiffers from the player's job. The server log showsesx_society: <identifier> attempted open a society boss menu!. - "Society has no shared account" in the console: the
addon_accountrow forsociety_<job>is missing or not markedshared = 1. - Salary change does nothing: the value is above
Config.MaxSalary, not a whole number, equal to the current value, or sent withinConfig.JobGradeUpdateCooldown. - Washed money never arrives: the
cronresource is not running, the server was offline at 03:00, or the wash module was disabled because a table is not InnoDB. Queued entries stay queued and are paid on the next run. - Employee list empty for offline players: with
Config.EnableESXIdentity = truethe query selectsfirstnameandlastname. Without esx_identity those columns may not exist; set the option tofalse.
Related
Frequently asked questions
Independent documentation
Last updated on
esx_shops
esx_shops adds 24/7, liquor and LTD stores with a cart-based NUI to ESX Legacy. Covers setup, config keys, tax, shop zones, exports and pitfalls.
esx_status
esx_status stores and syncs player needs like hunger, thirst and drunk in ESX Legacy. Covers the server-authoritative model, config, events and examples.