FiveM crafting system: ox_inventory benches and custom crafting

Build crafting on FiveM: ox_inventory crafting benches in data/crafting.lua with ingredients and zones, a custom server-checked recipe, and anti-dupe tips.

Crafting means a player turns some items into another item. You can use the crafting benches built into ox_inventory, or write your own recipe in a script. This article shows both, and then the checks that keep players from duplicating items.

ox_inventory crafting benches

Open ox_inventory/data/crafting.lua. It returns a list of benches. Each bench has a list of craftable items and a place in the world:

lua
return {
    {
        items = {
            {
                name = 'lockpick',
                ingredients = {
                    scrap_metal = 5,
                    plastic = 2,
                },
                duration = 5000,
                count = 1,
            },
            {
                name = 'bandage',
                ingredients = {
                    cloth = 2,
                },
                duration = 3000,
            },
        },
        points = {
            vec3(1087.3, -3099.4, -39.0),
        },
    },
}

What each field does:

Field Meaning
name The item that is produced
ingredients Item names and the count of each one needed
duration Crafting time in milliseconds
count How many of the result you get per craft
points Coordinates where the bench can be used
zones Box zones for ox_target, as an alternative to points

Everything named in name or ingredients must exist in data/items.lua. See adding items to ox_inventory. Restart ox_inventory after editing the file.

Tip: an ingredient value below 1 does not consume the item, it uses that fraction of its durability. A value of 0.1 on a tool with durability uses ten percent of it.

Zones instead of points

A zone is a box the player can target:

lua
zones = {
    {
        coords = vec3(1087.3, -3099.4, -39.0),
        size = vec3(1.5, 1.0, 1.0),
        distance = 1.5,
        rotation = 45.0,
    },
},

Zones need ox_target to be running. Look at the examples in your copy of data/crafting.lua for the exact fields of your version, as the format changes between releases. For restricting a bench to a job, ox_inventory also reads groups on the bench, the same idea as in ox_inventory stashes.

A custom recipe with server checks

If you want your own rules, such as a skill level, a cost or a progress bar of your own, write the recipe in your script and let ox_inventory only handle the items. The important part is that the server does everything:

lua
-- server.lua
local crafting = {}

local recipe = {
    result = 'lockpick',
    ingredients = { scrap_metal = 5, plastic = 2 },
}

lib.callback.register('my_script:craft', function(source)
    if crafting[source] then return false end
    crafting[source] = true

    local ok = false

    -- 1. check every ingredient
    local hasAll = true
    for item, count in pairs(recipe.ingredients) do
        if exports.ox_inventory:Search(source, 'count', item) < count then
            hasAll = false
            break
        end
    end

    -- 2. check the player can carry the result
    if hasAll and exports.ox_inventory:CanCarryItem(source, recipe.result, 1) then
        -- 3. remove, then add
        local removed = {}
        local allRemoved = true
        for item, count in pairs(recipe.ingredients) do
            if exports.ox_inventory:RemoveItem(source, item, count) then
                removed[item] = count
            else
                allRemoved = false
                break
            end
        end

        if allRemoved and exports.ox_inventory:AddItem(source, recipe.result, 1) then
            ok = true
        else
            -- give back what was removed
            for item, count in pairs(removed) do
                exports.ox_inventory:AddItem(source, item, count)
            end
        end
    end

    crafting[source] = nil
    return ok
end)

The client asks, plays the progress bar, and only reads the answer:

lua
-- client.lua
if lib.progressBar({ duration = 5000, label = 'Crafting' }) then
    local ok = lib.callback.await('my_script:craft', false)
    lib.notify({ description = ok and 'Crafted' or 'Missing items', type = ok and 'success' or 'error' })
end

For the callback and notification functions, see server callbacks on ESX, QBCore and ox_lib.

Anti-dupe tips

Crafting is a classic place for item duplication, because items are removed and added in two steps. Keep these rules:

  • Server only. The server checks ingredients and gives the result. The client sends "I want to craft recipe X", never counts or item names it made up.
  • Check, then remove, then add. Never add the result before the removal succeeded. If the add fails, give the ingredients back.
  • One craft at a time. A flag per player, like crafting[source] above, stops a spammed event from running the recipe several times in parallel.
  • Check CanCarryItem first. A full inventory is the usual reason AddItem fails, and the player would lose the ingredients.
  • Check the distance. If the recipe belongs to a bench, compare the player's coordinates to the bench on the server.
  • Clear state on drop. Reset the flag when a player leaves, so a disconnect in the middle does not lock the recipe.
lua
AddEventHandler('playerDropped', function()
    crafting[source] = nil
end)

For more on protecting events, see securing server events.

Checklist

Symptom Fix
Bench not appearing Edit data/crafting.lua, then restart ox_inventory
Recipe errors on start An item in name or ingredients is missing from data/items.lua
Zone does nothing ox_target must be running, and the zone fields must match your version
Ingredients lost, no result Check CanCarryItem first and refund when AddItem fails
Items duplicated Move the whole recipe to the server, add a one-at-a-time flag
Craft works from far away Check the player's distance to the bench on the server

Quick answers

How do I add a crafting bench to ox_inventory?

Add an entry to ox_inventory/data/crafting.lua with items (each with ingredients and duration) and either points or zones for where the bench stands. Restart ox_inventory.

Can a recipe use part of an item's durability?

Yes. An ingredient value below 1 uses that fraction of the durability of an item that has it, rather than consuming the item.

How do I stop players from duplicating items while crafting?

Do the whole recipe on the server: check the ingredients, remove them, then add the result, and refuse to start a second craft while one is running for that player.

Scripts that skip this problem

Item Creator V2Create usable items with animations, props, effects and more β€” without writing code.View script β†’Shop CreatorBuild a shop in under a minute β€” owners, employees, vaults and robberies included.View script β†’Quest CreatorA visual editor for quests and NPC dialogues, built node by node in game.View script β†’

Keep reading