> For the complete documentation index, see [llms.txt](https://docs.nursultan.fun/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nursultan.fun/scripting-api-ru/sobytiya/podpiska-na-sobytiya.md).

# Подписка на события

Событие — это момент в игре, на который скрипт может отреагировать: наступил тик, игрок ударил, пришёл пакет, нарисовался кадр.

## Как подписаться

```kotlin
on<ClientTickEvent> {
    // каждый игровой тик
}

on<AttackEvent> { e ->
    chat.print("бью " + e.target().name())
}
```

Тип события пишется в угловых скобках, а сам объект приходит в лямбду. Если параметр не именовать, он доступен как `it`.

Подписка живёт, пока скрипт **включён**. Выключил — обработчики отписались, включил — подписались обратно. Специально ничего чистить не надо.

## Приоритет

Если на одно событие подписаны несколько модулей и скриптов, порядок задаётся приоритетом:

```kotlin
on<MoveInputEvent>(priority = Priority.LAST) {
    it.sprint(false)
}
```

| Приоритет | Когда вызывается |
| --------- | ---------------- |
| `FIRST`   | раньше всех      |
| `EARLY`   |                  |
| `NORMAL`  | по умолчанию     |
| `LATE`    |                  |
| `LAST`    | позже всех       |

`LAST` берут, когда нужно сказать последнее слово — например, дожать ввод после всех остальных. `FIRST` — когда надо успеть отменить событие до того, как его увидят другие.

## Отмена

Часть событий можно отменить, и тогда игра не сделает то, что собиралась:

```kotlin
on<UseItemEvent> { e ->
    if (inventory.held().isA("ender_pearl")) {
        e.cancel()          // не дать использовать
    }
}
```

Отменяемые события помечены в [списке](/scripting-api-ru/sobytiya/spisok-sobytii.md). Отменённое событие по умолчанию до твоего обработчика не доходит — если ты всё равно хочешь его видеть, попроси об этом явно:

```kotlin
on<AttackEvent>(ignoreCancelled = true) { e ->
    chat.print("кто-то отменил атаку по " + e.target().name())
}
```

## Отписаться раньше

`on` возвращает подписку — её можно снять руками:

```kotlin
val sub = on<ClientTickEvent> { ... }

sub.unsubscribe()
sub.active()
```

Нужно это редко: обычно достаточно того, что подписки снимаются при выключении скрипта.

## На каком потоке всё это

Почти все события приходят на клиентском потоке — можно спокойно читать мир и что-то делать.

Исключение — **пакеты**: `PacketReceiveEvent` и `PacketSendEvent` вызываются на сетевом потоке. Читать поля пакета там можно, а вот трогать мир, игрока или инвентарь — нет. Для этого перепрыгни на клиентский поток:

```kotlin
on<PacketReceiveEvent> { e ->
    val packet = e.packet()
    if (packet !is S2CEntityVelocityPacket) return@on
    onClientThread {
        control.jump()
    }
}
```

Подробнее — [Пакеты](/scripting-api-ru/deistviya/pakety.md).

## Не вешай игру

Обработчик должен успевать за 250 мс. Не успел — клиент считает, что скрипт завис, выключает его и пишет в консоль файл и строку. Игра при этом продолжает работать.

Значит: никаких бесконечных циклов и никаких «пересчитать всё» внутри тика. Тяжёлую работу дели по тикам через [`everyTicks`](/scripting-api-ru/eshyo/taimery-i-zadachi.md).
