esx_addonaccount
esx_addonaccount stores extra money accounts such as society funds in ESX Legacy. Covers SQL setup, the account object, exports, events and pitfalls.
esx_addonaccount is a server-only library resource that stores money outside of the normal player accounts. Use it whenever money belongs to a job, a business or a property instead of a player's wallet, for example society funds used by esx_society and esx_billing.
Overview
| Resource | esx_addonaccount (version 1.1) |
| Side | Server only |
| Dependencies | esx_lib, es_extended, oxmysql |
| SQL file | esx_addonaccount.sql |
| Exports | GetAccount, GetSharedAccount, AddSharedAccount |
There are two kinds of accounts, controlled by the shared column of the addon_account table:
- Shared (
shared = 1): a single account without an owner. Typical forsociety_<job>accounts. - Personal (
shared = 0): one account per player identifier. The resource creates the row automatically when a player loads (esx:playerLoaded) and stores the loaded accounts on the player asxPlayer.get('addonAccounts').
Installation
Import the SQL
Run esx_addonaccount.sql against your ESX database. It creates addon_account (the definitions) and addon_account_data (the balances). Both tables must use InnoDB.
Ensure the resource
Start it after es_extended and before every resource that uses it:
ensure oxmysql
ensure esx_lib
ensure es_extended
ensure esx_addonaccount
ensure esx_society
ensure esx_billingDefine your accounts
Insert one row per account. Shared accounts get their balance row on startup automatically.
INSERT INTO `addon_account` (`name`, `label`, `shared`) VALUES
('society_mechanic', 'Mechanic', 1),
('property_black_money', 'Black money (property)', 0);On startup the resource runs a schema migration. It adds a generated owner_key column and a unique key on (account_name, owner_key) if they are missing. The migration aborts (and disables all accounts) instead of changing data when it finds problems:
addon_account_datais not InnoDB- rows with
account_name IS NULLorowner = '' - duplicate rows for the same account and owner
Configuration
esx_addonaccount has no config.lua. Everything is driven by the addon_account table.
| Column | Type | Description |
|---|---|---|
name | VARCHAR(60) | Unique account name, e.g. society_police |
label | VARCHAR(100) | Display label (stored, not used by this resource) |
shared | INT | 1 = shared account, 0 = one account per player |
Server exports
GetSharedAccount
exports.esx_addonaccount:GetSharedAccount(name)| Name | Type | Description |
|---|---|---|
name | string | Account name from addon_account |
Returns the account object or nil if the account does not exist or the resource is not ready yet.
local account = exports.esx_addonaccount:GetSharedAccount('society_mechanic')
if account and account.addMoney(2500) then
print(('Mechanic society now has $%s'):format(account.money))
endGetAccount
exports.esx_addonaccount:GetAccount(name, owner)| Name | Type | Description |
|---|---|---|
name | string | Name of a non-shared account |
owner | string | Player identifier (xPlayer.identifier) |
Returns the personal account object or nil. Accounts are only kept in memory for loaded players (offline accounts are evicted about once a minute after they are saved), so call this for online players.
local xPlayer = ESX.GetPlayerFromId(source)
local stash = exports.esx_addonaccount:GetAccount('property_black_money', xPlayer.identifier)
if stash then
stash.removeMoney(1000)
endAddSharedAccount
exports.esx_addonaccount:AddSharedAccount(society, amount)| Name | Type | Description |
|---|---|---|
society | table | { name = string, label = string } |
amount | number? | Starting balance, defaults to 0 |
Creates a shared account at runtime (inserts into both tables) and returns it. If the account already exists, the existing object is returned and nothing is inserted. Returns nil on invalid input or a failed insert.
exports.esx_addonaccount:AddSharedAccount({ name = 'society_bahama', label = 'Bahama Mamas' }, 10000)Account object
| Field / method | Description |
|---|---|
name | Account name |
owner | Identifier, or nil for shared accounts |
money | Current balance |
addMoney(amount) | Adds money. Returns true or false |
removeMoney(amount) | Removes money. Returns false if the balance would drop below 0 |
setMoney(amount) | Sets the balance. Returns true or false |
save(cb?) | Forces a write. cb(success) runs once the value is persisted |
isDirty() | true while a change has not been written yet |
withPersistence(action) | Runs action(money) while holding the persistence lock. Return success, delta to apply a change atomically |
amount must be a whole number between 0 and 2147483647. Floats, negative numbers and NaN are rejected and the method returns false. Every successful change is saved to the database right away (with automatic retry every 5 seconds on failure).
Events
All events are local server events. Clients cannot trigger them.
| Event | Arguments | When |
|---|---|---|
esx_addonaccount:getSharedAccount | name, cb | Legacy getter. cb(account) |
esx_addonaccount:getAccount | name, owner, cb | Legacy getter. cb(account) |
esx_addonaccount:addMoney | name, amount, owner | Fired after addMoney succeeded |
esx_addonaccount:removeMoney | name, amount, owner | Fired after removeMoney succeeded |
esx_addonaccount:setMoney | name, amount, owner | Fired after setMoney succeeded |
esx_addonaccount:refreshAccounts | none | Reloads account definitions from the database |
Listening for changes is useful for logging:
AddEventHandler('esx_addonaccount:removeMoney', function(name, amount, owner)
if name:find('^society_') then
print(('[audit] %s lost $%s'):format(name, amount))
end
end)The getter events still work, but the exports are simpler and avoid callback nesting:
TriggerEvent('esx_addonaccount:getSharedAccount', 'society_police', function(account)
if account then account.addMoney(500) end
end)Integration tips
- Society accounts follow the
society_<job name>pattern. esx_society and esx_billing rely on that naming. - Store item stashes in esx_addoninventory and generic key/value data in esx_datastore.
- New rows in
addon_accountare picked up without a restart when ESX firesesx:sqlCatalogReadyfor that table, or when you triggeresx_addonaccount:refreshAccountson the server.
Common issues
addMoneyreturnsfalse: you passed a float (for example a percentage cut likeprice * 0.15). Round first withmath.floor.- Console says "Duplicate addon accounts" or "Invalid account_name/empty owner": the migration stopped on purpose. Clean up the listed rows in
addon_account_databy hand, then restart the resource. - Account is
nilright after a server start: another resource asked before esx_addonaccount finished loading. Check forniland retry, or make sure the dependent resource starts later.
Related
Frequently asked questions
Independent documentation
Last updated on
esx_accessorymanager
esx_accessorymanager adds a radial wardrobe to ESX Legacy for taking off and restoring clothing and props. Config keys, client exports, F11 keybind and setup.
esx_addoninventory
esx_addoninventory stores items for societies and properties in ESX Legacy. SQL setup, inventory object, exports, events and ox_inventory stash behavior.