/*
* CSB Admin Menu
* Copyright (C) 2026 counter-strike-boost.com
*
* A central, flag-gated administration menu for AMX Mod X.
* Inspired by the classic "Admin Commands / amxmodmenu" plugin shipped with
* AMX Mod X by the AMX Mod X Development Team. This is an independent GPL
* re-implementation, not a copy of their 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
#include
#include
#include
new const PLUGIN[] = "CSB Admin Menu"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
#define ACT_KICK 1
#define ACT_BAN 2
#define ACT_SLAY 3
#define ACT_SLAP 4
#define ACT_MOVE_T 5
#define ACT_MOVE_CT 6
new g_pEnabled, g_pBanTime, g_pSlapDamage
new g_iTargetUserid[33]
new const g_szCvarName[][] =
{
"mp_friendlyfire",
"mp_freezetime",
"mp_roundtime",
"sv_gravity",
"mp_autoteambalance",
"sv_alltalk"
}
new const g_szCvarValues[][] =
{
"0 1",
"0 3 6 10",
"1.75 2.5 5",
"400 600 800 1000",
"0 1",
"0 1"
}
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_adminmenu_enabled", "1")
g_pBanTime = register_cvar("csb_adminmenu_bantime", "30")
g_pSlapDamage = register_cvar("csb_adminmenu_slapdmg", "5")
register_clcmd("amx_csbmenu", "cmdMenu", ADMIN_MENU, "- opens the CSB admin menu")
register_clcmd("say /admin", "cmdMenu", ADMIN_MENU, "- opens the CSB admin menu")
register_clcmd("say_team /admin", "cmdMenu", ADMIN_MENU, "- opens the CSB admin menu")
}
public cmdMenu(id, level, cid)
{
if (!cmd_access(id, level, cid, 1))
return PLUGIN_HANDLED
if (!get_pcvar_num(g_pEnabled))
{
client_print(id, print_chat, "[CSB] The admin menu is disabled (csb_adminmenu_enabled 0).")
return PLUGIN_HANDLED
}
showMainMenu(id)
return PLUGIN_HANDLED
}
showMainMenu(id)
{
new menu = menu_create("\yCSB Admin Menu^n\wPick a category", "handlerMain")
menu_additem(menu, "Player commands", "1", 0)
menu_additem(menu, "Change map", "2", 0)
menu_additem(menu, "Server cvars", "3", 0)
menu_additem(menu, "Swap teams", "4", 0)
menu_additem(menu, "Restart round", "5", 0)
menu_setprop(menu, MPROP_EXITNAME, "Close")
menu_display(id, menu, 0)
}
public handlerMain(id, menu, item)
{
if (item == MENU_EXIT)
{
menu_destroy(menu)
return PLUGIN_HANDLED
}
new info[8], name[32], access, callback
menu_item_getinfo(menu, item, access, info, charsmax(info), name, charsmax(name), callback)
menu_destroy(menu)
switch (str_to_num(info))
{
case 1: showPlayerMenu(id)
case 2: showMapMenu(id)
case 3: showCvarMenu(id)
case 4:
{
if (!(get_user_flags(id) & ADMIN_LEVEL_A))
{
client_print(id, print_chat, "[CSB] You do not have access to team swapping.")
return PLUGIN_HANDLED
}
swapTeams(id)
}
case 5:
{
if (!(get_user_flags(id) & ADMIN_CVAR))
{
client_print(id, print_chat, "[CSB] You do not have access to round control.")
return PLUGIN_HANDLED
}
server_cmd("sv_restartround 1")
announce(id, "restarted the round")
}
}
return PLUGIN_HANDLED
}
showPlayerMenu(id)
{
new menu = menu_create("\yCSB Admin Menu^n\wSelect a player", "handlerPlayer")
new players[32], num, pid, pname[32], info[8], line[64]
get_players(players, num, "ch")
for (new i = 0; i < num; i++)
{
pid = players[i]
get_user_name(pid, pname, charsmax(pname))
formatex(info, charsmax(info), "%d", get_user_userid(pid))
formatex(line, charsmax(line), "%s \r[%s]", pname, is_user_alive(pid) ? "alive" : "dead")
menu_additem(menu, line, info, 0)
}
menu_setprop(menu, MPROP_EXITNAME, "Back")
menu_display(id, menu, 0)
}
public handlerPlayer(id, menu, item)
{
if (item == MENU_EXIT)
{
menu_destroy(menu)
showMainMenu(id)
return PLUGIN_HANDLED
}
new info[8], name[32], access, callback
menu_item_getinfo(menu, item, access, info, charsmax(info), name, charsmax(name), callback)
menu_destroy(menu)
g_iTargetUserid[id] = str_to_num(info)
showActionMenu(id)
return PLUGIN_HANDLED
}
showActionMenu(id)
{
new target = find_player("k", g_iTargetUserid[id])
if (!target)
{
client_print(id, print_chat, "[CSB] That player already left the server.")
showPlayerMenu(id)
return
}
new pname[32], title[96]
get_user_name(target, pname, charsmax(pname))
formatex(title, charsmax(title), "\yCSB Admin Menu^n\wTarget: \r%s", pname)
new menu = menu_create(title, "handlerAction")
menu_additem(menu, "Kick", "1", 0)
menu_additem(menu, "Ban", "2", 0)
menu_additem(menu, "Slay", "3", 0)
menu_additem(menu, "Slap", "4", 0)
menu_additem(menu, "Move to T", "5", 0)
menu_additem(menu, "Move to CT", "6", 0)
menu_setprop(menu, MPROP_EXITNAME, "Back")
menu_display(id, menu, 0)
}
public handlerAction(id, menu, item)
{
if (item == MENU_EXIT)
{
menu_destroy(menu)
showPlayerMenu(id)
return PLUGIN_HANDLED
}
new info[8], name[32], access, callback
menu_item_getinfo(menu, item, access, info, charsmax(info), name, charsmax(name), callback)
menu_destroy(menu)
new action = str_to_num(info)
new target = find_player("k", g_iTargetUserid[id])
if (!target)
{
client_print(id, print_chat, "[CSB] That player already left the server.")
return PLUGIN_HANDLED
}
if (get_user_flags(target) & ADMIN_IMMUNITY)
{
client_print(id, print_chat, "[CSB] That player is immune.")
return PLUGIN_HANDLED
}
new flags = get_user_flags(id)
new tname[32], what[64]
get_user_name(target, tname, charsmax(tname))
switch (action)
{
case ACT_KICK:
{
if (!(flags & ADMIN_KICK)) { noAccess(id); return PLUGIN_HANDLED; }
server_cmd("kick #%d ^"Kicked by an admin^"", get_user_userid(target))
formatex(what, charsmax(what), "kicked %s", tname)
}
case ACT_BAN:
{
if (!(flags & ADMIN_BAN)) { noAccess(id); return PLUGIN_HANDLED; }
new mins = get_pcvar_num(g_pBanTime)
server_cmd("banid %d #%d kick", mins, get_user_userid(target))
server_cmd("writeid")
formatex(what, charsmax(what), "banned %s for %d minutes", tname, mins)
}
case ACT_SLAY:
{
if (!(flags & ADMIN_SLAY)) { noAccess(id); return PLUGIN_HANDLED; }
if (!is_user_alive(target))
{
client_print(id, print_chat, "[CSB] %s is already dead.", tname)
return PLUGIN_HANDLED
}
user_kill(target, 1)
formatex(what, charsmax(what), "slayed %s", tname)
}
case ACT_SLAP:
{
if (!(flags & ADMIN_SLAY)) { noAccess(id); return PLUGIN_HANDLED; }
if (!is_user_alive(target))
{
client_print(id, print_chat, "[CSB] %s is already dead.", tname)
return PLUGIN_HANDLED
}
user_slap(target, get_pcvar_num(g_pSlapDamage))
formatex(what, charsmax(what), "slapped %s", tname)
}
case ACT_MOVE_T, ACT_MOVE_CT:
{
if (!(flags & ADMIN_LEVEL_A)) { noAccess(id); return PLUGIN_HANDLED; }
new CsTeams:team = (action == ACT_MOVE_T) ? CS_TEAM_T : CS_TEAM_CT
if (is_user_alive(target))
user_kill(target, 1)
cs_set_user_team(target, team)
formatex(what, charsmax(what), "moved %s to %s", tname, (action == ACT_MOVE_T) ? "Terrorists" : "Counter-Terrorists")
}
}
announce(id, what)
showActionMenu(id)
return PLUGIN_HANDLED
}
showMapMenu(id)
{
if (!(get_user_flags(id) & ADMIN_MAP))
{
noAccess(id)
return
}
new menu = menu_create("\yCSB Admin Menu^n\wChange map", "handlerMap")
new line[64], txtlen, added = 0
for (new i = 0; i < 128; i++)
{
if (!read_file("mapcycle.txt", i, line, charsmax(line), txtlen))
break
trim(line)
if (!line[0] || line[0] == ';' || line[0] == '/')
continue
if (!is_map_valid(line))
continue
menu_additem(menu, line, line, 0)
added++
}
if (!added)
{
menu_destroy(menu)
client_print(id, print_chat, "[CSB] mapcycle.txt is empty or unreadable.")
return
}
menu_setprop(menu, MPROP_EXITNAME, "Back")
menu_display(id, menu, 0)
}
public handlerMap(id, menu, item)
{
if (item == MENU_EXIT)
{
menu_destroy(menu)
showMainMenu(id)
return PLUGIN_HANDLED
}
new map[32], name[32], access, callback
menu_item_getinfo(menu, item, access, map, charsmax(map), name, charsmax(name), callback)
menu_destroy(menu)
if (!is_map_valid(map))
{
client_print(id, print_chat, "[CSB] %s is not a valid map on this server.", map)
return PLUGIN_HANDLED
}
new what[64]
formatex(what, charsmax(what), "changed the map to %s", map)
announce(id, what)
set_task(3.0, "taskChangeLevel", 0, map, strlen(map) + 1)
return PLUGIN_HANDLED
}
public taskChangeLevel(map[])
{
server_cmd("changelevel %s", map)
}
showCvarMenu(id)
{
if (!(get_user_flags(id) & ADMIN_CVAR))
{
noAccess(id)
return
}
new menu = menu_create("\yCSB Admin Menu^n\wServer cvars (click to cycle)", "handlerCvar")
new line[64], value[16], info[8]
for (new i = 0; i < sizeof(g_szCvarName); i++)
{
get_cvar_string(g_szCvarName[i], value, charsmax(value))
formatex(line, charsmax(line), "%s \r[%s]", g_szCvarName[i], value)
formatex(info, charsmax(info), "%d", i)
menu_additem(menu, line, info, 0)
}
menu_setprop(menu, MPROP_EXITNAME, "Back")
menu_display(id, menu, 0)
}
public handlerCvar(id, menu, item)
{
if (item == MENU_EXIT)
{
menu_destroy(menu)
showMainMenu(id)
return PLUGIN_HANDLED
}
new info[8], name[64], access, callback
menu_item_getinfo(menu, item, access, info, charsmax(info), name, charsmax(name), callback)
menu_destroy(menu)
new idx = str_to_num(info)
if (idx < 0 || idx >= sizeof(g_szCvarName))
return PLUGIN_HANDLED
new cur[16], next[16], what[96]
get_cvar_string(g_szCvarName[idx], cur, charsmax(cur))
nextValue(g_szCvarValues[idx], cur, next, charsmax(next))
set_cvar_string(g_szCvarName[idx], next)
formatex(what, charsmax(what), "set %s to %s", g_szCvarName[idx], next)
announce(id, what)
showCvarMenu(id)
return PLUGIN_HANDLED
}
swapTeams(id)
{
new players[32], num, pid
get_players(players, num, "ch")
for (new i = 0; i < num; i++)
{
pid = players[i]
new CsTeams:team = cs_get_user_team(pid)
if (team == CS_TEAM_T)
cs_set_user_team(pid, CS_TEAM_CT)
else if (team == CS_TEAM_CT)
cs_set_user_team(pid, CS_TEAM_T)
}
server_cmd("sv_restartround 1")
announce(id, "swapped the teams")
}
nextValue(const list[], const cur[], out[], len)
{
new piece[16], first[16], pos = 0, i, found = 0
new listlen = strlen(list)
first[0] = 0
while (pos < listlen)
{
i = 0
while (pos < listlen && list[pos] != ' ')
{
if (i < charsmax(piece))
piece[i++] = list[pos]
pos++
}
piece[i] = 0
while (pos < listlen && list[pos] == ' ')
pos++
if (!first[0])
copy(first, charsmax(first), piece)
if (found)
{
copy(out, len, piece)
return
}
if (equal(piece, cur))
found = 1
}
copy(out, len, first)
}
announce(id, const what[])
{
new aname[32], authid[35], buf[192]
get_user_name(id, aname, charsmax(aname))
get_user_authid(id, authid, charsmax(authid))
formatex(buf, charsmax(buf), "^x04[CSB]^x03 %s^x01 %s.", aname, what)
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()
}
log_amx("[CSB Admin Menu] %s <%s> %s", aname, authid, what)
}
noAccess(id)
{
client_print(id, print_chat, "[CSB] You do not have the required access flag for that action.")
}