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