Documentation

Can-Check Hooks

Hook functions that run before shredder actions are executed. Return false to deny with an optional reason.

Can-check hooks allow you to control player actions in zyke_shredder before they execute. Return false with an optional locale key to deny an action and notify the player.

Client hooks return true by default. Server hooks allow every action at a scrapyard shredder, and apply compact.access to props of the compact set.

#File Locations

SideFile
Serverserver/unlocked/can_checks.lua
Clientclient/unlocked/can_checks.lua

Both files are unencrypted and safe to edit directly. Edit the existing functions inside Shredder.

Types / Classes

All types / classes can be found in types/types.lua. plyId is the requesting player's server ID. Server hooks receive the machine's ShredderMachineConfig, or a compact prop's ShredderPlacedConfig; client hooks receive its id.

A compact prop has a kind (compact for the shredder, generator, or box), the owner identifier of the character who set it up, the routing bucket key it stands in, and whether it stands indoors. Scrapyard machines have no kind.


#How Reason Strings Work

#Server-side reasons

The server returns the reason to the client, which passes it to Z.notify(), so use a locale key. Missing reasons fall back to "noPermission". Refusals also fire zyke_shredder:OnActionRefused and are written to the activity log when logs.refusals is enabled, except for CanEquipShredderCan.

#Client-side reasons

Client checks also use locale keys and fall back to "noPermission". They provide immediate feedback; enforce trusted restrictions in the corresponding server hook. Add custom reason keys to every resource locale.

This server-side example allows only one scrapyard machine to be started or stopped by players, and leaves compact props alone.

Example:

lua
---@param plyId PlayerId
---@param machine ShredderMachineConfig
---@param running boolean
---@return boolean allowed
---@return string? reason
function CanOperateShredder(plyId, machine, running)
    if (not machine.kind and machine.id ~= "sandy_scrapyard") then return false, "noPermission" end

    return true
end

#Compact Access Rules

The shipped server hooks call a local hasAccess helper for compact props. It reads the action's rule from compact.access: "owner" allows the character who set the prop up and admins with compact.admin.permission, and anything else allows everyone. Denied actions return "compactNotOwner".

Keep these calls when you edit a hook, or extend hasAccess itself to add jobs, gangs, or keys.

Example:

lua
---@param plyId PlayerId
---@param machine ShredderMachineConfig | ShredderPlacedConfig
---@param action "load" | "operate" | "collect" | "refuel" | "link" | "pickup"
---@return boolean allowed
local function hasAccess(plyId, machine, action)
    if (not machine.kind) then return true end
    if (access[action] ~= "owner") then return true end
    if (machine.owner == Z.getIdentifier(plyId)) then return true end

    return IsShredderCompactAdmin(plyId)
end

#Execution & Validation

Keep hooks synchronous and free of side effects. Returning true continues the normal flow; built-in distance, recipe, inventory, fuel, and capacity checks still apply.

Machines stopping by themselves, admin panel actions, the /shreddercompact command, and parts already on the line are not subject to player action hooks.


#Server-Sided Hooks

All server-sided hooks are in server/unlocked/can_checks.lua.

#Can Feed Shredder

Called before a part is loaded onto the infeed, or into a compact shredder's hopper. item is the inventory item being loaded.

Example:

lua
---@param plyId PlayerId
---@param machine ShredderMachineConfig | ShredderPlacedConfig
---@param item Item @ Part being loaded onto the infeed, or into the compact shredder's hopper
---@return boolean allowed
---@return string? reason
function CanFeedShredder(plyId, machine, item)
    if (not hasAccess(plyId, machine, "load")) then return false, "compactNotOwner" end

    return true
end

#Can Unload Shredder

Called before a part is taken back off the infeed. item is the part's item name. Scrapyard shredders only.

Example:

lua
---@param plyId PlayerId
---@param machine ShredderMachineConfig
---@param item string @ Name of the part being taken back off the infeed
---@return boolean allowed
---@return string? reason
function CanUnloadShredder(plyId, machine, item)
    return true
end

#Can Operate Shredder

Called before a player starts or stops a machine. running is true when starting. Also called for the portable generator's switch; machine.kind tells them apart.

