@amxts/perms 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/PAWN.md +53 -0
- package/PAWN.ru.md +53 -0
- package/README.md +266 -2
- package/README.ru.md +267 -0
- package/include/perms.inc +35 -0
- package/package.json +71 -5
- package/prebuilt/linux/perms.aot +0 -0
- package/prebuilt/manifest.json +205 -0
- package/prebuilt/windows/perms.aot +0 -0
- package/src/commands.ts +221 -0
- package/src/db-store.ts +174 -0
- package/src/editor.ts +277 -0
- package/src/file-store.ts +134 -0
- package/src/import.ts +63 -0
- package/src/index.ts +127 -0
- package/src/model.ts +387 -0
- package/src/natives.ts +46 -0
- package/src/players.ts +186 -0
- package/src/server.ts +62 -0
- package/src/state.ts +102 -0
- package/src/time.ts +48 -0
- package/src/types.ts +36 -0
- package/testing/index.ts +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kukson777
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/PAWN.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Perms for Pawn plugins
|
|
2
|
+
|
|
3
|
+
[English](PAWN.md) | [Русский](PAWN.ru.md)
|
|
4
|
+
|
|
5
|
+
A Pawn plugin needs nothing from Perms to keep working: the module gives each
|
|
6
|
+
player AMX Mod X's flags from his `amxx.*` nodes, so `get_user_flags`,
|
|
7
|
+
`access`, `cmd_access` and `register_concmd` with flags behave as they do with
|
|
8
|
+
`admin.amxx`.
|
|
9
|
+
|
|
10
|
+
To check permission nodes of its own, a plugin writes `#include <perms>`; the
|
|
11
|
+
package ships `include/perms.inc`:
|
|
12
|
+
|
|
13
|
+
```pawn
|
|
14
|
+
#include <amxmodx>
|
|
15
|
+
#include <perms>
|
|
16
|
+
|
|
17
|
+
public plugin_init()
|
|
18
|
+
{
|
|
19
|
+
register_plugin("My plugin", "1.0", "you");
|
|
20
|
+
perms_declare("myplugin.trail", "A trail behind the player");
|
|
21
|
+
register_clcmd("say /trail", "CmdTrail");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public CmdTrail(id)
|
|
25
|
+
{
|
|
26
|
+
if (!perms_has(id, "myplugin.trail"))
|
|
27
|
+
{
|
|
28
|
+
client_print(id, print_chat, "You have no trail");
|
|
29
|
+
return PLUGIN_HANDLED;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
new group[32];
|
|
33
|
+
perms_get_group(id, group, charsmax(group));
|
|
34
|
+
client_print(id, print_chat, "Your trail, %s", group);
|
|
35
|
+
return PLUGIN_HANDLED;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public perms_changed(id)
|
|
39
|
+
{
|
|
40
|
+
// the player's permissions changed: he joined, a command or the editor changed them
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| Native | What it does |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| `bool:perms_has(id, const node[])` | whether the player has the node |
|
|
47
|
+
| `perms_declare(const node[], const description[], bool:byDefault = false)` | declares a node for the web editor; `byDefault` gives it to every player |
|
|
48
|
+
| `perms_get_group(id, out[], len)` | the player's primary group |
|
|
49
|
+
| `bool:perms_in_group(id, const group[])` | whether the player is in a group |
|
|
50
|
+
| `perms_add_group(id, const group[], seconds = 0)` | puts him into a group, for `seconds` or for good |
|
|
51
|
+
| `perms_remove_group(id, const group[])` | takes him out of it |
|
|
52
|
+
|
|
53
|
+
`public perms_changed(id)` is called when a player's permissions change.
|
package/PAWN.ru.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Perms для Pawn-плагинов
|
|
2
|
+
|
|
3
|
+
[English](PAWN.md) | [Русский](PAWN.ru.md)
|
|
4
|
+
|
|
5
|
+
Pawn-плагину ничего не нужно от Perms, чтобы работать: модуль ставит каждому
|
|
6
|
+
игроку флаги AMX Mod X из его узлов `amxx.*`, поэтому `get_user_flags`,
|
|
7
|
+
`access`, `cmd_access` и `register_concmd` с флагами ведут себя так же, как с
|
|
8
|
+
`admin.amxx`.
|
|
9
|
+
|
|
10
|
+
Чтобы проверять свои узлы прав, плагин пишет `#include <perms>`; пакет
|
|
11
|
+
поставляет `include/perms.inc`:
|
|
12
|
+
|
|
13
|
+
```pawn
|
|
14
|
+
#include <amxmodx>
|
|
15
|
+
#include <perms>
|
|
16
|
+
|
|
17
|
+
public plugin_init()
|
|
18
|
+
{
|
|
19
|
+
register_plugin("My plugin", "1.0", "you");
|
|
20
|
+
perms_declare("myplugin.trail", "A trail behind the player");
|
|
21
|
+
register_clcmd("say /trail", "CmdTrail");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public CmdTrail(id)
|
|
25
|
+
{
|
|
26
|
+
if (!perms_has(id, "myplugin.trail"))
|
|
27
|
+
{
|
|
28
|
+
client_print(id, print_chat, "You have no trail");
|
|
29
|
+
return PLUGIN_HANDLED;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
new group[32];
|
|
33
|
+
perms_get_group(id, group, charsmax(group));
|
|
34
|
+
client_print(id, print_chat, "Your trail, %s", group);
|
|
35
|
+
return PLUGIN_HANDLED;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public perms_changed(id)
|
|
39
|
+
{
|
|
40
|
+
// права игрока изменились: он зашёл, их изменила команда или редактор
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| Натив | Что делает |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| `bool:perms_has(id, const node[])` | есть ли у игрока узел |
|
|
47
|
+
| `perms_declare(const node[], const description[], bool:byDefault = false)` | объявляет узел для веб-редактора; `byDefault` даёт его всем игрокам |
|
|
48
|
+
| `perms_get_group(id, out[], len)` | основная группа игрока |
|
|
49
|
+
| `bool:perms_in_group(id, const group[])` | состоит ли игрок в группе |
|
|
50
|
+
| `perms_add_group(id, const group[], seconds = 0)` | добавляет его в группу на `seconds` секунд или навсегда |
|
|
51
|
+
| `perms_remove_group(id, const group[])` | убирает его из группы |
|
|
52
|
+
|
|
53
|
+
`public perms_changed(id)` вызывается, когда права игрока меняются.
|
package/README.md
CHANGED
|
@@ -1,3 +1,267 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img src="assets/logo.svg" width="96" alt="Perms">
|
|
4
|
+
|
|
5
|
+
# Perms
|
|
6
|
+
|
|
7
|
+
*Permissions and groups in place of admin.amxx*
|
|
8
|
+
|
|
9
|
+
[](https://amxts.github.io/)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
**English** | [Русский](README.ru.md)
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
Permissions and groups for an amxts server, in place of AMX Mod X's `admin.amxx`:
|
|
17
|
+
permission nodes with wildcards and negation, groups with inheritance and
|
|
18
|
+
weights, temporary grants, a file or MySQL, AMX Mod X's admin flags for Pawn
|
|
19
|
+
plugins - and a web editor the server opens with one command.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
perms.declare({
|
|
23
|
+
"myplugin.kick": { description: "Kick players" },
|
|
24
|
+
"myplugin.help": { description: "Use /help", everyone: true },
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
server.addCommand<KickArgs>("/kick <target>", ({ player, target }) => {
|
|
28
|
+
if (!perms.has(player, "myplugin.kick")) return player.print("You may not kick");
|
|
29
|
+
|
|
30
|
+
target.kick("Kicked");
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
npx amxts module add perms
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`perms` is a global in your plugins, as the core's API is - no import line.
|
|
41
|
+
|
|
42
|
+
## Permissions
|
|
43
|
+
|
|
44
|
+
A permission is a **node**: dotted words, the plugin's name first -
|
|
45
|
+
`myplugin.kick`, `shop.discount`, `kz.admin.wipe`.
|
|
46
|
+
|
|
47
|
+
- `kz.admin.*` gives every node under `kz.admin`; `*` gives everything.
|
|
48
|
+
- `-kz.admin.wipe` takes a node away: a negation.
|
|
49
|
+
- The most specific grant wins: `kz.admin.*` with `-kz.admin.wipe` gives all of
|
|
50
|
+
`kz.admin` but `wipe`. Of a node granted and negated in one place, the
|
|
51
|
+
negation wins.
|
|
52
|
+
- A node nothing grants is what its plugin declared: `everyone: true` gives it
|
|
53
|
+
to every player, otherwise nobody has it.
|
|
54
|
+
|
|
55
|
+
## Groups
|
|
56
|
+
|
|
57
|
+
A group has its permissions, the groups it `inherits`, and a `weight`.
|
|
58
|
+
Every player is in `default`, without being written in.
|
|
59
|
+
|
|
60
|
+
A player's answer for a node comes from the first of these that names it:
|
|
61
|
+
|
|
62
|
+
1. his own permissions;
|
|
63
|
+
2. his groups, the highest weight first - each group before what it inherits;
|
|
64
|
+
3. `default`.
|
|
65
|
+
|
|
66
|
+
His **primary group** is his group of the highest weight - `perms.groupOf(player)`,
|
|
67
|
+
for a chat tag or a scoreboard.
|
|
68
|
+
|
|
69
|
+
A group or a permission may be **temporary**: it stops counting the moment it
|
|
70
|
+
ends, and the file drops it on its next save.
|
|
71
|
+
|
|
72
|
+
## Players
|
|
73
|
+
|
|
74
|
+
A player is found by his SteamID, then by his IP, then by his name with a
|
|
75
|
+
password - as `users.ini` finds him:
|
|
76
|
+
|
|
77
|
+
| Entry | Who it is |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| `STEAM_0:1:12345` | the player with that SteamID |
|
|
80
|
+
| `ip:1.2.3.4` | a player from that address - for a game without Steam |
|
|
81
|
+
| `name:Bob` | a player named `Bob` whose `setinfo _pw` is the entry's password |
|
|
82
|
+
|
|
83
|
+
## The file
|
|
84
|
+
|
|
85
|
+
`perms.yaml` (or `perms.json`) in the configs folder, `addons/amxmodx/configs/` -
|
|
86
|
+
or `configs/<baseDir>/` when the project sets config-core's `baseDir`, as other
|
|
87
|
+
configs are. The commands and the web editor write it, and it is edited by hand
|
|
88
|
+
too; the server console says at start which file it read:
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
groups:
|
|
92
|
+
default:
|
|
93
|
+
permissions: [kz.top]
|
|
94
|
+
vip:
|
|
95
|
+
weight: 10
|
|
96
|
+
permissions: [amxx.reservation, shop.discount]
|
|
97
|
+
admin:
|
|
98
|
+
weight: 50
|
|
99
|
+
inherits: [vip]
|
|
100
|
+
permissions: [amxx.kick, amxx.ban, "kz.admin.*", "-kz.admin.wipe"]
|
|
101
|
+
users:
|
|
102
|
+
- id: "STEAM_0:1:12345"
|
|
103
|
+
name: Bob
|
|
104
|
+
groups: [admin]
|
|
105
|
+
permissions: [kz.admin.wipe]
|
|
106
|
+
- id: "STEAM_0:0:777"
|
|
107
|
+
groups:
|
|
108
|
+
- { group: vip, until: 2026-12-01T00:00:00Z }
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
A permission or a group with an end, or for one server, is an object:
|
|
112
|
+
`{ node: kz.vip, until: 2026-12-01T00:00:00Z, server: kz1 }`. Times are UTC.
|
|
113
|
+
|
|
114
|
+
> [!WARNING]
|
|
115
|
+
> A file with an error is said in the server console with its line, and the
|
|
116
|
+
> permissions read before it stay - a typo never leaves the server without admins.
|
|
117
|
+
|
|
118
|
+
## MySQL
|
|
119
|
+
|
|
120
|
+
Several servers share one set of permissions in MySQL. Give the database's
|
|
121
|
+
address in the server's `addons/amxts/.env`:
|
|
122
|
+
|
|
123
|
+
```ini
|
|
124
|
+
PERMS_DB=mysql://perms:secret@127.0.0.1:3306/perms
|
|
125
|
+
PERMS_SERVER=kz1
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The module makes its tables (`perms_groups`, `perms_grants`, `perms_users`,
|
|
129
|
+
`perms_members`, `perms_revision`) on its first start. A change on one server
|
|
130
|
+
reaches the others within five seconds.
|
|
131
|
+
|
|
132
|
+
`PERMS_SERVER` names this server: a group or a permission given with
|
|
133
|
+
`server: kz1` counts on it alone.
|
|
134
|
+
|
|
135
|
+
## In code
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
perms.has(player, "myplugin.kick"); // true or false
|
|
139
|
+
perms.groupOf(player); // "admin"
|
|
140
|
+
perms.groupsOf(player); // ["admin", "default"]
|
|
141
|
+
perms.addGroup(player, "vip", { for: 30 * 86400 }); // seconds; permanent without for
|
|
142
|
+
perms.removeGroup(player, "vip");
|
|
143
|
+
perms.set(player, "myplugin.trail", true, { for: 3600 });
|
|
144
|
+
perms.unset(player, "myplugin.trail");
|
|
145
|
+
|
|
146
|
+
perms.addEventListener("change", ({ player }) => {
|
|
147
|
+
if (perms.has(player, "myplugin.trail")) startTrail(player);
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`"change"` comes when a player's permissions change: he joins, a command or
|
|
152
|
+
the editor changes them, or one ends.
|
|
153
|
+
|
|
154
|
+
## Commands
|
|
155
|
+
|
|
156
|
+
In the server console and over rcon:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
perms user <player|id> info
|
|
160
|
+
perms user <player|id> group add <group> [30d]
|
|
161
|
+
perms user <player|id> group remove <group>
|
|
162
|
+
perms user <player|id> permission set <node> [true|false] [30d]
|
|
163
|
+
perms user <player|id> permission unset <node>
|
|
164
|
+
perms group <group> create [weight]
|
|
165
|
+
perms group <group> delete | info
|
|
166
|
+
perms group <group> permission set <node> [true|false] [30d]
|
|
167
|
+
perms group <group> permission unset <node>
|
|
168
|
+
perms group <group> parent add|remove <group>
|
|
169
|
+
perms group <group> weight <n>
|
|
170
|
+
perms groups
|
|
171
|
+
perms check <player|id> <node>
|
|
172
|
+
perms nodes [plugin]
|
|
173
|
+
perms editor [close]
|
|
174
|
+
perms reload
|
|
175
|
+
perms import [users.ini]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
A player is `#userid`, a part of his name, or an entry's id
|
|
179
|
+
(`STEAM_0:1:12345`). A duration is `45s`, `90m`, `12h`, `30d` or `2w`.
|
|
180
|
+
|
|
181
|
+
In the game a player with `perms.view` runs the commands that only show -
|
|
182
|
+
`groups`, `check`, `nodes`, `user ... info` and `group ... info` - in his
|
|
183
|
+
console or in chat (`/perms groups`); the answer comes to his console.
|
|
184
|
+
Permissions are changed from the server console and in the web editor only.
|
|
185
|
+
|
|
186
|
+
## The web editor
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
perms editor
|
|
190
|
+
[perms] Open the editor (an hour): https://amxts.github.io/modules/perms/editor#k3x9q2mw7a
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The link opens the server's permissions in the browser: every plugin's nodes
|
|
194
|
+
with their descriptions, a column per group, the groups, the players. **Save**
|
|
195
|
+
sends the changes back; the server applies them within seconds, without a
|
|
196
|
+
restart, and the page says what it did.
|
|
197
|
+
|
|
198
|
+
- The server console opens it; no player can.
|
|
199
|
+
- Whoever has the link can edit until the session ends: after an hour, or
|
|
200
|
+
`perms editor close`. Share it like a password.
|
|
201
|
+
- What goes to the editor: groups, permissions, SteamIDs and names. Passwords and
|
|
202
|
+
the database's address never leave the server.
|
|
203
|
+
- The editor keeps a session for an hour on amxts's service. To keep them on
|
|
204
|
+
your own, run the store from this package's `store/` folder (a Cloudflare
|
|
205
|
+
Worker) and give its address as `PERMS_EDITOR_URL` in `.env`.
|
|
206
|
+
|
|
207
|
+
## In place of admin.amxx
|
|
208
|
+
|
|
209
|
+
1. `npx amxts module add perms`, build and deploy.
|
|
210
|
+
2. Comment out `admin.amxx` (or `admin_sql.amxx`) in `plugins.ini`. While one
|
|
211
|
+
of them runs, the module leaves the players' flags to it and says so in the
|
|
212
|
+
console.
|
|
213
|
+
3. In the server console: `perms import users.ini`. Each admin becomes an entry
|
|
214
|
+
with his flags as `amxx.*` permissions of his own. Check one with
|
|
215
|
+
`perms user <name> info`.
|
|
216
|
+
|
|
217
|
+
`users.ini` is not read again; it stays for going back.
|
|
218
|
+
|
|
219
|
+
Pawn plugins work unchanged: the module gives each player AMX Mod X's flags from
|
|
220
|
+
his `amxx.*` nodes, so `get_user_flags`, `access`, `cmd_access`,
|
|
221
|
+
`is_user_admin` and `cmdaccess.ini` see each player's rights. A player with no `amxx.*`
|
|
222
|
+
node gets `amx_default_access` (`z`). The module also keeps `amx_mode`,
|
|
223
|
+
`amx_password_field`, `amx_default_access` and `amx_reloadadmins`, which server
|
|
224
|
+
configs and plugins use.
|
|
225
|
+
|
|
226
|
+
| Node | Flag |
|
|
227
|
+
| --- | --- |
|
|
228
|
+
| `amxx.immunity` | `a` |
|
|
229
|
+
| `amxx.reservation` | `b` |
|
|
230
|
+
| `amxx.kick` | `c` |
|
|
231
|
+
| `amxx.ban` | `d` |
|
|
232
|
+
| `amxx.slay` | `e` |
|
|
233
|
+
| `amxx.map` | `f` |
|
|
234
|
+
| `amxx.cvar` | `g` |
|
|
235
|
+
| `amxx.cfg` | `h` |
|
|
236
|
+
| `amxx.chat` | `i` |
|
|
237
|
+
| `amxx.vote` | `j` |
|
|
238
|
+
| `amxx.password` | `k` |
|
|
239
|
+
| `amxx.rcon` | `l` |
|
|
240
|
+
| `amxx.levelA` ... `amxx.levelH` | `m` ... `t` |
|
|
241
|
+
| `amxx.menu` | `u` |
|
|
242
|
+
| `amxx.banTemp` | `v` |
|
|
243
|
+
| `amxx.admin` | `y` |
|
|
244
|
+
|
|
245
|
+
> [!WARNING]
|
|
246
|
+
> **amx_mode**
|
|
247
|
+
>
|
|
248
|
+
> `amx_mode 2` kicks a player without an entry, as `admin.amxx` does: a private
|
|
249
|
+
> server. `amx_mode 0` gives nobody anything.
|
|
250
|
+
|
|
251
|
+
Pawn plugins can check nodes themselves: [PAWN.md](PAWN.md).
|
|
252
|
+
|
|
253
|
+
## Options
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
export default defineConfig({
|
|
257
|
+
modules: ["@amxts/perms"],
|
|
258
|
+
perms: {
|
|
259
|
+
file: "perms", // configs/perms.yaml
|
|
260
|
+
editor: "", // the editor's store; PERMS_EDITOR_URL overrides it
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## License
|
|
266
|
+
|
|
267
|
+
MIT
|
package/README.ru.md
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/logo.svg" width="96" alt="Perms">
|
|
4
|
+
|
|
5
|
+
# Perms
|
|
6
|
+
|
|
7
|
+
*Права и группы вместо admin.amxx*
|
|
8
|
+
|
|
9
|
+
[](https://amxts.github.io/)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
[English](README.md) | **Русский**
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
Права и группы для сервера на amxts вместо `admin.amxx` из AMX Mod X: узлы прав
|
|
17
|
+
с масками и запретом, группы с наследованием и весом, временные права, файл или
|
|
18
|
+
MySQL, флаги AMX Mod X для Pawn-плагинов - и веб-редактор, который сервер
|
|
19
|
+
открывает одной командой.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
perms.declare({
|
|
23
|
+
"myplugin.kick": { description: "Кикать игроков" },
|
|
24
|
+
"myplugin.help": { description: "Пользоваться /help", everyone: true },
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
server.addCommand<KickArgs>("/kick <target>", ({ player, target }) => {
|
|
28
|
+
if (!perms.has(player, "myplugin.kick")) return player.print("Тебе нельзя кикать");
|
|
29
|
+
|
|
30
|
+
target.kick("Kicked");
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Установка
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
npx amxts module add perms
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`perms` в плагинах - глобальное имя, как API ядра: строка импорта не нужна.
|
|
41
|
+
|
|
42
|
+
## Права
|
|
43
|
+
|
|
44
|
+
Право - это **узел**: слова через точку, первым - имя плагина: `myplugin.kick`,
|
|
45
|
+
`shop.discount`, `kz.admin.wipe`.
|
|
46
|
+
|
|
47
|
+
- `kz.admin.*` даёт все узлы под `kz.admin`; `*` даёт всё.
|
|
48
|
+
- `-kz.admin.wipe` отнимает узел: запрет.
|
|
49
|
+
- Побеждает самое точное право: `kz.admin.*` вместе с `-kz.admin.wipe` даёт весь
|
|
50
|
+
`kz.admin`, кроме `wipe`. Если в одном месте узел и выдан, и запрещён,
|
|
51
|
+
побеждает запрет.
|
|
52
|
+
- Узел, который никто не выдал, - такой, каким его объявил плагин:
|
|
53
|
+
`everyone: true` даёт его всем игрокам, иначе он ни у кого.
|
|
54
|
+
|
|
55
|
+
## Группы
|
|
56
|
+
|
|
57
|
+
У группы есть её права, группы, которые она наследует (`inherits`), и вес
|
|
58
|
+
(`weight`). Каждый игрок состоит в `default`, даже если нигде не записан.
|
|
59
|
+
|
|
60
|
+
Ответ про узел для игрока берётся из первого, что этот узел называет:
|
|
61
|
+
|
|
62
|
+
1. его личные права;
|
|
63
|
+
2. его группы, начиная с самой тяжёлой, - каждая раньше того, что она наследует;
|
|
64
|
+
3. `default`.
|
|
65
|
+
|
|
66
|
+
**Основная группа** игрока - его группа с самым большим весом:
|
|
67
|
+
`perms.groupOf(player)`, для тега в чате или таблицы счёта.
|
|
68
|
+
|
|
69
|
+
Группа или право могут быть **временными**: они перестают действовать в момент
|
|
70
|
+
окончания, а файл убирает их при следующем сохранении.
|
|
71
|
+
|
|
72
|
+
## Игроки
|
|
73
|
+
|
|
74
|
+
Игрок находится по SteamID, затем по IP, затем по нику с паролем - так же, как
|
|
75
|
+
его находит `users.ini`:
|
|
76
|
+
|
|
77
|
+
| Запись | Кто это |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| `STEAM_0:1:12345` | игрок с этим SteamID |
|
|
80
|
+
| `ip:1.2.3.4` | игрок с этого адреса - для игры без Steam |
|
|
81
|
+
| `name:Bob` | игрок с ником `Bob`, у которого `setinfo _pw` - пароль записи |
|
|
82
|
+
|
|
83
|
+
## Файл
|
|
84
|
+
|
|
85
|
+
`perms.yaml` (или `perms.json`) в папке конфигов, `addons/amxmodx/configs/` -
|
|
86
|
+
или `configs/<baseDir>/`, если проект задал `baseDir` у config-core, как и для
|
|
87
|
+
остальных конфигов. Его пишут команды и веб-редактор, его можно править руками;
|
|
88
|
+
какой файл прочитан, сервер пишет в консоль при старте:
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
groups:
|
|
92
|
+
default:
|
|
93
|
+
permissions: [kz.top]
|
|
94
|
+
vip:
|
|
95
|
+
weight: 10
|
|
96
|
+
permissions: [amxx.reservation, shop.discount]
|
|
97
|
+
admin:
|
|
98
|
+
weight: 50
|
|
99
|
+
inherits: [vip]
|
|
100
|
+
permissions: [amxx.kick, amxx.ban, "kz.admin.*", "-kz.admin.wipe"]
|
|
101
|
+
users:
|
|
102
|
+
- id: "STEAM_0:1:12345"
|
|
103
|
+
name: Bob
|
|
104
|
+
groups: [admin]
|
|
105
|
+
permissions: [kz.admin.wipe]
|
|
106
|
+
- id: "STEAM_0:0:777"
|
|
107
|
+
groups:
|
|
108
|
+
- { group: vip, until: 2026-12-01T00:00:00Z }
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Право или группа со сроком или для одного сервера записываются объектом:
|
|
112
|
+
`{ node: kz.vip, until: 2026-12-01T00:00:00Z, server: kz1 }`. Время - UTC.
|
|
113
|
+
|
|
114
|
+
> [!WARNING]
|
|
115
|
+
> Ошибку в файле сервер называет в консоли со строкой, а права, прочитанные до
|
|
116
|
+
> неё, остаются: из-за опечатки сервер не останется без админов.
|
|
117
|
+
|
|
118
|
+
## MySQL
|
|
119
|
+
|
|
120
|
+
Несколько серверов делят один набор прав в MySQL. Адрес базы пишется в
|
|
121
|
+
`addons/amxts/.env` сервера:
|
|
122
|
+
|
|
123
|
+
```ini
|
|
124
|
+
PERMS_DB=mysql://perms:secret@127.0.0.1:3306/perms
|
|
125
|
+
PERMS_SERVER=kz1
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Модуль создаёт свои таблицы (`perms_groups`, `perms_grants`, `perms_users`,
|
|
129
|
+
`perms_members`, `perms_revision`) при первом запуске. Изменение на одном
|
|
130
|
+
сервере доходит до остальных за пять секунд.
|
|
131
|
+
|
|
132
|
+
`PERMS_SERVER` - имя этого сервера: группа или право, выданные с `server: kz1`,
|
|
133
|
+
действуют только на нём.
|
|
134
|
+
|
|
135
|
+
## В коде
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
perms.has(player, "myplugin.kick"); // true или false
|
|
139
|
+
perms.groupOf(player); // "admin"
|
|
140
|
+
perms.groupsOf(player); // ["admin", "default"]
|
|
141
|
+
perms.addGroup(player, "vip", { for: 30 * 86400 }); // секунды; без for - навсегда
|
|
142
|
+
perms.removeGroup(player, "vip");
|
|
143
|
+
perms.set(player, "myplugin.trail", true, { for: 3600 });
|
|
144
|
+
perms.unset(player, "myplugin.trail");
|
|
145
|
+
|
|
146
|
+
perms.addEventListener("change", ({ player }) => {
|
|
147
|
+
if (perms.has(player, "myplugin.trail")) startTrail(player);
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`"change"` приходит, когда права игрока меняются: он зашёл, их изменила команда
|
|
152
|
+
или редактор, или какое-то право закончилось.
|
|
153
|
+
|
|
154
|
+
## Команды
|
|
155
|
+
|
|
156
|
+
В консоли сервера и через rcon:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
perms user <игрок|id> info
|
|
160
|
+
perms user <игрок|id> group add <группа> [30d]
|
|
161
|
+
perms user <игрок|id> group remove <группа>
|
|
162
|
+
perms user <игрок|id> permission set <узел> [true|false] [30d]
|
|
163
|
+
perms user <игрок|id> permission unset <узел>
|
|
164
|
+
perms group <группа> create [вес]
|
|
165
|
+
perms group <группа> delete | info
|
|
166
|
+
perms group <группа> permission set <узел> [true|false] [30d]
|
|
167
|
+
perms group <группа> permission unset <узел>
|
|
168
|
+
perms group <группа> parent add|remove <группа>
|
|
169
|
+
perms group <группа> weight <n>
|
|
170
|
+
perms groups
|
|
171
|
+
perms check <игрок|id> <узел>
|
|
172
|
+
perms nodes [плагин]
|
|
173
|
+
perms editor [close]
|
|
174
|
+
perms reload
|
|
175
|
+
perms import [users.ini]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Игрок - это `#userid`, часть ника или id записи (`STEAM_0:1:12345`). Срок -
|
|
179
|
+
`45s`, `90m`, `12h`, `30d` или `2w`.
|
|
180
|
+
|
|
181
|
+
В игре игрок с правом `perms.view` выполняет команды, которые только показывают, -
|
|
182
|
+
`groups`, `check`, `nodes`, `user ... info` и `group ... info` - в своей
|
|
183
|
+
консоли или в чате (`/perms groups`); ответ приходит в его консоль. Права
|
|
184
|
+
меняются только из консоли сервера и в веб-редакторе.
|
|
185
|
+
|
|
186
|
+
## Веб-редактор
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
perms editor
|
|
190
|
+
[perms] Open the editor (an hour): https://amxts.github.io/modules/perms/editor#k3x9q2mw7a
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Ссылка открывает права сервера в браузере: узлы каждого плагина с описаниями,
|
|
194
|
+
колонка на каждую группу, группы, игроки. **Сохранить** отправляет изменения
|
|
195
|
+
обратно; сервер применяет их за несколько секунд, без рестарта, а страница
|
|
196
|
+
показывает, что он сделал.
|
|
197
|
+
|
|
198
|
+
- Его открывает консоль сервера; из игры его не открыть.
|
|
199
|
+
- Редактировать может любой, у кого есть ссылка, пока сессия жива: час или до
|
|
200
|
+
`perms editor close`. Храните её как пароль.
|
|
201
|
+
- В редактор уходят группы, права, SteamID и ники. Пароли и адрес базы никогда
|
|
202
|
+
не покидают сервер.
|
|
203
|
+
- Сессия хранится час на сервисе amxts. Чтобы хранить их у себя, запустите
|
|
204
|
+
хранилище из папки `store/` этого пакета (Cloudflare Worker) и укажите его
|
|
205
|
+
адрес в `PERMS_EDITOR_URL` в `.env`.
|
|
206
|
+
|
|
207
|
+
## Вместо admin.amxx
|
|
208
|
+
|
|
209
|
+
1. `npx amxts module add perms`, сборка и выкладка.
|
|
210
|
+
2. Закомментируйте `admin.amxx` (или `admin_sql.amxx`) в `plugins.ini`. Пока
|
|
211
|
+
один из них работает, модуль оставляет флаги игроков ему и пишет об этом в
|
|
212
|
+
консоль.
|
|
213
|
+
3. В консоли сервера: `perms import users.ini`. Каждый админ становится записью,
|
|
214
|
+
а его флаги - его личными правами `amxx.*`. Проверьте любого командой
|
|
215
|
+
`perms user <ник> info`.
|
|
216
|
+
|
|
217
|
+
`users.ini` больше не читается; он остаётся, чтобы можно было вернуться.
|
|
218
|
+
|
|
219
|
+
Pawn-плагины работают без изменений: модуль ставит каждому игроку флаги AMX Mod X
|
|
220
|
+
из его узлов `amxx.*`, поэтому `get_user_flags`, `access`, `cmd_access`,
|
|
221
|
+
`is_user_admin` и `cmdaccess.ini` видят права каждого игрока.
|
|
222
|
+
Игрок без узлов `amxx.*` получает `amx_default_access` (`z`). Модуль держит и
|
|
223
|
+
`amx_mode`, `amx_password_field`, `amx_default_access` и `amx_reloadadmins`,
|
|
224
|
+
которыми пользуются конфиги сервера и плагины.
|
|
225
|
+
|
|
226
|
+
| Узел | Флаг |
|
|
227
|
+
| --- | --- |
|
|
228
|
+
| `amxx.immunity` | `a` |
|
|
229
|
+
| `amxx.reservation` | `b` |
|
|
230
|
+
| `amxx.kick` | `c` |
|
|
231
|
+
| `amxx.ban` | `d` |
|
|
232
|
+
| `amxx.slay` | `e` |
|
|
233
|
+
| `amxx.map` | `f` |
|
|
234
|
+
| `amxx.cvar` | `g` |
|
|
235
|
+
| `amxx.cfg` | `h` |
|
|
236
|
+
| `amxx.chat` | `i` |
|
|
237
|
+
| `amxx.vote` | `j` |
|
|
238
|
+
| `amxx.password` | `k` |
|
|
239
|
+
| `amxx.rcon` | `l` |
|
|
240
|
+
| `amxx.levelA` ... `amxx.levelH` | `m` ... `t` |
|
|
241
|
+
| `amxx.menu` | `u` |
|
|
242
|
+
| `amxx.banTemp` | `v` |
|
|
243
|
+
| `amxx.admin` | `y` |
|
|
244
|
+
|
|
245
|
+
> [!WARNING]
|
|
246
|
+
> **amx_mode**
|
|
247
|
+
>
|
|
248
|
+
> `amx_mode 2` кикает игрока без записи, как `admin.amxx`: закрытый сервер.
|
|
249
|
+
> `amx_mode 0` никому ничего не даёт.
|
|
250
|
+
|
|
251
|
+
Pawn-плагины могут проверять узлы сами: [PAWN.ru.md](PAWN.ru.md).
|
|
252
|
+
|
|
253
|
+
## Настройки
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
export default defineConfig({
|
|
257
|
+
modules: ["@amxts/perms"],
|
|
258
|
+
perms: {
|
|
259
|
+
file: "perms", // configs/perms.yaml
|
|
260
|
+
editor: "", // хранилище редактора; PERMS_EDITOR_URL важнее
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Лицензия
|
|
266
|
+
|
|
267
|
+
MIT
|