JG Docs
Mechanic

Custom Inventory System

Connect another inventory to JG Mechanic

Use this page to connect an inventory that isn't built into JG Mechanic. You will add an adapter to the editable framework files. The adapter passes JG Mechanic's item and stash data to your inventory's API.

You need the API documentation for your inventory. It must provide server-side ways to register usable items, count items, add items and remove items. You also need a client or server API for opening stashes.

A server event that doesn't return a value can't be used to count items. Framework.Server.HasItem needs the count before it can continue.

What the adapter must do

JG Mechanic already provides the values in the table below. Your code only needs to pass them to the matching inventory API and normalise its result.

JG Mechanic functionRuns onValues availableRequired result
Framework.Server.RegisterUsableItemServeritemName, cbRun cb(src) when the player uses the item
Framework.Server.HasItemServersrc, itemName, qtySet itemCount to the amount the player has
Framework.Server.GiveItemServersrc, itemName, qtyAdd the items, or return false when the inventory rejects them
Framework.Server.RemoveItemServersrc, itemName, qtyRemove the items, or return false when the inventory rejects them
Framework.Client.OpenInventoryStashClientstashId, stashNameOpen the named stash
Framework.Server.OpenInventoryStashServersrc, stashId, stashNameOpen the named stash when your inventory uses a server API

src is the player's server ID. Keep that value separate from a character ID, licence or inventory owner ID unless your inventory API specifically requires one of those values.

The examples use these placeholder export names:

  • CreateUsableItem
  • GetItemCount
  • AddItem
  • RemoveItem
  • OpenStash
  • RegisterStash

Replace each placeholder with the exact export or event from your inventory's documentation. Keep the JG Mechanic variable names shown in the examples.

Build the adapter

Find the inventory APIs

Open your inventory's API documentation and find the server functions for:

  • registering a usable item
  • getting an item or item count for a player
  • adding an item
  • removing an item

Then find its stash documentation. Check whether stashes open on the client or server and whether they must be registered before they can open.

Pay attention to argument order and return values. For example, your inventory documentation might show:

local count = exports["inventory-name"]:GetItemCount(playerId, item)

The same call inside JG Mechanic uses its existing variable names:

itemCount = exports[Config.CustomInventory]:GetItemCount(src, itemName) or 0

Here, playerId becomes src and item becomes itemName. or 0 makes a missing item count as zero instead of nil.

Configure the inventory resource

Back up these files before editing them:

  • config/config.lua
  • framework/sv-functions.lua
  • framework/cl-functions.lua
  • server/sv-shops-stashes.lua

Keep the backup outside the jg-mechanic folder.

Open config/config.lua. Set Config.Inventory to custom, then add Config.CustomInventory directly below it:

config/config.lua
Config.Inventory = "custom"
Config.CustomInventory = "your-inventory-folder"

Replace your-inventory-folder with the exact resource folder name. Config.CustomInventory is used by the adapter code below. JG Mechanic doesn't read it anywhere else.

Make sure the inventory starts before JG Mechanic in server.cfg:

server.cfg
ensure your-inventory-folder
ensure jg-mechanic

If either resource is started through a folder such as ensure [standalone], keep that existing line. The inventory still needs to start first.

Register usable items

Open framework/sv-functions.lua and find:

framework/sv-functions.lua
function Framework.Server.RegisterUsableItem(itemName, cb)

Inside that function, find the existing branch that starts with:

framework/sv-functions.lua
elseif Config.Framework == "QBCore" or Config.Inventory == "qb-inventory" then

Paste this block directly above that branch:

framework/sv-functions.lua
elseif Config.Inventory == "custom" then
  exports[Config.CustomInventory]:CreateUsableItem(itemName, function(src)
    cb(src)
  end)

Replace CreateUsableItem with your inventory's server export. The callback must pass the player's server ID to cb.

If your inventory supplies the server ID in a table, adapt only the callback. For example, if it calls your handler with data.source:

framework/sv-functions.lua
elseif Config.Inventory == "custom" then
  exports[Config.CustomInventory]:CreateUsableItem(itemName, function(data)
    cb(data.source)
  end)

Keep the new elseif aligned with the existing elseif lines. Don't add another end.

Return the item count

In framework/sv-functions.lua, find:

framework/sv-functions.lua
function Framework.Server.HasItem(src, itemName, qty)

Inside that function, paste this block directly above the existing QBCore branch:

framework/sv-functions.lua
elseif Config.Inventory == "custom" then
  itemCount = exports[Config.CustomInventory]:GetItemCount(src, itemName) or 0

Replace GetItemCount and change the argument order if your inventory requires it. This branch must set itemCount to a number. Don't return from the branch. The rest of Framework.Server.HasItem compares itemCount with qty and displays the missing-item notification.

If your inventory returns an item table instead of a number, read the amount from that table:

framework/sv-functions.lua
elseif Config.Inventory == "custom" then
  local item = exports[Config.CustomInventory]:GetItem(src, itemName)
  itemCount = item and item.amount or 0

Replace GetItem and item.amount with the names used by your inventory.

If the inventory only returns true or false from a HasItem export, pass qty to it and convert the result:

framework/sv-functions.lua
elseif Config.Inventory == "custom" then
  local hasItem = exports[Config.CustomInventory]:HasItem(src, itemName, qty)
  itemCount = hasItem and qty or 0

Add items

In framework/sv-functions.lua, find:

framework/sv-functions.lua
function Framework.Server.GiveItem(src, itemName, qty)

Inside that function, paste this block directly above the existing QBCore branch:

framework/sv-functions.lua
elseif Config.Inventory == "custom" then
  local added = exports[Config.CustomInventory]:AddItem(src, itemName, qty)
  if added == false then return false end

