One flag-gated /admin menu for kick, ban, slay, slap, team moves, map change and the cvars you actually touch.
csb_admin_menu.sma v1.0.0
1
/*
2
* CSB Admin Menu
3
* Copyright (C) 2026 counter-strike-boost.com
4
*
5
* A central, flag-gated administration menu for AMX Mod X.
6
* Inspired by the classic "Admin Commands / amxmodmenu" plugin shipped with
7
* AMX Mod X by the AMX Mod X Development Team. This is an independent GPL
8
* re-implementation, not a copy of their source.
9
*
10
* This program is free software: you can redistribute it and/or modify it
11
* under the terms of the GNU General Public License as published by the
12
* Free Software Foundation, either version 3 of the License, or (at your
13
* option) any later version. It is distributed in the hope that it will be
14
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16
* Public License for more details: <https://www.gnu.org/licenses/>.
17
*/
18
19
#include <amxmodx>
20
#include <amxmisc>
21
#include <cstrike>
22
#include <fun>
23
24
new const PLUGIN[] = "CSB Admin Menu"
25
new const VERSION[] = "1.0.0"
26
new const AUTHOR[] = "counter-strike-boost.com"
27
28
#define ACT_KICK 1
29
#define ACT_BAN 2
30
#define ACT_SLAY 3
31
#define ACT_SLAP 4
32
#define ACT_MOVE_T 5
33
#define ACT_MOVE_CT 6
34
35
new g_pEnabled, g_pBanTime, g_pSlapDamage
36
new g_iTargetUserid[33]
37
38
new const g_szCvarName[][] =
39
{
40
"mp_friendlyfire",
41
"mp_freezetime",
42
"mp_roundtime",
43
"sv_gravity",
44
"mp_autoteambalance",
45
"sv_alltalk"
46
}
47
48
new const g_szCvarValues[][] =
49
{
50
"0 1",
51
"0 3 6 10",
52
"1.75 2.5 5",
53
"400 600 800 1000",
54
"0 1",
55
"0 1"
56
}
57
58
public plugin_init()
59
{
60
register_plugin(PLUGIN, VERSION, AUTHOR)
61
62
g_pEnabled = register_cvar("csb_adminmenu_enabled", "1")
63
g_pBanTime = register_cvar("csb_adminmenu_bantime", "30")
64
g_pSlapDamage = register_cvar("csb_adminmenu_slapdmg", "5")
65
66
register_clcmd("amx_csbmenu", "cmdMenu", ADMIN_MENU, "- opens the CSB admin menu")
67
register_clcmd("say /admin", "cmdMenu", ADMIN_MENU, "- opens the CSB admin menu")
68
register_clcmd("say_team /admin", "cmdMenu", ADMIN_MENU, "- opens the CSB admin menu")
69
}
70
71
public cmdMenu(id, level, cid)
72
{
73
if (!cmd_access(id, level, cid, 1))
74
return PLUGIN_HANDLED
75
76
if (!get_pcvar_num(g_pEnabled))
77
{
78
client_print(id, print_chat, "[CSB] The admin menu is disabled (csb_adminmenu_enabled 0).")
79
return PLUGIN_HANDLED
80
}
81
82
showMainMenu(id)
83
return PLUGIN_HANDLED
84
}
85
86
showMainMenu(id)
87
{
88
new menu = menu_create("\yCSB Admin Menu^n\wPick a category", "handlerMain")
89
90
menu_additem(menu, "Player commands", "1", 0)
91
menu_additem(menu, "Change map", "2", 0)
92
menu_additem(menu, "Server cvars", "3", 0)
93
menu_additem(menu, "Swap teams", "4", 0)
94
menu_additem(menu, "Restart round", "5", 0)
95
96
menu_setprop(menu, MPROP_EXITNAME, "Close")
97
menu_display(id, menu, 0)
98
}
99
100
public handlerMain(id, menu, item)
101
{
102
if (item == MENU_EXIT)
103
{
104
menu_destroy(menu)
105
return PLUGIN_HANDLED
106
}
107
108
new info[8], name[32], access, callback
109
menu_item_getinfo(menu, item, access, info, charsmax(info), name, charsmax(name), callback)
110
menu_destroy(menu)
111
112
switch (str_to_num(info))
113
{
114
case 1: showPlayerMenu(id)
115
case 2: showMapMenu(id)
116
case 3: showCvarMenu(id)
117
case 4:
118
{
119
if (!(get_user_flags(id) & ADMIN_LEVEL_A))
120
{
121
client_print(id, print_chat, "[CSB] You do not have access to team swapping.")
122
return PLUGIN_HANDLED
123
}
124
swapTeams(id)
125
}
126
case 5:
127
{
128
if (!(get_user_flags(id) & ADMIN_CVAR))
129
{
130
client_print(id, print_chat, "[CSB] You do not have access to round control.")
131
return PLUGIN_HANDLED
132
}
133
server_cmd("sv_restartround 1")
134
announce(id, "restarted the round")
135
}
136
}
137
138
return PLUGIN_HANDLED
139
}
140
141
showPlayerMenu(id)
142
{
143
new menu = menu_create("\yCSB Admin Menu^n\wSelect a player", "handlerPlayer")
144
new players[32], num, pid, pname[32], info[8], line[64]
145
146
get_players(players, num, "ch")
147
148
for (new i = 0; i < num; i++)
149
{
150
pid = players[i]
151
get_user_name(pid, pname, charsmax(pname))
152
formatex(info, charsmax(info), "%d", get_user_userid(pid))
153
formatex(line, charsmax(line), "%s \r[%s]", pname, is_user_alive(pid) ? "alive" : "dead")
154
menu_additem(menu, line, info, 0)
155
}
156
157
menu_setprop(menu, MPROP_EXITNAME, "Back")
158
menu_display(id, menu, 0)
159
}
160
161
public handlerPlayer(id, menu, item)
162
{
163
if (item == MENU_EXIT)
164
{
165
menu_destroy(menu)
166
showMainMenu(id)
167
return PLUGIN_HANDLED
168
}
169
170
new info[8], name[32], access, callback
171
menu_item_getinfo(menu, item, access, info, charsmax(info), name, charsmax(name), callback)
172
menu_destroy(menu)
173
174
g_iTargetUserid[id] = str_to_num(info)
175
showActionMenu(id)
176
return PLUGIN_HANDLED
177
}
178
179
showActionMenu(id)
180
{
181
new target = find_player("k", g_iTargetUserid[id])
182
183
if (!target)
184
{
185
client_print(id, print_chat, "[CSB] That player already left the server.")
186
showPlayerMenu(id)
187
return
188
}
189
190
new pname[32], title[96]
191
get_user_name(target, pname, charsmax(pname))
192
formatex(title, charsmax(title), "\yCSB Admin Menu^n\wTarget: \r%s", pname)
193
194
new menu = menu_create(title, "handlerAction")
195
196
menu_additem(menu, "Kick", "1", 0)
197
menu_additem(menu, "Ban", "2", 0)
198
menu_additem(menu, "Slay", "3", 0)
199
menu_additem(menu, "Slap", "4", 0)
200
menu_additem(menu, "Move to T", "5", 0)
201
menu_additem(menu, "Move to CT", "6", 0)
202
203
menu_setprop(menu, MPROP_EXITNAME, "Back")
204
menu_display(id, menu, 0)
205
}
206
207
public handlerAction(id, menu, item)
208
{
209
if (item == MENU_EXIT)
210
{
211
menu_destroy(menu)
212
showPlayerMenu(id)
213
return PLUGIN_HANDLED
214
}
215
216
new info[8], name[32], access, callback
217
menu_item_getinfo(menu, item, access, info, charsmax(info), name, charsmax(name), callback)
218
menu_destroy(menu)
219
220
new action = str_to_num(info)
221
new target = find_player("k", g_iTargetUserid[id])
222
223
if (!target)
224
{
225
client_print(id, print_chat, "[CSB] That player already left the server.")
226
return PLUGIN_HANDLED
227
}
228
229
if (get_user_flags(target) & ADMIN_IMMUNITY)
230
{
231
client_print(id, print_chat, "[CSB] That player is immune.")
232
return PLUGIN_HANDLED
233
}
234
235
new flags = get_user_flags(id)
236
new tname[32], what[64]
237
get_user_name(target, tname, charsmax(tname))
238
239
switch (action)
240
{
241
case ACT_KICK:
242
{
243
if (!(flags & ADMIN_KICK)) { noAccess(id); return PLUGIN_HANDLED; }
244
server_cmd("kick #%d ^"Kicked by an admin^"", get_user_userid(target))
245
formatex(what, charsmax(what), "kicked %s", tname)
246
}
247
case ACT_BAN:
248
{
249
if (!(flags & ADMIN_BAN)) { noAccess(id); return PLUGIN_HANDLED; }
250
new mins = get_pcvar_num(g_pBanTime)
251
server_cmd("banid %d #%d kick", mins, get_user_userid(target))
252
server_cmd("writeid")
253
formatex(what, charsmax(what), "banned %s for %d minutes", tname, mins)
254
}
255
case ACT_SLAY:
256
{
257
if (!(flags & ADMIN_SLAY)) { noAccess(id); return PLUGIN_HANDLED; }
258
if (!is_user_alive(target))
259
{
260
client_print(id, print_chat, "[CSB] %s is already dead.", tname)
261
return PLUGIN_HANDLED
262
}
263
user_kill(target, 1)
264
formatex(what, charsmax(what), "slayed %s", tname)
265
}
266
case ACT_SLAP:
267
{
268
if (!(flags & ADMIN_SLAY)) { noAccess(id); return PLUGIN_HANDLED; }
269
if (!is_user_alive(target))
270
{
271
client_print(id, print_chat, "[CSB] %s is already dead.", tname)
272
return PLUGIN_HANDLED
273
}
274
user_slap(target, get_pcvar_num(g_pSlapDamage))
275
formatex(what, charsmax(what), "slapped %s", tname)
276
}
277
case ACT_MOVE_T, ACT_MOVE_CT:
278
{
279
if (!(flags & ADMIN_LEVEL_A)) { noAccess(id); return PLUGIN_HANDLED; }
280
new CsTeams:team = (action == ACT_MOVE_T) ? CS_TEAM_T : CS_TEAM_CT
281
if (is_user_alive(target))
282
user_kill(target, 1)
283
cs_set_user_team(target, team)
284
formatex(what, charsmax(what), "moved %s to %s", tname, (action == ACT_MOVE_T) ? "Terrorists" : "Counter-Terrorists")
285
}
286
}
287
288
announce(id, what)
289
showActionMenu(id)
290
return PLUGIN_HANDLED
291
}
292
293
showMapMenu(id)
294
{
295
if (!(get_user_flags(id) & ADMIN_MAP))
296
{
297
noAccess(id)
298
return
299
}
300
301
new menu = menu_create("\yCSB Admin Menu^n\wChange map", "handlerMap")
302
new line[64], txtlen, added = 0
303
304
for (new i = 0; i < 128; i++)
305
{
306
if (!read_file("mapcycle.txt", i, line, charsmax(line), txtlen))
307
break
308
309
trim(line)
310
311
if (!line[0] || line[0] == ';' || line[0] == '/')
312
continue
313
314
if (!is_map_valid(line))
315
continue
316
317
menu_additem(menu, line, line, 0)
318
added++
319
}
320
321
if (!added)
322
{
323
menu_destroy(menu)
324
client_print(id, print_chat, "[CSB] mapcycle.txt is empty or unreadable.")
325
return
326
}
327
328
menu_setprop(menu, MPROP_EXITNAME, "Back")
329
menu_display(id, menu, 0)
330
}
331
332
public handlerMap(id, menu, item)
333
{
334
if (item == MENU_EXIT)
335
{
336
menu_destroy(menu)
337
showMainMenu(id)
338
return PLUGIN_HANDLED
339
}
340
341
new map[32], name[32], access, callback
342
menu_item_getinfo(menu, item, access, map, charsmax(map), name, charsmax(name), callback)
343
menu_destroy(menu)
344
345
if (!is_map_valid(map))
346
{
347
client_print(id, print_chat, "[CSB] %s is not a valid map on this server.", map)
348
return PLUGIN_HANDLED
349
}
350
351
new what[64]
352
formatex(what, charsmax(what), "changed the map to %s", map)
353
announce(id, what)
354
355
set_task(3.0, "taskChangeLevel", 0, map, strlen(map) + 1)
356
return PLUGIN_HANDLED
357
}
358
359
public taskChangeLevel(map[])
360
{
361
server_cmd("changelevel %s", map)
362
}
363
364
showCvarMenu(id)
365
{
366
if (!(get_user_flags(id) & ADMIN_CVAR))
367
{
368
noAccess(id)
369
return
370
}
371
372
new menu = menu_create("\yCSB Admin Menu^n\wServer cvars (click to cycle)", "handlerCvar")
373
new line[64], value[16], info[8]
374
375
for (new i = 0; i < sizeof(g_szCvarName); i++)
376
{
377
get_cvar_string(g_szCvarName[i], value, charsmax(value))
378
formatex(line, charsmax(line), "%s \r[%s]", g_szCvarName[i], value)
379
formatex(info, charsmax(info), "%d", i)
380
menu_additem(menu, line, info, 0)
381
}
382
383
menu_setprop(menu, MPROP_EXITNAME, "Back")
384
menu_display(id, menu, 0)
385
}
386
387
public handlerCvar(id, menu, item)
388
{
389
if (item == MENU_EXIT)
390
{
391
menu_destroy(menu)
392
showMainMenu(id)
393
return PLUGIN_HANDLED
394
}
395
396
new info[8], name[64], access, callback
397
menu_item_getinfo(menu, item, access, info, charsmax(info), name, charsmax(name), callback)
398
menu_destroy(menu)
399
400
new idx = str_to_num(info)
401
402
if (idx < 0 || idx >= sizeof(g_szCvarName))
403
return PLUGIN_HANDLED
404
405
new cur[16], next[16], what[96]
406
get_cvar_string(g_szCvarName[idx], cur, charsmax(cur))
407
nextValue(g_szCvarValues[idx], cur, next, charsmax(next))
408
set_cvar_string(g_szCvarName[idx], next)
409
410
formatex(what, charsmax(what), "set %s to %s", g_szCvarName[idx], next)
411
announce(id, what)
412
413
showCvarMenu(id)
414
return PLUGIN_HANDLED
415
}
416
417
swapTeams(id)
418
{
419
new players[32], num, pid
420
get_players(players, num, "ch")
421
422
for (new i = 0; i < num; i++)
423
{
424
pid = players[i]
425
new CsTeams:team = cs_get_user_team(pid)
426
427
if (team == CS_TEAM_T)
428
cs_set_user_team(pid, CS_TEAM_CT)
429
else if (team == CS_TEAM_CT)
430
cs_set_user_team(pid, CS_TEAM_T)
431
}
432
433
server_cmd("sv_restartround 1")
434
announce(id, "swapped the teams")
435
}
436
437
nextValue(const list[], const cur[], out[], len)
438
{
439
new piece[16], first[16], pos = 0, i, found = 0
440
new listlen = strlen(list)
441
442
first[0] = 0
443
444
while (pos < listlen)
445
{
446
i = 0
447
448
while (pos < listlen && list[pos] != ' ')
449
{
450
if (i < charsmax(piece))
451
piece[i++] = list[pos]
452
pos++
453
}
454
455
piece[i] = 0
456
457
while (pos < listlen && list[pos] == ' ')
458
pos++
459
460
if (!first[0])
461
copy(first, charsmax(first), piece)
462
463
if (found)
464
{
465
copy(out, len, piece)
466
return
467
}
468
469
if (equal(piece, cur))
470
found = 1
471
}
472
473
copy(out, len, first)
474
}
475
476
announce(id, const what[])
477
{
478
new aname[32], authid[35], buf[192]
479
get_user_name(id, aname, charsmax(aname))
480
get_user_authid(id, authid, charsmax(authid))
481
482
formatex(buf, charsmax(buf), "^x04[CSB]^x03 %s^x01 %s.", aname, what)
483
484
new players[32], num, msgid = get_user_msgid("SayText")
485
get_players(players, num, "ch")
486
487
for (new i = 0; i < num; i++)
488
{
489
message_begin(MSG_ONE, msgid, _, players[i])
490
write_byte(players[i])
491
write_string(buf)
492
message_end()
493
}
494
495
log_amx("[CSB Admin Menu] %s <%s> %s", aname, authid, what)
496
}
497
498
noAccess(id)
499
{
500
client_print(id, print_chat, "[CSB] You do not have the required access flag for that action.")
501
}
502









