Sunucu tanitimi
csb-wallhack-blocker / addons / amxmodx / scripting / csb_wallhack_blocker.sma
126 lines 3.9 KiB Pawn Raw Permalink History
/*
 * CSB Wallhack Blocker
 * Copyright (C) 2026 counter-strike-boost.com
 *
 * Server-side wallhack / ESP protection. In FM_AddToFullPack the plugin decides,
 * per viewer, whether an enemy player entity is actually visible: it must be in
 * the viewer's PVS and have a clear line of sight from the viewer's eyes to the
 * target's centre or head. If neither is clear (and the target has not fired
 * recently), the entity is dropped from that client's packet, so it cannot be
 * seen through walls. Teammates and recently-firing enemies are always shown.
 *
 * Inspired by the classic AddToFullPack wall blocker approach (Fysiks /
 * OrangeBox). Independent GPL re-implementation; no original code is reused.
 *
 * 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 <fakemeta>

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

new g_pEnabled, g_pGrace
new Float:g_fLastFire[33]
new g_iMaxPlayers

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

    g_pEnabled = register_cvar("csb_wh_enabled", "1")
    g_pGrace   = register_cvar("csb_wh_firegrace", "1.2")

    register_forward(FM_AddToFullPack, "fwAddToFullPack", 0)
    register_forward(FM_EmitSound, "fwEmitSound", 0)

    g_iMaxPlayers = get_maxplayers()
}

public client_putinserver(id)
{
    g_fLastFire[id] = 0.0
}

/* record when a player produces a weapon sound, for the fire grace window */
public fwEmitSound(ent, channel, const sample[])
{
    if (ent >= 1 && ent <= g_iMaxPlayers)
    {
        if (sample[0] == 'w' && sample[1] == 'e' && containi(sample, "weapons/") == 0)
            g_fLastFire[ent] = get_gametime()
    }

    return FMRES_IGNORED
}

public fwAddToFullPack(es, e, ent, host, hostflags, player, pSet)
{
    if (!get_pcvar_num(g_pEnabled))
        return FMRES_IGNORED

    /* only care about player entities being packed for a player host */
    if (!player)
        return FMRES_IGNORED

    if (ent < 1 || ent > g_iMaxPlayers || host < 1 || host > g_iMaxPlayers)
        return FMRES_IGNORED

    if (ent == host)
        return FMRES_IGNORED

    if (!is_user_alive(host) || !is_user_alive(ent))
        return FMRES_IGNORED

    /* never hide teammates */
    if (get_user_team(host) == get_user_team(ent))
        return FMRES_IGNORED

    /* recently fired -> position is already given away, keep visible */
    if (get_gametime() - g_fLastFire[ent] < get_pcvar_float(g_pGrace))
        return FMRES_IGNORED

    /* must be inside the host PVS at all */
    if (!engfunc(EngFunc_CheckVisibility, ent, pSet))
        return FMRES_SUPERCEDE

    /* eye position of the viewer */
    static Float:fEye[3], Float:fOfs[3]
    pev(host, pev_origin, fEye)
    pev(host, pev_view_ofs, fOfs)
    fEye[0] += fOfs[0]
    fEye[1] += fOfs[1]
    fEye[2] += fOfs[2]

    /* target centre and head */
    static Float:fTarget[3], Float:fHead[3], Float:fTOfs[3]
    pev(ent, pev_origin, fTarget)
    pev(ent, pev_view_ofs, fTOfs)

    fHead[0] = fTarget[0] + fTOfs[0]
    fHead[1] = fTarget[1] + fTOfs[1]
    fHead[2] = fTarget[2] + fTOfs[2]

    if (clearLine(fEye, fTarget, host) || clearLine(fEye, fHead, host))
        return FMRES_IGNORED

    /* no clear sight line -> drop from this client's packet */
    return FMRES_SUPERCEDE
}

bool:clearLine(const Float:start[3], const Float:end[3], ignore)
{
    engfunc(EngFunc_TraceLine, start, end, IGNORE_MONSTERS, ignore, 0)

    static Float:fFrac
    get_tr2(0, TR_flFraction, fFrac)

    return (fFrac >= 1.0)
}