Перейти к содержимому

События и жизненный цикл

События запуска, смены режима и Share Party в BDEngine: payload, безопасная подписка, очистка обработчиков и методы editorAPI.animator.

События редактора слушают через window.addEventListener(). У editorAPI нет отдельной шины on() / off(). Для снятия подписки используйте window.removeEventListener() с той же функцией или AbortController.

bde:started

bde:started - обычный Event без detail. Редактор отправляет его после editorAPI.initGUI(), когда доступен интерфейс API, включая editorAPI.headPaint.

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

(() => {
function init() {
window.editorAPI.addButtonTool('icon-info', () => {
console.log('[my-plugin] Редактор готов');
});
}
if (window.editorAPI?.headPaint) {
init();
} else {
window.addEventListener('bde:started', init, { once: true });
}
})();

Здесь наличие headPaint служит признаком выполненной инициализации GUI. Проверка только window.editorAPI недостаточна: основной объект создаётся до интерфейса.

Событие означает готовность интерфейса для плагина, но не является событием открытия проекта. Часть стартовой загрузки содержимого и последующие действия выполняются позже. Не считайте, что к этому моменту уже открыт проект из ссылки или восстановлена последняя сессия. Отдельного публичного события загрузки каждого проекта в этой группе API нет.

bde:change-mode

CustomEvent, в event.detail передаётся строка, равная текущему режиму:

Значение Режим
editor Редактирование модели.
animation Animator.
sound Звуки.
structure Структуры.
func Функции и интерактив.
function onModeChange(event) {
const mode = event.detail;
console.log('[my-plugin] Текущий режим:', mode);
}
window.addEventListener('bde:change-mode', onModeChange);
// При завершении работы своего кода:
// window.removeEventListener('bde:change-mode', onModeChange);

Используйте animation, а не animator: название режима в интерфейсе и значение события различаются. detail.mode и detail.previousMode не существуют. Событие отправляется в конце обновления основного режима; открытие HeadPaint или окна скриншотов не добавляет сюда новые строковые значения.

Подписка не сообщает текущее состояние автоматически. Если оно нужно сразу, можно прочитать window.editor.currentMode; это обращение к внутреннему объекту редактора, а не отдельный стабильный getter editorAPI. Не сохраняйте предположение о режиме вместо обработки следующих событий.

bde:server-connected

CustomEvent без дополнительных данных: event.detail === null. Отправляется, когда Share Party получил подтверждение входа в комнату (welcome) и обработал начальный список участников.

Это не просто открытие WebSocket: транспорт может открыться раньше подтверждения комнаты. Повторное подключение может снова вызвать событие. Оно не означает загрузку всей сцены других участников и не сообщает ID комнаты или пользователя.

bde:server-disconnected

CustomEvent с detail === null. Возникает при закрытии соединения Share Party, явном отключении и принудительном разрыве перед повторным подключением.

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

bde:server-error

CustomEvent с detail === null. Текущая реализация отправляет его при событии error активного WebSocket Share Party. Исходный объект ошибки в detail не передаётся. После этого редактор может отправить bde:server-disconnected и начать восстановление соединения.

Не используйте это событие как универсальный обработчик всех ошибок редактора. Отказ входа в комнату и другие ситуации могут обрабатываться без bde:server-error.

Все три события bde:server-* относятся к Share Party. Они не относятся к HTTP Server API для скачивания экспорта и не описывают соединение Minecraft Live Link.

HeadPaint и Minecraft Live Link

События bde:headPaint:started, bde:headPaint:paint, bde:headPaint:ended, bde:headPaint:fillStarted и bde:headPaint:fillEnded передают сведения об инструменте, цвете и рисовании. Их payload и ограничения пользовательских инструментов описаны в справочнике HeadPaint.

Внутренний клиент Minecraft Live Link отправляет ещё два CustomEvent. Это отдельная область, для которой editorAPI не предоставляет самостоятельного интерфейса управления соединением:

