Detection Hooks
The AddDetectionHook export lets your resource inspect every detection before FiniAC processes it. A hook can let the detection through unchanged, edit its data, or cancel it entirely. Cancelled detections are never logged and never trigger a screenshot.
Why Use Detection Hooks?
- Suppress detections your own scripts, such as
ArmedPedbeing triggered by guards from heist scripts - Add context from your framework to a detection, such as the player's job or the inventory they had open
- Feed detections into your own logging or moderation tooling
AddDetectionHook Export
typescript
interface FiniACHookPlayer {
source: number;
name: string;
identifiers: {
license?: string;
license2?: string;
discord?: string;
fivem?: string;
xbl?: string;
live?: string;
ip?: string;
};
}
interface FiniACHookDetection {
type: string;
data: string[];
}
global.exports.FiniAC.AddDetectionHook(
(
player: FiniACHookPlayer,
detection: FiniACHookDetection,
): FiniACHookDetection | false | void => {
// return false to cancel, return detection to edit, return nothing to keep as is
},
);lua
---@class FiniACHookPlayer
---@field source number The player's server ID
---@field name string The player's name
---@field identifiers { license?: string, license2?: string, discord?: string, fivem?: string, xbl?: string, live?: string, ip?: string }
---@class FiniACHookDetection
---@field type string The detection name, for example "NoclipV2"
---@field data (string|number)[] The detection data
---@param player FiniACHookPlayer
---@param detection FiniACHookDetection
---@return FiniACHookDetection|false|nil
exports.FiniAC:AddDetectionHook(function(player, detection)
-- return false to cancel, return detection to edit, return nil to keep as is
end)Return value:
false: The detection is cancelled- A table with the same
typeanddatashape: The detection's type and data are replaced with what you returned, then FiniAC continues processing it nilor no return: The detection continues unchanged
How detection hooks run:
- Hooks run on the server, synchronously, in the order they were registered. Each hook sees the result of the previous one.
- Hooks must not yield. Calling
Wait()inside a hook counts as an invalid return.
Registering Your Hook
FiniAC finishes starting shortly after the resource starts, so the export is not available in the first moments after start FiniAC. Two signals cover both start orders:
FiniAC:Started(server event): Fired every time FiniAC finishes starting. Register your hook in its handler, this also re-registers it after a FiniAC restart.FiniAC:Started(convar,1while FiniAC is running): Check it when your own resource starts, in case FiniAC was already running.
Usage Examples
lua
local function registerHooks()
---@param player FiniACHookPlayer
---@param detection FiniACHookDetection
exports.FiniAC:AddDetectionHook(function(player, detection)
-- Cancel BlacklistedWeapon detections while the player in paintball
if detection.type == 'BlacklistedWeapon' and Player(player.source).state.inPaintball then
return false
end
-- Add context to a detection and let it through
if detection.type == 'BadTaze' then
table.insert(detection.data, ('job:%s'):format(MyFramework.getJob(player.source)))
return detection
end
-- Nothing returned: the detection continues unchanged
end)
end
-- FiniAC starts (or restarts) after this resource
AddEventHandler('FiniAC:Started', registerHooks)
-- FiniAC was already running when this resource started
if GetConvarInt('FiniAC:Started', 0) == 1 then
registerHooks()
endtypescript
// FiniACHookPlayer and FiniACHookDetection as declared above
function registerHooks(): void {
global.exports.FiniAC.AddDetectionHook(
(player: FiniACHookPlayer, detection: FiniACHookDetection) => {
// Cancel BlacklistedWeapon detections while the player in paintball
if (
detection.type === "BlacklistedWeapon" &&
Player(player.source).state.inPaintball
) {
return false;
}
// Add context to a detection and let it through
if (detection.type === "BadTaze") {
detection.data.push(`job:${MyFramework.getJob(player.source)}`);
return detection;
}
// Nothing returned: the detection continues unchanged
},
);
}
// FiniAC starts (or restarts) after this resource
on("FiniAC:Started", registerHooks);
// FiniAC was already running when this resource started
if (GetConvarInt("FiniAC:Started", 0) === 1) {
registerHooks();
}