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

Добавить импорт или экспорт в меню «Файл»

Добавить пользовательское действие импорта/экспорта в меню «Файл».

Добавим два пункта: чтение локального файла проекта и сохранение JSON-экспорта модели. Редактор предоставляет место в меню, управление доступностью и ожидание обработчика. Выбор файла, проверку входа и скачивание результата выполняет плагин.

Перед началом нужно различать публичные и внутренние точки входа. Для проверки подготовьте копию небольшого проекта без настоящих структур.

Сценарий и формат данных

Скачать полный плагин file-actions.js. После установки и перезапуска появятся пункты:

  • Файл → Импорт → Добавить содержимое файла проекта - добавить содержимое .bdengine в текущий проект.
  • Файл → Экспорт → Скачать JSON-экспорт модели - получить данные для Minecraft и сохранить их в .json.

Это два разных направления обмена. Скачанный JSON-экспорт не является файлом проекта и не предназначен для обратного импорта этим пунктом. Пример не регистрирует новое расширение в общей системе открытия файлов и не добавляет обработку перетаскивания.

Публичные действия вызываются через editorAPI. Для проверки, что пользователь не сменил проект или режим во время выбора файла, пример читает editor.objects и флаги режимов. Это внутренняя зависимость: при обновлении редактора её нужно проверять отдельно.

Импорт и экспорт принимают одинаковую форму настроек:

const action = editorAPI.addButtonImportMenu({
id: 'my-plugin.import-project',
title: 'Добавить содержимое файла проекта',
icon: 'icon-file-input',
onClick: async (event) => {
await importProject();
}
});

importProject здесь обозначает ваш обработчик, полный вариант находится в скачиваемом файле. id, непустой title и функция onClick обязательны. icon - один CSS-класс иконки; его можно опустить. enabled при необходимости задаёт начальную доступность.

Выбирайте id с префиксом своего плагина. Повторная регистрация того же id в той же секции обновляет существующий пункт, а не создаёт второй. Одинаковый id в импорте и экспорте относится к разным секциям.

Пункт появляется после встроенных действий и разделителя. Подпись передаётся как обычный текст. Контракты: добавить импорт и добавить экспорт.

Доступность и асинхронная работа

Возвращаемый handle поддерживает два метода:

action.setEnabled(false);
action.setEnabled(true);
action.remove();

setEnabled принимает именно boolean и возвращает тот же handle. remove() удаляет пункт и возвращает true; повторное удаление возвращает false. Удаление пункта не отменяет уже запущенное чтение файла или другую асинхронную работу.

На время выполнения onClick редактор сам блокирует повторный запуск этого пункта. Чтобы блокировка охватывала асинхронную работу, верните Promise или используйте async и await:

// Правильно: меню дождётся окончания операции.
onClick: () => importProject()
// Не подходит: Promise не возвращён, меню сочтёт обработчик завершённым.
onClick: () => { importProject(); }

В полном примере общий флаг busy и setEnabled(false) временно блокируют оба пункта, чтобы импорт и экспорт этого плагина не пересекались. finally восстанавливает доступность после успеха, отмены и ошибки. Это не блокирует остальные команды редактора.

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

Обработать файл самостоятельно

Пример создаёт скрытый input с type="file", accept=".bdengine" и вызывает click(). Событие change отдаёт выбранный файл, cancel завершает операцию без файла. После обоих событий временный элемент удаляется. Отмена не считается ошибкой.

accept - фильтр диалога, а не проверка содержимого. Обработчик дополнительно проверяет расширение, непустой размер и границу 10 МБ. Последнее - ограничение учебного плагина, а не обещанный лимит BDEngine. Файл читается в байты:

const bytes = new Uint8Array(await file.arrayBuffer());
await editorAPI.mergeContent(bytes, true);

Проверка самого проекта остаётся на стороне импортёра BDEngine. Произвольный JSON, переименованный в .bdengine, от этого не становится корректным проектом. Пример предназначен для обычного режима моделирования; настоящие структуры этим способом не добавляются. Для файлов с собственными моделями важен весь контейнер проекта, а не только вырезанный из него текст сцены.

Обрабатывайте исключения arrayBuffer, ошибки JSON при самостоятельном чтении JSON, отсутствие результата экспорта и отмену. В примере ошибка показывается пользователю и записывается в консоль. Состояние меню восстанавливается в finally.

Получить либо добавить содержимое

mergeContent(content, decode) добавляет содержимое в текущий проект, сохраняя уже имеющиеся объекты. При decode: true читает контейнер или поддерживаемое закодированное представление. При decode: false ожидает строку JSON сцены BDEngine, а не разобранный объект и не SNBT:

await editorAPI.mergeContent(JSON.stringify(projectScene), false);

projectScene в этом фрагменте - уже подготовленные данные сцены BDEngine. Состав и ограничения: проверить обмен данными.

Обёртка editorAPI.mergeContent не передаёт наружу булев результат внутреннего импортёра. Успешное завершение await нельзя использовать как подтверждение, что объекты добавились: редактор может самостоятельно обработать ошибку и показать сообщение. Проверяйте результат в сцене; не выводите безусловное «Импорт успешно выполнен» только по завершению Promise.

Для Minecraft-данных вызывается другой метод:

const content = await editorAPI.exportToJSON(false);
if (!content) throw new Error('Экспорт не получен.');
const text = JSON.stringify(content, null, 2);

false отключает запрос полного экспорта через настройку окна, но не выводит редактор из Animator: в Animator результат всё ещё может иметь type: "full". Поэтому полный пример сначала требует обычный режим моделирования и после получения результата проверяет content.type === 'modelOnly'. Если режим или тип результата не подошёл, файл не скачивается и пользователь получает сообщение.

Метод использует окно экспорта редактора, меняет его тип/выбранную версию и показывает его; это не фоновая сериализация проекта. Полный пример сохраняет text через Blob, временный URL и ссылку с download, затем освобождает URL.

Результат не имеет HTTP-оболочки content, а маркер [BDESERVERTAG] не заменяется значением tag. Условия отказа и параметры: получить данные для экспорта.

Проверить меню и ошибочный ввод

  1. Найдите оба пункта после перезапуска. Убедитесь, что повторное открытие меню не создаёт дубликаты.
  2. Отмените выбор файла. Оба пункта должны снова стать доступными.
  3. Попробуйте пустой файл, неподходящее расширение и файл крупнее 10 МБ. Плагин должен сообщить о проблеме до вызова импортёра.
  4. Добавьте небольшую модель из настоящего .bdengine. Старые объекты должны остаться; проверьте новые в дереве, а не только отсутствие исключения в консоли.
  5. Во время выбора файла смените проект в другом доступном действии редактора. Старое чтение не должно начать импорт в новый корень проекта.
  6. В обычном режиме моделирования скачайте экспорт, откройте его как JSON и проверьте type: "modelOnly" и passengers. Затем запустите пункт в Animator: пример должен предложить перейти в моделирование, не начиная экспорт. Неподготовленные текстуры или ошибки экспорта не должны оставлять меню заблокированным.
  7. В собственной версии сохраните handle и проверьте remove(): исчезает только ваш пункт.

Обработка cancel и скачивание зависят от браузерного интерфейса файлов. Перед распространением проверьте оба действия в целевых веб-редакторе и BDEngine App.