ESX script generator for FiveM
Write what your ESX resource should do and ScriptForge builds every file for es_extended — manifest, client, server, config and NUI — then compiles and checks the result before you download it.
From a description to a resource
Choose ESX in ScriptForge and describe the script: what the player does, where, what it costs and who may use it. The files are written in front of you and arrive as one zip with a README covering installation, dependencies and the check results. Changes go in the same chat; the resource is updated rather than started from scratch.
For larger ideas, switch on Plan. The free plan lists the files, events, config options and dependencies before anything is built, and you can edit it until it matches the server you run.
The ESX details it gets right
ESX has habits that trip up generic code. Every ESX build is given a framework profile, checked against es_extended 1.15.2 and its addons. Some of its rules:
- Get ESX from @es_extended/imports.lua or exports['es_extended']:getSharedObject() — never the old esx:getSharedObject event.
- Accounts are 'money', 'bank' and 'black_money'. There is no 'cash' account.
- removeAccountMoney does not check the balance, so read xPlayer.getAccount(name).money first instead of trusting its return value.
- Amounts and counts are whole numbers of at least 1, and canCarryItem runs before addInventoryItem, which checks no weight on its own.
- Job checks read xPlayer.job.name and the numeric grade; an on-duty check is job.onDuty ~= false, and ESX.DoesJobExist runs before setJob.
- Server callbacks use ESX.RegisterServerCallback and answer on every path; notifications go through xPlayer.showNotification.
Checks on every build
The Lua is compiled as Lua 5.4 with FiveM's syntax extensions understood, then scanned for the mistakes that cost servers money: a client-chosen price or quantity paid out, a purchase without a balance check, string-built SQL, a reward handler with no server-side distance or cooldown check. The manifest is compared with the files, and a NUI page with the messages and callbacks its Lua uses. A failing draft gets one correction before delivery, and the findings are written into the README.
Treat the checks as a safety net, not a promise: run a new resource on a test server first.
Example: an ESX coffee counter
The description, sent with ESX selected:
“Coffee shop counter using ox_target: players buy a coffee for $5 (cash) and receive the 'coffee' item. Show an error if they cannot afford it.”
The purchase handler from the server file it wrote:
local ESX = exports['es_extended']:getSharedObject()
local busy = {}
local lastBuy = {}
local function nearCounter(src)
local ped = GetPlayerPed(src)
if not ped or ped == 0 then return false end
local pos = GetEntityCoords(ped)
for _, c in ipairs(Config.Counters) do
if #(pos - c.coords) <= Config.ServerMaxDistance then
return true
end
end
return false
end
local function itemCount(xPlayer)
local item = xPlayer.getInventoryItem(Config.Item)
return item and item.count or 0
end
ESX.RegisterServerCallback('coffee_counter:buy', function(source, cb)
local src = source
local xPlayer = ESX.GetPlayerFromId(src)
if not xPlayer then return cb({ ok = false, code = 'failed' }) end
if busy[src] then return cb({ ok = false, code = 'cooldown' }) end
local now = GetGameTimer()
if lastBuy[src] and now - lastBuy[src] < Config.PurchaseCooldownMs then
return cb({ ok = false, code = 'cooldown' })
end
busy[src] = true
lastBuy[src] = now
local code = 'failed'
local ok = false
repeat
if not nearCounter(src) then code = 'too_far'; break end
local price = Config.Price
local cash = xPlayer.getMoney()
if cash < price then code = 'not_enough_cash'; break end
if not xPlayer.canCarryItem(Config.Item, 1) then code = 'cannot_carry'; break end
-- ESX removeMoney has no documented return value: verify via balance change
local called = pcall(function() xPlayer.removeMoney(price, 'Coffee purchase') end)
local removed = called and xPlayer.getMoney() == cash - price
if not removed then
local diff = (cash - price) - xPlayer.getMoney()
if called and diff < 0 then
xPlayer.addMoney(-diff, 'Coffee purchase correction')
end
break
end
local before = itemCount(xPlayer)
pcall(function() xPlayer.addInventoryItem(Config.Item, 1) end)
if itemCount(xPlayer) ~= before + 1 then
xPlayer.addMoney(price, 'Coffee purchase refund')
break
end
ok = true
code = 'ok'
MySQL.insert('INSERT INTO coffee_purchases (identifier, item, price) VALUES (?, ?, ?)',
{ xPlayer.getIdentifier(), Config.Item, price })
until true
busy[src] = nil
cb({ ok = ok, code = code })
end)The price is read from the config on the server, the player must stand at a counter, the cash is compared before anything is taken, and the coffee is only handed over when it fits — the money goes back if it does not arrive.
Frequently asked questions
Which ESX version is the code written for?
Current ESX Legacy (es_extended 1.15). Where older ESX differs — the on-duty field, for example — the generated check is written so it still works there.
Does it work with ox_inventory on ESX?
Name ox_inventory or ox_target in your description so the build is written for them. Otherwise it targets the xPlayer item functions ESX provides.
Will it create the database tables?
A script that stores data uses oxmysql with parameterised queries. The README's Database Setup section tells you which SQL to run before the first start.
Can I start from a plan instead of code?
Yes. Plan is free: you see and edit the files, events, config options and dependencies, and only the build itself uses credits.
What happens if a generation fails?
You are not charged. Credits are held when a build starts and only kept when the build is delivered; an incomplete build, or one whose Lua still won't compile after the automatic retry, gives them back.
Build your ESX script
Open ScriptForge, pick ESX and say what the resource should do.
Open ScriptForgeNot ready to start yet?
Leave your email. We only send updates if you tick the box below.