--!strict
-- ServerScriptService/AbilitySecurity
-- Example secure ability-request handler for your own Roblox experience.

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local remote = ReplicatedStorage:WaitForChild("UseAbility") :: RemoteEvent

local COOLDOWN_SECONDS = 2
local MAX_DISTANCE = 18
local lastUse: {[Player]: number} = {}

local function isFiniteNumber(value: number): boolean
    return value == value
        and value ~= math.huge
        and value ~= -math.huge
end

local function validPosition(value: any): boolean
    if typeof(value) ~= "Vector3" then
        return false
    end

    return isFiniteNumber(value.X)
        and isFiniteNumber(value.Y)
        and isFiniteNumber(value.Z)
end

local function performServerAbility(player: Player, target: Vector3)
    -- Put your real game effect here.
    -- Validate targets, line of sight, team, and game state here too.
    print(("Validated ability request from %d"):format(player.UserId))
end

remote.OnServerEvent:Connect(function(
    player: Player,
    target: any
)
    -- Validate shape before using the data.
    if not validPosition(target) then
        return
    end

    -- The server owns cooldown timing.
    local now = os.clock()
    local previous = lastUse[player] or 0

    if now - previous < COOLDOWN_SECONDS then
        return
    end

    local character = player.Character
    if not character then
        return
    end

    local root = character:FindFirstChild("HumanoidRootPart")
    local humanoid = character:FindFirstChildOfClass("Humanoid")

    if not root or not root:IsA("BasePart") then
        return
    end

    if not humanoid or humanoid.Health <= 0 then
        return
    end

    -- The client cannot choose an arbitrary distant target.
    if (root.Position - target).Magnitude > MAX_DISTANCE then
        return
    end

    -- Only commit the cooldown after validation.
    lastUse[player] = now
    performServerAbility(player, target)
end)

Players.PlayerRemoving:Connect(function(player)
    lastUse[player] = nil
end)
