> ## Documentation Index
> Fetch the complete documentation index at: https://docs.atlasscripts.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Inventory Adapter

> Wire an unsupported inventory into atlasBridge by filling the editable adapter against the normalized contract.

If your inventory is not `ox_inventory`, `qb-inventory`, or `qs-inventory`, fill the **custom inventory adapter** to normalize item operations for every Atlas resource. The shape mirrors the [custom framework adapter](/atlas-bridge/custom-framework) — implement a small set of methods that return the normalized shapes, and consumers call `Bridge.Inventory.*` unchanged.

## How it works

Select the `custom` inventory provider:

```cfg theme={null}
setr atlas:inventory "custom"
```

* Server → `editable/inventory/server.lua`
* Client → `editable/inventory/client.lua`

If your `custom` adapter fails to load, the bridge falls back to the `_default` virtual inventory so the server still boots.

<Warning>
  Every method returns a **single value** — exports cross a VM boundary, so multi-return is lost.
</Warning>

## Normalized shapes

```lua theme={null}
-- a held item (one entry in an item list)
{ name = 'water', count = 3, slot = 5, metadata = {} }   -- slot/metadata only on inventories that support them

-- an item DEFINITION (not a held item)
{ name = 'water', label = 'Water', weight = 100, image = 'water.png', description = 'Refreshing.' }
```

## Server contract

Implement these in `editable/inventory/server.lua`:

<ParamField path="AddItem(src, item, count, metadata?, slot?)" type="→ bool" required>
  Give the player an item. Return `true` on success.
</ParamField>

<ParamField path="RemoveItem(src, item, count, metadata?, slot?)" type="→ bool" required>
  Take an item from the player. Return `true` on success.
</ParamField>

<ParamField path="HasItem(src, item, count?)" type="→ bool" required>
  Whether the player has at least `count` (default 1) of the item.
</ParamField>

<ParamField path="GetItemCount(src, item)" type="→ number" required>
  Total quantity of the item the player holds.
</ParamField>

<ParamField path="CanCarry(src, item, count)" type="→ bool" required>
  Whether the player can receive `count` of the item. Return `true` if your inventory has no capacity limit.
</ParamField>

<ParamField path="GetItems(src)" type="→ item[]" required>
  All held items as the normalized list shape.
</ParamField>

<ParamField path="GetItemInfo(item)" type="→ itemInfo | nil" required>
  The item **definition** `{ name, label, weight, image, description }`, or `nil` if unknown.
</ParamField>

<ParamField path="GetImagePath(item)" type="→ string" required>
  The inventory image URL/path for NUI rendering.
</ParamField>

## Client contract

Implement these in `editable/inventory/client.lua`:

<ParamField path="GetItems()" type="→ item[]" required>
  The local player's held items.
</ParamField>

<ParamField path="HasItem(item, count?)" type="→ bool" required>
  Whether the local player has at least `count` (default 1) of the item.
</ParamField>

<Note>
  Usable-item registration does **not** live here. It is implemented on the framework adapter's `RegisterUsable` method, because most frameworks own item usage — see [Usable items](/atlas-bridge/custom-framework#usable-items) on the custom framework page.
</Note>

## Worked example

A server adapter for a fictional `my_inventory`:

```lua theme={null}
-- editable/inventory/server.lua
-- Enable with:  setr atlas:inventory "custom"

local Inv = exports['my_inventory']

local M = {}

---@return boolean
function M.AddItem(src, item, count, metadata, slot)
    return Inv:AddItem(src, item, count or 1, metadata, slot) == true
end

---@return boolean
function M.RemoveItem(src, item, count, metadata, slot)
    return Inv:RemoveItem(src, item, count or 1, metadata, slot) == true
end

---@return boolean
function M.HasItem(src, item, count)
    return (Inv:GetItemCount(src, item) or 0) >= (count or 1)
end

---@return number
function M.GetItemCount(src, item)
    return Inv:GetItemCount(src, item) or 0
end

---@return boolean
function M.CanCarry(src, item, count)
    return Inv:CanCarryItem(src, item, count or 1) == true
end

---@return table[] items { name, count, slot?, metadata? }
function M.GetItems(src)
    local out = {}
    for _, entry in pairs(Inv:GetInventory(src) or {}) do
        out[#out + 1] = { name = entry.name, count = entry.count, slot = entry.slot, metadata = entry.metadata }
    end
    return out
end

---@param item string @return table|nil { name, label, weight, image, description }
function M.GetItemInfo(item)
    local def = Inv:GetItemData(item)
    if not def then return nil end
    return {
        name = def.name, label = def.label, weight = def.weight or 0,
        image = def.image or (item .. '.png'), description = def.description or '',
    }
end

---@param item string @return string
function M.GetImagePath(item)
    return ('nui://my_inventory/images/%s.png'):format(item)
end

return M
```

And the client adapter:

```lua theme={null}
-- editable/inventory/client.lua
-- Enable with:  setr atlas:inventory "custom"

local Inv = exports['my_inventory']

local M = {}

---@return table[] items { name, count, slot?, metadata? }
function M.GetItems()
    local out = {}
    for _, entry in pairs(Inv:GetPlayerItems() or {}) do
        out[#out + 1] = { name = entry.name, count = entry.count, slot = entry.slot, metadata = entry.metadata }
    end
    return out
end

---@return boolean
function M.HasItem(item, count)
    return (Inv:GetItemCount(item) or 0) >= (count or 1)
end

return M
```

## Test it

<Steps>
  <Step title="Enable">
    Set `setr atlas:inventory "custom"` and `setr atlas:debug "true"`, then restart `atlasBridge`.
  </Step>

  <Step title="Confirm the adapter loaded">
    Look for `[Inventory] server adapter: custom`. If you see `_default`, your stub errored at load.
  </Step>

  <Step title="Round-trip an item">
    Add an item, confirm it appears; remove it, confirm `HasItem` and `GetItemCount` reflect the change.
  </Step>
</Steps>
