Hooks

Learn how to use the hooks system.

Hooks

This resource exposes a lightweight hooks system that lets other resources influence or react to the crafting flow.

Why hooks?

  • Pre-crafting control: deny or allow crafting based on your own rules (whitelists, jobs, cooldowns, zones, etc.)
  • Post-crafting reactions: logging, analytics, trigger other systems.

Available hook points

  • crafting:preCraft (runnable, can veto)

    • Runs right before a craft is accepted. If any handler returns a denial, the craft is stopped.
  • crafting:postCraft (emit, fire-and-forget)

    • Runs after the craft has been queued/saved. Return values are ignored.

Context object

All hooks receive the same context table:

  • src (number): player's source ID
  • playerId (string): persistent player identifier
  • recipe (table): recipe object (contains e.g. uuid, title, ingredients, results, craftingTime)
  • quantity (number): amount to craft
  • benchType (table): bench type (e.g. uuid, name, fullAccess)
  • benchLocation (string): location UUID
  • inventory (table, preCraft only): player's item summary (itemName => count)

Register hooks

You can register either with an inline function or by pointing to an export in your resource.

local id = exports['nextgenfivem_crafting']:registerHook('crafting:preCraft', {
    priority = 0, -- lower runs earlier
    fn = function(ctx)
        -- Return false or { success=false, message='errors.key' } to block
        if ctx.quantity > 5 then
            return false, 'errors.invalid_quantity'
        end
        return true
    end
})

2) Register via export (if you want to reuse the same callback)

In your resource (fxmanifest): export a function, e.g. myPreCraftCheck.

-- register hook by export name
exports['nextgenfivem_crafting']:registerHook('crafting:preCraft', {
    priority = 10,
    resource = GetCurrentResourceName(),
    export = 'myPreCraftCheck' -- must be exported by your resource
})

Unregister

exports['nextgenfivem_crafting']:unregisterHook('crafting:preCraft', id)

Return values (preCraft only)

A handler may return any of the following to influence the flow:

  • true or nil → allow and continue to the next handler
  • false → stop crafting, use default message crafting.not_authorized
  • { success = false, message = 'errors.key' } → stop crafting with the given translation key
  • false, 'errors.key' → stop crafting with the given translation key

Note: Messages are expected to be translation keys already used by the resource.


Examples

Max quantity per craft

exports['nextgenfivem_crafting']:registerHook('crafting:preCraft', {
    priority = 0,
    fn = function(ctx)
        if ctx.quantity > 10 then
            return false, 'errors.invalid_quantity'
        end
        return true
    end
})

Job whitelist

exports['nextgenfivem_crafting']:registerHook('crafting:preCraft', {
    priority = 0,
    fn = function(ctx)
        -- Example: require a certain job (pseudo, adapt to your framework)
        local hasJob = exports['my_jobs']:playerHasJob(ctx.src, 'mechanic')
        if not hasJob then
            return { success = false, message = 'errors.not_authorized' }
        end
        return true
    end
})

Per-recipe cooldown

local cooldown = {}
 
exports['nextgenfivem_crafting']:registerHook('crafting:preCraft', {
    priority = 10,
    fn = function(ctx)
        local key = (ctx.playerId .. ':' .. ctx.recipe.uuid)
        local now = os.time()
        if cooldown[key] and cooldown[key] > now then
            return false, 'errors.cooldown'
        end
        cooldown[key] = now + 30 -- 30 seconds
        return true
    end
})

Log after crafting

exports['nextgenfivem_crafting']:registerHook('crafting:postCraft', {
    fn = function(ctx)
        print(('[Crafting] %s crafted %dx %s'):format(ctx.playerId, ctx.quantity, ctx.recipe.uuid))
    end
})

Advanced

Manual run/emit from other resources

If you need to trigger the hook chain yourself:

-- run (with veto), returns ok, msg
local ok, msg = exports['nextgenfivem_crafting']:runHook('crafting:preCraft', ctx)
 
-- emit (ignores return values)
exports['nextgenfivem_crafting']:emitHook('crafting:postCraft', ctx)

Notes

  • Handlers are automatically removed when the resource that registered them stops.
  • Priority: lower numbers run earlier. Default 0.
  • Keep handlers fast; avoid long synchronous operations in preCraft.
  • If a handler errors, it is logged and preCraft may fail with a generic error.