Событие это момент в игре, о котором клиент готов тебе сообщить: наступил новый тик, игрок ударил, сломался блок, пришёл кадр для отрисовки. Ты пишешь функцию и просишь звать её в этот момент.
Без событий скрипт мёртвый: файл один раз выполнится, и на этом всё. Именно события оживляют скрипт и заставляют его работать всё время, пока идёт игра.
Подписка выглядит как строчка @events.имя_события прямо над функцией. Функцию можно называть как угодно, важна строчка сверху.
@events.tick
def on_tick(event):
client.overlay("тик") # надпись обновляется 20 раз в секундуАргумент event обязателен, даже если он не нужен. В нём лежат подробности случившегося, и у каждого события они свои: у удара это цель, у отрисовки холст, у сообщения текст. Спрашивают их методами, например event.getEntity().
Событий ровно 50, это всё, что клиент отдаёт скриптам. Придумать своё имя нельзя: подписка на несуществующее событие просто никогда не сработает.
Колонка «Отмена» показывает, можно ли не дать событию случиться. Про это следующий раздел. Имя события в таблице это ссылка: на своей странице у каждого расписано, когда оно приходит, что можно у него спросить, и лежат рабочие примеры.
| Событие | Когда приходит | Отмена |
|---|---|---|
tick | Каждый тик игрока, 20 раз в секунду | нет |
motion | Перед отправкой твоей позиции на сервер | да |
input | Обработка нажатий на движение | нет |
move_post | Сразу после расчёта движения | нет |
travel_post | После перемещения, видна прошлая скорость | нет |
slowdown | Замедление от еды, лука или щита | да |
attack | Игрок атакует кого-то | да |
after_attack | Сразу после атаки | да |
block_break | Игрок ломает блок | да |
block_place | Игрок ставит блок | нет |
entity_jump | Любое живое существо прыгает | да |
pickup | Подобран предмет | нет |
firework | Разгон фейерверком в элитрах | нет |
rotate_camera | Игрок крутит камеру мышью | да |
send_message | Ты отправляешь сообщение в чат | да |
world_change | Сменился мир: вход на сервер, портал, реконнект | нет |
send_packet | Данные уходят на сервер | да |
receive_packet | Данные пришли с сервера | да |
connect | Подключение к серверу | нет |
render_2d | Рисование поверх экрана | нет |
pre_render_2d | То же, но под интерфейсом клиента | нет |
render_3d | Рисование прямо в мире | нет |
pre_render_3d | То же, но до мира: рисунок прячется за блоками | нет |
module_toggled | Включили или выключили любой модуль | нет |
newton_started | Newton взял новую задачу | нет |
newton_path | Маршрут построен или пересчитан | нет |
newton_node | Пройден очередной шаг маршрута | нет |
newton_finished | Задача Newton выполнена | нет |
newton_failed | Задача Newton сорвалась или отменена | нет |
game_tick | Тик самой игры, идёт и вне мира | нет |
tick_end | Конец тика игрока, движение уже посчитано | нет |
keep_sprint | Удар вот-вот погасит разгон | да |
trace | Луч взгляда: что сейчас под прицелом | да |
collision_shape | Игра спрашивает форму столкновения блока | да |
sound | Клиент проигрывает звук | нет |
entity_death | Живое существо умерло | нет |
finish_eat | Игрок доел или допил предмет | нет |
start_break_block | Первый удар по блоку | да |
break_totem | У кого-то сработал тотем | нет |
set_cooldown | Сервер ставит предмету перезарядку | нет |
close_screen | Игрок закрывает открытый экран | нет |
container_click | Клик мышью в контейнере | нет |
hand_render | Рисуется рука с предметом | да |
camera_update | Камера посчитала позицию и углы | нет |
game_render | Кадр игры, приходит и в меню | нет |
notification | Клиент показал уведомление | нет |
key | Клавишу нажали или отпустили | да |
char_typed | Игрок ввёл символ | да |
mouse | Кнопку мыши нажали или отпустили | да |
scroll | Прокрутка колеса мыши | да |
mouse_move | Курсор сдвинулся | нет |
language_changed | Игрок сменил язык клиента | нет |
menu_render | Кадр меню клиента, до того как оно нарисует себя | нет |
post_menu_render | Кадр меню клиента, поверх его панелей | нет |
hud_render | Кадр HUD клиента, под его элементами | нет |
post_hud_render | Кадр HUD клиента, поверх его элементов | нет |
effect_added | На игрока повесили эффект или подняли его уровень | нет |
effect_removed | С игрока спал эффект | нет |
Событие это не уведомление о том, что всё уже случилось. Это момент, когда ты ещё можешь вмешаться. У событий с пометкой «да» в таблице есть event.cancel(): после него действие просто не произойдёт.
BAD = ["/login", "/reg", "password"]
@events.send_message
def on_send(event):
text = event.getMessage()
if any(word in text.lower() for word in BAD):
event.cancel() # сообщение не уйдёт на сервер
client.warn("Заблокировано: похоже на пароль")Кроме отмены бывает подмена: у сообщения есть setMessage, у координат setY и так далее. Ты меняешь значение, и дальше пойдёт уже твоё.
@events.send_message
def on_send(event):
text = event.getMessage()
event.setMessage(text + " (отправлено скриптом)")Если событие уже отменил кто-то другой, это видно через event.isCancelled(). Пригодится, чтобы не делать лишнюю работу.
Ошибка внутри такой функции обходится дорого: клиент пишет в чат [Python Error] и выгружает весь скрипт целиком, вместе с модулями и всем, что он нарисовал.
Почти все падения случаются по трём причинам.
world.ingame() в начале.None.try / except.@events.tick
def on_tick(event):
if not mod.isEnabled() or not world.ingame():
return
target = world.nearest_player()
if target is None: # рядом никого, тихо выходим
return
client.overlay("ближайший на высоте %.1f" % target.getY())