Creating Custom Detectors

Extensions

Step-by-step tutorial for building, typing, and registering new server-side detection modules in RAC.

# Overview

RAC's modular architecture makes it easy to add custom detection logic for game-specific mechanics (e.g. fishing minigames, vehicle speed, or trading bots).

Every detector is a Luau ModuleScript placed in ServerScriptService/RAC/Detectors/Custom/.


# The Detector Interface

A valid detector implements the following methods:

--!strict
local Types = require(game.ReplicatedStorage.RAC.Shared.Types)

local CustomDetector = {
    Name = "FishingBotDetector",
    Category = "Economy" :: Types.DetectorCategory,
    Enabled = true,
}

local engineRef: any = nil

function CustomDetector:Init(engine: any)
    engineRef = engine
    print(string.format("[RAC] %s initialized successfully.", self.Name))
end

function CustomDetector:ScanPlayer(player: Player, playerState: Types.PlayerMonitoringState, deltaTime: number)
    if not self.Enabled then return end

    -- Custom detection logic here
    local reactionTime = player:GetAttribute("LastMinigameReactionMs")
    if reactionTime and reactionTime < 30 then
        -- Inhuman 30ms reaction speed: likely automated macro
        self:ReportIncident(player, reactionTime)
    end
end

function CustomDetector:ReportIncident(player: Player, reactionMs: number)
    local detectionEngine = engineRef:GetDetectionEngine()
    detectionEngine.RegisterIncident(player, {
        detectorName = self.Name,
        category = self.Category,
        severity = "High",
        confidence = 0.88,
        evidence = string.format("Minigame reaction time %d ms below human threshold (150ms).", reactionMs),
        metadata = { reactionMs = reactionMs },
    })
end

return CustomDetector

# Dynamic Auto-Registration

During server boot, ModuleRegistry scans all child folders in ServerScriptService/RAC/Detectors/. Any ModuleScript exposing Name and ScanPlayer is automatically indexed into the main loop—no manual wiring required!


# Next Steps