Replace AddItem and change the argument order to match your inventory.

The == false check is intentional. It supports inventories that return false when full and inventories that don't return a value on success. If your API uses a different failure value, convert that result to false here.

Don't add return true to this branch. The function already returns true after the inventory branches.

Remove items

In framework/sv-functions.lua, find:

framework/sv-functions.lua
function Framework.Server.RemoveItem(src, itemName, qty)

This function already calls Framework.Server.HasItem before trying to remove anything. Inside the inventory branches, paste this block directly above the existing QBCore branch:

framework/sv-functions.lua
elseif Config.Inventory == "custom" then
  local removed = exports[Config.CustomInventory]:RemoveItem(src, itemName, qty)
  if removed == false then return false end

Replace RemoveItem and change the argument order to match your inventory. As with GiveItem, convert the inventory's failure result to false and let the existing function return true after a successful removal.

Open stashes

Use one of the two options below. Don't add both.

Client API

Use this option when your inventory documentation opens a stash from client code.

Open framework/cl-functions.lua and find:

framework/cl-functions.lua
function Framework.Client.OpenInventoryStash(stashId, stashName)

Paste this block directly above the final else in that function:

framework/cl-functions.lua
elseif Config.Inventory == "custom" then
  exports[Config.CustomInventory]:OpenStash(stashId, stashName)

Replace OpenStash and its arguments with the client API from your inventory. Use stashId as the unique stash identifier and stashName as the display label.

Server API

Use this option when your inventory opens a stash from server code.

In framework/cl-functions.lua, paste this block directly above the final else in Framework.Client.OpenInventoryStash:

framework/cl-functions.lua
elseif Config.Inventory == "custom" then
  TriggerServerEvent("jg-mechanic:server:open-inventory-stash", stashId, stashName)

The event already exists in JG Mechanic. Don't register another copy.

Then open framework/sv-functions.lua and find:

framework/sv-functions.lua
function Framework.Server.OpenInventoryStash(src, stashId, stashName)

Paste this block directly above the final else:

framework/sv-functions.lua
elseif Config.Inventory == "custom" then
  exports[Config.CustomInventory]:OpenStash(src, stashId, stashName)

Replace OpenStash and its arguments with the server API from your inventory. If the inventory uses a server event, replace the export line with its TriggerEvent call.

Register stashes if required

Skip this step when your inventory creates a stash the first time it opens.

If the inventory has a separate RegisterStash API, open server/sv-shops-stashes.lua. Find the CreateThread under the -- Stashes comment. It currently registers stashes for ox_inventory.

Add this elseif branch after the complete ox_inventory branch and before the end that closes the inventory check:

server/sv-shops-stashes.lua
elseif Config.Inventory == "custom" then
  for mechanicId, location in pairs(Config.MechanicLocations) do
    for stashIndex, stash in ipairs(location.stashes or {}) do
      local stashId = mechanicId .. "_" .. stash.name:gsub(" ", "_"):lower() .. "_" .. stashIndex
      local label = stash.name
      local slots = stash.slots or 50
      local maxWeight = stash.weight or 50000

      exports[Config.CustomInventory]:RegisterStash(stashId, label, slots, maxWeight)
    end
  end

Replace RegisterStash and its argument order with the inventory's server API. The generated stashId matches the ID passed to Framework.Client.OpenInventoryStash.

Some inventories require coordinates, an owner or allowed groups when registering a stash. The current stash coordinates are available as stash.coords. Add the other values only when your inventory API requires them.

Add the items and images

The source item definitions are in jg-mechanic/install/inventory. Start with the file closest to your inventory's item format:

  • ox_inventory-items.lua for table-based item definitions
  • qb-core-items.lua for QBCore-style item definitions
  • esx-items.sql for database item definitions
  • quasar-inventory-items.lua for Quasar-style definitions

Convert the entries into your inventory's format. Keep each item name unchanged because JG Mechanic refers to those exact names in code.

If your inventory has a usable-item flag, enable it for:

  • lighting_controller
  • stancing_kit
  • nitrous_bottle
  • cleaning_kit
  • repair_kit
  • duct_tape
  • mechanic_tablet

Copy every image from jg-mechanic/install/inventory/images into the item-image folder used by your inventory.

Check the adapter

Before restarting, check each file:

  • config/config.lua contains Config.Inventory = "custom" and the correct resource folder in Config.CustomInventory.
  • framework/sv-functions.lua has custom branches in RegisterUsableItem, HasItem, GiveItem and RemoveItem.
  • framework/cl-functions.lua has one custom stash branch.
  • framework/sv-functions.lua also has a custom stash branch when you chose the server option.
  • server/sv-shops-stashes.lua has a custom registration branch only when the inventory requires one.
  • Every new elseif lines up with the existing branches and remains inside its function.
  • You didn't add or remove an end around an inventory branch.
  • Every placeholder export name has been replaced unless it is the real name used by your inventory.

Search all four files for Config.Inventory == "custom". You should find at least five branches: four server item branches and one client stash branch.

Test each operation

Fully restart your server and watch the console for errors from jg-mechanic or your inventory.

Test the adapter in this order:

  1. Give yourself a repair_kit and use it. The repair action should start. This tests usable-item registration and the callback's src value.
  2. Give yourself one engine_oil, then service a vehicle. JG Mechanic should detect it and remove it. This tests both the count and remove calls.
  3. Buy an item from a mechanic shop. The item should appear in your inventory. Fill the inventory first and repeat the purchase to confirm a rejected add returns false.
  4. Open a mechanic stash. Deposit an item, close the stash, then open it again. The same item should still be there.
  5. Restart the server and open the same stash again. If it is empty or has a different ID, compare the ID used by the registration and open calls.

Troubleshooting

Last updated 26 July 2026

On this page