> 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/eshyo/taimery-i-zadachi.md).

# Таймеры и задачи

## Таймер

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

```kotlin
val sinceAttack = timer()

on<ClientTickEvent> {
    if (!sinceAttack.passed(10)) return@on
    // прошло 10 тиков с последнего reset
    sinceAttack.reset()
}
```

| Метод                      | Что делает                 |
| -------------------------- | -------------------------- |
| `passed(ticks)`            | прошло ли столько тиков    |
| `passedMillis(ms)`         | то же в миллисекундах      |
| `passedAndReset(ticks)`    | проверить и сразу сбросить |
| `passedMillisAndReset(ms)` | то же в миллисекундах      |
| `elapsedTicks()`           | сколько прошло тиков       |
| `elapsedMillis()`          | сколько прошло миллисекунд |
| `reset()`                  | начать отсчёт заново       |

`passedAndReset` избавляет от лишней строки, когда после проверки всё равно нужен сброс:

```kotlin
if (sinceAttack.passedAndReset(10)) {
    interaction.attackCrosshair()
}
```

Таймеров можно завести сколько угодно — по одному на каждое действие со своей задержкой.

## Отложить и повторять

```kotlin
nextTick { ... }                  // на следующем тике
afterTicks(20) { ... }            // через секунду
val task = everyTicks(100) { ... } // каждые пять секунд
```

Всё это выполняется на клиентском потоке.

`everyTicks` возвращает задачу, которую можно остановить:

```kotlin
task.cancel()
task.cancelled()
```

Отдельно чистить задачи не надо — при выгрузке скрипта они снимаются сами.

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

Из этого следует, что отложить уборку из `onDisable` не выйдет: к моменту его вызова скрипт уже считается выключенным, и запланированный там `afterTicks` не сработает никогда. Убирай прямо в `onDisable`.

## Таймер или задача

| Нужно                                                 | Чем          |
| ----------------------------------------------------- | ------------ |
| «не чаще чем раз в N тиков» внутри обработчика        | таймер       |
| «сделать один раз через N тиков»                      | `afterTicks` |
| «делать регулярно, независимо от других обработчиков» | `everyTicks` |
| «доделать на следующем тике»                          | `nextTick`   |

## Перейти на клиентский поток

Из обработчика пакета трогать мир нельзя — там другой поток. Перепрыгнуть можно так:

```kotlin
on<PacketReceiveEvent> { e ->
    onClientThread {
        control.jump()
    }
}
```

Если ты уже на клиентском потоке, блок выполнится сразу. Проверить, где ты, можно через `client.onClientThread()`.

## Время

```kotlin
client.tick()             // сколько тиков прожил клиент
client.millis()           // системное время
client.nanos()
client.fps()
client.tickDelta()        // доля кадра между тиками
```

Для задержек лучше `client.tick()` и таймеры, а не `millis()`: игра идёт по тикам, и на лагающем сервере тики полезнее реального времени.
