/*
* CSB Players Menu
* Copyright (C) 2026 counter-strike-boost.com
*
* "say /players" opens a menu listing everyone on the server with their team,
* frag count and (in the detail view) their rank, headshots, country and time
* played. Selecting a player opens a detail page anyone can read. IP addresses
* and SteamIDs are never shown - only a two-letter country code from GeoIP.
*
* 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
#include
new const PLUGIN[] = "CSB Players Menu"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
#define VIP_FLAG ADMIN_LEVEL_H
new g_iConnectTime[33]
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
register_clcmd("say /players", "cmdPlayers")
register_clcmd("say_team /players", "cmdPlayers")
}
public client_putinserver(id)
{
g_iConnectTime[id] = get_systime()
}
public cmdPlayers(id)
{
showList(id)
return PLUGIN_HANDLED
}
teamName(id, out[], len)
{
switch (get_user_team(id))
{
case 1: copy(out, len, "T")
case 2: copy(out, len, "CT")
default: copy(out, len, "SPEC")
}
}
showList(id)
{
new menu = menu_create("\yPlayers Online\w - pick one:", "listHandler")
new players[32], num
get_players(players, num, "ch")
new name[32], line[80], info[8], teamTag[8]
for (new i = 0; i < num; i++)
{
new pl = players[i]
get_user_name(pl, name, charsmax(name))
teamName(pl, teamTag, charsmax(teamTag))
formatex(line, charsmax(line), "%s \d[%s] %d frags\w", name, teamTag, get_user_frags(pl))
num_to_str(pl, info, charsmax(info))
menu_additem(menu, line, info, 0)
}
if (!num)
menu_additem(menu, "\dNo players online", "0", 0)
menu_setprop(menu, MPROP_EXIT, MEXIT_ALL)
menu_display(id, menu, 0)
}
public listHandler(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)
new target = str_to_num(info)
if (target < 1 || target > 32 || !is_user_connected(target))
{
client_print(id, print_chat, "[CSB] That player already left.")
return PLUGIN_HANDLED
}
showDetail(id, target)
return PLUGIN_HANDLED
}
showDetail(id, target)
{
new name[32]
get_user_name(target, name, charsmax(name))
new teamTag[8]
teamName(target, teamTag, charsmax(teamTag))
/* rank / stats from csx */
new stats[8], body[8]
new rank = get_user_stats(target, stats, body)
new total = get_statsnum()
/* country code from GeoIP (no IP is ever shown to the viewer) */
new ip[32], ccode[3]
get_user_ip(target, ip, charsmax(ip), 1)
if (!geoip_code2(ip, ccode))
copy(ccode, charsmax(ccode), "??")
new mins = (get_systime() - g_iConnectTime[target]) / 60
new bool:vip = (get_user_flags(target) & VIP_FLAG) != 0
new title[256]
formatex(title, charsmax(title),
"\y%s\w^n\dTeam:\w %s \dFrags:\w %d^n\dRank:\w %d / %d \dHS:\w %d^n\dCountry:\w %s \dPlaytime:\w %d min^n\dVIP:\w %s",
name, teamTag, get_user_frags(target),
rank, total, stats[2],
ccode, mins,
vip ? "Yes" : "No")
new menu = menu_create(title, "detailHandler")
menu_additem(menu, "\wBack to list", "back", 0)
menu_setprop(menu, MPROP_EXIT, MEXIT_ALL)
menu_display(id, menu, 0)
}
public detailHandler(id, menu, item)
{
menu_destroy(menu)
if (item == MENU_EXIT)
return PLUGIN_HANDLED
showList(id)
return PLUGIN_HANDLED
}