/*
* CSB Connect Flood Guard
* Copyright (C) 2026 counter-strike-boost.com
*
* Rate-limits connections per IP using a sliding window kept in Tries. An IP
* that reconnects too many times inside the window is temporarily blocked,
* mitigating connect-flood and reconnect spam without touching real players.
*
* 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
new const PLUGIN[] = "CSB Connect Flood Guard"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
new g_pEnabled, g_pMaxConn, g_pWindow, g_pBanTime
new Trie:g_tFirst // ip -> first-seen systime for the current window
new Trie:g_tCount // ip -> connections seen in the current window
new Trie:g_tBlock // ip -> block-until systime
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_flood_enabled", "1")
g_pMaxConn = register_cvar("csb_flood_maxconn", "5")
g_pWindow = register_cvar("csb_flood_window", "10")
g_pBanTime = register_cvar("csb_flood_bantime", "60")
g_tFirst = TrieCreate()
g_tCount = TrieCreate()
g_tBlock = TrieCreate()
}
public plugin_end()
{
TrieDestroy(g_tFirst)
TrieDestroy(g_tCount)
TrieDestroy(g_tBlock)
}
public client_connect(id)
{
if (!get_pcvar_num(g_pEnabled))
return
new ip[24]
get_user_ip(id, ip, charsmax(ip), 1)
if (!ip[0])
return
new now = get_systime()
// still inside an active block?
new until
if (TrieGetCell(g_tBlock, ip, until))
{
if (now < until)
{
kickIp(id, "connecting too fast, try again shortly")
return
}
TrieDeleteKey(g_tBlock, ip)
}
new first, count
if (!TrieGetCell(g_tFirst, ip, first))
{
TrieSetCell(g_tFirst, ip, now)
TrieSetCell(g_tCount, ip, 1)
return
}
new window = get_pcvar_num(g_pWindow)
// window elapsed -> start a fresh one
if (now - first > window)
{
TrieSetCell(g_tFirst, ip, now)
TrieSetCell(g_tCount, ip, 1)
return
}
TrieGetCell(g_tCount, ip, count)
count++
TrieSetCell(g_tCount, ip, count)
if (count > get_pcvar_num(g_pMaxConn))
{
new banTime = get_pcvar_num(g_pBanTime)
TrieSetCell(g_tBlock, ip, now + banTime)
log_amx("[CSB Flood] %s made %d connects in %ds - blocked for %ds", ip, count, window, banTime)
kickIp(id, "connect flood protection")
}
}
kickIp(id, const reason[])
{
server_cmd("kick #%d ^"CSB: %s^"", get_user_userid(id), reason)
server_exec()
}