pymodcs 0.17.1__py3-none-any.whl

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.
Files changed (241) hide show
  1. pymod/__init__.py +740 -0
  2. pymod/__init__.pyi +1413 -0
  3. pymod/__main__.py +78 -0
  4. pymod/_access.py +555 -0
  5. pymod/_assets.py +173 -0
  6. pymod/_audio.py +127 -0
  7. pymod/_chat.py +116 -0
  8. pymod/_color.py +191 -0
  9. pymod/_config.py +309 -0
  10. pymod/_cs.py +445 -0
  11. pymod/_cs_batch.py +149 -0
  12. pymod/_cs_specs.py +1 -0
  13. pymod/_cs_types.py +235 -0
  14. pymod/_cvars.py +256 -0
  15. pymod/_database.py +132 -0
  16. pymod/_diagnostics.py +53 -0
  17. pymod/_easy.py +75 -0
  18. pymod/_effects.py +427 -0
  19. pymod/_entities.py +888 -0
  20. pymod/_events.py +70 -0
  21. pymod/_geometry.py +543 -0
  22. pymod/_hook_api.py +268 -0
  23. pymod/_hooks.py +205 -0
  24. pymod/_http_files.py +293 -0
  25. pymod/_hud.py +212 -0
  26. pymod/_i18n.py +164 -0
  27. pymod/_menus.py +128 -0
  28. pymod/_messages.py +220 -0
  29. pymod/_movement.py +396 -0
  30. pymod/_player.py +605 -0
  31. pymod/_plugin.py +735 -0
  32. pymod/_plugin.pyi +4540 -0
  33. pymod/_plugins.py +241 -0
  34. pymod/_position.py +66 -0
  35. pymod/_position.pyi +44 -0
  36. pymod/_resources.py +196 -0
  37. pymod/_runtime.py +8 -0
  38. pymod/_schema.py +259 -0
  39. pymod/_sdk_native.py +22 -0
  40. pymod/_services.py +820 -0
  41. pymod/_simple_db.py +408 -0
  42. pymod/_timing.py +208 -0
  43. pymod/_voice.py +118 -0
  44. pymod/_web.py +697 -0
  45. pymod/docs/async.md +49 -0
  46. pymod/docs/compatibility.md +41 -0
  47. pymod/docs/configuration.md +101 -0
  48. pymod/docs/geometry.md +62 -0
  49. pymod/docs/hook-add_account.md +45 -0
  50. pymod/docs/hook-add_multi_damage.md +48 -0
  51. pymod/docs/hook-add_player_item.md +44 -0
  52. pymod/docs/hook-add_points.md +44 -0
  53. pymod/docs/hook-add_points_to_team.md +44 -0
  54. pymod/docs/hook-apply_multi_damage.md +46 -0
  55. pymod/docs/hook-balance_teams.md +42 -0
  56. pymod/docs/hook-blind.md +46 -0
  57. pymod/docs/hook-bomb_defuse_end.md +45 -0
  58. pymod/docs/hook-bomb_defuse_start.md +44 -0
  59. pymod/docs/hook-bomb_explode.md +44 -0
  60. pymod/docs/hook-bot_event.md +47 -0
  61. pymod/docs/hook-buy_gun_ammo.md +45 -0
  62. pymod/docs/hook-buy_item.md +43 -0
  63. pymod/docs/hook-buy_weapon.md +43 -0
  64. pymod/docs/hook-can_have_player_item.md +44 -0
  65. pymod/docs/hook-can_hear_player.md +44 -0
  66. pymod/docs/hook-can_respawn.md +42 -0
  67. pymod/docs/hook-can_switch_team.md +43 -0
  68. pymod/docs/hook-can_take_damage.md +44 -0
  69. pymod/docs/hook-change_level.md +42 -0
  70. pymod/docs/hook-check_map_conditions.md +42 -0
  71. pymod/docs/hook-check_time_damage.md +42 -0
  72. pymod/docs/hook-check_win_conditions.md +42 -0
  73. pymod/docs/hook-choose_appearance.md +43 -0
  74. pymod/docs/hook-choose_team.md +43 -0
  75. pymod/docs/hook-classify.md +42 -0
  76. pymod/docs/hook-cleanup_map.md +42 -0
  77. pymod/docs/hook-clear_multi_damage.md +42 -0
  78. pymod/docs/hook-client_userinfo_changed.md +43 -0
  79. pymod/docs/hook-create_weaponbox.md +52 -0
  80. pymod/docs/hook-cs_postthink.md +42 -0
  81. pymod/docs/hook-cs_prethink.md +42 -0
  82. pymod/docs/hook-damage.md +48 -0
  83. pymod/docs/hook-damage_impulse.md +46 -0
  84. pymod/docs/hook-dead_player_weapons.md +42 -0
  85. pymod/docs/hook-death_notice.md +46 -0
  86. pymod/docs/hook-death_sound.md +42 -0
  87. pymod/docs/hook-disappear.md +42 -0
  88. pymod/docs/hook-drop_idle_player.md +43 -0
  89. pymod/docs/hook-drop_player_item.md +43 -0
  90. pymod/docs/hook-drop_shield.md +43 -0
  91. pymod/docs/hook-duck.md +42 -0
  92. pymod/docs/hook-fall_damage.md +42 -0
  93. pymod/docs/hook-fire_buckshots.md +51 -0
  94. pymod/docs/hook-fire_bullets.md +52 -0
  95. pymod/docs/hook-fire_bullets3.md +54 -0
  96. pymod/docs/hook-flash_explode.md +44 -0
  97. pymod/docs/hook-force_camera.md +42 -0
  98. pymod/docs/hook-free_game_rules.md +43 -0
  99. pymod/docs/hook-get_into_game.md +42 -0
  100. pymod/docs/hook-gib_land.md +42 -0
  101. pymod/docs/hook-gib_spawn.md +43 -0
  102. pymod/docs/hook-gib_touch.md +44 -0
  103. pymod/docs/hook-give_ammo.md +45 -0
  104. pymod/docs/hook-give_c4.md +42 -0
  105. pymod/docs/hook-give_default_items.md +42 -0
  106. pymod/docs/hook-give_named_item.md +43 -0
  107. pymod/docs/hook-give_shield.md +43 -0
  108. pymod/docs/hook-go_intermission.md +42 -0
  109. pymod/docs/hook-has_restrict_item.md +44 -0
  110. pymod/docs/hook-he_explode.md +44 -0
  111. pymod/docs/hook-hint_message.md +46 -0
  112. pymod/docs/hook-impulse_commands.md +42 -0
  113. pymod/docs/hook-install_game_rules.md +42 -0
  114. pymod/docs/hook-internal_command.md +46 -0
  115. pymod/docs/hook-is_penetrable.md +48 -0
  116. pymod/docs/hook-joining_think.md +42 -0
  117. pymod/docs/hook-jump.md +42 -0
  118. pymod/docs/hook-killed.md +46 -0
  119. pymod/docs/hook-make_bomber.md +42 -0
  120. pymod/docs/hook-make_vip.md +42 -0
  121. pymod/docs/hook-next_best_weapon.md +44 -0
  122. pymod/docs/hook-object_caps.md +42 -0
  123. pymod/docs/hook-observer_find_next.md +44 -0
  124. pymod/docs/hook-observer_set_mode.md +43 -0
  125. pymod/docs/hook-observer_think.md +42 -0
  126. pymod/docs/hook-observer_valid_target.md +44 -0
  127. pymod/docs/hook-on_spawn_equip.md +44 -0
  128. pymod/docs/hook-pain.md +44 -0
  129. pymod/docs/hook-plant_bomb.md +46 -0
  130. pymod/docs/hook-player_blind.md +50 -0
  131. pymod/docs/hook-player_death_think.md +42 -0
  132. pymod/docs/hook-player_got_weapon.md +44 -0
  133. pymod/docs/hook-player_precache.md +42 -0
  134. pymod/docs/hook-pm_air_accelerate.md +45 -0
  135. pymod/docs/hook-pm_air_move.md +43 -0
  136. pymod/docs/hook-pm_check_water_jump.md +42 -0
  137. pymod/docs/hook-pm_duck.md +42 -0
  138. pymod/docs/hook-pm_init.md +43 -0
  139. pymod/docs/hook-pm_jump.md +42 -0
  140. pymod/docs/hook-pm_ladder_move.md +43 -0
  141. pymod/docs/hook-pm_move.md +44 -0
  142. pymod/docs/hook-pm_play_step_sound.md +44 -0
  143. pymod/docs/hook-pm_unduck.md +42 -0
  144. pymod/docs/hook-pm_update_step_sound.md +42 -0
  145. pymod/docs/hook-pm_water_jump.md +42 -0
  146. pymod/docs/hook-radio.md +46 -0
  147. pymod/docs/hook-radius_flash_trace.md +49 -0
  148. pymod/docs/hook-remove_all_items.md +43 -0
  149. pymod/docs/hook-remove_guns.md +42 -0
  150. pymod/docs/hook-remove_player_item.md +44 -0
  151. pymod/docs/hook-remove_spawn_protection.md +42 -0
  152. pymod/docs/hook-reset_max_speed.md +42 -0
  153. pymod/docs/hook-reset_sequence_info.md +42 -0
  154. pymod/docs/hook-restart_round.md +42 -0
  155. pymod/docs/hook-round_end.md +45 -0
  156. pymod/docs/hook-round_freeze_end.md +42 -0
  157. pymod/docs/hook-round_respawn.md +42 -0
  158. pymod/docs/hook-rules_player_killed.md +46 -0
  159. pymod/docs/hook-rules_player_spawn.md +42 -0
  160. pymod/docs/hook-rules_server_deactivate.md +42 -0
  161. pymod/docs/hook-rules_spawn_spot.md +42 -0
  162. pymod/docs/hook-rules_think.md +42 -0
  163. pymod/docs/hook-select_spawn_point.md +42 -0
  164. pymod/docs/hook-send_death_message.md +53 -0
  165. pymod/docs/hook-send_say_message.md +50 -0
  166. pymod/docs/hook-set_animation.md +43 -0
  167. pymod/docs/hook-set_spawn_protection.md +43 -0
  168. pymod/docs/hook-set_userinfo_model.md +44 -0
  169. pymod/docs/hook-set_userinfo_name.md +44 -0
  170. pymod/docs/hook-should_switch_weapon.md +44 -0
  171. pymod/docs/hook-show_menu.md +46 -0
  172. pymod/docs/hook-show_vgui_menu.md +45 -0
  173. pymod/docs/hook-smoke_explode.md +42 -0
  174. pymod/docs/hook-spawn.md +47 -0
  175. pymod/docs/hook-spawn_head_gib.md +44 -0
  176. pymod/docs/hook-spawn_random_gibs.md +46 -0
  177. pymod/docs/hook-start_death_cam.md +42 -0
  178. pymod/docs/hook-start_observer.md +44 -0
  179. pymod/docs/hook-switch_team.md +42 -0
  180. pymod/docs/hook-take_health.md +44 -0
  181. pymod/docs/hook-team_full.md +43 -0
  182. pymod/docs/hook-team_stacked.md +44 -0
  183. pymod/docs/hook-throw_flashbang.md +47 -0
  184. pymod/docs/hook-throw_grenade.md +48 -0
  185. pymod/docs/hook-throw_he_grenade.md +49 -0
  186. pymod/docs/hook-throw_smoke_grenade.md +48 -0
  187. pymod/docs/hook-trace_attack.md +48 -0
  188. pymod/docs/hook-update_client_data.md +42 -0
  189. pymod/docs/hook-update_status_bar.md +42 -0
  190. pymod/docs/hook-use_empty.md +42 -0
  191. pymod/docs/hook-weapon_animation.md +44 -0
  192. pymod/docs/hook-weapon_can_deploy.md +42 -0
  193. pymod/docs/hook-weapon_deploy.md +47 -0
  194. pymod/docs/hook-weapon_kickback.md +49 -0
  195. pymod/docs/hook-weapon_post_frame.md +42 -0
  196. pymod/docs/hook-weapon_reload.md +45 -0
  197. pymod/docs/hook-weapon_shotgun_reload.md +48 -0
  198. pymod/docs/hook-weaponbox_set_model.md +43 -0
  199. pymod/docs/hooks.md +202 -0
  200. pymod/docs/hud.md +56 -0
  201. pymod/docs/index.md +45 -0
  202. pymod/docs/installation.md +76 -0
  203. pymod/docs/methods.md +1065 -0
  204. pymod/docs/packages.md +129 -0
  205. pymod/docs/permissions.md +61 -0
  206. pymod/docs/publishing.md +29 -0
  207. pymod/docs/pycharm.md +116 -0
  208. pymod/docs/remote.md +120 -0
  209. pymod/docs/resources.md +63 -0
  210. pymod/docs/sdk.md +81 -0
  211. pymod/docs/sqlite.md +86 -0
  212. pymod/docs/stock.md +38 -0
  213. pymod/docs/syntax.md +113 -0
  214. pymod/docs/tested.md +43 -0
  215. pymod/docs/web.md +82 -0
  216. pymod/examples/async_io.py +11 -0
  217. pymod/examples/custom_events.py +12 -0
  218. pymod/examples/damage_guard.py +11 -0
  219. pymod/examples/healing_cooldown.py +20 -0
  220. pymod/examples/hello_world.py +9 -0
  221. pymod/examples/panel_extension.py +8 -0
  222. pymod/examples/player_hud.py +11 -0
  223. pymod/examples/resources_demo.py +11 -0
  224. pymod/examples/simple_config.py +16 -0
  225. pymod/examples/sqlite_counter.py +13 -0
  226. pymod/examples/training_zone.py +16 -0
  227. pymod/examples/web_profile.py +18 -0
  228. pymod/hook_events.py +9 -0
  229. pymod/hook_events.pyi +3376 -0
  230. pymod/preferences.py +214 -0
  231. pymod/py.typed +0 -0
  232. pymod/web.css +19 -0
  233. pymod/web.js +64 -0
  234. pymod/web.py +316 -0
  235. pymodcs-0.17.1.dist-info/METADATA +104 -0
  236. pymodcs-0.17.1.dist-info/RECORD +241 -0
  237. pymodcs-0.17.1.dist-info/WHEEL +5 -0
  238. pymodcs-0.17.1.dist-info/entry_points.txt +2 -0
  239. pymodcs-0.17.1.dist-info/licenses/LICENSE +340 -0
  240. pymodcs-0.17.1.dist-info/licenses/LINKING_EXCEPTION.txt +10 -0
  241. pymodcs-0.17.1.dist-info/top_level.txt +1 -0
