Skip to main content
Inventory 3.0Deprecated

Installation

Configure the inventory system, framework functions, vehicle storage, items, shops and death events.

Required dependencies

FiveM artifact 4752+

Required for Lua 5.4 and escrow support.

MySQL

Database storage used by the inventory resource.

CFX baseevents

Default death-event integration; replaceable with another server-side event.

Configuration guide

danger

This resource requires additional configuration. Follow each section below to connect it to your server systems.

Adding faction safes

shared/config.lua
Config.FactionSafes = {
{
name = 'police',
position = vector3(3324.2, 5166.1, 18.4), -- Safe position
size = 100, -- Maximum safe weight
slots = 40, -- Maximum safe slots
header = 'Police-Safe', -- Inventory header name (when opened)
distance = 3 -- Distance to be able to open.
}
}

Vehicles and database rows

Q: Why do I have to define the SQL rows here?
A: The previous version loaded vehicle inventories when they were opened. It relied on the client to identify the vehicle type, and the weight was assigned afterward on the server.

info

Inventories are now loaded together with the database data. This makes it easier to identify each vehicle model and validate its plate safely.

shared/config.lua
Config.VehicleMysql = {
tablename = 'vehicles', -- Table name of the vehicles
platerow = 'plate', -- Whats the row name of the plate inside the 'vehicles' table?
modelrow = 'model', -- Whats the row name of the model inside the 'vehicles' table?
trunkSlots = 40, -- Default value if vehicle model is not present in Config.Vehicles, slots amount for vehicle inventories (TRUNK)
gloveboxSlots = 40, -- Default value if vehicle model is not present in Config.Vehicles, Slots amount for vehicle inventories (GLOVEBOX)
trunkSize = 100, -- Default value if vehicle model is not present in Config.Vehicles
gloveboxSize = 10 -- Default value if vehicle model is not present in Config.Vehicles
}

Death event

If you do not use the default CFX baseevents resource, identify the event provided by your player-death system and configure its name here.

danger

If this is not configured correctly, inventory items will not be deleted when the player dies.

shared/config.lua
Config.DeathEvent = 'baseevents:onPlayerDied'

Modify the GetPlayerFaction function

Connect this function to your faction getter. Otherwise, players will not be able to open faction safes.

server/server-utils.lua
--- Get the Player's Faction
---@param source string | number PlayerSrc
---@return string
Utils.GetPlayerFaction = function(source)
return 'police'
end

Registering items

  • You do not need to add every argument to the functions.
  • Omitted arguments use their default values.
shared/shared-items.lua
-- Example(s)
ItemController.registerItem('bread', "Bread", false, true, false, 5, "Bread is tasty..")
ItemController.registerItem('weapon_pistol', 'Pistol', true, false, true, 2.0)
ItemController.registerItem('cabbage', 'Cabbage')

--- Registers items so they can be added later.
---@param item string
---@param formatname string
---@param tradable boolean Whether the item can be traded and dropped.
---@param stackable boolean Whether the item can be stacked.
---@param candelete boolean Whether the system can delete the item on death.
---@param weight number Item weight; decimal values such as 0.25 are supported.
---@param description string Optional item description.
---@param defaultVariables table Default variables assigned when the item is added.
ItemController.registerItem = function
(
item,
formatname,
tradable,
stackable,
candelete,
weight,
description,
defaultVariables
)

Registering usable items

  • Before registering a usable-item event, define the item.
  • These events receive two arguments: the player source and the slot being used.
  • To remove an item after use, pass the used slot to the x_y:removeItem function.
shared/shared-items.lua
ItemController.registerUsableItem('bread', function(source, slot)
local playerInventory = StorageController.getPlayerInventory(source)
if playerInventory then
playerInventory:removeItem('bread', 1, slot)
end
end)

-- For weapons
ItemController.registerUsableItem('weapon_pistol', function(source)
TriggerClientEvent(Config.Events.use_weapon, source, 'weapon_pistol')
end)

Declaring new vehicle weight(s) in config

  • Vehicles not present in Config.Vehicles use the default weights and slot counts.
  • You do not need to provide all four table keys. For example, you can configure only the trunk.
shared/config-vehicles.lua
Config.Vehicles = {
blade = {
trunk = 200, -- Maximum weight
glovebox = 150, -- Maximum weight
trunkslots = 20, -- Maximum slots
gloveboxslots = 20 -- Maximum slots
},
infernus = {
trunk = 15
}
}

Using the inventory with multicharacter resources

  • Modify the Utils.GetIdentifier function to match your multicharacter system.
server/server-utils.lua
--- Get the Player's identifier.
---- If you use a custom multicharacter resource, adapt this function to your needs.
---@param source string | number PlayerSrc
---@return string
Utils.GetIdentifier = function(source)
if not Utils.PlayerExist(source) then
return
end

for k, v in pairs(GetPlayerIdentifiers(source)) do
if string.match(v, 'license:') then
return string.gsub(v, 'license:', '')
end
end

return "UNKNOWN_IDENTIFIER"
end

Creating shops

caution

Assign a unique UID to every shop. Registering two shops with the same UID causes an error and does not overwrite the first shop.

Why does each shop need a UID?

If you use a shop business system, you can connect its MySQL identifier to the shop UID to track purchases. This requires additional integration.

Adding shop images

