A /rules menu of server rules loaded from an ini, shown per-section as an MOTD, with a connect reminder and admin reload.
csb_rules_menu.sma v1.0.0
1
/*
2
* CSB Rules Menu
3
* Copyright (C) 2026 counter-strike-boost.com
4
*
5
* "say /rules" opens a menu of the server rules, loaded from an ini split into
6
* [Section] blocks. Selecting a section shows its text in an MOTD window. Players
7
* who just connected get a short one-line reminder to read the rules, and admins
8
* can reload the ini without changing the map with amx_reloadrules.
9
*
10
* This program is free software: you can redistribute it and/or modify it under
11
* the terms of the GNU General Public License as published by the Free Software
12
* Foundation, either version 3 of the License, or (at your option) any later
13
* version. It is distributed in the hope that it will be useful, but WITHOUT
14
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15
* FOR A PARTICULAR PURPOSE. See <https://www.gnu.org/licenses/> for details.
16
*/
17
18
#include <amxmodx>
19
#include <amxmisc>
20
21
new const PLUGIN[] = "CSB Rules Menu"
22
new const VERSION[] = "1.0.0"
23
new const AUTHOR[] = "counter-strike-boost.com"
24
25
#define MAX_SECTIONS 16
26
27
new g_szTitle[MAX_SECTIONS][48]
28
new g_szBody[MAX_SECTIONS][512]
29
new g_iSections
30
31
public plugin_init()
32
{
33
register_plugin(PLUGIN, VERSION, AUTHOR)
34
35
register_clcmd("say /rules", "cmdRules")
36
register_clcmd("say_team /rules", "cmdRules")
37
38
register_concmd("amx_reloadrules", "cmdReload", ADMIN_CFG, "- reload the server rules ini")
39
}
40
41
public plugin_cfg()
42
{
43
loadRules()
44
}
45
46
loadRules()
47
{
48
g_iSections = 0
49
50
new cfgdir[128], path[160]
51
get_localinfo("amxx_configsdir", cfgdir, charsmax(cfgdir))
52
formatex(path, charsmax(path), "%s/csb_rules.ini", cfgdir)
53
54
if (!file_exists(path))
55
{
56
/* fall back to a built-in rule set so the menu is never empty */
57
copy(g_szTitle[0], charsmax(g_szTitle[]), "General")
58
copy(g_szBody[0], charsmax(g_szBody[]),
59
"1. Respect all players and admins.^n2. No cheating of any kind.^n3. No advertising or spam.^n4. Admin decisions are final.")
60
g_iSections = 1
61
return
62
}
63
64
new f = fopen(path, "rt")
65
66
if (!f)
67
return
68
69
new line[256], cur = -1
70
71
while (!feof(f) && g_iSections < MAX_SECTIONS)
72
{
73
fgets(f, line, charsmax(line))
74
trim(line)
75
76
if (!line[0] || line[0] == ';' || line[0] == '#')
77
continue
78
79
if (line[0] == '[')
80
{
81
new title[48]
82
copy(title, charsmax(title), line[1])
83
84
new close = contain(title, "]")
85
if (close != -1)
86
title[close] = 0
87
88
trim(title)
89
90
cur = g_iSections
91
copy(g_szTitle[cur], charsmax(g_szTitle[]), title)
92
g_szBody[cur][0] = 0
93
g_iSections++
94
}
95
else if (cur >= 0)
96
{
97
if (g_szBody[cur][0])
98
add(g_szBody[cur], charsmax(g_szBody[]), "^n")
99
100
add(g_szBody[cur], charsmax(g_szBody[]), line)
101
}
102
}
103
104
fclose(f)
105
106
if (!g_iSections)
107
{
108
copy(g_szTitle[0], charsmax(g_szTitle[]), "General")
109
copy(g_szBody[0], charsmax(g_szBody[]), "Be excellent to each other.")
110
g_iSections = 1
111
}
112
}
113
114
public client_putinserver(id)
115
{
116
set_task(12.0, "taskReminder", id)
117
}
118
119
public taskReminder(id)
120
{
121
if (is_user_connected(id))
122
client_print(id, print_chat, "[CSB] Type /rules to read the server rules.")
123
}
124
125
public cmdRules(id)
126
{
127
new menu = menu_create("\yServer Rules\w - pick a section:", "rulesHandler")
128
129
new info[8]
130
for (new i = 0; i < g_iSections; i++)
131
{
132
num_to_str(i, info, charsmax(info))
133
menu_additem(menu, g_szTitle[i], info, 0)
134
}
135
136
menu_setprop(menu, MPROP_EXIT, MEXIT_ALL)
137
menu_display(id, menu, 0)
138
return PLUGIN_HANDLED
139
}
140
141
public rulesHandler(id, menu, item)
142
{
143
if (item == MENU_EXIT)
144
{
145
menu_destroy(menu)
146
return PLUGIN_HANDLED
147
}
148
149
new info[8], name[48], access, callback
150
menu_item_getinfo(menu, item, access, info, charsmax(info), name, charsmax(name), callback)
151
menu_destroy(menu)
152
153
new sec = str_to_num(info)
154
155
if (sec < 0 || sec >= g_iSections)
156
return PLUGIN_HANDLED
157
158
new head[64]
159
formatex(head, charsmax(head), "Rules - %s", g_szTitle[sec])
160
show_motd(id, g_szBody[sec], head)
161
return PLUGIN_HANDLED
162
}
163
164
public cmdReload(id, level, cid)
165
{
166
if (!cmd_access(id, level, cid, 1))
167
return PLUGIN_HANDLED
168
169
loadRules()
170
console_print(id, "[CSB] Reloaded %d rule section(s).", g_iSections)
171
client_print(id, print_chat, "[CSB] Rules reloaded (%d sections).", g_iSections)
172
return PLUGIN_HANDLED
173
}
174