pymod/__init__.py ADDED
@@ -0,0 +1,740 @@
1
+ """PyMod: class plugins, named events and optional ReGameDLL hooks for GoldSrc."""
2
+ from __future__ import annotations
3
+ import asyncio
4
+ import inspect
5
+ from dataclasses import dataclass
6
+ import math
7
+ import urllib.request
8
+ from ._sdk_native import native as _native, require_server, ServerRequiredError
9
+ __version__ = '0.17.1'
10
+
11
+ def _manager():
12
+ return require_server()
13
+
14
+ def log(message):
15
+ """RU:
16
+ Печатает сообщение в серверный журнал с контекстом плагина.
17
+ Параметры: message.
18
+
19
+ EN:
20
+ Writes a message to the server log in the plugin's context.
21
+ Parameters: message."""
22
+ _manager()
23
+ _native.log(str(message))
24
+ from ._movement import PlayerMovement, Vector, Movement, Buttons, PlayerFlags, MoveType
25
+ from ._color import Color
26
+ from ._position import Position
27
+ from ._player import PlayerActions, WeaponId, WeaponState, AmmoState, Rendering
28
+ from ._geometry import PlayerStateHelpers, Team, Box, Zone
29
+
30
+ class Player(PlayerMovement, PlayerActions, PlayerStateHelpers):
31
+ """RU:
32
+ Игрок и конкретное подключение. Сохранённый объект не начнёт управлять другим игроком при повторном использовании слота.
33
+ Примечания реализации / Implementation notes:
34
+ A connection handle, not a reusable player slot. Safe to retain over await.
35
+
36
+ EN:
37
+ A player connection handle. A retained object never targets a different connection reusing the same slot.
38
+ Implementation notes / Примечания реализации:
39
+ A connection handle, not a reusable player slot. Safe to retain over await."""
40
+ __slots__ = ('_index', '_token')
41
+
42
+ def __init__(self, index, token):
43
+ self._index = index
44
+ self._token = token
45
+
46
+ def __eq__(self, other):
47
+ if not isinstance(other, Player):
48
+ return NotImplemented
49
+ return (self.index, self.token) == (other.index, other.token)
50
+
51
+ def __hash__(self):
52
+ return hash((self.index, self.token))
53
+
54
+ @property
55
+ def index(self):
56
+ """RU:
57
+ Номер игрового слота.
58
+
59
+ EN:
60
+ Engine slot index."""
61
+ return self._index
62
+
63
+ @property
64
+ def token(self):
65
+ """RU:
66
+ Идентификатор конкретного подключения.
67
+
68
+ EN:
69
+ Token identifying this exact connection."""
70
+ return self._token
71
+
72
+ @property
73
+ def entity(self):
74
+ """RU:
75
+ Игровой объект игрока.
76
+
77
+ EN:
78
+ The player's game entity."""
79
+ if not self.connected:
80
+ raise RuntimeError('Player disconnected or belongs to a previous map')
81
+ result = entities.get(self.index)
82
+ if result is None or result.key[3] != self.token:
83
+ raise RuntimeError('Player entity is no longer valid')
84
+ return result
85
+
86
+ @property
87
+ def connected(self):
88
+ """RU:
89
+ Подключён ли ещё именно этот игрок.
90
+
91
+ EN:
92
+ Whether this exact player connection is still active."""
93
+ _manager()
94
+ return bool(_native.valid(self.index, self.token))
95
+
96
+ def _info(self):
97
+ _manager()
98
+ return _native.player_info(self.index, self.token)
99
+
100
+ @property
101
+ def name(self):
102
+ """RU:
103
+ Текущее имя игрока.
104
+
105
+ EN:
106
+ Current player name."""
107
+ return self._info()['name']
108
+
109
+ @property
110
+ def steam_id(self):
111
+ """RU:
112
+ Идентификатор Steam подключения.
113
+
114
+ EN:
115
+ Connection Steam ID."""
116
+ return self._info()['steam_id']
117
+
118
+ @property
119
+ def userid(self):
120
+ """RU:
121
+ Серверный userid подключения.
122
+
123
+ EN:
124
+ Server userid for this connection."""
125
+ return self._info()['userid']
126
+
127
+ @property
128
+ def alive(self):
129
+ """RU:
130
+ Жив ли игрок.
131
+
132
+ EN:
133
+ Whether the player is alive."""
134
+ return bool(self._info()['alive'])
135
+
136
+ @property
137
+ def observing(self):
138
+ """RU:
139
+ Режим наблюдения, включая мёртвых игроков.
140
+ Примечания реализации / Implementation notes:
141
+ In observer mode, including spectators and dead players watching others.
142
+
143
+ EN:
144
+ Observer mode, including dead players watching others.
145
+ Implementation notes / Примечания реализации:
146
+ In observer mode, including spectators and dead players watching others."""
147
+ return bool(self._info()['observing'])
148
+
149
+ @property
150
+ def health(self):
151
+ """RU:
152
+ Количество здоровья игрока.
153
+
154
+ EN:
155
+ The player's health."""
156
+ return self._info()['health']
157
+
158
+ @health.setter
159
+ def health(self, value):
160
+ """RU:
161
+ API PyMod: Player.health. Сигнатура и исходные примечания приведены ниже.
162
+ Параметры: value.
163
+
164
+ EN:
165
+ PyMod API: Player.health. See the signature and original implementation notes below.
166
+ Parameters: value."""
167
+ _manager()
168
+ value = float(value)
169
+ if not math.isfinite(value) or not 1 <= value <= 100000:
170
+ raise ValueError('health must be finite and between 1 and 100000')
171
+ _native.set_health(self.index, self.token, value)
172
+
173
+ @property
174
+ def armor(self):
175
+ """RU:
176
+ Количество брони игрока.
177
+
178
+ EN:
179
+ The player's armor."""
180
+ return self._info()['armor']
181
+
182
+ @armor.setter
183
+ def armor(self, value):
184
+ """RU:
185
+ API PyMod: Player.armor. Сигнатура и исходные примечания приведены ниже.
186
+ Параметры: value.
187
+
188
+ EN:
189
+ PyMod API: Player.armor. See the signature and original implementation notes below.
190
+ Parameters: value."""
191
+ _manager()
192
+ value = float(value)
193
+ if not math.isfinite(value) or not 0 <= value <= 100000:
194
+ raise ValueError('armor must be finite and between 0 and 100000')
195
+ _native.set_armor(self.index, self.token, value)
196
+
197
+ def say(self, message, *, color=None, prefix=None, **values):
198
+ """RU:
199
+ Отправляет сообщение игроку. color — цвет, prefix — префикс этого сообщения; цвета чата ограничены палитрой CS.
200
+ Параметры: message, color, prefix.
201
+
202
+ EN:
203
+ Sends a player a chat message. color sets color and prefix sets this message's prefix; CS chat has a limited palette.
204
+ Parameters: message, color, prefix."""
205
+ from ._chat import send
206
+ send(self, message, color=color, prefix=prefix, **values)
207
+ message = say
208
+
209
+ def in_group(self, name):
210
+ """RU:
211
+ Проверяет членство игрока в указанной группе.
212
+ Параметры: name.
213
+
214
+ EN:
215
+ Checks whether the player belongs to the named group.
216
+ Parameters: name."""
217
+ return name in permissions.groups(self)
218
+
219
+ def sound(self, resource):
220
+ """RU:
221
+ API PyMod: Player.sound. Сигнатура и исходные примечания приведены ниже.
222
+ Параметры: resource.
223
+
224
+ EN:
225
+ PyMod API: Player.sound. See the signature and original implementation notes below.
226
+ Parameters: resource."""
227
+ from ._assets import Sound
228
+ return (resource if isinstance(resource, Sound) else Sound(resource)).play(self)
229
+
230
+ def hud(self, text, *, color=None, seconds=3, position=None, x=-1, y=0.2, channel=None):
231
+ """RU:
232
+ Показывает HUD-сообщение: text — текст, seconds — секунды, position — Position или имя, color — Color/HEX/RGB.
233
+ Параметры: text, color, seconds, position, x, y, channel.
234
+ Примечания реализации / Implementation notes:
235
+ One line of plugin code for a colored HUD notice; accepts RGB or HEX.
236
+
237
+ EN:
238
+ Shows a HUD message: text, seconds of visibility, position as Position or a name, and color as Color/HEX/RGB.
239
+ Parameters: text, color, seconds, position, x, y, channel.
240
+ Implementation notes / Примечания реализации:
241
+ One line of plugin code for a colored HUD notice; accepts RGB or HEX."""
242
+ return Hud(color=color or Color.Cyan(), hold=seconds, position=position, x=x, y=y, fade_in=0, fade_out=0).show(self, text, channel=channel)
243
+
244
+ def console(self, message):
245
+ """RU:
246
+ Печатает сообщение в игровой консоли игрока.
247
+ Параметры: message.
248
+
249
+ EN:
250
+ Prints a message in the player's game console.
251
+ Parameters: message."""
252
+ _manager()
253
+ _native.console_print(self.index, self.token, str(message))
254
+
255
+ def kick(self, reason='Kicked by administrator'):
256
+ """RU:
257
+ Отключает конкретное подключение игрока с указанной причиной.
258
+ Параметры: reason.
259
+
260
+ EN:
261
+ Disconnects this exact player connection with the supplied reason.
262
+ Parameters: reason."""
263
+ userid = self.userid
264
+ if userid <= 0:
265
+ raise ValueError('Player has no engine userid')
266
+ server.command('kick', f'#{userid}', reason)
267
+
268
+ def has_permission(self, permission):
269
+ """RU:
270
+ Проверяет наличие права у игрока с учётом групп и срока действия.
271
+ Параметры: permission.
272
+
273
+ EN:
274
+ Checks a player's permission including group membership and expiration.
275
+ Parameters: permission."""
276
+ return permissions.has(self, permission)
277
+
278
+ def can(self, permission, *, description=None):
279
+ """RU:
280
+ Проверяет прямые права игрока и права его групп. Регистрирует неизвестное право, но не выдаёт его.
281
+ Параметры: permission, description.
282
+ Примечания реализации / Implementation notes:
283
+ Effective permission: direct + inherited groups, with live expiry/revocation.
284
+
285
+ EN:
286
+ Checks direct and inherited group permissions. Registers an unknown permission but never grants it.
287
+ Parameters: permission, description.
288
+ Implementation notes / Примечания реализации:
289
+ Effective permission: direct + inherited groups, with live expiry/revocation."""
290
+ from ._easy import remember
291
+ remember(permission)
292
+ if description is not None:
293
+ permissions.register(permission, description)
294
+ return permissions.has(self, permission)
295
+
296
+ def can_any(self, *names):
297
+ """RU:
298
+ Истина, если у игрока есть хотя бы одно из перечисленных прав.
299
+ Примечания реализации / Implementation notes:
300
+ Any exact permission; all names are automatically registered.
301
+
302
+ EN:
303
+ True if the player has at least one of the listed permissions.
304
+ Implementation notes / Примечания реализации:
305
+ Any exact permission; all names are automatically registered."""
306
+ return any(tuple((self.can(name) for name in names)))
307
+
308
+ def can_all(self, *names):
309
+ """RU:
310
+ Истина, если у игрока есть все перечисленные права.
311
+ Примечания реализации / Implementation notes:
312
+ Every exact permission; an empty request never grants access.
313
+
314
+ EN:
315
+ True if the player has every listed permission.
316
+ Implementation notes / Примечания реализации:
317
+ Every exact permission; an empty request never grants access."""
318
+ return bool(names) and all(tuple((self.can(name) for name in names)))
319
+
320
+ def heal(self, amount=100, *, maximum=None, damage_type=0):
321
+ """RU:
322
+ Лечит игрока. Ограниченный вариант с maximum возвращает фактическую прибавку; обычный вызов использует GameDLL.
323
+ Параметры: amount, maximum, damage_type.
324
+ Примечания реализации / Implementation notes:
325
+ Normal healing uses GameDLL; explicit maximum permits bounded bonus HP.
326
+
327
+ EN:
328
+ Heals a player. The bounded maximum variant returns the actual increase; the ordinary call uses GameDLL.
329
+ Parameters: amount, maximum, damage_type.
330
+ Implementation notes / Примечания реализации:
331
+ Normal healing uses GameDLL; explicit maximum permits bounded bonus HP."""
332
+ if maximum is None:
333
+ return PlayerActions.heal(self, amount, damage_type=damage_type)
334
+ from ._easy import increase
335
+ return increase(self, 'health', amount, maximum)
336
+
337
+ def add_armor(self, amount=100, *, maximum=100):
338
+ """RU:
339
+ Добавляет броню с ограничением maximum и возвращает фактическую прибавку.
340
+ Параметры: amount, maximum.
341
+ Примечания реализации / Implementation notes:
342
+ Add armor points up to maximum; helmet/equipment type is unchanged.
343
+
344
+ EN:
345
+ Adds armor up to maximum and returns the actual increase.
346
+ Parameters: amount, maximum.
347
+ Implementation notes / Примечания реализации:
348
+ Add armor points up to maximum; helmet/equipment type is unchanged."""
349
+ from ._easy import increase
350
+ return increase(self, 'armor', amount, maximum)
351
+
352
+ def motd(self, content, title='PyMod'):
353
+ """RU:
354
+ Открывает MOTD игрока с указанным содержимым и заголовком.
355
+ Параметры: content, title.
356
+ Примечания реализации / Implementation notes:
357
+ Send HTML/text/URL (1..1200 UTF-8 bytes); rendering depends on client.
358
+
359
+ EN:
360
+ Opens the player's MOTD using the supplied content and title.
361
+ Parameters: content, title.
362
+ Implementation notes / Примечания реализации:
363
+ Send HTML/text/URL (1..1200 UTF-8 bytes); rendering depends on client."""
364
+ _manager()
365
+ if not isinstance(content, str) or not isinstance(title, str) or '\x00' in content or ('\x00' in title):
366
+ raise ValueError('MOTD content/title must be strings without NUL')
367
+ _native.motd(self.index, self.token, content, title)
368
+
369
+ @dataclass(frozen=True)
370
+ class Disconnect:
371
+ """RU:
372
+ API PyMod: Disconnect. Сигнатура и исходные примечания приведены ниже.
373
+ Примечания реализации / Implementation notes:
374
+ Snapshot: the departed player is no longer a valid game handle.
375
+
376
+ EN:
377
+ PyMod API: Disconnect. See the signature and original implementation notes below.
378
+ Implementation notes / Примечания реализации:
379
+ Snapshot: the departed player is no longer a valid game handle."""
380
+ index: int
381
+ name: str
382
+ steam_id: str
383
+ userid: int
384
+
385
+ def players(*, alive=None, team=None, permission=None, exclude=None):
386
+ """RU:
387
+ API PyMod: players. Сигнатура и исходные примечания приведены ниже.
388
+ Параметры: alive, team, permission, exclude.
389
+
390
+ EN:
391
+ PyMod API: players. See the signature and original implementation notes below.
392
+ Parameters: alive, team, permission, exclude."""
393
+ _manager()
394
+ if alive is not None and type(alive) is not bool:
395
+ raise ValueError('alive must be bool or None')
396
+ if team is not None and team not in ('CT', 'TERRORIST', 'SPECTATOR', 'UNASSIGNED'):
397
+ raise ValueError('Invalid team')
398
+ if exclude is not None and (not isinstance(exclude, Player)):
399
+ raise TypeError('exclude must be Player')
400
+ if permission is not None:
401
+ from ._easy import remember
402
+ remember(permission)
403
+ result = []
404
+ for index, token in _native.players():
405
+ p = Player(index, token)
406
+ if exclude is not None and (index, token) == (exclude.index, exclude.token):
407
+ continue
408
+ if alive is not None and p.alive != alive:
409
+ continue
410
+ if team is not None and p.team != team:
411
+ continue
412
+ if permission is not None and (not permissions.has(p, permission)):
413
+ continue
414
+ result.append(p)
415
+ return result
416
+
417
+ def broadcast(message, **options):
418
+ """RU:
419
+ API PyMod: broadcast. Сигнатура и исходные примечания приведены ниже.
420
+ Параметры: message.
421
+
422
+ EN:
423
+ PyMod API: broadcast. See the signature and original implementation notes below.
424
+ Parameters: message."""
425
+ for player in players():
426
+ player.say(message, **options)
427
+
428
+ def cvar(name, value=None):
429
+ """RU:
430
+ API PyMod: cvar. Сигнатура и исходные примечания приведены ниже.
431
+ Параметры: name, value.
432
+
433
+ EN:
434
+ PyMod API: cvar. See the signature and original implementation notes below.
435
+ Parameters: name, value."""
436
+ _manager()
437
+ if value is None:
438
+ return _native.cvar_get(str(name))
439
+ _native.cvar_set(str(name), str(value))
440
+
441
+ class events:
442
+ """RU:
443
+ API PyMod: events. Сигнатура и исходные примечания приведены ниже.
444
+
445
+ EN:
446
+ PyMod API: events. See the signature and original implementation notes below."""
447
+
448
+ @staticmethod
449
+ def on(name):
450
+ """RU:
451
+ API PyMod: events.on. Сигнатура и исходные примечания приведены ниже.
452
+ Параметры: name.
453
+ Примечания реализации / Implementation notes:
454
+ Events: map_start(str), player_join(Player), player_disconnect(Disconnect).
455
+
456
+ EN:
457
+ PyMod API: events.on. See the signature and original implementation notes below.
458
+ Parameters: name.
459
+ Implementation notes / Примечания реализации:
460
+ Events: map_start(str), player_join(Player), player_disconnect(Disconnect)."""
461
+
462
+ def decorate(callback):
463
+ _manager().register_event(name, callback)
464
+ return callback
465
+ return decorate
466
+
467
+ @staticmethod
468
+ def filter(name):
469
+ """RU:
470
+ API PyMod: events.filter. Сигнатура и исходные примечания приведены ниже.
471
+ Параметры: name.
472
+ Примечания реализации / Implementation notes:
473
+ Synchronous filter: True consumes. chat_format runs after chat moderation
474
+ and only for messages that are not registered chat commands.
475
+
476
+ EN:
477
+ PyMod API: events.filter. See the signature and original implementation notes below.
478
+ Parameters: name.
479
+ Implementation notes / Примечания реализации:
480
+ Synchronous filter: True consumes. chat_format runs after chat moderation
481
+ and only for messages that are not registered chat commands."""
482
+
483
+ def decorate(callback):
484
+ _manager().register_filter(name, callback)
485
+ return callback
486
+ return decorate
487
+
488
+ class commands:
489
+ """RU:
490
+ API PyMod: commands. Сигнатура и исходные примечания приведены ниже.
491
+
492
+ EN:
493
+ PyMod API: commands. See the signature and original implementation notes below."""
494
+
495
+ @staticmethod
496
+ def client(name, *, permission=None):
497
+ """RU:
498
+ API PyMod: commands.client. Сигнатура и исходные примечания приведены ниже.
499
+ Параметры: name, permission.
500
+ Примечания реализации / Implementation notes:
501
+ Client-only command (including legacy binds); callback(context, args).
502
+
503
+ Uses an owned ClientCommand filter, so unloading restores engine handling.
504
+ Server/RCON commands continue to use the py_ namespace via console().
505
+
506
+ EN:
507
+ PyMod API: commands.client. See the signature and original implementation notes below.
508
+ Parameters: name, permission.
509
+ Implementation notes / Примечания реализации:
510
+ Client-only command (including legacy binds); callback(context, args).
511
+
512
+ Uses an owned ClientCommand filter, so unloading restores engine handling.
513
+ Server/RCON commands continue to use the py_ namespace via console()."""
514
+ import re
515
+ if not isinstance(name, str) or not re.fullmatch('[+\\-]?[a-zA-Z_][a-zA-Z0-9_]{0,62}', name):
516
+ raise ValueError('Invalid client command name')
517
+ if name.lower() in {'say', 'say_team', 'menuselect', 'pymod', 'pymodmenu'}:
518
+ raise ValueError('Use the dedicated chat/menu/core command API')
519
+ if permission is not None:
520
+ from ._services import validate_permission
521
+ validate_permission(permission)
522
+
523
+ def decorate(callback):
524
+ if not callable(callback):
525
+ raise TypeError('Command callback must be callable')
526
+
527
+ def intercept(player, command, args):
528
+ if command.lower() != name.lower():
529
+ return False
530
+ if permission and (not player.has_permission(permission)):
531
+ player.console('Missing permission: ' + permission)
532
+ return True
533
+
534
+ async def invoke():
535
+ if not player.connected:
536
+ return
537
+ try:
538
+ if permission:
539
+ permissions.require(player, permission)
540
+ from ._services import CommandContext
541
+ result = callback(CommandContext(player), list(args))
542
+ if inspect.isawaitable(result):
543
+ await result
544
+ except (ValueError, PermissionError) as error:
545
+ if player.connected:
546
+ player.console(str(error)[:200])
547
+ timers.after(0, invoke)
548
+ return True
549
+ manager = _manager()
550
+ if any((getattr(cb, 'client_command_name', None) == name.lower() for _, cb in manager.client_filters)):
551
+ raise ValueError('Client command already registered: ' + name)
552
+ intercept.client_command_name = name.lower()
553
+ manager.register_filter('client_command', intercept)
554
+ return callback
555
+ return decorate
556
+
557
+ @staticmethod
558
+ def chat(name, *, consume=True, permission=None, help=''):
559
+ """RU:
560
+ API PyMod: commands.chat. Сигнатура и исходные примечания приведены ниже.
561
+ Параметры: name, consume, permission, help.
562
+ Примечания реализации / Implementation notes:
563
+ Handler(player, args: list[str]); consume decision is synchronous.
564
+
565
+ EN:
566
+ PyMod API: commands.chat. See the signature and original implementation notes below.
567
+ Parameters: name, consume, permission, help.
568
+ Implementation notes / Примечания реализации:
569
+ Handler(player, args: list[str]); consume decision is synchronous."""
570
+
571
+ def decorate(callback):
572
+ _manager().register_command(name, callback, bool(consume), permission, help)
573
+ return callback
574
+ return decorate
575
+
576
+ @staticmethod
577
+ def console(name, *, permission=None, help=''):
578
+ """RU:
579
+ Печатает сообщение в игровой консоли игрока.
580
+ Параметры: name, permission, help.
581
+ Примечания реализации / Implementation notes:
582
+ Handler(context, args); available in both client and server console.
583
+
584
+ EN:
585
+ Prints a message in the player's game console.
586
+ Parameters: name, permission, help.
587
+ Implementation notes / Примечания реализации:
588
+ Handler(context, args); available in both client and server console."""
589
+
590
+ def decorate(callback):
591
+ _manager().register_console(name, callback, permission, help)
592
+ return callback
593
+ return decorate
594
+
595
+ @staticmethod
596
+ def catalog(player=None):
597
+ """RU:
598
+ API PyMod: commands.catalog. Сигнатура и исходные примечания приведены ниже.
599
+ Параметры: player.
600
+
601
+ EN:
602
+ PyMod API: commands.catalog. See the signature and original implementation notes below.
603
+ Parameters: player."""
604
+ return _manager().command_catalog(player)
605
+
606
+ class timers:
607
+ """RU:
608
+ API PyMod: timers. Сигнатура и исходные примечания приведены ниже.
609
+
610
+ EN:
611
+ PyMod API: timers. See the signature and original implementation notes below."""
612
+
613
+ @staticmethod
614
+ def every(seconds):
615
+ """RU:
616
+ Запускает метод периодически с интервалом в секундах, пока плагин активен.
617
+ Параметры: seconds.
618
+ Примечания реализации / Implementation notes:
619
+ A non-overlapping repeating async timer, restarted on each map.
620
+
621
+ EN:
622
+ Runs a method periodically at the given interval in seconds while the plugin is active.
623
+ Parameters: seconds.
624
+ Implementation notes / Примечания реализации:
625
+ A non-overlapping repeating async timer, restarted on each map."""
626
+ seconds = float(seconds)
627
+ if not math.isfinite(seconds) or seconds < 0.05:
628
+ raise ValueError('timer interval must be finite and >= 0.05 seconds')
629
+
630
+ def decorate(callback):
631
+ _manager().register_timer(seconds, callback)
632
+ return callback
633
+ return decorate
634
+
635
+ @staticmethod
636
+ def after(seconds, callback, *args):
637
+ """RU:
638
+ API PyMod: timers.after. Сигнатура и исходные примечания приведены ниже.
639
+ Параметры: seconds, callback.
640
+
641
+ EN:
642
+ PyMod API: timers.after. See the signature and original implementation notes below.
643
+ Parameters: seconds, callback."""
644
+ seconds = float(seconds)
645
+ if not math.isfinite(seconds) or seconds < 0:
646
+ raise ValueError('delay must be finite and nonnegative')
647
+
648
+ async def delayed():
649
+ await asyncio.sleep(seconds)
650
+ await _manager().invoke(callback, *args)
651
+ return create_task(delayed())
652
+
653
+ def create_task(coroutine):
654
+ """RU:
655
+ API PyMod: create_task. Сигнатура и исходные примечания приведены ниже.
656
+ Параметры: coroutine.
657
+ Примечания реализации / Implementation notes:
658
+ Owned task: automatically cancelled on reload, unload and map change.
659
+
660
+ EN:
661
+ PyMod API: create_task. See the signature and original implementation notes below.
662
+ Parameters: coroutine.
663
+ Implementation notes / Примечания реализации:
664
+ Owned task: automatically cancelled on reload, unload and map change."""
665
+ return _manager().spawn(coroutine)
666
+
667
+ async def run_in_thread(function, /, *args, **kwargs):
668
+ """RU:
669
+ API PyMod: run_in_thread. Сигнатура и исходные примечания приведены ниже.
670
+ Параметры: function.
671
+ Асинхронный метод: используйте await.
672
+ Примечания реализации / Implementation notes:
673
+ Run blocking I/O in a worker. Game APIs are prohibited in that worker.
674
+
675
+ Reload waits for the worker to finish. Always use I/O timeouts.
676
+
677
+ EN:
678
+ PyMod API: run_in_thread. See the signature and original implementation notes below.
679
+ Parameters: function.
680
+ Asynchronous method: use await.
681
+ Implementation notes / Примечания реализации:
682
+ Run blocking I/O in a worker. Game APIs are prohibited in that worker.
683
+
684
+ Reload waits for the worker to finish. Always use I/O timeouts."""
685
+ return await _manager().run_in_thread(function, *args, **kwargs)
686
+
687
+ async def http_get(url, *, timeout=5.0, max_bytes=1048576):
688
+ """RU:
689
+ API PyMod: http_get. Сигнатура и исходные примечания приведены ниже.
690
+ Параметры: url, timeout, max_bytes.
691
+ Асинхронный метод: используйте await.
692
+ Примечания реализации / Implementation notes:
693
+ GET http(s); return bytes. Uses the bundled stdlib, no pip packages needed.
694
+
695
+ EN:
696
+ PyMod API: http_get. See the signature and original implementation notes below.
697
+ Parameters: url, timeout, max_bytes.
698
+ Asynchronous method: use await.
699
+ Implementation notes / Примечания реализации:
700
+ GET http(s); return bytes. Uses the bundled stdlib, no pip packages needed."""
701
+ if not isinstance(url, str) or not url.startswith(('https://', 'http://')):
702
+ raise ValueError('an http:// or https:// URL is required')
703
+ if not math.isfinite(timeout) or not 0 < timeout <= 60:
704
+ raise ValueError('timeout must be between 0 and 60 seconds')
705
+ if not isinstance(max_bytes, int) or not 1 <= max_bytes <= 16 * 1024 * 1024:
706
+ raise ValueError('max_bytes must be between 1 and 16777216')
707
+
708
+ def request():
709
+ with urllib.request.urlopen(url, timeout=timeout) as response:
710
+ data = response.read(max_bytes + 1)
711
+ if len(data) > max_bytes:
712
+ raise ValueError('HTTP response exceeds max_bytes')
713
+ return data
714
+ return await run_in_thread(request)
715
+ from ._services import CommandContext, config, storage, permissions, targeting, server
716
+ from ._database import database
717
+ from ._menus import Menu, MenuItem
718
+ from . import web
719
+ from ._hooks import hooks, HookResult
720
+ from ._entities import Entity, EntityState, entities, trace, Trace, Hull, Solid, sounds
721
+ from ._resources import resources
722
+ from ._cvars import cvars, Cvar, CvarFlags, CvarChange
723
+ from ._hud import Hud
724
+ from ._events import PlayerSpawn, PlayerDeath, RoundEvent, GameMessage
725
+ from ._cs import cs, CsInfo, CsContext, CsSnapshot, DamageType, CsHookContext, CsHookSnapshot
726
+ from ._cs_types import CsTrace, CsMovement, CsPhysent, GameRulesState, CsLifecycleRecord
727
+ from ._effects import effects
728
+ from ._messages import Packet
729
+ from ._plugins import services
730
+ from ._voice import voice
731
+ from ._chat import chat
732
+ from ._i18n import i18n, tr
733
+ from ._simple_db import SQLite, Table, Record
734
+ from ._assets import Model, Sound, Sprite, Music
735
+ from ._plugin import Plugin, Players, PlayerGroup, command, console_command, every, hook, listen, service, page
736
+ from . import hook_events
737
+ from ._hook_api import Event, HookEvent, HookSnapshot
738
+ from ._timing import Cooldown, chance, wait
739
+ from ._schema import Config, ConfigField
740
+ __all__ = ['log', 'PlayerMovement', 'Vector', 'Movement', 'Buttons', 'PlayerFlags', 'MoveType', 'Color', 'Position', 'PlayerActions', 'WeaponId', 'WeaponState', 'AmmoState', 'Rendering', 'PlayerStateHelpers', 'Team', 'Box', 'Zone', 'Player', 'Disconnect', 'players', 'broadcast', 'cvar', 'events', 'commands', 'timers', 'create_task', 'run_in_thread', 'http_get', 'CommandContext', 'config', 'storage', 'permissions', 'targeting', 'server', 'database', 'Menu', 'MenuItem', 'web', 'hooks', 'HookResult', 'Entity', 'EntityState', 'entities', 'trace', 'Trace', 'Hull', 'Solid', 'sounds', 'resources', 'cvars', 'Cvar', 'CvarFlags', 'CvarChange', 'Hud', 'PlayerSpawn', 'PlayerDeath', 'RoundEvent', 'GameMessage', 'cs', 'CsInfo', 'CsContext', 'CsSnapshot', 'DamageType', 'CsHookContext', 'CsHookSnapshot', 'CsTrace', 'CsMovement', 'CsPhysent', 'GameRulesState', 'CsLifecycleRecord', 'effects', 'Packet', 'services', 'voice', 'chat', 'i18n', 'tr', 'SQLite', 'Table', 'Record', 'Model', 'Sound', 'Sprite', 'Music', 'Plugin', 'Players', 'PlayerGroup', 'command', 'console_command', 'every', 'hook', 'listen', 'service', 'page', 'hook_events', 'Event', 'HookEvent', 'HookSnapshot', 'Cooldown', 'chance', 'wait', 'Config', 'ConfigField', 'ServerRequiredError']