Sunucu tanitimi
csb-aimbot-detect / addons / amxmodx / scripting / csb_aimbot_detect.sma
174 lines 4.8 KiB Pawn Raw Permalink History
/*
 * CSB Aimbot Detection
 * Copyright (C) 2026 counter-strike-boost.com
 *
 * A heuristic aim-assist detector. Every frame it samples each alive player's
 * view angle (fakemeta pev_v_angle) and measures the single-frame angular
 * change. When a kill lands in the same frame as an impossibly large snap, a
 * snap counter is raised; a sustained inhuman headshot ratio is a second
 * signal. Suspects are logged with the evidence and admins are alerted. This is
 * a heuristic aid for human review, not an automatic punisher.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version. It is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See <https://www.gnu.org/licenses/> for details.
 */

#include <amxmodx>
#include <amxmisc>
#include <fakemeta>

new const PLUGIN[] = "CSB Aimbot Detection"
new const VERSION[] = "1.0.0"
new const AUTHOR[]  = "counter-strike-boost.com"

new g_pEnabled, g_pSnapAngle, g_pSnapHits, g_pHsRatio, g_pMinKills

new Float:g_fLast[33][2]
new bool:g_bValid[33]
new Float:g_fFrameDelta[33]

new g_iKills[33]
new g_iHs[33]
new g_iSnapKills[33]
new bool:g_bFlagged[33]

public plugin_init()
{
    register_plugin(PLUGIN, VERSION, AUTHOR)

    g_pEnabled   = register_cvar("csb_aim_enabled", "1")
    g_pSnapAngle = register_cvar("csb_aim_snapangle", "40.0")
    g_pSnapHits  = register_cvar("csb_aim_snaphits", "4")
    g_pHsRatio   = register_cvar("csb_aim_hsratio", "90")
    g_pMinKills  = register_cvar("csb_aim_minkills", "15")

    register_forward(FM_PlayerPreThink, "fwThink")
    register_event("DeathMsg", "evDeath", "a", "1>0")
}

public client_putinserver(id)
{
    g_bValid[id] = false
    g_fFrameDelta[id] = 0.0
    g_iKills[id] = 0
    g_iHs[id] = 0
    g_iSnapKills[id] = 0
    g_bFlagged[id] = false
}

public fwThink(id)
{
    if (!get_pcvar_num(g_pEnabled) || !is_user_connected(id))
        return FMRES_IGNORED

    if (is_user_bot(id) || is_user_hltv(id))
        return FMRES_IGNORED

    if (!is_user_alive(id))
    {
        g_bValid[id] = false
        return FMRES_IGNORED
    }

    new Float:ang[3]
    pev(id, pev_v_angle, ang)

    if (g_bValid[id])
    {
        new Float:dp = ang[0] - g_fLast[id][0]
        new Float:dy = ang[1] - g_fLast[id][1]

        while (dy > 180.0) dy -= 360.0
        while (dy < -180.0) dy += 360.0
        while (dp > 180.0) dp -= 360.0
        while (dp < -180.0) dp += 360.0

        g_fFrameDelta[id] = floatsqroot(dp * dp + dy * dy)
    }
    else
    {
        g_bValid[id] = true
        g_fFrameDelta[id] = 0.0
    }

    g_fLast[id][0] = ang[0]
    g_fLast[id][1] = ang[1]

    return FMRES_IGNORED
}

public evDeath()
{
    if (!get_pcvar_num(g_pEnabled))
        return

    new killer = read_data(1)
    new victim = read_data(2)
    new hs     = read_data(3)

    if (killer < 1 || killer > 32 || killer == victim)
        return

    if (is_user_bot(killer) || is_user_hltv(killer))
        return

    g_iKills[killer]++

    if (hs)
        g_iHs[killer]++

    /* snap heuristic: a very large single-frame view change on the killing frame */
    new Float:snapAngle = get_pcvar_float(g_pSnapAngle)

    if (g_fFrameDelta[killer] >= snapAngle)
    {
        g_iSnapKills[killer]++

        log_amx("[CSB Aim] snap on kill by %N - %.1f deg in one frame (snap kills: %d)",
            killer, g_fFrameDelta[killer], g_iSnapKills[killer])

        if (!g_bFlagged[killer] && g_iSnapKills[killer] >= get_pcvar_num(g_pSnapHits))
            flagSuspect(killer, "repeated impossible aim snaps")
    }

    /* headshot-ratio heuristic */
    new minKills = get_pcvar_num(g_pMinKills)

    if (!g_bFlagged[killer] && g_iKills[killer] >= minKills)
    {
        new pct = (g_iHs[killer] * 100) / g_iKills[killer]

        if (pct >= get_pcvar_num(g_pHsRatio))
            flagSuspect(killer, "inhuman headshot ratio")
    }
}

flagSuspect(id, const reason[])
{
    g_bFlagged[id] = true

    new name[32], authid[35]
    get_user_name(id, name, charsmax(name))
    get_user_authid(id, authid, charsmax(authid))

    new pct = g_iKills[id] ? (g_iHs[id] * 100) / g_iKills[id] : 0

    log_amx("[CSB Aim] SUSPECT %s <%s> - %s (kills %d, hs %d = %d%%, snap kills %d)",
        name, authid, reason, g_iKills[id], g_iHs[id], pct, g_iSnapKills[id])

    /* alert online admins */
    new players[32], num, pid
    get_players(players, num, "ch")

    for (new i = 0; i < num; i++)
    {
        pid = players[i]

        if (get_user_flags(pid) & ADMIN_KICK)
            client_print(pid, print_chat, "[CSB Aim] Possible aimbot: %s (%s). Please review.", name, reason)
    }
}