Minecraft Builder Camera
Клиентский Fabric-мод для minecraft-builder-mcp. Предоставляет настоящий PNG из framebuffer Minecraft через защищённый локальный HTTP-интерфейс. Отрисовка требует запущенного клиента с рабочим графическим окружением. Мод не входит в серверный JAR и не изменяет блоки.
Закреплённая платформа
- Minecraft Java Edition 26.2, Java 25.
- Fabric Loader 0.19.5, Fabric API 0.160.0+26.2.
- Fabric Loom 1.17.20, Gradle Wrapper 9.5.1 (SHA-256 дистрибутива проверяется).
- JUnit 5.12.2 используется только при сборке тестов.
Версии проверены по Fabric Maven, Fabric Meta и официальному примеру 26.2. Начиная с 26.1 Minecraft не обфусцирован; Yarn и перепривязка имён для этой сборки не нужны. Инструкция Fabric для 26.2.
Сборка и установка
cd camera-mod
JAVA_HOME=/path/to/jdk-25 ./gradlew build
Результат: build/libs/minecraft-builder-camera-0.1.0-SNAPSHOT.jar. Установить его и закреплённый Fabric API в отдельный профиль Minecraft 26.2 с Fabric Loader. Клиент обычного строителя не требует этого мода.
Перед запуском профиля задать окружение процесса:
export MCB_CAMERA_TOKEN='<отдельный секрет длиной не менее 32 символов>'
export MCB_CAMERA_PORT=8766
Тот же секрет указать в конфигурации Paper-плагина для подключения камеры. Paper принимает 32–512 символов из A–Z, a–z, 0–9, ., _, ~, -, без пробелов и переносов; автоматически созданное значение уже подходит. Все три ключа Paper должны различаться. Без MCB_CAMERA_TOKEN HTTP-служба отключена. MCB_CAMERA_PORT необязателен; допустимы порты 1024–65535. Адрес всегда 127.0.0.1, переключения на публичный интерфейс нет.
Запустить отдельного наблюдателя, подключиться к нужному Paper-серверу и перевести его в spectator разрешённым серверным способом. Указать UUID наблюдателя в Paper-плагине. Нужна допустимая отдельная игровая сессия, если строитель остаётся на сервере одновременно; мод не обходит вход или ограничения аккаунтов. Держать клиент с закрытыми меню, без слежения за другой сущностью. Свёрнутое окно может прекратить рендеринг и вызвать таймаут.
Один клиент через Prism
Для локального теста достаточно одной учётной записи: владелец проекта одновременно служит камерой. Этот вариант проверен на настоящем клиенте Prism с Paper 26.2. В выбранный профиль установить мод и зависимости, подключиться к серверу и привязать владельца через /ai setup. В приватном конфиге Paper camera-player-uuid должен совпадать с owner-uuid. Владелец должен быть онлайн, иметь разрешение minecraftbuilder.use и находиться в spectator. Изменения конфигурации применяются после перезапуска плагина/сервера.
Чтобы секрет не попадал в аргументы Java и журнал лаунчера, использовать scripts/camera-wrapper.py как WrapperCommand профиля Prism, например python3 /path/to/minecraft-builder-mcp/scripts/camera-wrapper.py. Обёртка читает camera-token и camera-port из приватного .runtime/server/plugins/MinecraftBuilderMCP/config.yml и передаёт их только через окружение дочернего процесса. Другой путь к конфигу задаётся переменной MCB_CAMERA_PAPER_CONFIG.
Сохранить ракурс внутри области проекта командой /ai camera save test. Затем из корня репозитория запустить:
python3 scripts/live-camera-test.py --camera-id test --delay 8
scripts/live-camera-test.py проверяет совпадение UUID владельца и камеры, spectator и доступ к Paper. Через восемь секунд он вызывает настоящий camera_capture по авторизованному HTTP-маршруту Paper, ждёт PNG и сохраняет исходные байты вместе с очищенными метаданными в .runtime/camera-test. Вместо сохранённого ракурса можно передать --pose X Y Z YAW PITCH, дополнив его --fov 85 для общего вида большой постройки (допустимо 30–110°); --after-operation-id связывает снимок с завершённой операцией.
До начала съёмки вернуться в окно Minecraft, закрыть чат и меню, остановиться и не двигать мышь. Допустимое изменение поворота всего 0.1°, поэтому даже небольшой сдвиг отменяет кадр. Переключение в другое окно может открыть меню паузы. В этом режиме снимок временно использует твой игровой вид; серверная телепортация меняет твою позицию. Режим игры и прежняя позиция автоматически не восстанавливаются. Для возврата к строительству выбрать нужный режим и место вручную.
Протокол
Все запросы, включая health и чтение изображения, требуют Authorization: Bearer <MCB_CAMERA_TOKEN>. JSON не записывается в лог.
GET /health— кэш состояния последнего клиентского тика:status,connected,spectator,busy,dimension,playerId,updatedAt. СтароеupdatedAtозначает, что клиент перестал обновляться.POST /v1/capture— поставить один снимок в работу. Ответ HTTP 202:{"status":"pending","captureId":"<uuid>"}. При занятой камере HTTP 409 иcamera_busy.GET /v1/captures/<uuid>— получитьpending,completedлибоerror. Неизвестный/истёкший ID: HTTP 404. Терминальныйerrorимеетerrorиmessage, без изображения.
Пример тела capture:
{
"x": 16.5, "y": 90, "z": 16.5,
"yaw": 45, "pitch": 25,
"fov": 70, "width": 1280, "height": 720,
"dimension": "minecraft:overworld",
"afterOperationId": "operation-id"
}
x/y/z — позиция ног игрока-наблюдателя, как в Paper teleport. Paper сначала проверяет область/права и телепортирует настроенного наблюдателя; затем вызывает capture. Мод ждёт получения нужной позиции и измерения, но сам не отправляет /tp и не подменяет локальную позицию. dimension — клиентский ключ измерения, не имя папки и не Bukkit UUID. Дополнительные world/world_id принимаются как совместимые поля конверта, но не используются как доказательство измерения. dimension необязателен в низкоуровневом интерфейсе; серверный маршрут должен передавать его.
Серверный маршрут Paper camera_capture передаёт POST, а при наличии capture_id опрашивает соответствующий GET. Конкретные названия внешних MCP-инструментов определяет Bridge.
Результат completed содержит imageBase64, mimeType: "image/png", captureId, capturedAt, dimension, позицию ног, eyeY, фактические yaw/pitch, базовый FOV, размеры исходного framebuffer и изображения, а также метаданные готовности.
width/height задают максимальные размеры выходного изображения. Снимок вписывается в них с сохранением пропорций и без увеличения; разрешение окна не меняется. Это предотвращает искажение геометрии. Для точных 1280×720 следует использовать framebuffer такого же соотношения сторон и достаточного размера. Базовый FOV ограничен 30–110, ширина 320–1920, высота 180–1080; исходный framebuffer ограничен 16 мегапикселями. Поза требует конечных чисел, yaw -360..360 и pitch -90..90.
Что означает готовность
Перед снимком проверяются spectator, совпадение позиции (±0.05 блока), измерения и собственного вида наблюдателя. Мод скрывает HUD, включает первый вид, отключает покачивание и влияние движения на FOV. Затем ждёт:
- Девять клиентских чанков вокруг наблюдателя доступны не менее 20 тиков подряд.
- В течение трёх кадров камера инициализирована, чанки доступны, очередь подготовки геометрии пуста.
- Поза и окно остаются подходящими до чтения framebuffer.
PNG снимается через Screenshot.takeScreenshot после рендера кадра, с GPU readback через Blaze3D; прямого OpenGL-кода нет. Кодирование и уменьшение PNG выполняются отдельным потоком. HUD, FOV, перспектива, покачивание и поворот, сохранённые при начале работы мода с кадром, восстанавливаются на клиентском потоке после завершения или ошибки. Сохранение начинается после получения серверной позиции; это не возврат к положению игрока до телепортации. Позиция после серверной телепортации остаётся серверной.
Это проверяемая эвристика загрузки, а не подтверждение конкретной серверной ревизии. Ответ всегда содержит readiness: "local_chunks_and_render_queue_stable" и serverRevisionVerified: false. Поле afterOperationId служит корреляцией; само по себе оно не доказывает, что клиент получил все обновления операции. Нельзя выдавать такой результат за проверку ревизии. Для строгой свежести нужен дополнительный серверный маркер и подтверждение обработки соответствующих пакетов. Дальняя геометрия вне проверенных чанков и изменения после кадра остаются ограничениями.
Ошибки загрузки, отключение, смена мира/позиции, открытые меню, вмешательство в поворот и неполученный framebuffer возвращают ошибку вместо старого кадра. Таймаут 20 секунд контролируется отдельным потоком даже при зависшем рендере. Следующий снимок разрешается после восстановления состояния на клиентском потоке. Хранятся максимум четыре результата не дольше двух минут; PNG до 8 MiB, тело запроса до 8192 байт. Изображения находятся в памяти и не записываются в общий каталог screenshots.
Проверки и границы прототипа
./gradlew build компилирует мод против настоящих зависимостей Minecraft 26.2; тесты проверяют bearer-аутентификацию HTTP, ограничение размера запроса и валидацию параметров. Для них не запускаются клиент или вход в аккаунт.
12 сентября 2026 года выполнен реальный графический тест: один клиент Prism, владелец проекта в spectator, одинаковый UUID владельца и камеры, Paper 26.2 и построенная башня из 575 блоков. Проверены загрузка Mixin, подключение клиента, серверная телепортация, чтение framebuffer и доставка PNG через Paper HTTP. На изображении видна построенная башня без HUD.
Первый запрос завершился view_changed: фактический поворот отличался от заданного. Повтор после стабилизации дал PNG 1280×720 за 2.052 секунды, с yaw 140°, pitch 31°, после 20 тиков и 3 кадров готовности. Это подтверждённый локальный замер одного запроса, а не гарантия времени для других сцен и компьютеров. Артефакты проверки: .runtime/camera-test/20260912T192813Z-2b100930.png и соответствующий JSON; они остаются локальными и не входят в Git.
Успешный кадр получен после завершения строительной операции и содержит её afterOperationId, но serverRevisionVerified остаётся false: подтверждения обработки конкретной серверной ревизии ещё нет. Отдельно остаются проверки восстановления всех настроек вида, таймаута при свёрнутом окне, отключения посреди снимка, сторонних шейдеров и отдельного аккаунта камеры. Рабочий графический цикл подтверждён для описанного сценария с одним клиентом.