> For the complete documentation index, see [llms.txt](https://docs.gkshop.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.gkshop.org/gksphone-v2/configuration/custom-inventory-1.md).

# Custom Inventory

{% hint style="info" %}
This guide is intended for developers who want to use a custom inventory system that is not natively supported by the phone. You must have basic Lua knowledge to complete these steps.
{% endhint %}

## Custom Inventory Integration Guide

This guide describes how to integrate your custom inventory system with **GKSPHONE v2**.

### 1. Configuration Changes

Tell the phone to use the "custom" inventory setting.

{% stepper %}
{% step %}

### Configuration edit

Open `gksphone/config/config.lua`, find the `Config.Inventory` setting and set it to:

```lua
Config.Inventory = "custom"
```

{% endstep %}
{% endstepper %}

### 2. Server-Side Integration

You need to create a bridge file to handle server-side inventory actions. The phone checks `Config.Inventory`. When set to `"custom"`, the phone expects you to handle the logic. You may need to add your new server script to `fxmanifest.lua` or ensure it's loaded via existing mechanisms.

{% stepper %}
{% step %}

### Create server file

Navigate to `gksphone/server/inventory/` and create `custom.lua` (or modify an existing server script). Ideally, add your custom logic in `gksphone/server/inventory/`.&#x20;
{% endstep %}
{% endstepper %}

The phone expects the following server-side functions to be available (either globally or via the phone calling them). Implement these functions so the phone can interact with your inventory.

#### Required Functions

**SearchPhoneItems(source)**

Searches the player's inventory for the phone item(s). Return a table with item data (or an empty table).

```lua
--- Searches the player's inventory for the specified item.
--- @param source number The player's source ID.
--- @return table item data if found, an empty table otherwise.
function SearchPhoneItems(source)
    local itemData = {}
    -- Your Custom Inventory Logic Here
    -- Example (Pseudo-code):
    -- local playerItems = exports['my-inventory']:GetPlayerItems(source)
    -- for _, item in pairs(playerItems) do
    --     if Config.ItemName[item.name] then
    --         table.insert(itemData, item)
    --     end
    -- end
    return itemData
end
```

**SetItemData(source, item, data)**

Set the metadata of the phone item for a player.

```lua
--- Sets the metadata of an item for a player.
--- @param source number The player's source ID.
--- @param item table The item object (usually returned from SearchPhoneItems).
--- @param data table The metadata table to set.
--- @return boolean True if successful.
function SetItemData(source, item, data)
    -- Your Custom Inventory Logic Here
    -- Example:
    -- exports['my-inventory']:SetMetadata(source, item.slot, data)
    return true
end
```

**UpdateItemData(source, item, datatype, data)**

Update a specific key in the metadata.

```lua
function UpdateItemData(source, item, datatype, data)
    -- Your Custom Inventory Logic Here
    -- Example:
    -- local metadata = item.info or {}
    -- metadata[datatype] = data
    -- exports['my-inventory']:SetMetadata(source, item.slot, metadata)
end
```

**RegisterUsableItem**

Register phone items as usable so they open when clicked. Use your framework's registration method; if unavailable, use the phone-provided wrapper.

```lua
-- Register items defined in Config.ItemName
for itemName, _ in pairs(Config.ItemName) do
    -- Using QBCore or ESX native wrapper usually, 
    -- but for custom inventory, use your inventory's method.
    
    -- Example if using standard ESX/QB wrapper provided by the phone:
    RegisterUsableItem(itemName, function(source, item)
        TriggerClientEvent('gksphone:client:usePhone', source, itemName, item)
    end)
end
```

### 3. Client-Side Integration

If your inventory supports standard ESX/QB item usage events, integration may be automatic. If your inventory uses custom events for adding/removing items, add listeners on the client.

{% stepper %}
{% step %}

### Client file

Navigate to `gksphone/client/inventory/` and create `custom.lua` or modify `client.lua` to listen for your inventory events.
{% endstep %}
{% endstepper %}

#### Handling Item Removal

If the phone item is removed (dropped, used up, etc.), the phone UI should close. Example:

```lua
-- Example Event Listener
RegisterNetEvent('my-inventory:client:ItemRemoved', function(itemName, count)
    if Config.ItemName[itemName] then
        -- Check if it's the currently active phone
        if PhoneUniqueId and LastItemData and LastItemData.name == itemName then
            -- Calls the phone's internal cleanup function
            ItemPhoneDeleted()
        end
    end
end)
```

#### Handling Item Addition

If a player receives a phone, optionally force an update or open it.

```lua
RegisterNetEvent('my-inventory:client:ItemAdded', function(itemName)
    if Config.ItemName[itemName] then
        -- Optional: Logic when receiving a phone
    end
end)
```

### Checklist

* [x] Set `Config.Inventory = "custom"` in `config.lua`
* [x] Implement `SearchPhoneItems` in server-side script
* [x] Implement `SetItemData` / `UpdateItemData` in server-side script
* [x] Register Usable Items for phone models
* [x] Listen for item removal events on client-side

### Reference Example (Based on ox\_inventory)

If your custom inventory uses export-based methods similar to `ox_inventory`, you can structure your server/client code like the examples below.

#### Server-Side Example (`server/inventory/custom.lua`)

```lua
-- Ensure this code only runs if Config.Inventory is "custom"
if Config.Inventory ~= "custom" then return end

print('[GKSPHONE] Custom Inventory Loaded')

-- 1. SearchPhoneItems
-- Checks if the player has the phone item and returns its data (including slot and metadata)
function SearchPhoneItems(source)
    local src = source
    local itemData = {}
    
    -- Assuming your inventory has a search function like exports.my_inv:Search(source, type, item)
    -- Here we iterate through all configured phone items
    for itemName, _ in pairs(Config.ItemName) do
        -- Example: searching for items in 'slots'
        local foundItems = exports.my_inventory:Search(src, 'slots', itemName)
        if #foundItems > 0 then
            itemData = foundItems
            break -- Return the first valid phone found
        end
    end
    
    return itemData
end

-- 2. SetItemData
-- Updates metadata for a specific item slot
function SetItemData(source, item, data)
    local src = source
    if item and item.slot then
        -- Example: exports.my_inventory:SetMetadata(source, slot, data)
        exports.my_inventory:SetMetadata(src, item.slot, data)
        return true
    end
    return false
end

-- 3. UpdateItemData
-- Updates a SINGLE key within the metadata
function UpdateItemData(source, item, datatype, data)
    local src = source
    local metadata = item.metadata or item.info or {}
    metadata[datatype] = data
    
    -- Re-save the complete metadata object
    exports.my_inventory:SetMetadata(src, item.slot, metadata)
end

-- 4. Register Usable Items
-- This allows the item to open the phone when used from inventory
for itemName, _ in pairs(Config.ItemName) do
    -- Standard ESX/QB registration provided by the phone core usually handles "RegisterUsableItem"
    -- providing you trigger the event:
    RegisterUsableItem(itemName, function(source, item)
        TriggerClientEvent('gksphone:client:usePhone', source, itemName, item)
    end)
end
```

#### Client-Side Example (`client/inventory/custom.lua`)

```lua
if Config.Inventory ~= "custom" then return end

-- 1. Item Usage Export (if your inventory supports client-side use exports)
exports("UsePhoneItem", function(data, itemData)
    -- data contains generic info, itemData contains specific item info (metadata, slot)
    TriggerEvent("gksphone:client:usePhone", data.name, itemData)
end)

-- 2. Check Item Count for Jobs (Optional helper)
function JobCenterHasItem(itemName)
    local count = exports.my_inventory:Search('count', itemName)
    return count > 0
end

-- 3. Listen for Item Events (Added/Removed)
-- This is crucial for closing the phone if the item is removed while open
RegisterNetEvent("my_inventory:client:ItemRemoved", function(item, count)
    -- If the currently open phone is removed, close the interface
    if Config.ItemName[item] then
         if PhoneUniqueId and LastItemData and LastItemData.name == item then
            ItemPhoneDeleted()
        end
    end
end)
```
