81 lines
17 KiB
Markdown
81 lines
17 KiB
Markdown
# Сервер, протокол и эксплуатация MVP
|
||
|
||
## Запуск
|
||
|
||
Нужны Rust 1.96+ и современный браузер с WebGL2. Java не нужна для запуска Shacraft: проверенный каталог включён в исходники. Node 22+ используется для проверок клиента и сети.
|
||
|
||
```sh
|
||
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 секунд:
|
||
|
||
```json
|
||
{"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-совместимости.
|