Skip to main content

Requirements

No framework is required. DD Inventory calls no framework API of its own, so neither VORP nor RSG is a manifest dependency: dd_lib detects at boot whether vorp_core, rsg-core or neither is running and routes every identity, money and notification call accordingly. Three deployments are supported.
Standalone servers must change one setting before first boot. The default currencyMode = 'framework' has no wallet to read when no framework is present, so every balance reads as zero and every credit or debit fails silently. Set currencyMode = 'native' in config/config.lua. See Currency mode.
dd_lib and dd_shellkit are free and MIT licensed. Both are fully open and editable, nothing in them is encrypted. Grab the zip from the releases page of each and drop the folder into your resources directory.

dd_lib

Detects your framework and routes every money, identity, notification and logging call. Carries config/overrides.lua, where you can re-point any of those calls at your own resources.

dd_shellkit

Hosts the inventory’s browser page, owns NUI focus, and provides the Alt-held cursor mode plus the right-click player context menu the trade flow uses. If you already run dd_hud, dd_shellkit stays passive.
No build step is required - the interface ships pre-built.

Installation

1

Download

Get the latest release from our Tebex store and download it from your Keymaster.
2

Extract

Drop the folders into your resources/ directory. The exact bracket folders do not matter, the load order in server.cfg does.
3

Copy your item icons

Only if you are replacing an existing inventory. With the server stopped, copy its icons across. Skip files that already exist if asked - DD Inventory ships around 980 standard icons.
On a standalone server there is nothing to copy: drop your own PNGs into dd_inventory/img/items/, named after the item.
4

Delete the old inventory

Only if you are replacing one. Delete the whole resources/[VORP]/vorp_inventory/ folder, or resources/[rsg]/rsg-inventory/. Keep a backup zip somewhere outside resources/ if you like. Most servers need nothing else here - if a resource then refuses to start, see “If a script still expects the old inventory” below.
5

Set the load order

Add the block below to server.cfg, after your framework core if you run one, and before any resource that consumes the inventory.
6

Restart

Start the server and watch the console for the first-boot lines.

If a script still expects the old inventory

Skip this on a standalone server, there is no old inventory to miss. Every exports.vorp_inventory:* call, and on RSG every exports['rsg-inventory']:* call, is answered by DD Inventory, so scripts keep working. Two things still look for the name of the deleted resource, and both only matter once you have deleted the folder:
  • a manifest line dependencies { 'vorp_inventory' }, which stops that resource from starting at all
  • a runtime check GetResourceState('vorp_inventory') == 'started', which makes that script quietly skip its inventory path
Normally you just delete the line. In vorp_fishing/fxmanifest.lua, remove 'vorp_inventory' from its dependencies block and it starts again. The same applies to any open script with that check, on either framework. If you cannot edit the script - an encrypted or escrowed paid resource, for example - copy extras/vorp_inventory/ out of this download into resources/[VORP]/, or extras/rsg-inventory/ 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 the placeholder before DD Inventory, and never restart it on a live server: FXServer stops every resource that declares it as a dependency and does not bring them back.

Third-party UIs with hardcoded image paths

VORP only in practice: vorp_menu and vorp_stores, or another UI that shows item images, hardcode the old image URL and need a one-line path change from nui://vorp_inventory/html/img/items/ to nui://dd_inventory/img/items/. On RSG, rsg-inventory, rsg-core and rsg-weapons resolve item images relatively and need no change; only your own scripts that hardcode nui://rsg-inventory/html/images/ do.
vorp_stores/client/client.lua has one occurrence, vorp_menu/html/js/app.js has two. Any other custom UI that loads item images the same way needs the same one-liner.

server.cfg load order

Place this block after your framework core, if you run one, and before any resource that consumes the inventory (stores, banking, housing, stables, jobs, your own scripts):
The order is not cosmetic: dd_lib must be loaded before anything that uses the bridge, dd_shellkit must exist before the inventory registers its panel, and the placeholder, if you needed one, must start before the scripts whose dependency checks it resolves.

First boot

