Frameworks
Writing your first ESX Legacy script
Learn ESX Legacy scripting: importing ESX, the xPlayer object, money and bank accounts, inventory items, jobs and grades, usable items, ESX.RegisterCommand, client events like esx:playerLoaded and esx:setJob, and a complete example resource.
Overview
ESX Legacy is one of the two most widely used roleplay frameworks. Almost everything you do in an ESX script goes through one object — xPlayer on the server — and a handful of events on the client. Learn those and most ESX code becomes readable.
01Importing ESX
fx_version 'cerulean'
game 'gta5'
lua54 'yes'
shared_scripts {
'@es_extended/imports.lua',
'config.lua',
}
client_script 'client.lua'
server_script 'server.lua'
dependency 'es_extended'The xPlayer object
| Task | Code |
|---|---|
| Get the player | local xPlayer = ESX.GetPlayerFromId(source) |
| Identifier | xPlayer.identifier |
| Cash | xPlayer.getMoney(), xPlayer.addMoney(n, reason), xPlayer.removeMoney(n, reason) |
| Bank / other accounts | xPlayer.getAccount('bank').money, xPlayer.addAccountMoney('bank', n) |
| Items | xPlayer.addInventoryItem('bread', 1), xPlayer.getInventoryItem('bread').count |
| Carry check | xPlayer.canCarryItem('bread', 5) |
| Job | xPlayer.job.name, xPlayer.job.grade, xPlayer.setJob('police', 0) |
| Notify | xPlayer.showNotification('Saved') |
| All police online | ESX.GetExtendedPlayers('job', 'police') |
02A complete example: selling fish
local PRICE = 45
local MARKET = vec3(-1845.0, -1195.0, 14.3)
RegisterNetEvent('fishmarket:sell', function()
local src = source
local xPlayer = ESX.GetPlayerFromId(src)
if not xPlayer then return end
if #(GetEntityCoords(GetPlayerPed(src)) - MARKET) > 5.0 then return end
local count = xPlayer.getInventoryItem('fish').count
if count < 1 then
return xPlayer.showNotification('You have no fish')
end
xPlayer.removeInventoryItem('fish', count)
xPlayer.addMoney(count * PRICE, 'fish market')
xPlayer.showNotification(('Sold %d fish for $%d'):format(count, count * PRICE))
end)local MARKET = vec3(-1845.0, -1195.0, 14.3)
lib.points.new({
coords = MARKET,
distance = 2.0,
onEnter = function() lib.showTextUI('[E] Sell fish') end,
onExit = function() lib.hideTextUI() end,
nearby = function()
if IsControlJustReleased(0, 38) then TriggerServerEvent('fishmarket:sell') end
end,
})The client only says “I want to sell”; the server checks the location, counts the fish and pays a price it defines. The client uses ox_lib points — add '@ox_lib/init.lua' to shared scripts, or use ESX.ShowHelpNotification with a distance loop instead.
Usable items
ESX.RegisterUsableItem('bandage', function(source)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.removeInventoryItem('bandage', 1)
TriggerClientEvent('medical:useBandage', source)
end)The item must exist in your inventory’s item list (the items table for default ESX, or ox_inventory/data/items.lua with ox_inventory).
Commands
ESX.RegisterCommand('givefish', 'admin', function(xPlayer, args, showError)
args.playerId.addInventoryItem('fish', args.count)
end, true, {
help = 'Give fish to a player',
validate = true,
arguments = {
{ name = 'playerId', help = 'Player ID', type = 'player' },
{ name = 'count', help = 'Amount', type = 'number' },
},
})Client-side player data
RegisterNetEvent('esx:playerLoaded', function(xPlayer)
ESX.PlayerData = xPlayer
print('Loaded as', xPlayer.job.name)
end)
RegisterNetEvent('esx:setJob', function(job)
ESX.PlayerData.job = job
end)Client data is for display. Never let the client decide job, money or items — the server re-checks with xPlayer. The same script written for QBCore is in the QBCore tutorial.
Frequently asked questions
How do I get the ESX object in a script?
Add '@es_extended/imports.lua' to shared_scripts, or call exports.es_extended:getSharedObject().
How do I give money in ESX?
xPlayer.addMoney(amount, reason) for cash, xPlayer.addAccountMoney('bank', amount) for the bank.
How do I check a player’s job in ESX?
On the server: xPlayer.job.name and xPlayer.job.grade.
Why is xPlayer nil?
The player has not loaded a character yet, or you used a stale source after a wait. Check for nil.
Need this built, not just explained?
Ten years of FiveM work, from Lua to NUI
Custom resources, React NUI, ESX / QBCore / Qbox integration, OneSync performance audits and security reviews — plus the websites and SEO around your server brand.
Related guides
- FrameworksWriting your first QBCore scriptGet the core with local QBCore = exports['qb-core']:GetCoreObject(). On the server, QBCore.Functions.GetPlayer(source) returns the Player: read Player.PlayerData (citizenid, job, money) and change state with Player.Functions.AddMoney('cash', n), AddItem, RemoveItem, SetJob. Register usable items with QBCore.Functions.CreateUseableItem, commands with QBCore.Commands.Add. On the client use QBCore.Functions.GetPlayerData() and the QBCore:Client:OnPlayerLoaded / OnJobUpdate events.
- FrameworksCallbacks: asking the server a question and getting an answerA callback registers a named handler on one side and lets the other side call it and receive its return value. With ox_lib use lib.callback.register on the server and lib.callback.await on the client (it also works server → client). ESX uses ESX.RegisterServerCallback / ESX.TriggerServerCallback, QBCore QBCore.Functions.CreateCallback / QBCore.Functions.TriggerCallback. Validate inside callbacks exactly like events.
- EngineeringWriting server events that cannot be abusedAny client can call any event you registered with RegisterNetEvent, with any arguments. In each handler: copy source into a local, check argument types and ranges, re-derive everything from server state (prices, amounts, rewards), verify the player can do this now (distance, job, item, cooldown), and log refusals. Events only other server scripts should use are registered with AddEventHandler alone, so clients cannot trigger them.
- FrameworksGetting started with ox_libStart ox_lib before your resource, add shared_script '@ox_lib/init.lua' and lua54 'yes' to your fxmanifest, and the global lib becomes available. Use cache.ped/cache.vehicle instead of calling natives in loops, lib.callback for client↔server requests, lib.notify, lib.progressBar, lib.registerContext/lib.showContext, lib.inputDialog, lib.points and lib.zones for interaction, and lib.addCommand for commands with ACE restrictions.