Introduction
The RSG Inventory system (rsg-inventory) is a comprehensive, weight-based inventory management system for RedM. It provides players with persistent inventory storage, item management, drops, stashes, and shops.
Version : 2.7.1
RSG Inventory is tightly integrated with rsg-core and rsg-weapons. All three work together to provide a complete item and weapon management system.
Core Features
📊 Weight & Slot System
Weight-Based : Every item has a weight value, and players have a maximum carry capacity
Slot-Based : Inventory is divided into slots (default: 25 slots)
Configurable : Weight and slots can be adjusted per player via PlayerData
🎒 Inventory Types
The system supports multiple inventory types:
Player Inventory - Personal inventory for each player
Stashes - Persistent storage locations (houses, gang storage, etc.)
Drops - Ground items that can be picked up
Shops - Vendor inventories for purchasing items
🔄 Item Management
Add/Remove Items : Server-side functions to modify inventory
Item Transfer : Move items between inventories
Item Info : Custom metadata for items (serial numbers, quality, etc.)
Item Decay : Time-based item quality degradation
⚔️ Weapon Integration
Weapons are stored as inventory items with type 'weapon'
Weapon-specific data (serial number, quality, ammo) stored in item info
Seamless integration with rsg-weapons for durability and repair
How It Works
Item Structure
Every item in the inventory follows this structure:
{
name = 'bread' , -- Item identifier (from shared/items.lua)
label = 'Bread' , -- Display name
amount = 5 , -- Quantity
type = 'item' , -- Type: 'item' or 'weapon'
slot = 3 , -- Inventory slot number
weight = 200 , -- Weight per item (in grams)
info = { -- Custom metadata
quality = 100 , -- Item quality (0-100)
lastUpdate = 1234567890 -- Timestamp for decay
},
unique = false , -- If true, each item takes a slot
useable = true , -- If true, can be used
image = 'bread.png' , -- Image file
description = 'Fresh bread' , -- Item description
shouldClose = true , -- Close inventory on use
combinable = {} -- Crafting combinations
}
Weapon Item Structure
Weapons have additional information:
{
name = 'weapon_revolver_cattleman' ,
label = 'Cattleman Revolver' ,
amount = 1 ,
type = 'weapon' ,
slot = 1 ,
weight = 2000 ,
info = {
serie = 'ABC12XYZ34DEF56' , -- Unique serial number
quality = 85 , -- Weapon condition (0-100)
ammo = 6 -- Current ammo loaded
},
unique = true ,
useable = false ,
image = 'weapon_revolver_cattleman.png'
}
Weight System
How Weight Works
Items have a base weight value (in grams)
Total weight = item.weight * item.amount
Player carry capacity defined in PlayerData.weight (default: 35000g = 35kg)
Example Weight Calculation
-- Player has:
-- 5x Bread (weight: 200g each) = 1000g
-- 2x Water Canteen (weight: 500g each) = 1000g
-- 1x Revolver (weight: 2000g) = 2000g
-- Total: 4000g (4kg)
local totalWeight = exports [ 'rsg-inventory' ]: GetTotalWeight ( Player . PlayerData . items )
-- totalWeight = 4000
local maxWeight = Player . PlayerData . weight
-- maxWeight = 35000
local freeWeight = maxWeight - totalWeight
-- freeWeight = 31000 (31kg available)
If a player’s inventory is full (weight or slots), the system will automatically drop items on the ground instead of deleting them!
Slot System
How Slots Work
Each inventory has a maximum number of slots
Each item occupies one slot, regardless of quantity
Exception : Unique items always take one slot each
-- Player slots: 25
-- Non-unique items stack in one slot:
-- Slot 1: 50x Bread (one slot used)
-- Unique items take individual slots:
-- Slot 2: Revolver #1 (serial: ABC123)
-- Slot 3: Revolver #2 (serial: XYZ789)
-- Each weapon takes its own slot
Checking Slots
local slotsUsed , slotsFree = exports [ 'rsg-inventory' ]: GetSlots ( source )
print ( 'Player has ' .. slotsUsed .. ' slots used and ' .. slotsFree .. ' slots free' )
Item Decay System
The inventory supports time-based item quality degradation.
How Decay Works
Items with the decay property in shared/items.lua will lose quality over time:
-- In rsg-core/shared/items.lua
[ 'bread' ] = {
name = 'bread' ,
label = 'Bread' ,
weight = 200 ,
type = 'item' ,
image = 'bread.png' ,
unique = false ,
useable = true ,
shouldClose = true ,
combinable = nil ,
description = 'Fresh baked bread' ,
decay = 2.0 -- Decay rate (days to go from 100 to 0 quality)
},
Quality : Ranges from 0-100
Decay Rate : Time it takes for quality to reach 0
Auto-Remove : Items at 0 quality are automatically removed on inventory load
Quality Effects
100-75% : Fresh/Good condition
74-50% : Decent condition
49-25% : Poor condition
24-1% : Very poor condition
0% : Item is destroyed/unusable
Decayed items (0% quality) are permanently deleted when the inventory is loaded. Players cannot recover them!
Ground Drops
How Drops Work
When items are dropped (or forced out of full inventory), they spawn on the ground as physical objects.
Features:
Visual Object : A physical bag/satchel spawns at the location
Persistence : Drops remain until picked up or server restart
Network Synced : All players can see and interact with drops
Weight Limit : Drops can hold multiple items up to weight limit
Drop Properties
{
id = 'drop_12345' , -- Unique drop identifier
coords = vector3 ( x , y , z ), -- Drop location
items = {}, -- Array of items in drop
isOpen = false , -- If someone is accessing it
label = 'Ground Stash' , -- Display name
maxweight = 250000 , -- Max weight (default: 250kg)
slots = 50 -- Number of slots
}
Stashes
Stashes are persistent storage locations that can be accessed by multiple players (with proper permissions).
Stash Types
Player Stashes : Personal storage (houses, apartments)
Job Stashes : Shared storage for job members
Gang Stashes : Shared storage for gang members
Public Stashes : Accessible by anyone
Stash Properties
{
identifier = 'house_123' , -- Unique stash ID
label = 'House Storage' , -- Display name
maxweight = 500000 , -- Max weight (500kg)
slots = 50 , -- Number of slots
items = {}, -- Stored items
isOpen = false -- Access control
}
Creating a Stash
-- SERVER SIDE
exports [ 'rsg-inventory' ]: CreateInventory ( 'house_123' , {
label = 'House Storage' ,
maxweight = 500000 ,
slots = 50
})
Opening a Stash
-- SERVER SIDE
exports [ 'rsg-inventory' ]: OpenInventory ( source , 'house_123' , {
label = 'House Storage' ,
maxweight = 500000 ,
slots = 50
})
Shops
The shop system allows players to purchase items from vendors.
Shop Features
Item Pricing : Each item has a defined price
Stock Management : Items can have limited or unlimited stock
Job Restrictions : Shops can be restricted to specific jobs
Gang Restrictions : Shops can be restricted to specific gangs
Shop Structure
Shops are defined server-side and opened via exports:
-- SERVER SIDE
local shopItems = {
{ name = 'bread' , price = 5 , amount = 50 },
{ name = 'water' , price = 3 , amount = 100 },
{ name = 'bandage' , price = 10 , amount = 25 }
}
-- Open shop for player
exports [ 'rsg-inventory' ]: OpenShop ( source , 'general_store' , shopItems )
Database Storage
Player Inventory
Player inventories are stored in the players table:
CREATE TABLE IF NOT EXISTS `players` (
`citizenid` VARCHAR ( 50 ) NOT NULL ,
`inventory` LONGTEXT DEFAULT '[]' ,
-- ... other columns
PRIMARY KEY ( `citizenid` )
);
The inventory column stores a JSON array of items.
Stash Inventory
Stashes are stored in the inventories table:
CREATE TABLE IF NOT EXISTS `inventories` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`identifier` VARCHAR ( 50 ) NOT NULL ,
`items` LONGTEXT DEFAULT '[]' ,
PRIMARY KEY ( `id` ),
UNIQUE KEY `identifier` ( `identifier` )
);
Common Use Cases
Example 1: Give Player an Item
-- SERVER SIDE
local Player = RSGCore . Functions . GetPlayer ( source )
if Player then
local canAdd = exports [ 'rsg-inventory' ]: CanAddItem ( source , 'bread' , 5 )
if canAdd then
exports [ 'rsg-inventory' ]: AddItem ( source , 'bread' , 5 , nil , nil , 'shop-purchase' )
lib . notify ({ description = 'You bought 5 bread' , type = 'success' })
else
lib . notify ({ description = 'Inventory full!' , type = 'error' })
end
end
Example 2: Remove Item from Player
-- SERVER SIDE
local hasItem = exports [ 'rsg-inventory' ]: HasItem ( source , 'bread' , 3 )
if hasItem then
exports [ 'rsg-inventory' ]: RemoveItem ( source , 'bread' , 3 , nil , 'consumed' )
lib . notify ({ description = 'You ate 3 bread' , type = 'success' })
else
lib . notify ({ description = 'You don \' t have enough bread' , type = 'error' })
end
Example 3: Check Player’s Items
-- SERVER SIDE
local breadCount = exports [ 'rsg-inventory' ]: GetItemCount ( source , 'bread' )
print ( 'Player has ' .. breadCount .. ' bread' )
local breadItem = exports [ 'rsg-inventory' ]: GetItemByName ( source , 'bread' )
if breadItem then
print ( 'Bread quality: ' .. breadItem . info . quality .. '%' )
print ( 'In slot: ' .. breadItem . slot )
end
Example 4: Create House Stash
-- SERVER SIDE
RegisterNetEvent ( 'housing:server:openStash' , function ( houseId )
local src = source
local Player = RSGCore . Functions . GetPlayer ( src )
if not Player then return end
-- Check if player owns house
local hasAccess = -- ... your house ownership check
if hasAccess then
local stashId = 'house_' .. houseId
exports [ 'rsg-inventory' ]: OpenInventory ( src , stashId , {
label = 'House #' .. houseId ,
maxweight = 500000 ,
slots = 50
})
else
lib . notify ({ description = 'You don \' t have access!' , type = 'error' })
end
end )
Best Practices
Always check capacity before adding items : Use CanAddItem to prevent errors
Never trust client data : Always validate on server before modifying inventory
Use reasons for logging : Always provide a reason parameter for auditing
Batch operations when possible : Don’t add items in a loop, accumulate and add once
Cache item checks : Don’t repeatedly call HasItem for the same item
Clean up stashes : Remove unused stashes to prevent database bloat
Optimize drops : Limit the number of simultaneous drops on the server
Integration with Other Resources
Creating Useable Items
Items can be made useable in rsg-core:
-- SERVER SIDE (in your resource)
RSGCore . Functions . CreateUseableItem ( 'bread' , function ( source , item )
local Player = RSGCore . Functions . GetPlayer ( source )
if not Player then return end
-- Remove the item
if exports [ 'rsg-inventory' ]: RemoveItem ( source , 'bread' , 1 , item . slot , 'consumed' ) then
-- Restore hunger
Player . Functions . SetMetaData ( 'hunger' , Player . PlayerData . metadata . hunger + 25 )
lib . notify ({
description = 'You ate bread and feel less hungry' ,
type = 'success'
})
end
end )
Inventory Events
Listen for inventory events in your resources:
-- SERVER SIDE
AddEventHandler ( 'rsg-inventory:server:itemRemovedFromPlayerInventory' , function ( source , itemName , data , reason , isMove )
print ( 'Player ' .. source .. ' removed ' .. itemName )
print ( 'Amount: ' .. data . amount )
print ( 'Reason: ' .. reason )
end )
Troubleshooting
Inventory Not Saving
Check database connection
Ensure oxmysql is properly configured and connected
Look in server console for SQL errors or Lua errors
Make sure the item is defined in rsg-core/shared/items.lua
Items Disappearing
Common causes:
Item quality reached 0% (decay)
Item not defined in shared items
Database not saving properly
Server crash before save
Inventory Full Errors
When inventory is full:
Items are automatically dropped on the ground
Check ground drops near player
Increase player weight/slots if needed
Next Steps
Need more help? Join the RSG Framework Discord !