# Dimasik Scripts — полная документация

https://docs.dimasapi.tech/

## Оглавление

- [Быстрый старт](#doc-quickstart)
- [Создание скрипта](#doc-create-script)
- [Жизненный цикл](#doc-lifecycle)
- [Настройки](#doc-settings)
- [Мир и сущности](#doc-world)
- [Инвентарь и действия](#doc-actions)
- [Свои ротации Aura](#doc-aura-rotations)
- [События, таймеры и данные](#doc-events)
- [Рендер 2D и 3D](#doc-render)
- [Своя ClickGUI](#doc-custom-gui)
- [Модули и их настройки](#doc-module-options)
- [GUI-хуки: мышь, сцена и звуки](#doc-gui-hooks)
- [Встроенные ресурсы](#doc-resources)
- [Редактор сайта и IDE](#doc-editor)
- [MCP и команды](#doc-mcp)
- [Встроенные хуки](#doc-mixins)
- [Справочник API](#doc-api-reference)
- [Примеры](#doc-examples)
- [Ошибки и диагностика](#doc-errors)

---

<a id="doc-quickstart"></a>

# Быстрый старт

Один `.java` — один скрипт. Пишите в [Dimasik Studio](https://scripts.dimasapi.tech) или своей IDE; клиент загружает и запускает скрипты, показывает их настройки и ошибки.

## Через сайт

1. Запустите клиент и откройте ClickGUI → Скрипты → «Сайт» либо введите `.script site`.
2. Для входа выполните в клиенте команду с кодом, которую показывает сайт: `.script login КОД`.
3. Создайте скрипт в редакторе сайта, сохраните его и отправьте в клиент. Для готового скрипта используйте установку из маркетплейса.
4. Дождитесь загрузки и включите скрипт во вкладке «Скрипты». Состояние синхронизации: `.script status`.

Обычный исходник компилируется сервером; для этого нужно подключение к интернету. Пользователю не требуется отдельный локальный компилятор Java. API уже поставляется лоадером в `libraries`.

## Локальный файл

Команда `.script dir` открывает папку скриптов текущего экземпляра игры. Положите туда `Hello.java`, затем при необходимости выполните `.script reload`. Изменения файлов также обнаруживаются автоматически. Кнопки «Папка» и встроенного редактора в текущей вкладке нет.

```java
import dimasik.script.api.*;

public class Hello extends Script {
    public String name() { return "Hello"; }
    public void onRender2D(Render2D r) {
        r.text("Hello!", 16, 16, 14, r.theme("accent"));
    }
}
```

## Что можно сделать

- HUD, отметки сущностей и геометрию в мире.
- Собственный экран, включая [замену ClickGUI](#doc-custom-gui), управление модулями и их настройками.
- Обработку ввода, таймеры, хранение настроек, реакции на игровые события.
- Алгоритм ротации Aura и обработчики встроенных хуков.

Доступны только опубликованные методы API. Объектов Minecraft и внутренних модулей, файлового и сетевого доступа, reflection и собственных потоков нет. Полный перечень методов — [справочник API](#doc-api-reference).

## Полная документация

«Вся документация .md» скачивает все разделы выбранного языка, включая примеры и справочник. Текущая вкладка и поиск не ограничивают скачивание.

---

<a id="doc-create-script"></a>

# Создание скрипта

Создайте `MyHud.java` в редакторе сайта или в папке, открываемой командой `.script dir`.

## Минимальный файл

```java
import dimasik.script.api.*;

public final class MyHud extends Script {
    private final Setting<String> title = input("title", "Заголовок", "My HUD");
    public String name() { return "My HUD"; }
    public void onRender2D(Render2D r) {
        r.text(title.value(), 16, 16, 14, r.theme("accent"));
    }
}
```

## Требования к формату

- Один публичный, не абстрактный класс `extends Script` на файл. Имя класса совпадает с именем файла.
- Имя файла: латинская буква или `_`, затем буквы, цифры и `_`; до 64 символов перед `.java`.
- `package` необязателен. Можно использовать `example`, но не пакеты `java`, `javax`, `dimasik`, `org`, `net`, `sun` и их подпакеты.
- Нужен публичный конструктор без аргументов. Если конструктор не объявлен, Java создаёт его автоматически.
- Вложенные вспомогательные классы и лямбды допустимы. Второй класс верхнего уровня и зависимости на другие скрипты не поддерживаются.
- UTF-8, до 512 КиБ на файл. Локальный загрузчик принимает до 64 файлов за загрузку. Дополнительных ресурсов и JAR внутри скрипта нет.

## Загрузка и обновление

Сохраните файл. Клиент отправит исходник на сервер компиляции, проверит результат и загрузит его. При ошибке смотрите имя файла, строку и причину в «Скрипты → Консоль». После успешной загрузки включите скрипт.

ID локального скрипта — имя файла, например `MyHud.java`. Настройки, сохранённые строки и включённое состояние связаны с ID; переименование создаёт другую запись. Название в списке задаёт `name()`.

Для передачи открытого примера достаточно `.java`. Установка из маркетплейса использует защищённый `.dscript`; исходник такого пакета не редактируется через клиент или MCP.

## IDE

Для разработки выберите JDK 25 и подключите бинарный `dimasik-script-api-2.1.0.jar` из актуального клиента или по ссылке ниже. Экспорт используемого клиентом API находится в `Dimasik/scripts/.api/dimasik-script-api.jar`. Не подключайте несколько версий одновременно.

Собирать скрипт в JAR не нужно. IDE проверяет код и показывает подсказки; выполнение всё равно ограничено разрешёнными возможностями API. То, что обычный Java-код компилируется в IDE, ещё не означает, что он разрешён скриптам.

Для GUI нужны методы `openGui()`, `onGuiRender(...)`, `clientModules()` и `moduleOptions(...)`. В старых выпусках библиотеки с тем же номером 2.1.0 их нет. Если IDE их не видит, замените зависимость актуальным бинарным файлом; одному номеру версии доверять недостаточно.

## Старые JAR

JAR-скрипты больше не загружаются. Перенесите свой исходник в отдельный `.java`; декомпиляция чужих скриптов не является способом разработки. Настройки прежнего `Demo.jar/demo.Demo` могут переноситься в `Demo.java`, если совпали имя файла и полный класс. Старые архивы автоматически не удаляются.

## Скачать пример и API

Ниже доступны новый учебный `MyHud.java` и бинарная библиотека для IDE. Исходники реализации клиента и библиотеки здесь не публикуются.

---

<a id="doc-lifecycle"></a>

# Жизненный цикл

Публичный наследник `dimasik.script.api.Script`, публичный конструктор без аргументов. Текущая версия — `Script.API_VERSION == 2`. Классы разных файлов изолированы.

## Методы Script

| Метод | Когда вызывается |
| --- | --- |
| `String name()` | При создании; название до 80 символов |
| `String description()` | При создании; описание до 300 символов |
| `void activate()` | При включении и восстановлении включённого скрипта |
| `void deactivate()` | При выключении, перезагрузке и закрытии клиента |
| `void onTick()` | Каждый клиентский тик, если открыт мир |
| `void onRender2D(Render2D r)` | При отрисовке интерфейса |
| `void onRender3D(Render3D r)` | При отрисовке мира |
| `List<Setting<?>> settings()` | Неизменяемый список настроек |

Создавайте настройки в полях, подписки и таймеры — в `activate()`. Конструктор работает в песочнице; восстановление настроек и хранилища происходит после него.

## Перезагрузка

Фоновая загрузка читает .java, отправляет исходник серверу компиляции и проверяет полученный байткод. Затем на клиентском потоке старые экземпляры выключаются и заменяются. Подписки, таймеры и текстуры освобождаются. Настройки, бинд, хранилище и включённое состояние восстанавливаются по ID вида `Demo.java`. Переименование файла меняет ID.

Изменения .java обнаруживаются автоматически после стабилизации файла. «Обновить» запускает загрузку вручную. Запрос во время компиляции запускает ещё одну загрузку после текущей. При ошибке компиляции старая версия файла не продолжает работу.

## Ошибки

Ошибка callback выключает этот экземпляр и попадает в консоль со стеком вызовов. После исправления перезагрузите файл. Статические поля и несохранённые данные сбрасываются. Все callbacks работают на клиентском потоке: избегайте тяжёлых вычислений. В режиме Unhook игровые события, тики и рендер приостанавливаются.

## Собственный экран

Помимо HUD-методов существуют `onGuiRender`, `onGuiMouse`, `onGuiScroll`, `onGuiKey`, `onGuiChar`: они вызываются для открытого экрана этого скрипта. Их полный контракт — [Своя ClickGUI](#doc-custom-gui). GUI-callbacks не заменяют onTick и не приостанавливают мир.

## Сбой и очистка

После ошибки клиент очищает подписки, таймеры, ротации и закрывает экран. `deactivate()` не гарантирован при аварийном отключении; не полагайтесь на него как на единственный способ сохранить важные данные. Вызывайте `save()` при изменениях. Локальное поле `Setting` можно объявить в конструкторе, но игровые операции и подписки откладывайте до activate.

---

<a id="doc-settings"></a>

# Настройки

Настройки используют родные элементы ClickGUI. Значения сохраняются по стабильному ID.

## Объявление

```java
private final Setting<Boolean> hud = checkBox("hud", "HUD", true);
private final Setting<Double> range = slider("range", "Дистанция", 24, 1, 64, 1);
private final Setting<String> mode = choice("mode", "Режим", "A", "A", "B");
private final Setting<String> title = input("title", "Название", "Dimasik");
private final Setting<Integer> tint = color("tint", "Цвет", 0xFF83A9FF);
```

Это поля наследника `Script`. Чтение: `hud.value()`. Запись: `hud.value(false)`. `slider` возвращает Double; для координат используйте `.floatValue()`.

## Setting

| Метод | Результат |
| --- | --- |
| `id()`, `label()` | Идентификатор и подпись |
| `kind()` | BOOLEAN, NUMBER, CHOICE, TEXT или COLOR |
| `value()`, `value(T)` | Чтение и проверяемая запись |
| `min()`, `max()`, `step()` | Параметры ползунка |
| `options()` | Неизменяемый список вариантов |
| `serialized()`, `parse(String)` | Строковое представление и его разбор |

До 64 настроек; ID `[A-Za-z0-9_-]{1,64}`, подпись до 100 символов. До 64 вариантов по 100 символов, текст до 2048. Число ограничивается диапазоном; NaN/Infinity запрещены. Шаг задаёт интерфейс; программная запись не округляется к шагу.

## Цвет и сохранение

API и команды: **AARRGGBB**, например `FF83A9FF`. `parse` принимает восемь hex-цифр с необязательным `#` или `0x`. Родная палитра ClickGUI подписана как RRGGBBAA — это формат поля интерфейса.

Изменения через интерфейс/MCP сохраняются в `.state.json`. Программные изменения записываются при следующем сохранении состояния, выключении или перезагрузке. Сохраняйте ID при переводе подписи.

---

<a id="doc-world"></a>

# Мир и сущности

Методы защищённые: вызывайте их из наследника Script. Все значения — неизменяемые снимки; обновляйте их в нужном callback.

## Игрок

`Player player()` — свой игрок; проверяйте `present()`. Без мира остальные поля пустые/нулевые. `List<Player> players()` — до 256 игроков, включая своего.

Поля: `present()`, `name()`, `x()`, `y()`, `z()`, `yaw()`, `pitch()`, `health()`, `onGround()`.

## Мир

`World world()`: `present()`, `dimension()` (например minecraft:overworld), `time()` (возраст мира в тиках, не время суток), `raining()`, `thundering()`. Без мира present=false.

## Блоки

`Block block(int x,int y,int z)` читает загруженный чанк. Поля: `loaded()`, `x()`, `y()`, `z()`, `type()` (ID реестра), `air()`, `solid()` (полная непрозрачная форма для рендера).

Вне загруженного мира/границ loaded=false — это не означает воздух. Координаты игрока округляйте вниз через Math.floor, особенно отрицательные.

## Сущности

`List<Entity> entities()` возвращает до 512 сущностей клиентского мира в порядке игрового итератора. Список не отсортирован по расстоянию.

Поля Entity: `id()`, `type()`, `name()`, `x()`, `y()`, `z()`, `width()`, `height()`, `health()`, `alive()`, `player()`. Для неживых типов health=0; alive означает, что объект не удалён. ID действителен только в текущем мире. Проверяйте тип, здоровье и расстояние.

Смотрите [WorldHud и EntityBoxes](#doc-examples).

---

<a id="doc-actions"></a>

# Инвентарь и действия

Операции выполняются на клиентском потоке. Сервер может их отклонить. true означает выполнение запроса клиентом, не подтверждение сервера.

## Инвентарь

`List<Item> inventory()` — снимки инвентаря игрока, без игрока список пуст. Поля: `slot()`, `type()`, `name()`, `count()`, `damage()`, `maxDamage()`. Пустой слот: count=0.

| Индексы | Слоты |
| --- | --- |
| 0–8 | Хотбар |
| 9–35 | Основной инвентарь |
| 36–39 | Броня |
| 40 | Вторая рука |

Это индексы инвентаря, не открытого контейнера. API 2 не предоставляет перестановку предметов и клики по контейнерам.

## Действия

| Метод | Поведение |
| --- | --- |
| `void jump()` | Прыгнуть с земли |
| `boolean selectSlot(int slot)` | Выбрать хотбар 0–8; неверный индекс вызывает ошибку |
| `boolean look(float yaw,float pitch)` | Углы; pitch ограничен −90…90; только конечные числа |
| `boolean useItem(boolean offHand)` | Использовать предмет: false основная рука, true вторая |
| `boolean attack(int entityId)` | Атаковать живую существующую сущность до 6 блоков; себя нельзя |

useItem/attack возвращают false при открытом меню или без игрового контекста. selectSlot/look возвращают false без игрока. useItem не заменяет клик по грани блока.

В [ActionKeys](#doc-examples) действия изначально отключены настройкой; клавиши J/K/L/H.

---

<a id="doc-aura-rotations"></a>

# Свои ротации Aura

Начиная с библиотеки **2.1.0**, скрипт может добавить свой алгоритм наведения в Aura. Minecraft, классы клиента и маппинги для этого не нужны.

## Подключение

1. Обновите клиент и библиотеку лоадера до API 2.1.0. В classpath должна быть одна версия API, в папке `libraries`, как у других Java-библиотек.
2. Для разработки подключите `dimasik-script-api-2.1.0.jar` вместо старого API.
3. Скачайте `CustomAuraRotation.java` на странице [Примеры](#doc-examples), положите в папку скриптов клиента и включите во вкладке «Скрипты».
4. В Aura выберите **Тип ротации → Script**, затем **Ротация скрипта → Script Smooth**. В конце названия указан скрипт и ID, чтобы различать одинаковые имена.
5. Включите Aura. Скорость примера меняется в настройках самого скрипта: `Degrees per tick`.

Регистрация не включает Aura и не меняет выбранный тип ротации автоматически.

## Регистрация

Вызовите `registerAuraRotation(id, name, callback)` внутри `activate()`. Callback принимает `RotationFrame` и возвращает `RotationAngles`: абсолютные углы в градусах.

```java
package example;
import dimasik.script.api.*;

public final class MyRotation extends Script {
    @Override public String name() { return "My Rotation"; }

    @Override public void activate() {
        registerAuraRotation("smooth", "My Smooth", frame -> {
            float yawDelta = (frame.targetYaw() - frame.yaw()) % 360;
            if (yawDelta >= 180) yawDelta -= 360;
            if (yawDelta < -180) yawDelta += 360;
            float pitchDelta = frame.targetPitch() - frame.pitch();
            float step = 8;
            return new RotationAngles(
                frame.yaw() + Math.max(-step, Math.min(step, yawDelta)),
                frame.pitch() + Math.max(-step, Math.min(step, pitchDelta))
            );
        });
    }
}
```

Код поворачивается к цели не более чем на 8 градусов за вызов по каждой оси. Переход через −180/180 градусов идёт по короткому пути. Сохраните код в MyRotation.java; библиотека API предоставляется клиентом.

## Данные RotationFrame

| Метод | Значение |
| --- | --- |
| `yaw()`, `pitch()` | Текущие углы системы ротаций Aura, с которых надо продолжить движение |
| `targetYaw()`, `targetPitch()` | Углы до выбранной Aura точки наведения, включая её штатную логику точки цели |
| `player()` | Неизменяемый снимок своего игрока: позиция, здоровье, углы, состояние на земле |
| `target()` | Неизменяемый снимок цели: ID, тип, имя, позиция, размеры и здоровье |
| `tick()` | Счётчик тиков своего игрока; подходит для периодических алгоритмов |

Callback вызывается на клиентском потоке при обновлении ротации Aura, обычно один раз за игровой тик. Он не вызывается без цели, когда Aura выключена, при возврате камеры или когда выбран другой алгоритм. Частота зависит от игровых тиков, а не от FPS.

## Ограничения и жизненный цикл

- ID: 1–32 символа, латинские буквы, цифры, `_` и `-`. Имя: до 48 символов, без управляющих символов.
- До 8 ротаций на скрипт, до 128 на все скрипты. Повторная регистрация своего ID заменяет callback.
- `unregisterAuraRotation("smooth")` удаляет свою ротацию и возвращает `true`, если она существовала.
- Отключение, перезагрузка и ошибка скрипта автоматически снимают все его ротации. Выбор сохраняется по имени/ID для повторного включения; не переименовывайте их без необходимости.
- Если выбранная ротация недоступна, Aura не атакует в режиме Script. Включите её скрипт или выберите другой алгоритм.
- Pitch ограничивается диапазоном −90…90. `null`, `NaN`, бесконечные углы, исключения и превышение бюджета отключают скрипт; причина появляется в консоли скриптов.
- Callback исполняется в той же песочнице, что `onTick()`: нет доступа к классам Aura, Minecraft, reflection или маппингам. Не вызывайте `look()` вместо возврата углов: наведение применяет Aura.

Обновление клиента и API требуется вместе. Старые скрипты API 2 продолжают работать; новые методы регистрации требуют клиента с поддержкой ротаций.

---

<a id="doc-events"></a>

# События, таймеры и данные

Подписывайтесь в activate() через `on(String, Consumer<ScriptEvent>)`. До 64 подписок на экземпляр; при выключении удаляются.

## События

| Имя | Поля data |
| --- | --- |
| `tick` | Нет; тик в мире |
| `key` | key, action (0 отпускание, 1 нажатие, 2 повтор), screenOpen |
| `joinWorld` | Нет; переход от отсутствующего мира к открытому |
| `leaveWorld` | Нет; мир закрыт |
| `attack` | id, name атакуемой сущности |
| `rightClickBlock` | x, y, z, face, hand (MAIN_HAND/OFF_HAND) |
| `entityRemoved` | id, name удалённой сущности |

`ScriptEvent.name()` и `data()` — имя и неизменяемая карта строк. `get(key)` возвращает строку или пустую строку, `integer(key)` разбирает целое и вызывает ошибку при неверном значении. Клавиатура использует GLFW-коды, мышь 0–7. Проверяйте screenOpen перед действием.

Игровые события отражают вызовы клиента, не подтверждённый урон сервера. Вложенная генерация этих событий из обработчика подавляется. События неизменяемые: для отмены используйте поддерживающий отмену [встроенный хук](#doc-mixins). joinWorld не повторяется только из-за смены измерения при постоянно открытом мире.

## Таймеры

`long after(long millis,Runnable body)` — один вызов на ближайшем подходящем клиентском тике. Задержка ограничивается 0…86400000 мс. До 128 ожидающих таймеров. `boolean cancel(long id)` удаляет ожидающий таймер и сообщает, был ли он найден. Таймеры очищаются при выключении/перезагрузке. Для повтора запланируйте следующий вызов из callback.

## Данные и журнал

`save(String key,String value)` / `load(String key,String fallback)` — данные одного ID скрипта. До 256 ключей, ключ до 100 символов, значение до 4096. Запись ограничена по частоте и выполняется также при выключении/закрытии клиента.

`log(String)` — консоль: до 500 записей по 4000 символов. `chat(String)` — локальное сообщение до 1024 символов; на сервер не отправляется.

---

<a id="doc-render"></a>

# Рендер 2D и 3D

Контекст действует только в текущем `onRender2D`, `onRender3D` или `onGuiRender`. Не сохраняйте его для таймера или следующего кадра. Клиент создаёт и закрывает контексты; конструкторы и `close()` не нужны скрипту. Лимит — 2048 обращений на контекст, включая размеры, измерение текста и цвет темы.

## Render2D

Координаты — единицы GUI, не физические пиксели. Начало слева сверху, X вправо, Y вниз. `width()` и `height()` учитывают масштаб интерфейса.

```java
int width();
int height();
int theme(String token);
void rect(float x, float y, float width, float height, float radius, int color);
void outline(float x, float y, float width, float height, float radius, float thickness, int color);
void text(String text, float x, float y, int size, int color);
void text(String text, float x, float y, int size, int color, String font);
float textWidth(String text, int size, String font);
void text(String text, float x, float y, float size, int color);
float textWidth(String text, float size);
void image(String resource, float x, float y, float width, float height, float radius, int tint);
```

Все цвета — ARGB: `0xFF83A9FF` непрозрачный, `0x4083A9FF` полупрозрачный. В `rect` radius задаёт скругление; квадрат с radius=width/2 становится кружком.

Размеры фигур ограничены 8192; отрицательные размеры и неконечные координаты пропускаются. Радиус 0…половина меньшей стороны; толщина контура 0…32. Это не автоматическое обрезание содержимого вашего окна: самостоятельно ограничивайте видимые строки и геометрию.

## Текст

`font`: `medium`, `regular`, `icons`; другие имена вызывают ошибку. Без имени используется medium. Целочисленный size ограничен 6…64; дробный size — 1…64 и всегда использует medium. Строки длиннее 2048 символов не рисуются. `textWidth` возвращает ширину для выравнивания; выбирайте ту же перегрузку и шрифт, что при рисовании.

```java
String label = "Centered";
float size = 13.5f;
r.text(label, (r.width() - r.textWidth(label, size)) / 2f, 24, size, r.theme("gui.text"));
```

Это фрагмент внутри callback с параметром `Render2D r`. `icons` — встроенный шрифт значков; API не предоставляет каталог его символов.

## Палитра

| Токен | Назначение |
| --- | --- |
| `accent` | Основной акцент темы |
| `muted` | Второстепенный текст окна |
| `background` | Фон окна |
| `gui.background` | Фон GUI |
| `gui.text` | Активный текст |
| `gui.muted` | Неактивный текст |
| `gui.outline` | Контур |
| `gui.line` | Разделитель |
| `gui.selected` | Выбранный элемент |
| `gui.active`, `gui.inactive` | Активное и неактивное состояние |
| `gui.circle` | Круглый элемент управления |
| `gui.scroll` | Прокрутка |
| `gui.category`, `gui.categoryMuted` | Активная и неактивная категория |
| `gui.paletteVersion` | Число версии палитры, сейчас 1; не цвет |
| `gui.blur` | Флаг поддержки blur, сейчас 1; не цвет |

Неизвестный токен возвращает основной цвет текста окна. Читайте цвета при отрисовке, чтобы учитывать смену темы. `image()` поддерживает встроенные изображения, описанные в [Ресурсах](#doc-resources).

## Render3D

```java
float delta();
void line(double x1, double y1, double z1, double x2, double y2, double z2, int color);
void box(double x1, double y1, double z1, double x2, double y2, double z2, int color);
void filledBox(double x1, double y1, double z1, double x2, double y2, double z2, int color);
```

Координаты мировые, в блоках. `delta()` — доля тика для интерполяции, не секунды. `line` рисует отрезок, `box` — каркас, `filledBox` — грани. Передавайте минимальный угол первым, максимальный вторым. Геометрия видна сквозь стены; используйте прозрачную заливку. Неконечные координаты пропускаются. [EntityBoxes](#doc-examples) показывает рамки вокруг сущностей.

---

<a id="doc-custom-gui"></a>

# Своя ClickGUI

Скрипт может рисовать отдельный экран и открывать его вместо штатной ClickGUI обычной клавишей клиента. Нужны актуальная библиотека с GUI-методами и клиент с хуком `gui:open_clickgui`. Исходники стандартной ClickGUI для этого не нужны.

## Подключение замены

Сохраните пример как `SimpleGui.java`, загрузите, включите и нажмите клавишу ClickGUI внутри мира:

```java
import dimasik.script.api.*;

public class SimpleGui extends Script {
    public String name() { return "Simple GUI"; }

    public void activate() {
        hook("gui:open_clickgui", h -> openGui());
    }

    public void onGuiRender(Render2D r, float mouseX, float mouseY, float deltaSeconds) {
        float x = (r.width() - 280) / 2f;
        float y = (r.height() - 110) / 2f;
        r.rect(x, y, 280, 110, 8, r.theme("gui.background"));
        r.outline(x, y, 280, 110, 8, 1, r.theme("gui.outline"));
        r.text("My ClickGUI", x + 16, y + 18, 16, r.theme("gui.text"));
        r.text("Escape: close", x + 16, y + 62, 12, r.theme("gui.muted"));
    }
}
```

Новый пример [MyClickGui.java](#doc-examples) добавляет поиск, переключение модулей, просмотр настроек и изменение toggle/slider/choice. Поля text/color/bind в нём показаны для чтения: собственные виджеты для них можно написать по [контракту настроек модулей](#doc-module-options).

## Как выбирается экран

- Подписка создаётся в `activate()` и удаляется вместе с остальными при выключении, ошибке или перезагрузке.
- По нажатию клавиши ClickGUI клиент вызывает `gui:open_clickgui` у включённых скриптов по возрастанию ID файла. Первый, который открыл свой экран, получает управление; остальные не вызываются.
- Обработчик должен сразу вызвать `openGui()`. Аргументов нет; `cancel()` и `result(...)` здесь не используются. Вызов через таймер не считается заменой текущего открытия.
- Если никто не открыл экран, скрипт выключен либо упал в обработчике, открывается штатная ClickGUI. Ошибка не скрывает штатный интерфейс.
- При отключении или ошибке владельца открытого экрана замены клиент возвращает штатную ClickGUI. После перезагрузки замену можно открыть заново.
- Замена работает в мире. Вне мира открывается штатный интерфейс. Лучше держать включённой одну замену.

Закрытие через Escape не выключает скрипт. Следующее нажатие клавиши ClickGUI снова откроет его экран. Для возврата к штатному интерфейсу отключите скрипт: `.script toggle "SimpleGui.java"`. В MyClickGui можно также выключить настройку Replace ClickGUI.

## Управление экраном

| Метод | Поведение |
| --- | --- |
| `openGui()` | Открыть свой экран, если нет другого; внутри `gui:open_clickgui` разрешена замена текущего экрана |
| `closeGui()` | Закрыть только собственный экран; чужой экран не затрагивается |
| `guiOpen()` | Открыт ли сейчас экран этого экземпляра скрипта |

`openGui()` требует включённого скрипта и присутствующего игрока; в Unhook не открывает экран. Самостоятельный экран можно открывать из события клавиши, проверив, что другой экран закрыт. Он не становится заменой ClickGUI без соответствующего хука. Мир во время открытого экрана продолжает работать.

## Рисование и ввод

| Callback | Значение |
| --- | --- |
| `onGuiRender(Render2D r, float mouseX, float mouseY, float deltaSeconds)` | Кадр своего экрана; координаты GUI; deltaSeconds в секундах, первый кадр 0, максимум 0.1 |
| `onGuiMouse(float x, float y, int button, boolean pressed)` | Нажатие и отпускание кнопки мыши; 0 левая, 1 правая, 2 средняя |
| `onGuiScroll(float x, float y, float amount)` | Вертикальное колесо; дробное значение, знак задаёт направление |
| `onGuiKey(int key, int modifiers)` | Нажатие/повтор клавиши; true означает, что обработана; false позволяет Escape закрыть экран |
| `onGuiChar(String text)` | Введённый символ Unicode для текстовых полей |

Отдельного callback отпускания клавиши GUI нет. GLFW-коды: Escape 256, Enter 257, Backspace 259; modifiers — битовая маска Shift=1, Ctrl=2, Alt=4, Super=8. Ввод текста обрабатывайте через `onGuiChar`, а не переводом кода клавиши в букву. Не поглощайте Escape, если не предоставили понятное закрытие экрана.

Для кнопки проверяйте одновременно X/Y, `pressed` и номер кнопки. Геометрию клика считайте в тех же координатах, что и рисунок; после изменения списка или размеров дождитесь нового кадра. Не храните сам Render2D в полях. `onRender2D` остаётся HUD-callback; интерфейс собственного экрана рисуйте в `onGuiRender`.

## Анимации и оформление

Умножайте скорость анимации на `deltaSeconds`, чтобы она не зависела от FPS. Используйте [палитру и шрифты](#doc-render), а также `builtin:gui-blur`. Скрипт получает снимки модулей, а не виджеты штатной ClickGUI: расположение, прокрутку, фокус и ввод реализует ваш экран.

Захват мыши, встроенная 3D-сцена и звуки описаны в [GUI-хуках](#doc-gui-hooks).

---

<a id="doc-module-options"></a>

# Модули и их настройки

Эти методы доступны внутри наследника `Script`. Они позволяют строить собственную ClickGUI, не обращаться к реализации модулей.

## Список и переключение

```java
List<ClientModule> clientModules();
void moduleEnabled(String name, boolean enabled);
List<ModuleOption> moduleOptions(String name);
void moduleOption(String name, String id, String value);
void saveClientSettings();
```

`ClientModule` — неизменяемый снимок с `name()`, `description()`, `category()`, `enabled()`. Используйте точное имя из списка: регистр важен. Список может меняться между сборками; категории тоже берите из снимков. Сам модуль ClickGui исключён: замену его экрана подключайте через [GUI-хук](#doc-custom-gui).

`moduleEnabled(name, state)` задаёт состояние; при совпадении ничего не переключает. Чтобы инвертировать состояние, получите свежий снимок. `saveClientSettings()` сохраняет конфигурацию модулей после законченного изменения, например отпускания ползунка. Не вызывайте сохранение каждый кадр.

## ModuleOption

| Метод | Тип и значение |
| --- | --- |
| `id()` | String, идентификатор для записи |
| `name()` | String, подпись поля |
| `kind()` | String, тип из таблицы ниже |
| `value()` | String, текущее сериализованное значение |
| `min()`, `max()`, `step()` | double, параметры числового поля |
| `choices()` | List<String>, доступные варианты choice |

`moduleOptions(name)` возвращает только видимые поля и бинд модуля. Коллекции — снимки. После изменения режима перечитайте список: поля и границы могут измениться. ID непрозрачный: передавайте его обратно без разбора и не сохраняйте между версиями клиента.

## Типы полей

| kind | Запись через moduleOption | Виджет |
| --- | --- | --- |
| `toggle` | `"true"` или `"false"`, строчными буквами | Переключатель |
| `slider` | Конечное число строкой, например `"3.5"` | Ползунок с актуальными min/max/step |
| `choice` | Точная строка из choices() | Список вариантов |
| `text` | Строка до 2048 символов | Поле ввода |
| `color` | Восемь hex-цифр AARRGGBB, например `"FF83A9FF"`; допускаются # и 0x | Палитра |
| `bind` | Целый GLFW-код строкой, −1…348; −1 снять бинд | Ожидание клавиши |
| `group` | Только чтение | Заголовок группы |

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

## Пример смены варианта

Фрагмент метода внутри своего `Script`; имя модуля приходит из выбранной строки интерфейса:

```java
private void nextChoice(String moduleName, String optionId) {
    for (ModuleOption option : moduleOptions(moduleName)) {
        if (!option.id().equals(optionId)) continue;
        if (!option.kind().equals("choice") || option.choices().isEmpty()) return;
        int next = (option.choices().indexOf(option.value()) + 1) % option.choices().size();
        moduleOption(moduleName, option.id(), option.choices().get(next));
        saveClientSettings();
        return;
    }
}
```

`Setting<?>` описывает настройки самого скрипта, а `ModuleOption` — поля модулей клиента. У `ModuleOption` нет setter: запись выполняется только через `moduleOption(...)`. API не позволяет менять исходники модулей, их набор методов или дерево встроенных GUI-виджетов.

---

<a id="doc-gui-hooks"></a>

# GUI-хуки: мышь, сцена и звуки

Эти три хука вызываются только для владельца открытого скриптового экрана. Подписывайтесь в `activate()`. Они дополняют [GUI-callbacks](#doc-custom-gui); не получают объекты Minecraft. В отличие от уведомлений, здесь клиент читает `h.result(...)`.

## gui:mouse

Аргументы: `Float dx`, `Float dy`, `Boolean focused`. Смещения появляются при захваченной мыши и активном окне; по каждой оси ограничены −500…500. Первый вызов до захвата даёт нулевое смещение. Единицы смещения курсора не следует считать координатами GUI.

Установите `h.result(true)` для захвата курсора. Другой результат либо его отсутствие освобождает курсор. Не захватывайте мышь для обычного меню с кнопками. При закрытии экрана захват освобождается.

Фрагмент внутри `activate()`, когда скрипт уже имеет поля `yaw` и `pitch`:

```java
hook("gui:mouse", h -> {
    boolean focused = (Boolean) h.arg(2);
    if (focused) {
        yaw += (Float) h.arg(0) * 0.003f;
        pitch = Math.max(-1.3f, Math.min(1.3f, pitch + (Float) h.arg(1) * 0.003f));
    }
    h.result(focused);
});
```

## gui:scene

Аргументов нет. Верните строку через `h.result(sceneText)`. Хук запрашивается при `r.image("builtin:script-scene", x, y, w, h, 0, tint)` внутри `onGuiRender`. Если не рисовать это изображение, сцена не запрашивается. Пустой результат не рисует сцену. За кадр поддерживается один такой вызов; размеры изображения должны быть не меньше 1.

Формат `chicken-v1` описывает встроенную арену. Строка до 32768 символов, 2…67 строк, разделитель полей `;`. Углы камеры и направления сущностей в **радианах**, в отличие от игровых `look()` и ротаций Aura.

Первая строка:

```text
chicken-v1;px;pz;yaw;pitch;time;weapon;recoil;reload;flash;showWeapon[;eye;aim]
```

| Поле | Допустимые значения |
| --- | --- |
| px, pz | 0…32; выбирайте координаты внутри своей карты |
| yaw, pitch | yaw −1000…1000; pitch −1.3…1.3 |
| time | 0…1000000, время анимации |
| weapon | Целое 0…3, встроенный вариант оружия |
| recoil, reload, flash | 0…2, 0…1, 0…1 соответственно |
| showWeapon | 0 или 1 |
| eye, aim | Оба поля необязательны вместе: eye 0.35…2 (по умолчанию 0.78), aim 0…1 (по умолчанию 0) |

Вторая строка — карта: ровно 400 символов для 20×20 или 1024 для 32×32. Каждая клетка — цифра `0`…`5`; `0` свободная, `1`…`5` варианты препятствий. Индекс клетки — `z * размер + x`; пробелы и разделители между клетками не нужны.

Остальные строки задают до 65 объектов:

| Строка | Поля |
| --- | --- |
| `c;x;z;direction;type;phase;damage[;team]` | direction −10…10 радиан, type 0…2, phase 0…1000000, damage 0…1; team 0…2, по умолчанию 2 |
| `e;x;z` | Снаряд |
| `d;x;z;medicine` | Предмет; medicine 0 или 1 |

X/Z объектов — 0…32. Все числа должны быть конечными; используйте десятичную точку. Квадратные скобки в описании обозначают необязательные поля и не входят в строку. Передаются только данные сцены; изменения не создают сущности в игровом мире.

Минимальная сцена без объектов, фрагмент внутри `activate()`:

```java
String map = "0".repeat(400);
hook("gui:scene", h -> h.result("chicken-v1;10;10;0;0;0;0;0;0;0;0" + "\n" + map));
```

## gui:sounds

Аргументов нет. После отрисовки GUI верните очередь звуков строкой `громкость;событие;событие`. Громкость 0…1, до 16 событий и 1024 символов. Пустая строка — тишина.

Допустимые события: `blaster`, `automatic`, `shotgun`, `sniper`, `run_step`, `step`, `crouch_step`, `reload`, `reload_end`, `hit`, `chicken`, `chicken_hurt`, `chicken_death`, `hurt`, `pickup`.

Например, `h.result("0.4;pickup")` воспроизведёт один звук. Если возвращать это каждый кадр, звук будет повторяться: храните очередь в поле и очищайте после выдачи. Клиент ограничивает поток до 40 событий в секунду с запасом до 16, одновременно до 32 звуков. При закрытии экрана звуки останавливаются. Звуки локальные, другим игрокам не отправляются.

---

<a id="doc-resources"></a>

# Встроенные ресурсы

Скрипт состоит из одного `.java`. Отдельные PNG, шрифты, текстовые файлы, URL изображений и папки ресурсов не загружаются. Текст храните в строках, формы рисуйте через Render2D/Render3D.

## Изображения, предоставленные клиентом

| Имя в image() | Результат |
| --- | --- |
| `builtin:gui-blur` | Размытый фон; radius задаёт скругление, альфа tint — прозрачность |
| `builtin:script-scene` | Встроенная 3D-сцена только внутри собственного GUI; содержимое задаёт gui:scene |

Пример внутри `onGuiRender`:

```java
r.image("builtin:gui-blur", 20, 20, 260, 160, 8, 0xAAFFFFFF);
r.rect(20, 20, 260, 160, 8, 0x99151A24);
```

Для `builtin:script-scene` разрешён один вывод сцены за GUI-кадр. Это готовая сцена с документированным форматом, а не загрузка произвольных моделей; см. [GUI-хуки](#doc-gui-hooks).

## resource(String path)

Метод сохранён в публичном API для совместимости. В формате одного `.java` пользовательских текстовых ресурсов нет: обращение к несуществующему ресурсу вызовет ошибку. Аналогично `image("picture.png", ...)` не найдёт картинку рядом с исходником.

API не предоставляет чтение файлов клиента, получение его исходников, загрузку внешних картинок или доступ к внутренним текстурам по произвольному имени.

---

<a id="doc-editor"></a>

# Редактор сайта и IDE

## Основной рабочий процесс

1. Откройте [Dimasik Studio](https://scripts.dimasapi.tech), войдите через запущенный клиент и создайте скрипт.
2. Напишите один класс `extends Script`, сохраните изменения и отправьте скрипт в клиент.
3. В клиенте проверьте консоль, включите скрипт и протестируйте его в мире.
4. Измените код на сайте и повторно отправьте обновление. После установки обновления проверьте включённое состояние: обновление с сайта может выключить старый экземпляр.

Встроенная вкладка клиента служит для запуска, настроек, бинда, удаления и журнала. Кнопка «Сайт» открывает веб-редактор. Исходник своего локального `.java` можно отправить на сайт командой `.script upload MyHud.java`.

## Сессия

Вход подтверждается командой `.script login КОД` из сайта. Используется профиль запущенного клиента. Проверить подключение можно через `.script status`, отключить синхронизацию — `.script logout`. Если сайт просит войти заново, получите новый код и подтвердите его в нужном клиенте.

## IDE и MCP

Подключите актуальную бинарную библиотеку, выберите JDK 25 и редактируйте файл в каталоге `.script dir`. Автоперезагрузка отслеживает сохранение. Внешний агент может читать и менять ваши локальные `.java` через [MCP](#doc-mcp).

Выберите одно место редактирования текущей версии: сайт либо локальный файл. После внешних правок перечитайте свежий исходник перед сохранением. В MCP передавайте `expectedSource`, чтобы обнаружить конфликт.

## Чужие скрипты

Установленный пакет маркетплейса запускается как `.dscript`. Наличие доступа к запуску не даёт доступ к исходнику. Правки и новые версии делает автор; административная проверка выполняется через модерацию сайта.

---

<a id="doc-mcp"></a>

# MCP и команды

ClickGUI → Скрипты → MCP: включите сервер и скопируйте URL и токен.

`http://127.0.0.1:<port>/mcp`, Streamable HTTP, `Authorization: Bearer <token>`. JSON-RPC POST, protocol 2025-06-18. No GET/SSE. Keep the token private.

## Tools

| Tool | Arguments |
| --- | --- |
| api_docs | — |
| client_state | — |
| scripts_list | — |
| script_read | file |
| script_write | file, source, expectedSource (optional) |
| scripts_reload | — |
| script_toggle | id, enabled |
| script_settings | id |
| script_setting_set | id, setting, value |
| console_read | since, level (optional) |
| console_clear | — |

## script_write

```json
{"file":"Hello.java","source":"import dimasik.script.api.*; public class Hello extends Script { public String name(){return \"Hello\";} }"}
```

Сохранение атомарное, компиляция асинхронная. Дождитесь нового revision и compiling=false в scripts_list, затем проверьте console_read. Для правки сначала вызовите script_read; передайте полученный source как expectedSource, чтобы не затереть внешние изменения. Поля jar/files/remove больше не используются.

## script_toggle

```json
{"id":"Hello.java","enabled":true}
```

## Commands

```text
.script site
.script login CODE
.script status
.script upload MyHud.java
.script logout
.script list
.script reload
.script dir
.script toggle "Demo.java"
.script settings "Demo.java"
.script set "Demo.java" hud false
.script set "Demo.java" title Hello
.script mcp
```

`api_docs` возвращает руководство, встроенное в конкретную сборку клиента. Если оно отстаёт от сайта, скачайте здесь всю документацию .md и передайте её агенту. `script_read`/`script_write` работают с локальными .java; исходники чужих .dscript через них недоступны. Токен MCP даёт управление локальными скриптами: передавайте его только доверенному инструменту.

---

<a id="doc-mixins"></a>

# Встроенные хуки

Подписывайтесь через `hook("имя", h -> ...)` в `activate()`. Доступны 19 хуков. Нужны актуальный клиент и API с GUI-методами; список ниже описывает текущую версию исходников клиента.

## Тики и интерфейс

| Имя | Когда | Аргументы по порядку |
| --- | --- | --- |
| `client:before_tick` | Перед клиентским тиком, в том числе в меню | `Long` — время в миллисекундах |
| `client:after_tick` | После клиентского тика | `Long` — время в миллисекундах |
| `client:screen_changed` | После вызова смены экрана | `Boolean` — открыт ли экран, `String` — его заголовок или пустая строка |
| `input:key` | Событие клавиатуры игрового окна | `Integer` — GLFW-код клавиши, `Integer` — действие, `Boolean` — открыт ли экран |
| `input:mouse` | Событие кнопки мыши | `Integer` — кнопка, `Integer` — действие, `Boolean` — открыт ли экран |

Действие: `0` — отпущена, `1` — нажата, `2` — повтор клавиатуры. Кнопки мыши: `0` — левая, `1` — правая, `2` — средняя. Хуки ввода уведомляют о событии и не блокируют управление. `screen_changed` может приходить повторно для того же экрана.

## Мир

| Имя | Когда | Аргументы |
| --- | --- | --- |
| `world:join` | Клиент обнаружил новый мир на конце тика | `String` — измерение, например `minecraft:overworld` |
| `world:leave` | Клиент обнаружил выход из старого мира | `String` — прежнее измерение |

При смене измерения сначала приходит `leave`, затем `join`. Это уведомления об изменении клиентского мира, а не подтверждение сервера. При включении скрипта уже внутри мира прошлый `join` не повторяется: прочитайте начальное состояние через `world()`.

## Действия игрока

| Имя | Аргументы по порядку | Отмена |
| --- | --- | --- |
| `player:before_attack` | `Integer` — ID цели, `String` — имя, три `Double` — X/Y/Z цели | Не выполнять атаку |
| `player:before_use_item` | `String` — `MAIN_HAND` или `OFF_HAND` | Вернуть отказ использования |
| `player:before_use_block` | Три `Integer` — X/Y/Z блока, `String` — грань, `String` — рука | Вернуть отказ взаимодействия |
| `player:before_break_block` | Три `Integer` — X/Y/Z блока, `String` — грань | Не начинать разрушение |

Эти четыре хука вызываются в начале соответствующего метода управления игроком. `h.cancel()` отменяет этот вызов до отправки им действия. Остальные модули могут отменить действие независимо. Хук разрушения относится к началу, а не каждому шагу удержания кнопки. Прямые пакеты, отправленные другими модулями в обход этих методов, не перехватываются.

Не вызывайте `attack()` внутри `before_attack` или `useItem()` внутри `before_use_item`: повторный вход в тот же отменяемый хук блокирует вложенное действие. Для отложенного действия используйте таймер и отдельное условие, чтобы не создать бесконечный цикл.

## Рендер

| Имя | Когда | Аргументы |
| --- | --- | --- |
| `render:before_2d` | Перед вызовами `onRender2D` скриптов | Два `Integer` — ширина и высота GUI |
| `render:after_2d` | После вызовов `onRender2D` скриптов | Два `Integer` — ширина и высота GUI |
| `render:before_3d` | Перед вызовами `onRender3D` скриптов | `Float` — partial ticks |
| `render:after_3d` | После вызовов `onRender3D` скриптов | `Float` — partial ticks |

Это границы рендера скриптов, а не всего кадра Minecraft. Рисуйте в `onRender2D` и `onRender3D`: объекты рендера в хуки не передаются.

## Пример отмены атаки

Сохраните как `ProtectPlayer.java`:

```java
import dimasik.script.api.*;

public class ProtectPlayer extends Script {
    private final Setting<String> protectedName = input("name", "Не атаковать", "Friend");

    public void activate() {
        hook("player:before_attack", h -> {
            String target = (String) h.arg(1);
            if (target.equalsIgnoreCase(protectedName.value())) h.cancel();
        });
    }
}
```

Готовый `HookDemo.java` на странице [Примеры](#doc-examples) показывает ввод, смену мира и настройку запрета атак и разрушения.

## Правила вызова

- `h.arg(0)` — первый аргумент, `h.size()` — количество. Типы в таблицах точные: приводите к указанному типу.
- До 64 подписок на экземпляр. При выключении и перезагрузке подписки удаляются.
- Обработчики работают на клиентском потоке с общим для вызова ограничением исполнения. Ошибка выключает скрипт и записывается в консоль.
- `cancel()` действует только на четыре хука действий. Для остальных он ничего не отменяет. `result(...)` читается тремя GUI-хуками; см. ниже.
- Хуки передают только строки, числа и логические значения. Доступа к объектам или классам клиента нет.
- В Unhook обработчики не вызываются. Неизвестное имя вызывает ошибку. Повторное вложенное уведомление с тем же именем пропускается.
- Новые точки доступа добавляются в клиент. Пользовательские миксины не загружаются.

## Свой экран

| Хук | Аргументы | Действие или результат |
| --- | --- | --- |
| `gui:open_clickgui` | Нет | Вызвать openGui(), чтобы открыть замену штатной ClickGUI |
| `gui:mouse` | Float dx, Float dy, Boolean focused | result(true) захватывает курсор |
| `gui:scene` | Нет | result(String) задаёт встроенную 3D-сцену |
| `gui:sounds` | Нет | result(String) возвращает очередь звуков |

Открытие вызывается по запросу ClickGUI. Три остальных хука работают только у владельца открытого экрана. Их результат не отменяет тики или игровые действия. Подробности: [своя ClickGUI](#doc-custom-gui), [форматы GUI-хуков](#doc-gui-hooks).

---

<a id="doc-api-reference"></a>

# Справочник API

Все типы ниже находятся в `dimasik.script.api`. Это публичные сигнатуры и контракт для авторов скриптов; реализация клиента и библиотеки не публикуется. `List`, `Map` и `Consumer` — стандартные Java-типы.

## Script

`Script.API_VERSION` равен 2. Callback-методы public переопределяются скриптом. Методы protected final вызываются из своего наследника и не переопределяются. `settings()` final возвращает список зарегистрированных настроек.

```java
public String name();
public String description();
public void activate();
public void deactivate();
public void onTick();
public void onRender2D(Render2D render);
public void onRender3D(Render3D render);
public void onGuiRender(Render2D render, float mouseX, float mouseY, float deltaSeconds);
public void onGuiMouse(float x, float y, int button, boolean pressed);
public void onGuiScroll(float x, float y, float amount);
public boolean onGuiKey(int key, int modifiers);
public void onGuiChar(String text);
protected final void openGui();
protected final void closeGui();
protected final boolean guiOpen();
protected final List<ClientModule> clientModules();
protected final void moduleEnabled(String name, boolean enabled);
protected final List<ModuleOption> moduleOptions(String name);
protected final void moduleOption(String name, String id, String value);
protected final void saveClientSettings();
public final List<Setting<?>> settings();
protected final Setting<Boolean> checkBox(String id, String label, boolean value);
protected final Setting<Double> slider(String id, String label, double value, double min, double max, double step);
protected final Setting<String> choice(String id, String label, String value, String... options);
protected final Setting<String> input(String id, String label, String value);
protected final Setting<Integer> color(String id, String label, int argb);
protected final void on(String name, Consumer<ScriptEvent> handler);
protected final void hook(String name, Consumer<Hook> handler);
protected final void registerAuraRotation(String id, String name, AuraRotation rotation);
protected final boolean unregisterAuraRotation(String id);
protected final void log(String message);
protected final void chat(String message);
protected final Player player();
protected final List<Player> players();
protected final World world();
protected final List<Entity> entities();
protected final Block block(int x, int y, int z);
protected final List<Item> inventory();
protected final boolean selectSlot(int slot);
protected final boolean look(float yaw, float pitch);
protected final boolean useItem(boolean offHand);
protected final boolean attack(int entityId);
protected final String resource(String path);
protected final boolean cancel(long taskId);
protected final void jump();
protected final long after(long millis, Runnable body);
protected final String load(String key, String fallback);
protected final void save(String key, String value);
```

Методы, обращающиеся к игре, экрану, подпискам или хранилищу, вызывайте из callbacks. В полях и конструкторе объявляйте настройки и свои данные; сохранённые значения восстанавливаются позднее. `name()`/`description()` должны просто возвращать метаданные.

## Setting<T>

Создавайте через checkBox/slider/choice/input/color, чтобы настройка появилась в `settings()` и интерфейсе. Конструктор Setting не является публичным API для скрипта.

```java
String id();
String label();
Setting.Kind kind();
T value();
void value(T next);
double min();
double max();
double step();
List<String> options();
void parse(String raw);
String serialized();
```

`Setting.Kind`: `BOOLEAN`, `NUMBER`, `CHOICE`, `TEXT`, `COLOR`.

## Снимки и значения

Поля ниже доступны одноимёнными методами: например `player.x()`, `item.count()`, `option.choices()`. Это неизменяемые record-значения, а не живые объекты Minecraft. Канонический конструктор принимает перечисленные поля в указанном порядке; самостоятельно созданный снимок не меняет игру. Для действий используйте методы Script.

| Тип | Поля в порядке конструктора |
| --- | --- |
| Player | boolean present; String name; double x, y, z; float yaw, pitch, health; boolean onGround |
| World | boolean present; String dimension; long time; boolean raining, thundering |
| Entity | int id; String type, name; double x, y, z; float width, height, health; boolean alive, player |
| Block | boolean loaded; int x, y, z; String type; boolean air, solid |
| Item | int slot; String type, name; int count, damage, maxDamage |
| ClientModule | String name, description, category; boolean enabled |
| ModuleOption | String id, name, kind, value; double min, max, step; List<String> choices |
| RotationFrame | float yaw, pitch, targetYaw, targetPitch; Player player; Entity target; int tick |
| RotationAngles | float yaw, pitch |
| ScriptEvent | String name; Map<String, String> data |

`ScriptEvent` дополнительно имеет `String get(String key)` и `int integer(String key)`. Первый вернёт пустую строку для отсутствующего ключа, второй выбросит ошибку при нечисловом значении. `RotationAngles` принимает только конечные углы. Коллекции в снимках доступны для чтения.

## Hook

```java
Hook(List<Object> args);
Object arg(int index);
int size();
void cancel();
boolean cancelled();
void result(Object value);
Object result();
boolean hasResult();
```

Обычно Hook предоставляет callback; новый объект не отправляет событие клиенту. Аргументы и результат — скалярные значения или null. Индекс arg начинается с 0. `hasResult()` различает отсутствие результата и явно заданный null. Отмена и результат влияют на клиент только там, где это указано в [таблице хуков](#doc-mixins) и [GUI-хуках](#doc-gui-hooks).

## AuraRotation

```java
RotationAngles rotate(RotationFrame frame);
```

Функциональный интерфейс: можно передать лямбду. Возвращайте абсолютные yaw/pitch в градусах; не меняйте взгляд вручную вместо результата. См. [ротации Aura](#doc-aura-rotations).

## Render2D / Render3D

Все доступные операции и перегрузки перечислены в [Рендере](#doc-render). Контекст принадлежит кадру, его выдаёт callback. Публичный `close()` завершает контекст и вызывается клиентом; скрипту не нужно его вызывать. Создание контекстов самостоятельно запрещено.

## Где смотреть поведение

- [Жизненный цикл](#doc-lifecycle), [настройки](#doc-settings), [события и хранение](#doc-events).
- [Мир](#doc-world), [действия](#doc-actions), [модули и настройки](#doc-module-options).
- [Своя ClickGUI](#doc-custom-gui), [GUI-хуки](#doc-gui-hooks), [ресурсы](#doc-resources).

## Доступные средства Java

Для вычислений используйте примитивы, строки, Math/StrictMath, числовые обёртки, StringBuilder и обычные циклы. Доступны коллекции List/Map/Set, ArrayList, HashMap/LinkedHashMap, HashSet/LinkedHashSet, ArrayDeque, Iterator/ListIterator, Collections, Arrays, Objects, Optional и его числовые варианты, Random, Comparator, а также java.util.function.

Stream API, внешние зависимости, java.io/java.nio/java.net, reflection и потоки недоступны. Для времени разрешены только `System.currentTimeMillis()` и `System.nanoTime()`, для журнала — `log()`. Собственные вспомогательные типы размещайте внутри класса скрипта; не используйте records как вспомогательные классы — java.lang.Record не входит в разрешённые типы. Массивы — одномерные, максимум 65536 элементов на создаваемый массив; большие данные разбивайте на ограниченные порции. Компиляция Java ещё не заменяет проверку разрешённых возможностей клиентом.

---

<a id="doc-examples"></a>

# Примеры

Ниже — открытые учебные скрипты, использующие только публичный API. Каждый блок сохраняйте в отдельный файл с указанным именем. Скачать отдельные `.java` можно в конце страницы; полная документация .md также включает весь код этих примеров.

`MyClickGui.java` требует клиента с `gui:open_clickgui` и актуальной API-библиотеки. Включите скрипт, войдите в мир и нажмите обычную клавишу ClickGUI. ЛКМ переключает модуль, ПКМ открывает его параметры. Колесо прокручивает; текст ищет модули; Backspace возвращает список, Escape закрывает экран. В параметрах ЛКМ/ПКМ меняют toggle/slider/choice, остальные типы только показываются.

Действия в `ActionKeys` изначально выключены отдельной настройкой; перед тестом прочитайте назначение клавиш в коде. Для `CustomAuraRotation` выберите Script в настройках Aura. Эти примеры — отправная точка; включайте только нужные.

## MyClickGui.java

```java
import dimasik.script.api.*;
import java.util.ArrayList;
import java.util.List;

/** Educational example using only the published script API. */
public final class MyClickGui extends Script {
    private final Setting<Boolean> replace = checkBox("replace", "Replace ClickGUI", true);
    private List<ClientModule> visible = List.of();
    private List<ModuleOption> options = List.of();
    private String selected = "";
    private String query = "";
    private int offset;
    private int rows;
    private float left, top, width;
    private boolean layoutReady;

    public String name() { return "My ClickGUI"; }
    public String description() { return "Modules, search and basic options. Escape returns to the game."; }

    public void activate() {
        hook("gui:open_clickgui", h -> {
            if (replace.value()) {
                layoutReady = false;
                openGui();
            }
        });
    }

    public void onGuiRender(Render2D r, float mouseX, float mouseY, float deltaSeconds) {
        int screenWidth = r.width(), screenHeight = r.height();
        width = Math.max(120, Math.min(520, screenWidth - 24));
        rows = Math.max(1, Math.min(12, (screenHeight - 130) / 25));
        float height = 98 + rows * 25;
        left = (screenWidth - width) / 2;
        top = Math.max(4, (screenHeight - height) / 2);
        int bg = r.theme("gui.background"), text = r.theme("gui.text");
        int muted = r.theme("gui.muted"), accent = r.theme("accent");
        r.image("builtin:gui-blur", left, top, width, height, 9, 0xCCFFFFFF);
        r.rect(left, top, width, height, 9, bg);
        r.outline(left, top, width, height, 9, 1, r.theme("gui.outline"));
        r.text(selected.isEmpty() ? "My ClickGUI" : shorten(selected), left + 14, top + 12, 15, text);
        r.text(selected.isEmpty() ? "Search: " + query : "Backspace: modules", left + 14, top + 37, 11, muted);
        if (selected.isEmpty()) {
            visible = new ArrayList<>();
            for (ClientModule module : clientModules()) {
                if (module.name().toLowerCase().contains(query.toLowerCase())) visible.add(module);
            }
            clampOffset(visible.size());
            for (int row = 0; row < rows && offset + row < visible.size(); row++) {
                ClientModule module = visible.get(offset + row);
                float y = top + 60 + row * 25;
                r.text(shorten(module.name()), left + 14, y, 12, module.enabled() ? accent : text);
                String status = module.enabled() ? "ON" : "OFF";
                r.text(status, left + width - 38, y, 11, module.enabled() ? accent : muted);
            }
        } else {
            options = moduleOptions(selected);
            clampOffset(options.size());
            for (int row = 0; row < rows && offset + row < options.size(); row++) {
                ModuleOption option = options.get(offset + row);
                r.text(shorten(option.name() + ": " + option.value()), left + 14, top + 60 + row * 25, 11, text);
            }
        }
        r.text("LMB: toggle / +   RMB: options / -   Esc: close", left + 14, top + height - 20, 9, muted);
        layoutReady = true;
    }

    public void onGuiMouse(float x, float y, int button, boolean pressed) {
        if (!pressed || !layoutReady || x < left || x > left + width || y < top + 60) return;
        int row = (int) ((y - top - 60) / 25);
        if (row >= rows) return;
        int index = offset + row;
        if (selected.isEmpty()) {
            if (index >= visible.size()) return;
            ClientModule module = visible.get(index);
            if (button == 0) {
                // Read current state instead of toggling a previous frame's snapshot.
                for (ClientModule current : clientModules()) {
                    if (current.name().equals(module.name())) {
                        moduleEnabled(current.name(), !current.enabled());
                        saveClientSettings();
                        break;
                    }
                }
            } else if (button == 1) {
                selected = module.name();
                offset = 0;
                layoutReady = false;
            }
        } else if (index < options.size() && (button == 0 || button == 1)) {
            String id = options.get(index).id();
            for (ModuleOption option : moduleOptions(selected)) {
                if (!option.id().equals(id)) continue;
                String value = null;
                if (option.kind().equals("toggle")) value = "" + !Boolean.parseBoolean(option.value());
                if (option.kind().equals("slider")) {
                    double step = option.step() > 0 ? option.step() : 1;
                    double next = Double.parseDouble(option.value()) + (button == 0 ? step : -step);
                    value = "" + Math.max(option.min(), Math.min(option.max(), next));
                }
                if (option.kind().equals("choice") && !option.choices().isEmpty()) {
                    int next = option.choices().indexOf(option.value()) + (button == 0 ? 1 : -1);
                    value = option.choices().get(Math.floorMod(next, option.choices().size()));
                }
                if (value != null) {
                    moduleOption(selected, id, value);
                    saveClientSettings();
                }
                break;
            }
        }
    }

    public void onGuiScroll(float x, float y, float amount) {
        if (amount == 0) return;
        offset += amount > 0 ? -2 : 2;
        clampOffset(selected.isEmpty() ? visible.size() : options.size());
        layoutReady = false;
    }

    public void onGuiChar(String text) {
        if (selected.isEmpty() && query.length() + text.length() <= 24) {
            query += text;
            offset = 0;
            layoutReady = false;
        }
    }

    public boolean onGuiKey(int key, int modifiers) {
        if (key != 259) return false; // Do not consume Escape (256).
        if (!selected.isEmpty()) selected = "";
        else if (!query.isEmpty()) query = query.substring(0, query.offsetByCodePoints(query.length(), -1));
        offset = 0;
        layoutReady = false;
        return true;
    }

    private void clampOffset(int count) { offset = Math.max(0, Math.min(offset, Math.max(0, count - rows))); }
    private String shorten(String text) { return text.length() > 34 ? text.substring(0, 31) + "..." : text; }
}
```

## WorldHud.java

```java
package examples;
import dimasik.script.api.*;
public class WorldHud extends Script {
    private final Setting<Integer> tint=color("tint","Color",0xFF83A9FF);
    public String name(){return "World HUD";}
    public void onRender2D(Render2D r){
        Player p=player(); World w=world(); if(!p.present())return;
        String title=w.dimension()+" / tick "+w.time();
        r.rect(12,12,r.textWidth(title,13,"medium")+24,64,8,r.theme("background"));
        r.text(title,24,22,13,tint.value());
        Block below=block((int)Math.floor(p.x()),(int)Math.floor(p.y())-1,(int)Math.floor(p.z()));
        r.text("Below: "+below.type(),24,43,12,r.theme("text"),"regular");
    }
}
```

## EntityBoxes.java

```java
package examples;
import dimasik.script.api.*;
public class EntityBoxes extends Script {
    private final Setting<Boolean> fill=checkBox("fill","Fill boxes",false);
    private final Setting<Double> range=slider("range","Range",24,1,64,1);
    public String name(){return "Entity boxes";}
    public void onRender3D(Render3D r){
        Player p=player();if(!p.present())return;
        for(Entity e:entities()){
            double dx=e.x()-p.x(),dy=e.y()-p.y(),dz=e.z()-p.z();
            if(e.player()||!e.alive()||dx*dx+dy*dy+dz*dz>range.value()*range.value())continue;
            double half=e.width()/2;
            r.box(e.x()-half,e.y(),e.z()-half,e.x()+half,e.y()+e.height(),e.z()+half,0xFF83A9FF);
            if(fill.value())r.filledBox(e.x()-half,e.y(),e.z()-half,e.x()+half,e.y()+e.height(),e.z()+half,0x2083A9FF);
        }
    }
}
```

## InventoryHud.java

```java
package examples;
import dimasik.script.api.*;
public class InventoryHud extends Script {
    public String name(){return "Inventory HUD";}
    public void onRender2D(Render2D r){
        if(!player().present())return;
        int row=0;
        for(Item item:inventory()){
            if(item.slot()>8||item.count()==0)continue;
            r.text((item.slot()+1)+". "+item.name()+" x"+item.count(),12,90+row++*17,12,r.theme("text"));
        }
    }
}
```

## ActionKeys.java

```java
package examples;
import dimasik.script.api.*;
public class ActionKeys extends Script {
    private final Setting<Boolean> actions=checkBox("actions","Enable actions",false);
    public String name(){return "Action keys";}
    public void activate(){on("key",e->{
        if(!actions.value()||e.integer("action")!=1||e.get("screenOpen").equals("true"))return;
        int key=e.integer("key");
        if(key==74)jump(); // J
        if(key==75){selectSlot(0);useItem(false);} // K: use hotbar slot 1
        if(key==76)look(player().yaw(),0); // L: look horizontally
        if(key==72){ // H: nearest non-player living entity within 4 blocks
            Player p=player();double best=16;int id=-1;
            for(Entity target:entities()){
                if(target.player()||!target.alive()||target.health()<=0)continue;
                double dx=target.x()-p.x(),dy=target.y()-p.y(),dz=target.z()-p.z(),distance=dx*dx+dy*dy+dz*dz;
                if(distance<best){best=distance;id=target.id();}
            }
            if(id>=0)attack(id);
        }
    });}
}
```

## EventLog.java

```java
package examples;
import dimasik.script.api.*;
public class EventLog extends Script {
    private long reminder;
    public String name(){return "Events and storage";}
    public void activate(){
        log("Previous world: "+load("dimension","none"));
        on("joinWorld",e->{save("dimension",world().dimension());log("Joined "+world().dimension());});
        on("leaveWorld",e->log("Left world"));
        on("attack",e->log("Attack: "+e.get("name")));
        on("rightClickBlock",e->log("Block: "+e.get("x")+", "+e.get("y")+", "+e.get("z")));
        on("entityRemoved",e->log("Removed #"+e.get("id")));
        reminder=after(5000,()->chat("Events and storage example is running"));
    }
    public void deactivate(){cancel(reminder);}
}
```

## CustomAuraRotation.java

```java
package examples;

import dimasik.script.api.*;

public final class CustomAuraRotation extends Script {
    private final Setting<Double> speed = slider("speed", "Degrees per tick", 12, 1, 90, 1);
    @Override public String name() { return "Custom Aura Rotation"; }
    @Override public String description() { return "Selectable smooth Aura rotation"; }
    @Override public void activate() {
        registerAuraRotation("smooth", "Script Smooth", frame -> {
            float yawDelta = wrap(frame.targetYaw() - frame.yaw());
            float pitchDelta = frame.targetPitch() - frame.pitch();
            float limit = speed.value().floatValue();
            return new RotationAngles(frame.yaw() + clamp(yawDelta, limit),
                    frame.pitch() + clamp(pitchDelta, limit));
        });
    }
    private static float wrap(float angle) {
        angle %= 360;
        if (angle >= 180) angle -= 360;
        if (angle < -180) angle += 360;
        return angle;
    }
    private static float clamp(float value, float limit) { return Math.max(-limit, Math.min(limit, value)); }
}
```

## HookDemo.java

```java
import dimasik.script.api.*;

public final class HookDemo extends Script {
    private final Setting<Boolean> protect = checkBox("protect", "Block attacks and breaking", false);
    private String lastInput = "none";
    private String dimension = "";

    public String name() { return "Hook Demo"; }
    public void activate() {
        hook("input:key", h -> { if ((Integer) h.arg(1) == 1) lastInput = "Key " + h.arg(0); });
        hook("input:mouse", h -> { if ((Integer) h.arg(1) == 1) lastInput = "Mouse " + h.arg(0); });
        hook("world:join", h -> dimension = (String) h.arg(0));
        hook("world:leave", h -> dimension = "");
        hook("player:before_attack", h -> { if (protect.value()) h.cancel(); });
        hook("player:before_break_block", h -> { if (protect.value()) h.cancel(); });
        // A script may be enabled after the world has already loaded.
        dimension = world().dimension();
    }
    public void onRender2D(Render2D r) {
        r.text(lastInput + "  " + dimension, 16, 42, 14, r.theme("accent"));
    }
}
```

---

<a id="doc-errors"></a>

# Ошибки и диагностика

Сначала откройте «Скрипты → Консоль». Сохраните полное сообщение, имя файла и строку. Через MCP используйте `console_read`; в чате — `.script list` и `.script status`.

## Файл не появился

Командой `.script dir` откройте каталог именно запущенной игры. Файл должен лежать прямо в нём и называться `MyScript.java`, а не `MyScript.java.txt`. Публичный класс — `MyScript extends Script`, конструктор без аргументов. После сохранения выполните `.script reload` и дождитесь результата.

## Не находится метод API

`cannot find symbol`, `NoSuchMethodError` или `Unknown built-in hook` обычно означают различие возможностей сборок. Сверьте API в IDE, лоадере и сервере компиляции; новый хук требует обновлённого клиента. В старом JAR с номером 2.1.0 могут отсутствовать GUI-методы. Не добавляйте второй JAR поверх первого.

## Сеть и сервер компиляции

`SSLHandshakeException`, `Connection reset`, `curl 28` и таймаут означают, что запрос компиляции не завершился. Скрипт ещё не запущен: изменение `onRender2D` это не исправляет. Проверьте доступность сети, повторите `.script reload`, передайте администратору полный журнал и время ошибки. Доступность главной страницы документации не доказывает работу сервиса компиляции.

Сообщение о несовместимом API или ответе сервера требует согласовать версию API между клиентом и сервисом. Не меняйте исходник скрипта и не отключайте проверки наугад. Требование локального JDK-компилятора в журнале указывает на старую сборку клиента; текущая отправляет исходники серверу.

## Ограничение исполнения

`BudgetExceeded` означает превышение бюджета callback. Уберите бесконечные циклы, сократите перебор сущностей, считайте данные по тикам и рисуйте готовый результат. `after()` переносит работу на другой тик, но не запускает её в отдельном потоке и не снимает ограничения.

`Render2D frame expired or drawing limit exceeded` — использован старый контекст или больше 2048 обращений за кадр. Не храните `Render2D`/`Render3D` в поле; сохранять можно числа, строки и снимки данных.

## Forbidden type или method

Импортированный тип или вызов недоступен скрипту. Даже если IDE компилирует код, Minecraft, reflection, сеть, файлы, потоки и Stream API не входят в разрешённые возможности. Используйте опубликованные методы и обычные циклы. `System.nanoTime()` и `System.currentTimeMillis()` доступны для времени; остальные возможности `System` недоступны.

## Своя ClickGUI не открывается

Проверьте актуальность библиотеки и клиента, включите скрипт и войдите в мир. Подписка на `gui:open_clickgui` должна в самом обработчике вызвать `openGui()`. Один `cancel()` или `result(true)` интерфейс не заменяет. Если включены несколько замен, используется первая открывшая экран по порядку ID; отключите остальные.

Escape закрывает экран, если `onGuiKey` не поглотил эту клавишу. Для отключения замены закройте экран, откройте чат и выполните `.script toggle "MyClickGui.java"`. После ошибки исправьте код и перезагрузите скрипт.

## Настройка модуля стала недоступна

`Setting is no longer visible` — другая настройка изменила доступность поля. Повторно вызовите `moduleOptions(name)`, обновите интерфейс и используйте полученные ID. Не сохраняйте внутренние индексы полей между версиями клиента.

## Доступ к пакету истёк

Продлите доступ на сайте и установите пакет заново. Если пакет удалён или доступ отозван, обратитесь к автору/администратору. Для приватного скрипта вход должен быть выполнен под логином, которому выдали доступ.
