/*
* CSB Reserved Slots
* Copyright (C) 2026 counter-strike-boost.com
*
* Keeps a configurable number of slots free for flagged VIPs/admins. When the
* free-slot count drops below the reserve, the plugin removes the "worst"
* non-VIP present (bots first, then the highest-ping player) so a reserved slot
* is available again. Enforced on connect and on a short repeating timer.
*
* Inspired by the Reserved Slots plugin shipped with AMX Mod X (AMX Mod X
* Development Team). Independent GPL re-implementation; no original code 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 Reserved Slots"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
new g_pEnabled, g_pReserved, g_pFlag
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_slots_enabled", "1")
g_pReserved = register_cvar("csb_slots_reserved", "1")
g_pFlag = register_cvar("csb_slots_flag", "a")
set_task(8.0, "taskEnforce", 0, _, _, "b")
}
public client_putinserver(id)
{
if (is_user_bot(id))
return
set_task(1.5, "taskEnforce")
}
hasReserved(id)
{
new flags[8]
get_pcvar_string(g_pFlag, flags, charsmax(flags))
new access = read_flags(flags)
return (get_user_flags(id) & access) ? 1 : 0
}
pickVictim()
{
new players[32], num, pid
get_players(players, num)
new bestBot = 0
new worst = 0, worstPing = -1
for (new i = 0; i < num; i++)
{
pid = players[i]
if (is_user_bot(pid))
{
bestBot = pid
continue
}
if (is_user_hltv(pid))
continue
/* never kick a VIP or an immune admin to free a reserved slot */
if (hasReserved(pid))
continue
if (get_user_flags(pid) & ADMIN_IMMUNITY)
continue
new ping, loss
get_user_ping(pid, ping, loss)
if (ping > worstPing)
{
worstPing = ping
worst = pid
}
}
/* prefer removing a bot before a real player */
if (bestBot)
return bestBot
return worst
}
public taskEnforce()
{
if (!get_pcvar_num(g_pEnabled))
return
new reserved = get_pcvar_num(g_pReserved)
if (reserved < 1)
return
new maxSlots = get_maxplayers()
new players[32], num
get_players(players, num)
new free = maxSlots - num
while (free < reserved)
{
new victim = pickVictim()
if (!victim)
break
new name[32]
get_user_name(victim, name, charsmax(name))
server_cmd("kick #%d ^"Slot reserved for a VIP - please rejoin later^"", get_user_userid(victim))
log_amx("[CSB Slots] freed a reserved slot by removing %s", name)
free++
}
}