/*
* 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()
}
}