cef/img/shopimages/example-drinks.png
server/server-shops-oop.lua
Shops:__init__({
uid = 'grocery-1',
position = vector3(3324.2, 5166.1, 18.4),
shopname = 'Grocery Store',
shopimage = 'grocery.png',
tables = {
{
name = 'Foods',
categoryimage = 'example-category.png',
categories = {
{
name = 'Sandwiches',
items = {
{
item = 'sandwich',
price = 20
}
}
},
{
name = 'Vegetables',
items = {
{
item = 'cabbage',
price = 35
}
}
}
}
}
}
})

Dropped items

You have two options for dropping an item:

  • Drag the item to the Drop button.
  • Press shift and drag it out to the world.
info

Dropped items use a single model because individual models could fall through the world. You can change the shared model if needed.

shared/config.lua
Config.Dropped = {
remainOnGround = 20, -- Minutes to remain the item on the ground.
model = 'ba_prop_battle_ps_box_01', -- Dropped item model.
textRange = 6, -- Text rendering max range.
HudPickupRange = 4, -- Pickup range (to be showed in the CEF/NUI)
keyPickupRange = 2, -- Pickup range with key.
pickupKey = 38, -- Pickup control key (**IsControlJustPressed**)
streamRange = 15, -- Stream range of the objects.
streamUpdater = 1000, -- How often update the streamed objects. (for loop)
EnableDropFromVehicle = false -- Enable or disable dropping out items from any vehicle.
}

Aiming / Raycasting / Trade

info

When AimConfig.enabled is set to true, holding Shift activates the raycasting system.

Raycasting currently supports two features:

  • Trading items with a target player.
  • Dropping items to the ground.

Vehicle targeting was planned but is not included.

shared/config.lua
Config.AimConfig = {
enabled = true, -- Enable or disable the entire raycast system.
enableKey = 21, -- Shift (Sprint / Run)
distance = 5, -- Raycast maximum distance
renderMS = 10, -- Raycast rendering speed MS
EnableDrawLine = true,
EnableSprite = true,
EnableMeters = true,
PlayerSpriteDict = 'mpinventory', -- Texture dictory. What to show when targetPlayer is hovered?
PlayerSpriteName = 'mp_specitem_ped', -- Texture name. What to show when targetPlayer is hovered?
DropSpriteDict = 'mpinventory', -- Texture dictory. What to show when the ground is hovered?
DropSpriteName = 'mp_arrow' -- Texture name. What to show when the ground is hovered?
}

Weapons

Weapons use the same ammunition system, so magazines do not need to be applied separately to the character.

danger

Why is the weapon deleted after the player fires?

This occurs when the item name does not match the weapon name. Register weapons using their actual names, such as weapon_pistol, so GetHashKey can resolve them correctly.

Example of registering weapons & bullets as items.
ItemController.registerItem('weapon_pistol', 'Pistol', true, false, true, 2.0)
ItemController.registerUsableItem('weapon_pistol', function(source)
TriggerClientEvent(Config.Events.use_weapon, source, 'weapon_pistol')
end)
Set weapon bullet types in the config.lua
Config.Weapons = {
['WEAPON_PISTOL'] = '9mm_rounds',
['WEAPON_PISTOL50'] = '556_rounds',
['WEAPON_STUNGUN'] = {},
['WEAPON_KNIFE'] = {}
}

Server event listeners

Config.ServerEvents = {
item_defined = 'aquiver-invapi:item-defined',
player_item_added = 'aquiver-invapi:item-added-toplayer',
player_item_removed = 'aquiver-invapi:item-removed-player'
}

AddEventHandler(Config.ServerEvents.item_defined, function(itemTable)
print(string.format('invapi-item-defined: %s', json.encode(itemTable)))
end)
AddEventHandler(Config.ServerEvents.player_item_added, function(source, itemTable, quantity)
print(string.format('invapi-item-added-toplayer: %s quantity: %s', json.encode(itemTable), quantity))
end)
AddEventHandler(Config.ServerEvents.player_item_removed, function(source, item, quantity)
print(string.format('invapi-item-removed-player: %s, %s', item, quantity))
end)

Server export functions

To add more exports, review the functions available on the inventory class and expose the ones your integration requires.

exports('av_givePlayerItem', av_givePlayerItem)
exports('av_removePlayerItem', av_removePlayerItem)
exports('av_getPlayerItemCount', av_getPlayerItemCount)
exports('av_playerHasItems', av_playerHasItems)
exports('av_playerGetInventoryWeight', av_playerGetInventoryWeight)

function av_givePlayerItem(source, item, quantity, vars, slot)
local playerInventory = StorageController.getPlayerInventory(source)
if playerInventory then
return playerInventory:addItem(item, quantity, vars, slot)
end
end

function av_removePlayerItem(source, item, quantity, slot)
local playerInventory = StorageController.getPlayerInventory(source)
if playerInventory then
return playerInventory:removeItem(item, quantity, slot)
end
end

function av_getPlayerItemCount(source, item)
local playerInventory = StorageController.getPlayerInventory(source)
if playerInventory then
return playerInventory:getItemCount(item)
else
return 0
end
end

function av_playerHasItems(source, itemsTable)
local playerInventory = StorageController.getPlayerInventory(source)
if playerInventory then
return playerInventory:hasItems(itemsTable)
end
end

function av_playerGetInventoryWeight(source)
local playerInventory = StorageController.getPlayerInventory(source)
if playerInventory then
return playerInventory:getInventoryWeight()
end
end