There is no manual SQL to run. On the first start DD Inventory creates all of its tables (inventories, inventory_rights, item_definitions, item_instances, inventory_audit, character_currency, account_sigils) with CREATE TABLE IF NOT EXISTS. What happens next depends on the detected framework. On VORP, watch the console for these lines:
The import runs a few seconds after start, reads your items table, and inserts every name that does not yet exist as an item definition - carrying over label, description, stack limit (-1/unlimited becomes 100), type, weight and the usable flag. Existing DD Inventory definitions are never overwritten, so the import is safe to let run on every boot. The vorp compatibility layer is not gated on the detected framework, so this import is attempted everywhere. On a database with no items table it logs items table not found - skipping VORP item import and moves on, which is the normal line on a standalone or RSG server.

Running without a framework

DD Inventory runs on a bare FXServer, with DDLib.framework detected as standalone. Everything that is purely inventory logic works unchanged. Four consequences follow from there being no framework to ask, and all four are worth planning for before you open the server.
This is the one that bites silently. The money bridge has no wallet to read, so with the default currencyMode = 'framework' every balance reads as zero, and every credit or debit returns a failure code (FRAMEWORK_ADD_FAILED) rather than an error you would notice. Set currencyMode = 'native' and the cash and gold balances live in DD Inventory’s own character_currency table, where they behave exactly as on a framework server. The account-wide premium balance is unaffected either way, it is always DD Inventory-owned.
With no character record to resolve, the bridge falls back to the account’s license as the character id. A standalone server therefore has exactly one inventory per account, and no concept of multiple characters. If you run your own character system, point DDLibOverrides['identity.charId'] at it (see Framework overrides) and multiple characters work normally.
dd_lib has no standalone branch for the character lifecycle: it hooks vorp:SelectedCharacter on VORP and RSGCore:Server:PlayerLoaded on RSG, and on standalone there is no equivalent event to hook. The client gates the open keybind on an active session, and on standalone that session is only marked active by the replay DD Inventory runs a couple of seconds after the resource starts, for players who are connected at that moment. Players who connect later are not covered by that replay.Two ways around it, both server-side: trigger dd_inventory:client:sessionActive towards a player once your own spawn or character flow has finished, or drive the whole lifecycle from your own resource. There is no override key for this hook today.
There is no framework item table to import from, so item_definitions starts empty and /dd_give answers Unknown item until you populate it. Register definitions from your own resource with exports.dd_inventory:registerItemDefinition({ ... }), documented in the developer reference, or insert the rows directly.Notifications fall back to chat:addMessage, which reaches the player on a default FXServer chat resource and does nothing at all if you removed chat. Point DDLibOverrides['notify'] at your own notification resource.

Player controls

Configuration

Upgrading from 2.3.0 or older: the main config moved from shared/config.lua to config/config.lua. Carry your settings over into the new file. The global is still called DDInvConfig, so nothing else changes. shared/config.lua is no longer read.
Everything you configure lives in the config/ folder: config/config.lua holds the global DDInvConfig, config/language.lua holds every piece of player-facing text. Both files - along with the framework compat shim and the NUI host indirection - are escrow-ignored and stay fully readable and editable after purchase.

Slots, weight and grid

config/config.lua
Overweight tiers are matched on carriedWeight / maxWeight. speedPenalty is a percentage, blockSprint additionally disables sprinting. Add, remove or re-tune tiers freely, just keep the ranges contiguous.
The penalties are applied through a stat/buff provider (a dd_buffcore-style export surface). Without one installed nothing breaks: the weight bar and all limits still work, only the movement debuff is skipped.
openKey is a Windows virtual-key code, not a RedM control hash: the key is polled with IsRawKeyDown. The codes you are most likely to want: A value outside 0x01-0xFF is refused at start with a console warning and falls back to I, so a mistyped config never leaves your players without an inventory key. The same code is registered with the panel host, so the key also closes the inventory while the interface has focus. That part covers the letters, the digits, F1-F12, the arrow keys, TAB, ENTER, SPACE, BACKSPACE and ESC. A code outside that set still opens the inventory from the game world, it just cannot close it again from inside the panel, and the console says so once at start. To confirm what the key resolved to, from a client script:

Currency mode

config/config.lua
On a standalone server currencyMode must be 'native'. dd_lib’s money bridge has no framework branch to fall through to, so in 'framework' mode every balance reads as 0 and every credit or debit fails with FRAMEWORK_ADD_FAILED. Nothing throws and nothing is logged as an error, so the symptom is a currency bar stuck at zero rather than a visible failure.
Note for both frameworks: an offline character has no reachable wallet in 'framework' mode, because the framework wallet is only addressable through a connected player object. getCharacterCurrency reports zero for an offline character and addCurrency returns PLAYER_OFFLINE rather than guessing.

