RSG Inventory Overview
Complete guide to the RSG Framework inventory system
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.8.4
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
weightvalue (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 slotChecking 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
endExample 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' })
endExample 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)
endExample 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
Tip
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
Performance Considerations
- Batch operations when possible: Don't add items in a loop, accumulate and add once
- Cache item checks: Don't repeatedly call
HasItemfor 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)Item Box Notifications
The inventory:client:ItemBox event shows the small on-screen item pickup/drop popup (item icon with an "add" or "remove" indicator).
From the server, target a specific player with TriggerClientEvent:
-- SERVER SIDE
local Player = RSGCore.Functions.GetPlayer(source)
local item = RSGCore.Shared.Items['bread']
-- Show the "added" item box
TriggerClientEvent('inventory:client:ItemBox', playerId, item, "add")
-- Show the "removed" item box
TriggerClientEvent('inventory:client:ItemBox', playerId, item, "remove")Troubleshooting
Inventory Not Saving
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
- Inventory Functions Reference - Complete list of all inventory exports
- Weapons System - Integration with rsg-weapons
- Item Configuration - How to define items in rsg-core
Need more help? Join the RSG Framework Discord!

