/*
* CSB Name Filter
* Copyright (C) 2026 counter-strike-boost.com
*
* Blocks unwanted player names on connect and on rename. Names are checked
* against a case-insensitive substring blacklist loaded from an ini file
* (advertising URLs, IP addresses, slurs, admin impersonation). A matching
* player is either auto-renamed to a generated name or kicked, and a per-player
* cooldown stops name-change spam.
*
* This is an independent GPL implementation; it is not based on any other
* plugin's source.
*
* 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 Name Filter"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
#define MAX_PATTERNS 128
#define COOLDOWN 3.0
new g_pEnabled, g_pAction, g_pMinLen, g_pBlockIp
new g_szBad[MAX_PATTERNS][32]
new g_iBadCount = 0
new g_szLastName[33][32]
new Float:g_flLastChange[33]
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_namefilter_enabled", "1")
g_pAction = register_cvar("csb_namefilter_action", "0") /* 0 = rename, 1 = kick */
g_pMinLen = register_cvar("csb_namefilter_minlen", "2")
g_pBlockIp = register_cvar("csb_namefilter_blockip", "1")
}
public plugin_cfg()
{
loadPatterns()
}
loadPatterns()
{
g_iBadCount = 0
new path[128]
get_localinfo("amxx_configsdir", path, charsmax(path))
if (!path[0])
copy(path, charsmax(path), "addons/amxmodx/configs")
add(path, charsmax(path), "/csb_namefilter.ini")
if (!file_exists(path))
{
log_amx("[CSB Name Filter] %s not found - using a small built-in list.", path)
seedDefaults()
return
}
new line[64], len
for (new i = 0; i < 4096; i++)
{
if (!read_file(path, i, line, charsmax(line), len))
break
trim(line)
if (!line[0] || line[0] == ';' || line[0] == '#')
continue
if (g_iBadCount >= MAX_PATTERNS)
break
strtolower(line)
copy(g_szBad[g_iBadCount], charsmax(g_szBad[]), line)
g_iBadCount++
}
if (!g_iBadCount)
seedDefaults()
log_amx("[CSB Name Filter] loaded %d name patterns.", g_iBadCount)
}
seedDefaults()
{
new const defaults[][] =
{
"http://", "https://", "www.", ".com", ".net", ".ru", ".org",
"admin", "owner", "console", "[csb]"
}
for (new i = 0; i < sizeof(defaults) && g_iBadCount < MAX_PATTERNS; i++)
{
copy(g_szBad[g_iBadCount], charsmax(g_szBad[]), defaults[i])
g_iBadCount++
}
}
public client_putinserver(id)
{
g_szLastName[id][0] = 0
g_flLastChange[id] = 0.0
if (!is_user_bot(id) && !is_user_hltv(id))
checkName(id)
}
public client_infochanged(id)
{
if (!is_user_connected(id) || is_user_bot(id) || is_user_hltv(id))
return
new newname[32]
get_user_info(id, "name", newname, charsmax(newname))
if (!newname[0] || equal(newname, g_szLastName[id]))
return
checkName(id)
}
checkName(id)
{
if (!get_pcvar_num(g_pEnabled))
return
new name[32]
get_user_name(id, name, charsmax(name))
copy(g_szLastName[id], charsmax(g_szLastName[]), name)
new reason[32]
reason[0] = 0
if (isBad(name, reason, charsmax(reason)))
{
punish(id, name, reason)
return
}
/* rate-limit legitimate but rapid renames */
new Float:now = get_gametime()
if (g_flLastChange[id] > 0.0 && (now - g_flLastChange[id]) < COOLDOWN)
{
client_print_color(id, print_team_default, "^x04[CSB]^x01 Please stop changing your name so fast.")
}
g_flLastChange[id] = now
}
bool:isBad(const name[], reason[], rlen)
{
new lname[32]
copy(lname, charsmax(lname), name)
strtolower(lname)
trim(lname)
if (strlen(lname) < get_pcvar_num(g_pMinLen))
{
copy(reason, rlen, "name too short")
return true
}
if (get_pcvar_num(g_pBlockIp) && looksLikeIp(lname))
{
copy(reason, rlen, "looks like an IP")
return true
}
for (new i = 0; i < g_iBadCount; i++)
{
if (contain(lname, g_szBad[i]) != -1)
{
formatex(reason, rlen, "matched '%s'", g_szBad[i])
return true
}
}
return false
}
bool:looksLikeIp(const s[])
{
new dots = 0, digits = 0
for (new i = 0; s[i]; i++)
{
if (s[i] == '.')
dots++
else if (s[i] >= '0' && s[i] <= '9')
digits++
}
return (dots >= 3 && digits >= 4)
}
punish(id, const name[], const reason[])
{
if (get_pcvar_num(g_pAction) == 1)
{
log_amx("[CSB Name Filter] kicked '%s' (%s)", name, reason)
server_cmd("kick #%d ^"Your name is not allowed on this server^"", get_user_userid(id))
return
}
new newname[32]
formatex(newname, charsmax(newname), "Player_%d", get_user_userid(id))
set_user_info(id, "name", newname)
copy(g_szLastName[id], charsmax(g_szLastName[]), newname)
client_print_color(id, print_team_default, "^x04[CSB]^x01 Your name was rejected (%s) and changed to^x04 %s^x01.", reason, newname)
log_amx("[CSB Name Filter] renamed '%s' -> '%s' (%s)", name, newname, reason)
}
public client_disconnected(id)
{
g_szLastName[id][0] = 0
g_flLastChange[id] = 0.0
}