Example:

lua
---@param plyId PlayerId
---@param machine ShredderMachineConfig | ShredderPlacedConfig
---@param running boolean @ Whether the player is starting or stopping it
---@return boolean allowed
---@return string? reason
function CanOperateShredder(plyId, machine, running)
    if (not hasAccess(plyId, machine, "operate")) then return false, "compactNotOwner" end

    return true
end

#Can Collect Shredder Bin

Called before a player takes scrap from the bin. For the compact set, machine is the catch crate.

Example:

lua
---@param plyId PlayerId
---@param machine ShredderMachineConfig | ShredderPlacedConfig @ The line's machine, or the catch box
---@return boolean allowed
---@return string? reason
function CanCollectShredderBin(plyId, machine)
    if (not hasAccess(plyId, machine, "collect")) then return false, "compactNotOwner" end

    return true
end

#Can Equip Shredder Can

Called when a player uses a fuel can to take it out. Server only.

Example:

lua
---@param plyId PlayerId
---@param item Item @ Fuel can being taken out
---@return boolean allowed
---@return string? reason
function CanEquipShredderCan(plyId, item)
    return true
end

#Can Refuel Shredder

Called before the can in hand is poured into a generator. For the compact set, machine is the portable generator.

Example:

lua
---@param plyId PlayerId
---@param machine ShredderMachineConfig | ShredderPlacedConfig @ The line's machine, or the portable generator
---@param item Item @ Can in hand being poured into the generator
---@return boolean allowed
---@return string? reason
function CanRefuelShredder(plyId, machine, item)
    if (not hasAccess(plyId, machine, "refuel")) then return false, "compactNotOwner" end

    return true
end

#Can Place Shredder Prop

Called before a compact prop is set up from its item, once the room and the distance are fine. The admin command does not pass through here.

Example:

lua
---@param plyId PlayerId
---@param kind ShredderPlacedKind
---@param coords vector4 @ Where it would stand
---@return boolean allowed
---@return string? reason
function CanPlaceShredderProp(plyId, kind, coords)
    return true
end

#Can Pickup Shredder Prop

Called before a compact prop is packed back into its item, ahead of the wait and of what it holds or is linked to.

Example:

lua
---@param plyId PlayerId
---@param machine ShredderPlacedConfig
---@return boolean allowed
---@return string? reason
function CanPickupShredderProp(plyId, machine)
    if (not hasAccess(plyId, machine, "pickup")) then return false, "compactNotOwner" end

    return true
end

Called at both ends of the cable: taking it off the generator, plugging it into the shredder, and pulling it out again. linking is false when the cable is being pulled out.

Example:

lua
---@param plyId PlayerId
---@param machine ShredderPlacedConfig @ The generator or the shredder the player stands at
---@param linking boolean @ false when the cable is being pulled out
---@return boolean allowed
---@return string? reason
function CanLinkShredder(plyId, machine, linking)
    if (not hasAccess(plyId, machine, "link")) then return false, "compactNotOwner" end

    return true
end

#Get Shredder Bucket Key

Returns the key a routing bucket is known by, 64 characters at most. A compact prop is saved under the key of the bucket it was set up in and only exists for players whose bucket has the same key. When your housing hands out bucket numbers anew each restart, return a key that stays the same, such as the apartment the player is in.

Example:

lua
---@param bucket integer @ Routing bucket the player is in
---@param plyId PlayerId
---@return string key @ 64 characters at most
function GetShredderBucketKey(bucket, plyId)
    return tostring(bucket)
end

#Is Shredder Prop Indoors

Decides whether a compact prop being set up stands indoors, where a running generator's fumes build up. reported is what the player's client found through IsShredderPlacementIndoors.

Example:

lua
---@param plyId PlayerId
---@param coords vector4 @ Where it is set up
---@param bucket integer @ Routing bucket the player is in
---@param reported boolean
---@return boolean indoors
function IsShredderPropIndoors(plyId, coords, bucket, reported)
    if (reported) then return true end

    return fumes.bucketsAreIndoors == true and bucket ~= 0
end

#Can Sell Scrap

Called before a player sells scrap to a buyer. buyer is the entry from buyer.positions.

