/*
* CSB Fake Player Guard
* Copyright (C) 2026 counter-strike-boost.com
*
* Mitigates fake-player / FakeFull floods: caps concurrent connections per IP,
* and drops clients that connect but never finish the handshake or send any
* usercmds within a timeout (counted from CmdStart).
*
* 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 for details.
*/
#include
#include
new const PLUGIN[] = "CSB Fake Player Guard"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
#define TASK_BASE 31337
new g_pEnabled, g_pTimeout, g_pMaxPerIp
new g_iCmds[33]
new bool:g_bInGame[33]
new g_szIp[33][24]
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_fpg_enabled", "1")
g_pTimeout = register_cvar("csb_fpg_timeout", "12")
g_pMaxPerIp = register_cvar("csb_fpg_maxperip", "3")
register_forward(FM_CmdStart, "fwCmdStart")
}
public client_connect(id)
{
g_iCmds[id] = 0
g_bInGame[id] = false
get_user_ip(id, g_szIp[id], charsmax(g_szIp[]), 1)
if (!get_pcvar_num(g_pEnabled))
return
// concurrent connections from the same address
new same = countIp(g_szIp[id], id)
if (same >= get_pcvar_num(g_pMaxPerIp))
{
log_amx("[CSB FakeGuard] %s already has %d connections - dropping new one", g_szIp[id], same)
dropClient(id, "too many connections from your address")
return
}
set_task(float(get_pcvar_num(g_pTimeout)), "taskHandshake", TASK_BASE + id)
}
public client_putinserver(id)
{
g_bInGame[id] = true
}
public fwCmdStart(id)
{
if (id >= 1 && id <= 32)
g_iCmds[id]++
return FMRES_IGNORED
}
public taskHandshake(taskid)
{
new id = taskid - TASK_BASE
if (id < 1 || id > 32 || !is_user_connected(id))
return
// a genuine client is in the game and has produced input by now
if (!g_bInGame[id] && g_iCmds[id] == 0)
{
new authid[35]
get_user_authid(id, authid, charsmax(authid))
log_amx("[CSB FakeGuard] slot %d from %s <%s> never finished the handshake - dropping", id, g_szIp[id], authid)
dropClient(id, "incomplete connection")
}
}
countIp(const ip[], exclude)
{
new players[32], num
get_players(players, num)
new c = 0
for (new i = 0; i < num; i++)
{
new pid = players[i]
if (pid == exclude)
continue
new pip[24]
get_user_ip(pid, pip, charsmax(pip), 1)
if (equal(pip, ip))
c++
}
return c
}
dropClient(id, const reason[])
{
server_cmd("kick #%d ^"CSB Fake Guard: %s^"", get_user_userid(id), reason)
server_exec()
}