Currencies

The three config keys (breakdust, seamshards, sigils) are internal identifiers used by the export API and the database columns. No player ever sees a key - every visible detail comes from the label and icon you set here.
config/config.lua
  • breakdust is the cash wallet. In 'framework' mode this is the framework’s money: VORP currency 0, or rsg-core’s cash account.
  • seamshards is the gold wallet. In 'framework' mode this is the framework’s gold: VORP currency 1, or rsg-core’s gold account. RSG’s bloodmoney is a separate currency and is deliberately never used as a gold stand-in. rsg-core also rejects fractional amounts on gold, so round before crediting if your VORP setup deals in fractions.
  • sigils is the account-wide premium balance, owned by DD Inventory itself (account_sigils table) in both modes and on every framework, and shared across all of a player’s characters. It is keyed on the account license rather than a character, which is why it is the default currency for slot purchases: it works identically on VORP, RSG and standalone.
  • icon selects a built-in inline SVG: 'dollar', 'gold' or 'sigil'.
  • enabled = false hides that currency everywhere - currency bar, trade window, bank controls. Omitting enabled means enabled.

Slot purchase tiers

Prices are in slotPurchaseCurrency.
config/config.lua

Rarities

The list order is the ascending prestige order, and it is also the NUI’s sort priority. Rename labels, change colours, remove entries or add your own keys.
config/config.lua
The key must match the value stored in the rarity column of DD Inventory’s item_definitions, and on VORP also in the rarity column DD Inventory adds to the items table. An item with an unlisted rarity falls back to the first entry’s styling. Assign rarities live with /dd_setrarity <item> <key>, list the valid keys with /dd_rarities.

Sort priority and trade

config/config.lua
maxDistance is enforced on the server, so a modified client cannot trade across the map. It is checked when the trade window is requested, once a second for as long as it stays open, and one last time immediately before the items change hands. Walk out of range and the trade cancels itself for both players with a message. Set it to 0 to switch the distance check off entirely. To see what the server measures, for example when a player reports a trade cancelling on its own:

Bind types and bind restrictions

Which inventory types enforce these rules is configurable, so a custom storage resource gets the same protection just by listing its type:
config/config.lua
A bind_on_equip item becomes bound when your gear or equipment resource calls exports.dd_inventory:setItemBound(itemId) on first equip.

Framework overrides

Every framework call DD Inventory makes goes through dd_lib, and every one of them can be replaced without touching DD Inventory itself. This is how you run a custom notification, economy or identity system, and it is also how you drive DD Inventory from a core that is neither VORP nor RSG. Leave a key at nil to keep the built-in behaviour for the detected framework, so you can override one call and leave the rest alone. Reroute the bridge in @dd_lib/config/overrides.lua:
config/overrides.lua
Available keys: identity.charId, identity.charName, identity.license, money.add, money.remove, money.get, money.onChanged, notify, log - all documented inside the file itself. money.remove must return false when the player cannot pay, because DD Inventory reads that boolean to decide whether a purchase went through.
There is no override key for the character-selection hook. On VORP and RSG the bridge subscribes to the framework’s own event; a core that fires neither has to trigger dd_inventory:client:sessionActive towards the player itself.

Language

Every player-facing string - notifications, admin command feedback and all UI text - lives in config/language.lua. English and German ship out of the box:
config/language.lua
Set DDLanguage = '' to follow a server-wide setting instead, so all your DD resources switch together:
To add your own language, copy a whole Translations['en'] = { ... } block, rename the key to your language code, and translate the values only. Keys must stay identical across blocks, and every %s / %d placeholder must be kept in the same order. Keys with a dot in them (trade.accept, tooltip.label_weight) are the ones shipped to the UI.
Currency names, rarity names and the weight unit are not in the language file - they come from config/config.lua. Item labels come from your items database table.

Admin commands