Example:

lua
---@param plyId PlayerId
---@param buyer ShredderBuyerPosition
---@return boolean allowed
---@return string? reason
function CanSellScrap(plyId, buyer)
    return true
end

#Client-Sided Hooks

All client-sided hooks are in client/unlocked/can_checks.lua. The server re-validates every action.

#Can Feed Shredder

Called before the loading flow starts. slot is the inventory slot of the part.

Example:

lua
---@param machineId string
---@param slot integer @ Inventory slot of the part being loaded
---@return boolean allowed
---@return string? reason
function CanFeedShredder(machineId, slot)
    return true
end

#Can Unload Shredder

Called before a part is taken back off the infeed. loadId identifies the part on the belt.

Example:

lua
---@param machineId string
---@param loadId string @ Part riding the infeed being taken back off
---@return boolean allowed
---@return string? reason
function CanUnloadShredder(machineId, loadId)
    return true
end

#Can Operate Shredder

Called before the start or stop request is sent.

Example:

lua
---@param machineId string
---@param running boolean @ Whether the player is starting or stopping it
---@return boolean allowed
---@return string? reason
function CanOperateShredder(machineId, running)
    return true
end

#Can Collect Shredder Bin

Called before the bin menu collects scrap.

Example:

lua
---@param machineId string
---@return boolean allowed
---@return string? reason
function CanCollectShredderBin(machineId)
    return true
end

#Can Refuel Shredder

Called before pouring starts at the generator's filler.

Example:

lua
---@param machineId string
---@return boolean allowed
---@return string? reason
function CanRefuelShredder(machineId)
    return true
end

#Can Place Shredder Prop

Called before the placement preview of a compact prop starts, and again as it is confirmed. coords is nil before the preview has started.

Example:

lua
---@param kind ShredderPlacedKind @ "compact" for the shredder, "generator" or "box"
---@param coords? vector4 @ Where the preview stands, nil before it has started
---@return boolean allowed
---@return string? reason
function CanPlaceShredderProp(kind, coords)
    return true
end

#Can Pickup Shredder Prop

Called before the pack-up request is sent.

Example:

lua
---@param machineId string
---@return boolean allowed
---@return string? reason
function CanPickupShredderProp(machineId)
    return true
end

Called before a cable is plugged in or pulled out. machineId is the generator or the shredder the player stands at.

Example:

lua
---@param machineId string @ The generator or the shredder the player stands at
---@param linking boolean @ false when the cable is being pulled out
---@return boolean allowed
---@return string? reason
function CanLinkShredder(machineId, linking)
    return true
end

#Is Shredder Placement Indoors

Reports whether a compact prop set up at coords stands indoors. The result is sent along as the prop is placed. Return true for shells and other builds the game does not know as an interior.

Example:

lua
---@param coords vector4
---@return boolean indoors
function IsShredderPlacementIndoors(coords)
    return GetInteriorFromEntity(PlayerPedId()) ~= 0 or GetInteriorAtCoords(coords.x, coords.y, coords.z + 0.5) ~= 0
end

#Can Sell Scrap

Called before the sell request is sent. buyerIndex is the index in buyer.positions.

Example:

lua
---@param buyerIndex integer
---@return boolean allowed
---@return string? reason
function CanSellScrap(buyerIndex)
    return true
end

#Common Patterns

#Restrict Compact Shredders to a Job

Denies setting up any compact prop unless the player has the job.

Example:

lua
---@param plyId PlayerId
---@param kind ShredderPlacedKind
---@param coords vector4
---@return boolean allowed
---@return string? reason
function CanPlaceShredderProp(plyId, kind, coords)
    if (not Z.isJob(plyId, "scrapyard")) then return false, "noPermission" end

    return true
end

#Restrict the Scrap Buyer to a Job

Uses the server hook so the restriction cannot be bypassed from the client.

Example:

lua
---@param plyId PlayerId
---@param buyer ShredderBuyerPosition
---@return boolean allowed
---@return string? reason
function CanSellScrap(plyId, buyer)
    if (not Z.isJob(plyId, "scrapyard")) then return false, "noPermission" end

    return true
end