Hooks
Hooks let you gate an inventory action before it happens. Register a callback for a beforeX event, inspect the payload, optionally mutate it in place, and return false to cancel.
local id = exports.one_inventory:RegisterHook('beforeItemAdd', function(payload)
if payload.item == 'lockpick' and not isAllowed(payload.source) then
return false -- cancels the AddItem call
end
-- mutating payload.metadata is honored; not every field is (see each hook below)
payload.metadata = payload.metadata or {}
payload.metadata.stamped = true
end)
-- later
exports.one_inventory:RemoveHook(id)
false cancels the operation. Returning true, nil or nothing allows it. Callbacks run in registration order. Errors are caught and logged, they do not abort the chain. Only some payload fields feed back into the action once mutated. This is noted per-field below where it applies.Post-hook events
Every RegisterHook registration gets its own post-hook event, fired once the hooked action finishes:
local id = exports.one_inventory:RegisterHook('beforeItemAdd', function(payload)
if payload.item == 'lockpick' then return false end
end)
AddEventHandler(('one_inventory:onHookHandled:%d'):format(id), function(success, payload)
-- success is false when a hook (yours or another resource's) cancelled the action
end)
false when a hook cancelled the action (yours or any other resource's registered on the same event).fn may be nil when you pass an options table, for a post-events-only registration: you get the post event without ever gating the action. RemoveHook is scoped to the calling resource, and passing no id removes every hook that resource registered. Hooks are also auto-removed when the registering resource stops.Exports
See RegisterHook and RemoveHook for the export signatures.
Filters
RegisterHook's third argument, options, narrows a hook so it only fires (and only sends a post event) for the cases you actually care about, instead of every single beforeX in the game. All filters you set must pass (AND-combined, not OR).
exports.one_inventory:RegisterHook('beforeItemAdd', function(payload)
if payload.item == 'lockpick' then return false end
end, {
itemFilter = { lockpick = true, weapon_pistol = true }, -- only these two items
inventoryFilter = { '^stash:' }, -- only stashes
typeFilter = { stash = true }, -- only inventoryType == 'stash'
print = true, -- log every match to console
})
{ lockpick = true, weapon_pistol = true }. Without it, the hook fires for every item. Matches on either side of a swap (both the item moving and, if present, the item it swaps with).{ '^stash:' } to only match stashes, or { '^123$' } to only match player server id 123. A player inventory reference is its server id, converted to a string before matching.{ stash = true, trunk = true }. Matches the resolved inventory type (player, stash, trunk, glovebox, container, drop, dumpster, or, for shop hooks, the specific shop's name).beforeItemRemove, beforeItemUse, and beforeInventoryClose never set a type field, so a typeFilter on those never matches. beforeItemGive sets no inventory reference at all, so inventoryFilter never matches it. For shop hooks, the type field holds the specific shop's name, not a generic "shop" category, so typeFilter targets one shop at a time.inventoryId, fromInventory, toInventory) is string | number: a number (the player's server id) when the inventory belongs to a player, otherwise the internal string id (stash:<name>, trunk:<plate>, ...).beforeItemAdd
Fires before an item is added to an inventory. Cancel to block the add.
nil for non-player inventories.payload.metadata; the add uses the mutated value.player, stash, trunk, ...).nil if the item is unknown.beforeItemRemove
Fires before an item is removed from an inventory. Cancel to block the removal.
nil for non-player inventories.beforeItemAdd, mutating count or metadata here has no effect on the actual removal.beforeItemUse
Fires before a player uses or consumes an item. Cancel to block the use.
payload.metadata to a new table does not.beforeItemSwap
Fires before an item swap between slots or between inventories. Cancel to block the swap.
nil for a programmatic move, e.g. one made through the SwapSlots export.nil when the target slot is empty (a move).move (target empty), stack (same stackable item), or swap (different items).source identifies the acting player, not "which inventory changed". To determine that, read the per-side fromInventory / toInventory and fromType / toType instead: source can be nil for programmatic moves, and for a transfer triggered by another player it identifies that player rather than either side of the swap.beforeShopPurchase
Fires before a shop purchase is finalised. Cancel to block the sale.
cash, black_money, custom).unitPrice * count).shop.shopId.item.beforeShopOpen
Fires before a shop UI opens. Cancel to block the open.
shopId.beforeItemCraft
Fires before a craft begins. Cancel to block the craft.
bench.nil at fire time (the craft hasn't landed a slot yet).beforeItemDrop
Fires before an item is dropped on the ground. Cancel to block the drop.
beforeItemGive
Fires before a player-to-player item transfer. Cancel to block the give.
onItemGiven event (which builds its own separate payload).beforeItemSearch
Fires before a player frisks another player's inventory. Cancel to block the search.
beforeInventoryOpen
Fires before any inventory UI opens. Cancel to block the open.
nil when opening the player's own inventory.data for the resolved type (e.g. the raw stash/shop/bench name, the drop or dumpster id, the vehicle plate, or the target's server id for player). Unlike other hooks' inventoryId, this is the client's raw request value, not a resolved inventory reference.player, stash, shop, crafting, drop, dumpster, trunk, or glovebox.data.data.beforeInventoryClose
Fires before an inventory UI closes. Cancel to keep it open.
beforeInventoryOpen's data.