/* * CSB Anti Flood * Copyright (C) 2026 counter-strike-boost.com * * Rate-limits chat, radio and console command spam per player using a simple * token bucket for each command class. A player who exceeds the allowed rate * has the offending command swallowed and is warned; repeated flooding earns a * short automatic gag so the rest of the server gets some quiet. * * Inspired by the classic "Anti Flood" plugins for AMX Mod X. This is an * 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 the GNU General * Public License for more details: . */ #include new const PLUGIN[] = "CSB Anti Flood" new const VERSION[] = "1.0.0" new const AUTHOR[] = "counter-strike-boost.com" enum _:Bucket { B_CHAT = 0, B_RADIO, B_CMD, B_COUNT } new g_pEnabled, g_pChatRate, g_pRadioRate, g_pCmdRate, g_pWindow, g_pGagTime, g_pMaxWarn new g_iCount[33][B_COUNT] new Float:g_flReset[33][B_COUNT] new g_iWarnings[33] new Float:g_flGagUntil[33] public plugin_init() { register_plugin(PLUGIN, VERSION, AUTHOR) g_pEnabled = register_cvar("csb_flood_enabled", "1") g_pChatRate = register_cvar("csb_flood_chat", "4") /* say/say_team per window */ g_pRadioRate = register_cvar("csb_flood_radio", "3") /* radio/menu per window */ g_pCmdRate = register_cvar("csb_flood_cmd", "10") /* other console cmds/window */ g_pWindow = register_cvar("csb_flood_window", "3.0") /* window length in seconds */ g_pGagTime = register_cvar("csb_flood_gagtime", "20") /* gag seconds after abuse */ g_pMaxWarn = register_cvar("csb_flood_maxwarn", "4") /* warnings before a gag */ } public client_putinserver(id) { resetPlayer(id) } public client_disconnected(id) { resetPlayer(id) } resetPlayer(id) { for (new b = 0; b < B_COUNT; b++) { g_iCount[id][b] = 0 g_flReset[id][b] = 0.0 } g_iWarnings[id] = 0 g_flGagUntil[id] = 0.0 } public client_command(id) { if (!get_pcvar_num(g_pEnabled) || !is_user_connected(id) || is_user_bot(id)) return PLUGIN_CONTINUE new cmd[24] read_argv(0, cmd, charsmax(cmd)) new bucket = classify(cmd) if (bucket == -1) return PLUGIN_CONTINUE new Float:now = get_gametime() /* a still-active gag blocks chat and radio outright */ if (bucket != B_CMD && g_flGagUntil[id] > now) { client_print_color(id, print_team_default, "^x04[CSB]^x01 You are muted for flooding (%d s left).", floatround(g_flGagUntil[id] - now, floatround_ceil)) return PLUGIN_HANDLED } new limit = bucketLimit(bucket) if (limit <= 0) return PLUGIN_CONTINUE if (now >= g_flReset[id][bucket]) { g_iCount[id][bucket] = 0 g_flReset[id][bucket] = now + get_pcvar_float(g_pWindow) } g_iCount[id][bucket]++ if (g_iCount[id][bucket] > limit) { onFlood(id, bucket) return PLUGIN_HANDLED } return PLUGIN_CONTINUE } classify(const cmd[]) { if (equali(cmd, "say") || equali(cmd, "say_team")) return B_CHAT if (equali(cmd, "menuselect") || (equal(cmd, "radio", 5))) return B_RADIO /* ignore movement / build-in stuff that the engine spams legitimately */ if (equali(cmd, "fullupdate") || equali(cmd, "nextspawn") || equali(cmd, "spectate") || equali(cmd, "jointeam") || equali(cmd, "joinclass") || equali(cmd, "chooseteam")) return B_CMD return -1 } bucketLimit(bucket) { switch (bucket) { case B_CHAT: return get_pcvar_num(g_pChatRate) case B_RADIO: return get_pcvar_num(g_pRadioRate) case B_CMD: return get_pcvar_num(g_pCmdRate) } return 0 } onFlood(id, bucket) { g_iWarnings[id]++ new maxWarn = get_pcvar_num(g_pMaxWarn) if (maxWarn > 0 && g_iWarnings[id] >= maxWarn && bucket != B_CMD) { new gag = get_pcvar_num(g_pGagTime) if (gag > 0) { g_flGagUntil[id] = get_gametime() + float(gag) g_iWarnings[id] = 0 new name[32] get_user_name(id, name, charsmax(name)) client_print_color(id, print_team_default, "^x04[CSB]^x01 You have been muted for^x04 %d^x01 seconds for flooding.", gag) log_amx("[CSB Anti Flood] auto-muted %s for %d s", name, gag) return } } client_print_color(id, print_team_default, "^x04[CSB]^x01 Slow down - you are sending %s too fast.", bucket == B_CHAT ? "messages" : (bucket == B_RADIO ? "radio commands" : "commands")) }