dependencies { ... } manifest line or a GetResourceState check in a third-party script. Normally you delete that line; for scripts you cannot edit, a scripts-free one-file placeholder ships in extras/ for both frameworks. There is deliberately no provide directive - FXServer’s provide does not alias runtime exports.
DDLib.framework == 'rsg', so on a VORP or standalone server not a single line of it executes. The vorp layer is deliberately not gated: its exports register on every framework and simply go uncalled where nothing looks for them.vorp_inventory
The completevorp_inventory export and event surface is re-registered under both the dd_inventory and the vorp_inventory export names. That includes the legacy vorp_inventoryApi() factory, the vorpCore:* server events and the vorp_inventory:* client events.
Covered exports
VORP semantics are preserved, including the sync-return plus optional-callback duality ofrespond().
Items
Items
addItem, subItem, subItemID, subItemById, getItemCount, getItemDB, getItem, getItemById, getItemByMainId, getItemByName, getItemContainingMetadata, getItemMatchingMetadata, setItemMetadata, getUserInventoryItems, getUserInventoryCapacity, usables, open and close
Capacity, usables, open and close
canCarryItem, canCarryItems, canCarryWeapons, registerUsableItem, unRegisterUsableItem, openInventory, closeInventory, openPlayerInventoryCustom inventories
Custom inventories
registerInventory, removeInventory, isCustomInventoryRegistered, getCustomInventoryData, updateCustomInventoryData, updateCustomInventorySlots, getCustomInventorySlots, setCustomInventoryItemLimit, setCustomInventoryWeaponLimit, addItemsToCustomInventory, addWeaponsToCustomInventory, getCustomInventoryItems, getCustomInventoryWeapons, getCustomInventoryItemCount, getCustomInventoryWeaponCount, removeItemFromCustomInventory, removeWeaponFromCustomInventory, removeCustomInventoryWeaponById, updateCustomInventoryItem, deleteCustomInventory, AddPermissionMoveToCustom, AddPermissionTakeFromCustom, AddCharIdPermissionMoveToCustom, AddCharIdPermissionTakeFromCustom, BlackListCustomAnyWeapons and ammo
Weapons and ammo
createWeapon, giveWeapon, subWeapon, deleteWeapon, getUserInventoryWeapons, getUserWeapon, getWeaponBullets, getWeaponComponents, setWeaponCustomLabel, setWeaponSerialNumber, setWeaponCustomDesc, getUserAmmo, addBullets, subBullets, removeAllUserAmmoLegacy API
Legacy API
vorp_inventoryApi() factory plus the full vorpCore:* and vorp_inventory:Server:* event list from inventoryApiController.lua.Not provided
Not provided
getUserTotalCountItems and getUserTotalCountWeapons (internal in the original), the pickup and drop API, and the client-side weapon-data exports getWeaponDefaultWeight, getWeaponDefaultDesc, getWeaponDefaultLabel, getWeaponsDefaultData, getWeaponAmmoTypes, getAmmoLabel and getServerItem - those read config/weapons.lua and config/ammo.lua tables that do not exist here.Mapping model
- User inventory is the active character’s
personalinventory, withcharIdresolved through the framework bridge. - Item name is
item_definitions.item_name, identical to VORP’sitems.item. The boot-time import guarantees every legacy name exists. - Quantity. VORP stores one row per stack with an
amount; DD Inventory stores one row per physical unit spread over slots. All read paths aggregate by(item_name + metadata)sogetItemCountandgetUserInventoryItemslook like VORP; all write paths handle stacking. - Item id. The VORP crafted id maps to the
item_instances.item_idof the first unit in a stack group.subItemById(src, id, cb, allow, amount)removes that instance plusamount - 1siblings from the same group.
percentage is pinned to 100 and isDegradable to false. type is 'item_weapon' for weapon definitions and 'item_standard' otherwise.
Weapon shape:
weapon_<lowercased vorp name> (item_type = 'weapon', stack_size = 1, auto-registered on first grant). All VORP weapon fields live in the instance metadata.
Item import
On boot, DD Inventory readsSELECT * FROM items and inserts every missing name into item_definitions:
SHOW COLUMNS FROM items, so version differences in VORP’s schema are tolerated. Counts are printed on boot.
Limitations
Weapon equip and holster UX
Weapon equip and holster UX
used and used2, weapon objects, component attachment and pickups. This layer has none of it. Using a weapon item in the DD NUI calls GiveWeaponToPed, SetCurrentPedWeapon and SetPedAmmoByType with the exact argument list vorp_inventory itself used. Holstering, dual-wield attach points, used2 semantics and weapon component visuals are not reproduced.No degradation
No degradation
degradation, percentage, useExpired). DD Inventory has no such concept - every item reports percentage = 100 and isDegradable = false, so vorp_stores prices everything as pristine.Job permissions on custom inventories
Job permissions on custom inventories
PermissionMoveTo and PermissionTakeFrom are stored in compat_custom_inventories.config and returned by getCustomInventoryData, but not enforced: they are keyed by job, and resolving a player’s job would mean calling the host framework directly, which DD Inventory does not do.CharIdPermissionMoveTo and CharIdPermissionTakeFrom are enforced when UsePermissions is set. On registration they are projected onto DD Inventory’s own inventory_rights access control list (put and take respectively), so there is one permission model rather than two competing ones. Clearing UsePermissions, or removing the last character, clears the list and reopens the container. See Per-inventory access control.Also enforced: acceptWeapons, UseBlackList / BlackListItems, whitelistItems / whitelistWeapons (the matching limited* table doubles as the allow-list) and the limitedItems / limitedWeapons per-item count ceilings. All of these are deposit-side rules in VORP, so they gate what a container accepts, not what may be taken back out of it.Capacity model
Capacity model
invCapacity), DD Inventory is slot-based. canCarryItems(src, amount) answers “are there at least amount free slots”. canCarryItem runs the add simulation, which is the accurate answer for the slot model. Scripts that pass a weight to canCarryItems will get a stricter answer than before.Custom inventory limit
Custom inventory limit
limit is a weight capacity for some scripts (vorp_housing passes MaxWeight) and a slot count for others. It is clamped into 10 to 100 slots, because inventories.max_slots is a TINYINT. Houses and stables configured with, for example, MaxWeight = 500 become 100-slot containers.openPlayerInventory
openPlayerInventory
subWeapon limbo
subWeapon limbo
subWeapon detaches a weapon without deleting it, so the id stays resolvable. Detached weapons are parked in an internal inventory registered as __vorp_weapon_limbo__. If that inventory is full the weapon is hard-deleted instead, and the deletion is logged.rsg-inventory
DD Inventory can replacersg-inventory on an RSG server. Your existing rsg-* scripts keep calling exports['rsg-inventory']:AddItem(...) and friends, and DD Inventory answers them.
What an RSG server gets
- DD Inventory’s own NUI, slot model, weight model, rarities, trading and currency bar, in place of the
rsg-inventoryinterface. - All 29
rsg-inventoryserver exports, registered under both thedd_inventoryand thersg-inventoryexport names. - The
rsg-inventoryclient exportHasItem. - A one-way projection of the authoritative inventory into
Player.PlayerData.items, so everyrsg-*script that reads that table, and the client mirror rsg-core derives from it, sees the truth. - An automatic import of
RSGCore.Shared.Itemsintoitem_definitions, re-run whenever rsg-core broadcasts a core object update. - Weapon handling: serial generation in the upstream format,
info.qualityinitialisation,player_weaponsbookkeeping, un-equipping a weapon that leaves the inventory, and dispatch torsg-weapons:client:UseWeapon,UseThrownWeaponandUseEquipmentwith the unmodified RSG item shape.
rsg-core itself is not patched. Every access rsg-core makes to the inventory goes through an export or an event, so a script-free placeholder plus the export registrations is sufficient.
Installation differences
Follow the standard Setup, with three changes.Icons live elsewhere
html/images/, not html/img/items/:rsg-inventory, rsg-core and rsg-weapons resolve item images relatively and need no path change. Only your own scripts that hardcode nui://rsg-inventory/html/images/ need pointing at nui://dd_inventory/img/items/.Keep the name resolvable, if anything still needs it
exports['rsg-inventory']:* call is answered by DD Inventory, so scripts keep working. What still looks for the name is a manifest line dependencies { 'rsg-inventory' }, which stops that resource from starting, and a runtime GetResourceState('rsg-inventory') check, which makes a script quietly skip its inventory path.Normally you just delete that line. If you cannot edit the script, because it is encrypted or escrowed, copy extras/rsg-inventory/ out of this download into resources/[rsg]/. It is a single manifest with no scripts and no files, and it exists only so the name resolves. Nothing to rename, nothing to configure. Start it before dd_inventory.Check four settings before first boot
Server export reference
Call asexports['rsg-inventory']:Name(...) exactly as before, or as exports.dd_inventory:Name(...). Signatures are the ones in rsg-inventory/server/exports.lua. Where the published documentation disagrees with that file, the file wins.
Status values: implemented behaves as upstream for the documented contract. partial works, with a behavioural difference called out in the note. no-op is accepted and returns the upstream return type, but does nothing, because DD Inventory has already done the work.
slots on a stash is clamped to 100, because inventories.max_slots is a TINYINT. RSG’s own default stash size is exactly 100, so only servers that raised it are affected.
CreateShop, OpenShop, RestockShop and DoesShopExist are not supported - see the limitations below.
Client export and events
rsg-inventory:server:closeInventory, :useItem, :updateHotbar, :SetInventoryData, :openPlayerInventory, :openStash, :openDrop, :openVending and the seven :*Trade* events are the transport protocol of the rsg-inventory web interface. DD Inventory ships its own NUI and that interface does not run, so these have no callers.
police-, marshal-, gang-, admin-, evidence-) live only in those NUI event handlers upstream, never in the OpenInventory export. Access control on the export path is the caller’s responsibility upstream and remains so here. If you need it enforced inside the inventory, register a move validator.Item model mapping
RSGCore.Shared.Items is imported into item_definitions on boot and again on RSGCore:Server:UpdateObject. Existing DD Inventory definitions are never overwritten, so your own balance data stays authoritative.
rarity, sell_value, bind_type and equipment_data, which RSG has no concept of. Imported items start on the defaults and can be filled in with /dd_setrarity and registerItemDefinition.
DD Inventory stores one row per physical unit, each carrying a slot. An RSG stack is therefore the set of instances sharing one slot, and the RSG slot is the DD Inventory slot. There is no separate slot projection table and slot addressing is stable across relogs by construction. info maps flat onto the instance metadata column: the metadata object is the info table. That makes DD Inventory’s stack matching, which compares whole metadata objects, equivalent to RSG’s rule that items with different info - specifically different info.quality - do not stack.
RSG weapons are ordinary items, which makes them considerably simpler than VORP’s. Everything that identifies a specific gun lives in info:
Documentation versus source deviations
Every signature was checked against both the published rsg documentation andRexshack-RedM/rsg-inventory at main. Where they disagree, the source code is authoritative. These are the differences found.
AddItem argument order
AddItem argument order
AddItem(source, itemName, amount, info, slot, reason). Source, server/exports.lua:619: AddItem(identifier, item, amount, slot, info, reason).Source wins. slot is the 4th argument and info is the 5th. Confirmed twice inside the upstream repository itself: server/events/callbacks.lua:60 calls Inventory.AddItem(target, item, amount, false, serverInfo, reason), and the published Weapons page shows AddItem(source, 'weapon_revolver_cattleman', 1, nil, weaponInfo). Both put slot before info. The Functions reference agrees with the source. The Overview page is wrong.RemoveItem missing argument
RemoveItem missing argument
RemoveItem(source, itemName, amount, slot, reason). Source, server/exports.lua:761: RemoveItem(identifier, item, amount, slot, reason, isMove).Source wins. The Overview page omits the 6th argument isMove, which controls whether rsg-core:client:RemoveWeaponFromTab fires. The Functions reference lists all six.HasItem input forms
HasItem input forms
HasItem(source, itemName, amount). Source, server/exports.lua:396: items may be a string, an array of names, or a map of name to required amount.Source wins. All three forms are supported here.CanAddItem return values
CanAddItem return values
server/exports.lua:296: returns boolean plus a reason string 'weight' or 'slots'.Source wins. The Functions reference also documents the second return value.UseItem call form
UseItem call form
UseItem(itemName, ...) at server/exports.lua:134, but rsg-core/server/functions.lua:447 calls exports['rsg-inventory']:UseItem(source, item).Upstream contradicts itself, so both forms are accepted. The first argument decides: an item name is never a number, a source always is.OpenInventory third argument name
OpenInventory third argument name
options, server/exports.lua:502 names it data. Cosmetic only, same table.GetSlots on an unknown identifier
GetSlots on an unknown identifier
number, number, but server/exports.lua:251 returns 0, maxSlots when no inventory is found, and maxSlots is nil in that branch. That is an upstream latent bug. This implementation returns 0, 0 for an unknown identifier rather than 0, nil.Limitations
Read this section before you deploy. These are deliberate boundaries, not bugs.No world drops
No world drops
ForceDropItem removes the item from the player and returns false instead of a network id, so a script that calls it directly destroys the item rather than dropping it. AddItem on a full inventory simply fails and returns false rather than force-dropping the overflow, so nothing is lost on that path. The drop-related events (createDrop, updateDrop, GetCurrentDrops, openDrop, setupDropTarget, removeDropTarget) are not provided. If your server runs a death-loot or robbery script that expects world drops, this layer is not sufficient for it.No shops
No shops
CreateShop, OpenShop, RestockShop and DoesShopExist are not provided, and neither is the shop_stock table, restock cron, defaultstock, maxStock or minQuality. DD Inventory has no pricing model, so this is a separate product rather than a compatibility gap.No trade bridge
No trade bridge
rsg-inventory:server:*Trade* events are not reimplemented. DD Inventory ships its own player-to-player trade window, reached through exports.dd_inventory:requestTrade(...).No direct PlayerData.items writes
No direct PlayerData.items writes
Player.PlayerData.items. There is no reverse channel. A resource that writes into Player.PlayerData.items directly and expects the change to persist is not supported: the write is not intercepted, and the next projection overwrites it. Upstream rsg-weapons does exactly this when it degrades or repairs a weapon. Use exports['rsg-inventory']:SetItemData(source, itemName, 'info', info) instead, which is supported and persists. Everything that reads PlayerData.items works unchanged.Quality and decay are passed through, not simulated
Quality and decay are passed through, not simulated
info.quality and info.lastUpdate are created where upstream creates them and then preserved verbatim, so rsg-weapons keeps full control of weapon wear and its repair loop behaves normally. What is not provided is rsg-inventory’s ambient decay: food and other items with a decay value in RSGCore.Shared.Items will not lose quality over time on their own, will never be auto-deleted at zero quality, and the decay<N> stash name suffix has no effect.Job and gang stash prefixes are not enforced
Job and gang stash prefixes are not enforced
police-, marshal-, gang-, admin- and evidence- prefixes inside its own web-interface event handlers, never in the OpenInventory export. That behaviour is mirrored. Register a DD Inventory move validator if you want the rule enforced inside the inventory itself.Money items must be off
Money items must be off
RSGConfig.Money.EnableMoneyItems = true makes rsg-core model cash as inventory items and re-synchronise them on every player data update, which conflicts with DD Inventory’s currency handling. Set it to false. DD Inventory prints a boot warning if it is left on.Weights are in grams
Weights are in grams
DDInvConfig.defaultMaxWeight = 35000.0 to match RSG’s default carry capacity. DD Inventory’s own capacity is then the single authority: GetFreeWeight, CanAddItem and GetSlots answer from DD Inventory’s numbers, including purchased slot expansions, not from Player.PlayerData.weight and .slots.Stash slot ceiling of 100
Stash slot ceiling of 100
inventories.max_slots is a TINYINT. RSG’s own default stash size is 100, so only servers that raised it are affected; larger values are clamped.Character ids are surrogates
Character ids are surrogates
citizenid is a string and DD Inventory’s owner_char_id column is an INT, so the layer mints a stable surrogate integer per citizenid in compat_rsg_citizenids. This is transparent in normal operation, but if you query DD Inventory’s tables by hand, join through that table rather than expecting a citizenid.Not verified against a live RSG server
Not verified against a live RSG server