Событие event.detail
bde:minecraft-live-link:status Снимок status() клиента: в том числе loaded, connecting, connected, ready, state, version, lastError, playerPosition, chatLog, sync, worldPreview.
bde:minecraft-live-link:chat Запись журнала: type, message, time; у сообщения игрока также могут быть sender и raw.

time записи чата - результат Date.now() в миллисекундах. Поля состояния могут содержать null, а подробности sync, worldPreview и исходного сообщения зависят от внутренних подсистем. При их использовании учитывайте возможные изменения между версиями редактора. Проверяйте конкретные поля, которые нужны плагину, и не считайте connected и ready взаимозаменяемыми.

Методы editorAPI.animator

В публичном API есть три синхронных метода для текущей анимации, звука и длины видимой шкалы. Вызывайте их после подготовки интерфейса. Если нужная подсистема или текущая запись отсутствует, они возвращают null.

Метод Аргументы и результат
setCurrentAnimationName(name) Непустая строка. Убирает пробелы по краям, заменяет пробельные последовательности на _, оставляет латинские буквы, цифры, _ и скобки. При совпадении имени без учёта регистра добавляет суффикс _1, _2 и далее. Возвращает итоговое имя или null, если имя непригодно.
setCurrentSoundBPM(value) Несмотря на название, принимает число тиков шага, а не BPM. Использует parseInt(value, 10) и ограничивает результат значениями 1-3. Возвращает принятое число или null.
setLength(seconds, resetCurrent = false) Меняет длину видимой шкалы в секундах. Использует parseInt(seconds, 10); нижний предел - максимум из 10 секунд и длины имеющейся анимации, верхний - 3600. Возвращает принятое значение или null. При resetCurrent: true переводит воспроизведение в начало.

Для setCurrentSoundBPM() интерфейс вычисляет BPM как 60000 / (tick * 50 * 4): 1 соответствует 300 BPM, 2 - 150, 3 - 100. Передача 120 установит 3, а не 120 BPM.

const actualName = editorAPI.animator.setCurrentAnimationName('door open');
const actualTick = editorAPI.animator.setCurrentSoundBPM(2);
const visibleSeconds = editorAPI.animator.setLength(30);

Изменение имени анимации и шага звука записывается в историю Undo. Повторная установка уже принятого значения не добавляет новую команду. setLength() меняет отображаемый диапазон шкалы, не обрезает ключи и не устанавливает длительность анимации как самостоятельного ресурса. Эта настройка не создаёт команду Undo. Если редактор не смог подобрать уникальное имя за 1000 попыток, переименование может выбросить ошибку.

Регистрация и снятие обработчиков

Официального события bde:plugin-unload и автоматически вызываемого dispose() в загрузчике нет. Функцию очистки организует сам плагин. Удаление его кнопки или скрипта из DOM не снимает обработчики window, таймеры и другие созданные ресурсы.

Для повторного запуска собственного кода можно сохранить одну функцию очистки в глобальном ключе с уникальным именем:

(() => {
const cleanupKey = '__samplePluginModeCleanup';
window[cleanupKey]?.();
const subscriptions = new AbortController();
window[cleanupKey] = () => subscriptions.abort();
function init() {
window.addEventListener('bde:change-mode', event => {
console.log('[sample-plugin] Режим:', event.detail);
}, { signal: subscriptions.signal });
}
if (window.editorAPI?.headPaint) init();
else window.addEventListener('bde:started', init, {
once: true,
signal: subscriptions.signal,
});
})();

Глобальный ключ и вызов очистки здесь принадлежат примеру, это не встроенный hook BDEngine. abort() снимает только обработчики, зарегистрированные с этим signal. Таймеры, DOM, окна, внешние соединения и ресурсы графики очищайте отдельно.

Проверки повторной установки и закрытия плагина: «Отладка и корректное завершение работы».