All commands are registered through ox_lib and gated by the ace object command.<name>, granted to the principal group.admin on first boot. Grant it to anyone else with add_ace <principal> command.<name> allow in server.cfg. All of them work from the server console, RCON and txAdmin as well as in-game; when run from the console the reply is printed to the console instead of being sent as an in-game notification. These commands are DD Inventory’s own and work on every framework. Your framework’s existing admin commands (VORP’s /additem, /giveitem, /setjob, RSG’s equivalents) keep working unchanged, because DD Inventory answers the inventory exports they call. It does not replace or remove any of them.
/dd_give fails with Unknown item: <name> if no item_definitions row exists for that name. Every grant is written to the audit log with a correlation id.
Moves a single item instance (by its item_instances.item_id) into another inventory id. Useful for recovering items that ended up in a stash or bank the owner can no longer reach.
Sets an item definition’s rarity and pushes the change live to every online player holding that item - no restart, no relog.
The write goes to both item_definitions.rarity and, on a VORP install, the framework’s own items.rarity column, so the change survives the item re-import that runs on every boot.Unknown rarity keys are allowed - you may add your own keys to DDInvConfig.rarities later. The command then prints a warning and the NUI falls back to default styling for that item until the key is configured. Error replies: MISSING_ITEM_NAME, MISSING_RARITY_KEY, ITEM_NOT_FOUND.
Prints every configured rarity - key, label and colour - in ascending prestige order. Use it to find the exact key strings /dd_setrarity expects.
If DDInvConfig.enableTestTui = true, a text-mode debug UI registers the extra client commands /dd_invtui, /dd_invtui_close, /dd_invtui_refresh, /dd_invtui_select, /dd_invtui_use, /dd_invtui_drop, /dd_invtui_move. Leave enableTestTui = false in production.

Restarting and updating

  • Restart in dependency order: dd_lib then dd_shellkit then dd_inventory.
  • Restarting dd_shellkit alone is safe - the inventory detects the reloaded shell and re-registers its panel and keybind automatically.
  • Updating DD Inventory itself: restart dd_inventory is enough. Schema migrations and the item import are idempotent and re-run safely on every start.
restart dd_lib stops its dependents. FXServer brings down dd_inventory with it and does not necessarily bring it back - run start dd_inventory (and any other dd_lib consumer) again afterwards. On a live server, prefer a full restart.The same applies to the placeholder, whichever name you installed it under: never restart vorp_inventory or restart rsg-inventory on a live server. FXServer stops dd_inventory alongside it and does not bring it back. If it happens, run start dd_inventory. The placeholder has no code, so there is never a reason to restart it.

Troubleshooting

In the inventory itself: the icons were not copied into dd_inventory/img/items/ - redo the icon copy step and restart dd_inventory (new files also need a refresh first). In third-party UIs such as vorp_menu or vorp_stores: those hardcode the old path nui://vorp_inventory/html/img/items/..., which no longer resolves once the original resource is deleted - apply the one-line path change above.
Two things must be true. First, the item must be flagged usable - check is_usable in item_definitions, which is imported from VORP’s usable column or RSG’s useable field. Second, something has to handle the use: a registerUsableItem callback on VORP, a CreateUseableItem callback on RSG, or a server-side handler registered via registerItemUseHandler on any framework. With neither, the use is allowed but nothing happens.
The import runs at boot. Restart dd_inventory and watch for the VORP item import: N imported line, or its RSG equivalent. Existing definitions are never overwritten, so if you edited an item that was already imported, change it in item_definitions or use registerItemDefinition. On standalone there is no import at all and registerItemDefinition is the only route.
Standalone servers: currencyMode is still on 'framework' and there is no framework wallet to read. Set it to 'native'. On VORP and RSG the same symptom for a single character usually means the character is offline, which is reported as PLAYER_OFFLINE rather than guessed at.
There is no character-selection event on standalone, so the session that gates the keybind is only marked active by the replay that runs shortly after dd_inventory starts. Trigger dd_inventory:client:sessionActive towards the player from your own spawn flow. See Running without a framework.
Equipping works, the legacy deep holster and component subsystem does not exist in v1. See the weapons note on the Compatibility page.
That is intentional. A temporary slot buff expired. The items are safe and can be dragged out, but nothing new goes in until the player re-buys or re-buffs those slots.
It points at a currency you disabled. DD Inventory falls back to the first enabled one - set it explicitly to silence the warning.
Check that dd_shellkit is started (or dd_hud, if you run it - it takes over as shell), and that openKey in config/config.lua is not colliding with another resource’s bind.

Support

Discord support

Include your server’s console output from resource start, your config/config.lua, and the exact steps to reproduce.

Developer reference

Exports, events, use handlers and move validators.