/* * CSB High Ping Kicker * Copyright (C) 2026 counter-strike-boost.com * * Samples every player's ping on a timer, keeps a rolling average and kicks the * ones that stay above the limit for several samples in a row. A connect grace * period stops false positives while a client is still loading, and admins with * a configurable flag are never touched. * * Inspired by "High Ping Kicker" by Nomexous. 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 for details. */ #include #include new const PLUGIN[] = "CSB High Ping Kicker" new const VERSION[] = "1.0.0" new const AUTHOR[] = "counter-strike-boost.com" #define SAMPLES 5 new g_pEnabled, g_pMaxPing, g_pInterval, g_pStrikes, g_pGrace, g_pImmunity, g_pMinPlayers new g_iPing[33][SAMPLES] new g_iSampleCount[33] new g_iSampleIdx[33] new g_iStrikes[33] new Float:g_fJoinTime[33] public plugin_init() { register_plugin(PLUGIN, VERSION, AUTHOR) g_pEnabled = register_cvar("csb_ping_enabled", "1") g_pMaxPing = register_cvar("csb_ping_max", "150") /* average ping allowed */ g_pInterval = register_cvar("csb_ping_interval", "5.0") /* seconds between samples */ g_pStrikes = register_cvar("csb_ping_strikes", "3") /* consecutive bad samples before a kick */ g_pGrace = register_cvar("csb_ping_grace", "60") /* seconds after connect that are ignored */ g_pImmunity = register_cvar("csb_ping_immunity", "a") /* flag that is never kicked */ g_pMinPlayers = register_cvar("csb_ping_minplayers", "0") /* only enforce above this player count */ register_clcmd("say /ping", "cmdPing") register_clcmd("say_team /ping", "cmdPing") register_concmd("amx_pinglist", "cmdPingList", ADMIN_KICK, "- shows every player's average ping") } public plugin_cfg() { new Float:interval = get_pcvar_float(g_pInterval) if (interval < 1.0) interval = 1.0 set_task(interval, "taskSample", 0, "", 0, "b") } public client_putinserver(id) { resetPlayer(id) } public client_disconnected(id) { resetPlayer(id) } resetPlayer(id) { g_iSampleCount[id] = 0 g_iSampleIdx[id] = 0 g_iStrikes[id] = 0 g_fJoinTime[id] = get_gametime() for (new i = 0; i < SAMPLES; i++) g_iPing[id][i] = 0 } public taskSample() { if (!get_pcvar_num(g_pEnabled)) return new players[32], num, id get_players(players, num, "ch") if (num <= get_pcvar_num(g_pMinPlayers)) return new maxPing = get_pcvar_num(g_pMaxPing) new maxStrikes = get_pcvar_num(g_pStrikes) new Float:grace = float(get_pcvar_num(g_pGrace)) new flagStr[8] get_pcvar_string(g_pImmunity, flagStr, charsmax(flagStr)) new immunity = read_flags(flagStr) if (maxPing <= 0 || maxStrikes <= 0) return for (new i = 0; i < num; i++) { id = players[i] new ping, loss get_user_ping(id, ping, loss) g_iPing[id][g_iSampleIdx[id]] = ping g_iSampleIdx[id] = (g_iSampleIdx[id] + 1) % SAMPLES if (g_iSampleCount[id] < SAMPLES) g_iSampleCount[id]++ if (immunity && (get_user_flags(id) & immunity)) continue if (get_gametime() - g_fJoinTime[id] < grace) continue if (g_iSampleCount[id] < SAMPLES) continue new avg = averagePing(id) if (avg <= maxPing) { g_iStrikes[id] = 0 continue } g_iStrikes[id]++ if (g_iStrikes[id] >= maxStrikes) { new name[32] get_user_name(id, name, charsmax(name)) announce("^x04[CSB]^x03 %s^x01 was kicked: average ping^x04 %d^x01 (limit^x04 %d^x01).", name, avg, maxPing) log_amx("[CSB Ping] kicked %s - average ping %d over %d samples (limit %d)", name, avg, SAMPLES, maxPing) server_cmd("kick #%d ^"Your ping (%d) is too high for this server (max %d)^"", get_user_userid(id), avg, maxPing) resetPlayer(id) continue } client_print(id, print_chat, "[CSB] Your average ping is %d, the limit is %d. You will be kicked in %d checks.", avg, maxPing, maxStrikes - g_iStrikes[id]) } } averagePing(id) { new count = g_iSampleCount[id] if (!count) return 0 new sum = 0 for (new i = 0; i < count; i++) sum += g_iPing[id][i] return sum / count } public cmdPing(id) { new ping, loss get_user_ping(id, ping, loss) client_print(id, print_chat, "[CSB] Ping: %d (average %d, loss %d%%). Server limit: %d.", ping, averagePing(id), loss, get_pcvar_num(g_pMaxPing)) return PLUGIN_HANDLED } public cmdPingList(id, level, cid) { if (!cmd_access(id, level, cid, 1)) return PLUGIN_HANDLED new players[32], num, pid, name[32], ping, loss get_players(players, num, "ch") console_print(id, "[CSB] Ping report (limit %d):", get_pcvar_num(g_pMaxPing)) for (new i = 0; i < num; i++) { pid = players[i] get_user_name(pid, name, charsmax(name)) get_user_ping(pid, ping, loss) console_print(id, " %-24s now: %4d avg: %4d loss: %3d strikes: %d", name, ping, averagePing(pid), loss, g_iStrikes[pid]) } return PLUGIN_HANDLED } announce(const fmt[], any:...) { new buf[192] vformat(buf, charsmax(buf), fmt, 2) new players[32], num, msgid = get_user_msgid("SayText") get_players(players, num, "ch") for (new i = 0; i < num; i++) { message_begin(MSG_ONE, msgid, _, players[i]) write_byte(players[i]) write_string(buf) message_end() } }