Files
shacraft-core/docs/SERVER.md
T

17 KiB
Raw Blame History

Сервер, протокол и эксплуатация MVP

Запуск

Нужны Rust 1.96+ и современный браузер с WebGL2. Java не нужна для запуска Shacraft: проверенный каталог включён в исходники. Node 22+ используется для проверок клиента и сети.

cargo build --release --locked --workspace
bash scripts/run.sh --data data --listen 127.0.0.1:4000

Открыть http://127.0.0.1:4000. Для друзей в локальной сети можно задать --listen 0.0.0.0:4000; браузерная проверка SHA-256 требует HTTPS для адресов вне localhost. Для внешней сети нужен обычный HTTPS/WSS reverse proxy. В MVP нет сервиса аккаунтов; имя игрока не является подтверждённой личностью. Контрольный токен выдаётся только владельцу сервера, клиент его не получает.

--data — каталог WorldStore, --cache-sections — ёмкость общего кэша (по умолчанию 64). --client и --packages задают каталоги статического клиента и проверенных пакетов. Скрипт запуска явно задаёт пути относительно распакованного проекта. Остановка — Ctrl+C; уже подтверждённые правки не зависят от корректного завершения процесса.

Первый запуск создаёт лобби, галерею gallery, неизменяемую основу Spleef и две независимые арены. Каталог содержит все состояния Java 26.2 и авторский trampoline. Импортированный WorldStore тоже можно открыть сервером; импортированные миры появятся в выборе миров рядом с демонстрационными. Галерея содержит 1 197 образцов состояний по умолчанию на полу 128×128, с шагом 3; это обзор всех типов блоков, а не всех 32 тысяч вариантов. Начальная позиция галереи — [-50,2,58]; клиент подгружает её частями по мере перемещения.

Владение состоянием

Один выделенный поток владеет WorldStore, физикой и игровым состоянием. HTTP и WebSocket ставят ограниченные команды в очередь. Движение считается 20 раз в секунду: скорость 5 блоков/с, гравитация 20 блоков/с², обычный прыжок 7 блоков/с. Игрок занимает AABB 0.6×1.8 блока; позиция задаёт середину стоп. Оси +Y вверх, yaw растёт вправо, pitch вверх; направление взгляда [sin(yaw)*cos(pitch),sin(pitch),-cos(yaw)*cos(pitch)].

Игровые позиции и точки появления ограничены диапазоном ±32 700 по каждой оси: физика этого клиента использует f32. Хранилище и конвертер сохраняют более широкий контракт ±30 000 000; это не обещание игровой физики на дальних координатах. Выход игрока за игровой диапазон возвращает его на spawn, а в активном матче означает выбывание.

Ввод содержит намерение двигаться, а не позицию. Сервер проверяет дальность 6 блоков, ближайшее пересечение форм, ячейку установки, пересечение с игроком и правила арены. Просроченный ввод обнуляется через секунду. Игровая физика использует ограниченную область соседних блоков на каждый тик; независимые миры без игроков не требуют массива загруженных секций.

В worlds.sqlite3 хранятся блоки и их история. В server.sqlite3 — конфигурация и сохраняемые сущности с отдельной ревизией. Это две транзакционные области: создание мира и добавление его настроек не заявляются одной общей транзакцией. Если сохранение настроек не удалось после создания мира, мир доступен с безопасными настройками по умолчанию; ошибку нельзя интерпретировать как разрешение повторно создать тот же мир. Для сущностей и настроек неуспешная запись восстанавливает состояние из последней сохранённой версии. Если даже чтение SQLite невозможно, операция возвращает ошибку; ошибка хранения не объявляется успешным подтверждением.

HTTP и авторизация

Публичные GET: /api/health, /api/manifest, /api/worlds, /api/catalog, /api/entities, /api/metrics. Каталог принимает query, offset, limit (1–256) ids с максимум 128 runtime ID или states с каноническими состояниями, разделёнными запятыми вне скобок свойств. Runtime ID берётся из ответа; он не совпадает с числовым ID Minecraft.

POST /api/control принимает { "method": "world.read", "params": {...} }, возвращает { "result": ... } или HTTP 400 с { "error": "..." }. Требуется Authorization: Bearer <token>. При первом запуске файл control.token создаётся с режимом 0600 на Unix. Параметры операций доступны в схемах tools/list отдельного MCP. Названия Control API используют точку: world.edit, build.plan, camera.capture и т.д.

Токен не является секретом от администратора локальной машины. Хеш manifest подтверждает совместимость ресурсов; он не доказывает неизменность исполняемого кода клиента. Защита игрового состояния основана на серверных проверках. Origin для браузерного WebSocket и Control API проверяется; межсайтовый доступ не включён.

WebSocket, снимки и правки

Первое сообщение на /ws в течение 10 секунд:

{"type":"join","protocol":1,"manifest_hash":"из /api/manifest","name":"Игрок","world":"lobby"}

welcome содержит id, world, revision, blocks:[{pos,block}], определения использованных materials, players, entities, spawn, view_center и manifest_hash. Снимок покрывает [centerX-32, centerY-8, centerZ-32]…[centerX+31, centerY+31, centerZ+31], максимумы включительные. При перемещении центра видимости сервер присылает новый snapshot; клиент полностью заменяет геометрию. Полный строковый реестр в снимке не передаётся. materials ограничен 256 записями и 128 КиБ; остальные определения клиент последовательно запрашивает через /api/catalog?ids=1,2,...&limit=128. Это позволяет войти в мир с большой палитрой, не переполняя исходящую очередь.

