QBCore script generator

QBCore script generator for FiveM

Describe the resource you want for your QBCore server and ScriptForge writes the whole thing — fxmanifest.lua, client and server Lua, config and any NUI page — against the qb-core API, then checks it before you download it.

What it does

ScriptForge is a chat. Pick QBCore, write what the script should do in a few sentences, and send it. The files are written while you watch, and the finished resource comes as a zip with a README that lists the install steps, the dependencies it needs and the results of the automatic checks. If something should change, say so in the same chat and the resource is updated.

You do not have to know every detail up front. Turn on Plan and you first get a free plan: the files, events, config options and dependencies the script will have. Edit it, ask for changes, and only build when it says what you want.

Written for qb-core, not for "any framework"

Every QBCore build is given a framework profile, checked against the qb-core, qb-inventory and qb-banking source, that tells the model how to talk to the framework. Some of its rules:

  • Fetch the core object with exports['qb-core']:GetCoreObject(), once per file, and nil-check QBCore.Functions.GetPlayer(source) in server handlers.
  • Money types are cash, bank and crypto. A qb-core bank account can go negative, so compare Player.PlayerData.money.bank with the price before charging the bank.
  • Move items with Player.Functions.AddItem / RemoveItem or qb-inventory's exports. A negative amount would add items, so a count that comes from the client must be a whole number of at least 1.
  • A usable item removes itself inside its CreateUseableItem callback, because using an item does not consume it.
  • Require qb-target, ox_lib, an inventory resource or oxmysql only when the script actually uses them.

Checked before you get it

Every build is compiled as Lua 5.4 — FiveM's own syntax such as backtick hashes and compound assignment is understood — and scanned by FiveM-specific security rules: payouts that trust a price or amount sent by the client, purchases without a balance check, server-only events exposed to clients, SQL built from strings. The manifest is checked against the files, and a NUI page against the Lua that drives it. A draft that fails is corrected once before you see it, and the report ships in the README so you know what was found.

Automatic checks are not a guarantee. Test a new resource on a development server before you put it on a live one.

Example: a QBCore fishing job

One description, sent with QBCore selected:

“Fishing job: rent a boat at the pier for $500 (returned on boat return), fish at three configurable spots with an ox_lib skill check, and sell fish at the fish market. Fish prices are defined on the server only.”

The fish market's sell callback from the server file it wrote:

server/main.lua
QBCore.Functions.CreateCallback('qb-fishingjob:server:sell', function(source, cb, key, amount)
    local src = source
    local Player, cid = getIdentity(src)
    if not Player then return cb({ ok = false, error = 'Player not loaded.' }) end
    if type(key) ~= 'string' or not FishByKey[key] then return cb({ ok = false, error = 'Unknown fish.' }) end
    if type(amount) ~= 'number' or amount ~= math.floor(amount) or amount < 1 or amount > 1000 then
        return cb({ ok = false, error = 'Invalid amount.' })
    end
    if busy[src] then return cb({ ok = false, error = 'Please wait.' }) end
    if onCooldown(src, 1000) then return cb({ ok = false, error = 'Slow down.' }) end
    local _, coords = pedInfo(src)
    if not coords or not nearMarket(coords) then return cb({ ok = false, error = 'You are not at the fish market.' }) end

    busy[src] = true
    local fish = FishByKey[key]
    local affected = MySQL.update.await('UPDATE fishing_catch SET amount = amount - ? WHERE citizenid = ? AND fish = ? AND amount >= ?', { amount, cid, key, amount })
    if affected ~= 1 then
        busy[src] = nil
        local m = buildMarket(cid)
        m.ok = false
        m.error = 'You do not have that many.'
        return cb(m)
    end

    Player = QBCore.Functions.GetPlayer(src)
    if not Player or Player.PlayerData.citizenid ~= cid then
        MySQL.update.await('UPDATE fishing_catch SET amount = amount + ? WHERE citizenid = ? AND fish = ?', { amount, cid, key })
        busy[src] = nil
        return cb({ ok = false, error = 'Player unavailable.' })
    end

    local payout = fish.price * amount
    local ok, res = pcall(Player.Functions.AddMoney, ServerConfig.SaleAccount, payout, 'fishing-market-sale')
    if not ok then
        print(('[qb-fishingjob] RECONCILE: sale payout error for %s, %dx %s ($%d): %s'):format(cid, amount, key, payout, tostring(res)))
        busy[src] = nil
        return cb({ ok = false, error = 'Payment error. Contact staff.' })
    end
    if res == false then
        MySQL.update.await('UPDATE fishing_catch SET amount = amount + ? WHERE citizenid = ? AND fish = ?', { amount, cid, key })
        busy[src] = nil
        local m = buildMarket(cid)
        m.ok = false
        m.error = 'Payment failed. Your fish were returned.'
        return cb(m)
    end

    MySQL.update.await('DELETE FROM fishing_catch WHERE citizenid = ? AND amount = 0', { cid })
    local m = buildMarket(cid)
    busy[src] = nil
    m.message = ('Sold %dx %s for $%d.'):format(amount, fish.label, payout)
    cb(m)
end)
Real output of the RPForge 2.0 tier, from our benchmark run of 2026-10-03, where the same description went through the production ScriptForge pipeline. Copied as it came out, comments included. The same resource also had fxmanifest.lua, a shared and a server-only config, the client file, the SQL table and a NUI page.

The amount is checked on the server, the player has to be at the market, the price comes from a server-only config, and the fish are taken in one conditional UPDATE before the money is paid — and given back if the payment fails.

Frequently asked questions

Do I need to know Lua to use it?

No. You describe the behaviour in plain words. Knowing a little Lua helps when you read the README or want to adjust a config value, but the resource comes complete — not as a snippet you have to finish.

Which inventory and target resources does it use?

By default the build targets qb-inventory's API for items and only adds a target resource when the script needs one. If your server runs something else — ox_inventory or ox_target, for example — name it in your description so the code is written for it.

Can I change the script after it is generated?

Yes. Ask for the change in the same chat ("add a cooldown", "pay into the bank instead") and the resource is rebuilt with it. Every chat and its files stay in your history.

Can I write the description in another language?

Descriptions in English and Bulgarian are understood. The generated code, its comments and the README are always in English.

What does a script cost?

Generation uses prepaid credits: a pack you buy once, or a monthly plan you can cancel any time. The send button shows the credit price before you send, planning a script is free, and a generation that fails is not charged.

Build your QBCore script

Open ScriptForge, pick QBCore and describe what the script should do.

Open ScriptForge

Not ready to start yet?

Leave your email. We only send updates if you tick the box below.

How we handle your address: Privacy Policy.