Inventory (server)
Writes, stashes, shops, and hooks. Local reads live on client. Shared types: Inventory.
Argument order: metadata comes before slot on add/remove.
-- community_bridge (metadata LAST on remove)
-- Bridge.Inventory.RemoveItem(src, item, count, slot, metadata)
-- Newb_Bridge (metadata BEFORE slot)
bridge.inventory.removeItem(src, item, count, metadata, slot)Items(itemName?)
Signature: Items(itemName?: string): ItemDefinition | table<string, ItemDefinition> | nil
Same as client.
local def = bridge.inventory.Items('bandage')getImagePath(itemName) / getItemInfo(itemName)
Same signatures as the client. Use these on the server when you need an icon URL or a normalized label/weight/image row.
local info = bridge.inventory.getItemInfo('bandage')addItem(inv, item, count?, metadata?, slot?)
Signature: addItem(inv: InventoryRef, item: string, count?: number, metadata?: table, slot?: number): any
| Param | Type | Description |
|---|---|---|
inv | InventoryRef | Player source or stash id |
item | string | Item name |
count | number? | Default 1 |
metadata | table? | Item metadata |
slot | number? | Target slot |
| Returns | any (provider-specific) |
bridge.inventory.addItem(source, 'water', 2)
bridge.inventory.addItem(source, 'water', 1, { durability = 100 })
bridge.inventory.addItem(source, 'water', 1, { durability = 100 }, 5) -- optional slotremoveItem(inv, item, count?, metadata?, slot?)
Signature: removeItem(inv: InventoryRef, item: string, count?: number, metadata?: table, slot?: number): any
| Param | Type |
|---|---|
inv | InventoryRef |
item | string |
count | number? |
metadata | table? |
slot | number? |
| Returns | any |
bridge.inventory.removeItem(source, 'water', 1)
bridge.inventory.removeItem(source, 'water', 1, { durability = 100 })
bridge.inventory.removeItem(source, 'water', 1, nil, 5) -- metadata omitted, slot 5hasItem(inv, item, count?, metadata?)
Signature: hasItem(inv: InventoryRef, item: string, count?: number, metadata?: table): boolean
| Param | Type |
|---|---|
inv | InventoryRef |
item | string |
count | number? |
metadata | table? |
| Returns | boolean |
if not bridge.inventory.hasItem(source, 'water', 1) then return endgetItemCount(inv, item, metadata?)
Signature: getItemCount(inv: InventoryRef, item: string, metadata?: table): number
| Param | Type |
|---|---|
inv | InventoryRef |
item | string |
metadata | table? |
| Returns | number |
local count = bridge.inventory.getItemCount(source, 'water')getInventoryItems(inv)
Signature: getInventoryItems(inv: InventoryRef): table
| Param | Type |
|---|---|
inv | InventoryRef |
| Returns | occupied slots — slot-keyed map on ox / m-Inventory / one_inventory; sorted array on other adapters. Iterate with pairs. |
local items = bridge.inventory.getInventoryItems(source)setMetadata(inv, slot, metadata)
Signature: setMetadata(inv: InventoryRef, slot: number, metadata: table): boolean?
| Param | Type |
|---|---|
inv | InventoryRef |
slot | number |
metadata | table |
| Returns | boolean? |
bridge.inventory.setMetadata(source, 3, { durability = 50 })getSlot(inv, slot)
Signature: getSlot(inv: InventoryRef, slot: number): InventorySlotItem?
| Param | Type |
|---|---|
inv | InventoryRef |
slot | number |
| Returns | InventorySlotItem? |
local item = bridge.inventory.getSlot(source, 1)clearInventory(inv, keep?)
Signature: clearInventory(inv: InventoryRef, keep?: string | string[]): any
| Param | Type | Description |
|---|---|---|
inv | InventoryRef | Player or stash |
keep | string | string[]? | Item names to keep |
| Returns | any |
bridge.inventory.clearInventory(source)
bridge.inventory.clearInventory(source, { 'id_card' })canCarryItem(inv, item, count?, metadata?)
Signature: canCarryItem(inv: InventoryRef, item: string, count?: number, metadata?: table): boolean
| Param | Type |
|---|---|
inv | InventoryRef |
item | string |
count | number? |
metadata | table? |
| Returns | boolean |
if bridge.inventory.canCarryItem(source, 'water', 5) then
bridge.inventory.addItem(source, 'water', 5)
endregisterStash(id, label, slots, maxWeight, owner?, groups?, coords?)
Signature: registerStash(id: string, label: string, slots: number, maxWeight: number, owner?: string | boolean, groups?: table, coords?: vector3 | vector3[]): boolean?
| Param | Type | Description |
|---|---|---|
id | string | Stash id |
label | string | Display label |
slots | number | Slot count |
maxWeight | number | Max weight |
owner | string | boolean? | Owner scope |
groups | table? | Job/group access |
coords | vector3 | vector3[]? | Stash locations |
| Returns | boolean? |
bridge.inventory.registerStash('pharmacy_storage', 'Pharmacy', 50, 100000, false, { ambulance = 0 })registerHook(eventName, callback, options?) / removeHooks(hookId?)
Signature: registerHook(eventName: string, callback: function, options?: table): number | false | nil
Ox_inventory-style inventory hooks. Real wrap on ox_inventory and m-Inventory. one_inventory, tgiann-inventory, and origen_inventory forward only if that resource exposes registerHook / RegisterHook. Everyone else (qb-inventory, qs-inventory, jaksam_inventory, core_inventory, codem-inventory, qb-framework, esx-framework) logs a debug warning and returns false.
local hookId = bridge.inventory.registerHook('swapItems', function(payload)
print(payload.fromInventory, payload.toInventory)
end, { print = true })
bridge.inventory.removeHooks(hookId)openStash(src, id)
Signature: openStash(src: number, id: string): boolean?
Opens a registered stash. Gate distance/restrictions before calling. Returns false when the adapter has no server-side force-open (codem-inventory). origen_inventory opens via OpenInventoryById; qs-inventory reuses RegisterStash(src, …) (inferred, not a dedicated force-open).
bridge.inventory.openStash(source, 'pharmacy_storage')registerShop(shopType, shopData) / openShop(src, shopType)
Signatures: registerShop(shopType: string, shopData: BridgeShopData): boolean? · openShop(src: number, shopType: string): boolean?
Ox-format shop data. Adapters map inventory / groups / locations as needed. Script-opened shops omit coords on ox/qb so the provider’s proximity check does not block a scripted open.
Shops work on ox_inventory, m-Inventory, one_inventory, tgiann-inventory, and qb-inventory.
Shops return false on origen_inventory, qs-inventory, jaksam_inventory, core_inventory, codem-inventory, qb-framework, and esx-framework.
bridge.inventory.registerShop('247', {
name = '24/7',
inventory = {
{ name = 'water', price = 5 },
{ name = 'sandwich', price = 8 },
},
})
bridge.inventory.openShop(source, '247')Examples
Give a reward
local function giveReward(src, item, count, metadata)
count = count or 1
if not bridge.inventory.canCarryItem(src, item, count) then
bridge.notifications.notify(src, {
description = 'Not enough inventory space.',
type = 'error',
duration = 4000,
})
return false
end
return bridge.inventory.addItem(src, item, count, metadata)
end
giveReward(source, 'water', 3)
giveReward(source, 'weapon_license', 1, {
issued = os.date('%Y-%m-%d'),
holder = bridge.framework.getIdentifier(source),
})Check ingredients before crafting
local recipe = {
{ item = 'iron', count = 2 },
{ item = 'copper', count = 1 },
}
local function hasIngredients(src, ingredients)
for i = 1, #ingredients do
local entry = ingredients[i]
if bridge.inventory.getItemCount(src, entry.item) < entry.count then
return false, entry.item
end
end
return true
end
local function consumeIngredients(src, ingredients)
for i = 1, #ingredients do
local entry = ingredients[i]
if not bridge.inventory.removeItem(src, entry.item, entry.count) then
return false
end
end
return true
end
RegisterNetEvent('myresource:craft', function()
local src = source
local ok, missing = hasIngredients(src, recipe)
if not ok then
bridge.notifications.notify(src, {
description = ('Missing: %s'):format(missing),
type = 'error',
})
return
end
if not consumeIngredients(src, recipe) then return end
bridge.inventory.addItem(src, 'lockpick', 1)
end)Metadata-specific remove
Use when multiple stacks of the same item exist with different metadata (QB-style info, ox-style metadata):
local plate = 'ABC123'
bridge.inventory.removeItem(source, 'vehiclekeys', 1, { plate = plate })
local slotItem = bridge.inventory.getSlot(source, 4)
if slotItem and slotItem.name == 'vehiclekeys' then
bridge.inventory.removeItem(source, 'vehiclekeys', 1, nil, slotItem.slot)
endIterate player inventory
Slots use ox field names (name, count, slot, metadata). Iterate with pairs — ox_inventory keys by slot number, so #items / ipairs miss items in gapped slots.
local items = bridge.inventory.getInventoryItems(source)
for _, slot in pairs(items) do
print(slot.slot, slot.name, slot.count, json.encode(slot.metadata))
end
local function findItemByMetadata(src, itemName, filter)
for _, slot in pairs(bridge.inventory.getInventoryItems(src)) do
if slot.name == itemName and lib.table.contains(slot.metadata or {}, filter) then
return slot
end
end
endStash registration and open
Requires a full inventory provider — not qb-framework / esx-framework.
bridge.inventory.registerStash('mechanic_parts', 'Mechanic parts', 60, 120000, false, {
mechanic = 0,
})
RegisterNetEvent('myshop:openPartsBin', function()
bridge.inventory.openStash(source, 'mechanic_parts')
end)Shop registration
bridge.inventory.registerShop('247', {
name = '24/7',
inventory = {
{ name = 'water', price = 5 },
{ name = 'sandwich', price = 8 },
},
})
bridge.framework.registerCommand('shop', 'Open the 24/7', {}, nil, function(src)
bridge.inventory.openShop(src, '247')
end)Inventory hooks
Real on ox_inventory / m-Inventory; optional wrap on one_inventory / tgiann-inventory / origen_inventory. Callback receives ox-shaped payload; return false to cancel.
local hookId = bridge.inventory.registerHook('swapItems', function(payload)
if payload.fromInventory == 'police_evidence' then
return false
end
end, {
inventoryFilter = { 'police_evidence' },
})
-- Later: bridge.inventory.removeHooks(hookId)