Клиент отправляет input с seq,yaw,pitch,forward,strafe,jump; break с pos; place с pos,block или state; switch_world, resync, respawn, start_match, chat, ping. Ответ state содержит авторитетные позиции, tick, подтверждённый ack и состояние матча. Блоки приходят в blocks с новой ревизией. При пропуске ревизии клиент запрашивает снимок. Подписка и создание снимка сериализованы с правками в одном потоке, поэтому промежуточная правка не теряется.

Для управляющих правок сохраняется контракт ядра: ожидаемая ревизия, уникальный operation_id, durable-подтверждение, повтор того же запроса без второй записи, отказ при конфликте. План строительства занимает не более 32 768 ячеек, хранится 5 минут, максимум 16 планов; preview не меняет мир. Планы эфемерны и исчезают после перезапуска, принятые правки остаются на диске. camera.capture возвращает PNG изометрической серверной проекции и точную ревизию; это не кадр WebGL-камеры игрока.

Правила Spleef

Матч проходит waiting → countdown → active → finished → waiting. По умолчанию нужны 2 игрока, отсчёт занимает 3 секунды, раунд — до 180 секунд. Разрешено разрушать только minecraft:snow_block в активном раунде; установка блоков и разрушение границы запрещены. Падение ниже Y=−6 означает выбывание. При одном оставшемся участнике объявляется победитель; истечение времени с несколькими оставшимися даёт ничью. После результата через 5 секунд мир возвращается к своему закреплённому шаблону, игроки — на spawn. Арены независимы. Отключение/уход из мира исключает участника; повторный вход во время раунда остаётся входом зрителя. После перезапуска прерванный матч сбрасывается к шаблону.

arena.configure сохраняет настройки мира. Изменять их можно между матчами; попытка во время countdown/active/finished отклоняется. Доступны mode, spawn и следующие поля:

  • countdown_seconds: целое 1…30, по умолчанию 3; round_seconds: целое 1…3600, по умолчанию 180.
  • min_players: целое 2…32, по умолчанию 2.
  • elimination_y: конечная координата в пределах ±32 700, по умолчанию −6.
  • spawn_points: до 32 точек [x,y,z]. Пустой список означает круг радиуса 7 вокруг X/Z=0 на высоте spawn[1]. Непустой список должен вмещать минимум участников, а при старте — всех вошедших; точки располагаются не ближе 0.8 блока друг к другу.
  • spectator_spawn: точка зрителя, по умолчанию [13,2,0].
  • floor_block: известное каноническое состояние с коллизией, которое участникам разрешено разрушать. По умолчанию minecraft:snow_block. Пол в закреплённой карте должен уже содержать этот материал; configure не перекрашивает и не перестраивает карту.

Все точки появления находятся в игровом диапазоне, их Y выше elimination_y + 0.5. Необязательный expected_revision проверяет общую ревизию метаданных, доступную через entity.list/metrics; это отдельная ревизия от блоков мира. Правила возвращаются в match.rules. Старые настройки с двумя полями mode/spawn читаются с указанными значениями по умолчанию.

Журнал идемпотентности ядра относится к правкам блоков, undo/reset и подтверждению плана. Операции сущностей и настройки арен пока не имеют такого журнала: повторный entity.spawn создаёт ещё один экземпляр. После неопределённого сетевого результата сначала следует прочитать текущее состояние.

Лимиты и наблюдаемость

  • 32 WebSocket-сессии, включая ожидающие join; 16 одновременно обслуживаемых HTTP-запросов, очередь 64 команд.
  • Входное сообщение WebSocket до 64 КиБ, максимум 80 сообщений/с на соединение; действия блоков не чаще 110 мс, чат не чаще 700 мс, явный resync не чаще 500 мс, переход между мирами не чаще секунды, автоматический снимок при смене секции не чаще 2 секунд.
  • Control API до 2 МиБ JSON; штатная правка до 32 768 ячеек; чтение до 262 144 ячеек и 6 МиБ ответа; при превышении байтового лимита требуется уменьшить область.
  • Исходящая очередь на клиента: 16 сообщений и 8 МиБ. При переполнении или превышении срока отправки медленное соединение закрывается. Общее верхнее ограничение очередей зависит от числа клиентов; эти байты не следует путать с кэшем секций.
  • Метаданные сервера до 8 МиБ, до 4096 сущностей, свойства одной сущности до 16 КиБ. В игровые снимки входят компактные представления без произвольного JSON свойств; полные свойства читаются через entity.list с offset и limit (по умолчанию 64, максимум 128).
  • RAM процесса в /api/metrics — фактический Linux VmRSS, а storage.cache_payload_bytes — только полезные байты кодированных секций. Каталог, строки, SQLite, сетевые буферы, временные чтения и память аллокатора существуют отдельно.

Максимальная разрешённая конфигурация лимитов не равна измеренному целевому профилю нагрузки. Условия и результаты benchmark публикуются в VERIFICATION; заявлений о выигрыше относительно Paper без сопоставимого прогона нет.

Сущности и совместимость

158 определений сущностей имеют реальные размеры и авторские процедурные модели. Сохраняемые экземпляры создаются, перемещаются и удаляются через MCP. Полноценные ванильные AI, redstone, жидкости, инвентари и игровая логика Minecraft не реализованы. Формы контекстно зависимых блоков явно отмечены в каталоге. Изменённые и неизвестные данные конвертер обрабатывает по правилам docs/interop.md, без молчаливого объявления lossless-совместимости.