From ddfcef2e1df589b171dbfa11251d03ca60e87dc9 Mon Sep 17 00:00:00 2001 From: Emil Date: Mon, 14 Sep 2026 16:12:02 +0300 Subject: [PATCH] docs: preserve Shacraft requirements and staged implementation plan --- .gitignore | 6 ++ Cargo.toml | 16 ++++ README.md | 17 ++++ docs/ACCEPTANCE.md | 149 +++++++++++++++++++++++++++++ docs/COMPATIBILITY.md | 104 ++++++++++++++++++++ docs/CONTRACT.md | 42 +++++++++ docs/DECISIONS.md | 37 ++++++++ docs/MEMORY_AND_STORAGE.md | 188 +++++++++++++++++++++++++++++++++++++ docs/PLAN.md | 79 ++++++++++++++++ docs/REQUIREMENTS.md | 31 ++++++ docs/STATUS.md | 25 +++++ 11 files changed, 694 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 docs/ACCEPTANCE.md create mode 100644 docs/COMPATIBILITY.md create mode 100644 docs/CONTRACT.md create mode 100644 docs/DECISIONS.md create mode 100644 docs/MEMORY_AND_STORAGE.md create mode 100644 docs/PLAN.md create mode 100644 docs/REQUIREMENTS.md create mode 100644 docs/STATUS.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a1c30d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/target/ +/data/ +/artifacts/ +*.token +.env + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d68592d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[workspace] +resolver = "2" +members = ["crates/shacraft-core", "crates/shacraft-server", "crates/shacraft-mcp", "crates/shacraft-compat"] + +[workspace.package] +version = "0.1.0" +edition = "2024" +license = "MIT OR Apache-2.0" + +[workspace.dependencies] +serde = { version = "1", features = ["derive", "rc"] } +serde_json = "1" +anyhow = "1" +clap = { version = "4", features = ["derive"] } +shacraft-core = { path = "crates/shacraft-core" } + diff --git a/README.md b/README.md new file mode 100644 index 0000000..932a113 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# Shacraft Core + +Самостоятельный открытый воксельный движок на Rust. Главный приоритет — низкое и предсказуемое потребление **серверной оперативной памяти**. Сервер Shacraft служит первым практическим применением; ядро не привязано к его мирам, аккаунтам и минииграм. + +Проект начинается заново локально: исходники из зависшей облачной среды не восстановлены. Сведения о ранее выполненных сборках и тестах не являются результатами этого репозитория. + +## Контекст и порядок чтения + +1. [Требования](docs/REQUIREMENTS.md) — что запросил пользователь. +2. [План](docs/PLAN.md) — этапы и условия перехода. +3. [Решения](docs/DECISIONS.md) — принятые решения и их границы. +4. [Интерфейсы](docs/CONTRACT.md) — рабочие контракты компонентов. +5. [Текущее состояние](docs/STATUS.md) — проверенные результаты и ближайшая работа. +6. [Память и хранение](docs/MEMORY_AND_STORAGE.md), [совместимость](docs/COMPATIBILITY.md), [приёмка](docs/ACCEPTANCE.md). + +Работа ведётся локально в `/home/emil/Desktop/shacraft-core`. Продакшен-серверы и существующие проекты лаунчера не изменяются автоматически. + diff --git a/docs/ACCEPTANCE.md b/docs/ACCEPTANCE.md new file mode 100644 index 0000000..f1565f2 --- /dev/null +++ b/docs/ACCEPTANCE.md @@ -0,0 +1,149 @@ +# Приёмка Shacraft Core + +Статус: план проверок; ни один пункт пока не считается выполненным. Основа — исходный запрос пользователя и текущий `docs/CONTRACT.md`. Последний описывает первый рабочий протокол, но пока не покрывает весь согласованный MVP. + +Shacraft Core — самостоятельный открытый движок на Rust. Shacraft-сервер и Spleef проверяют его пригодность на практике. Экономия серверной RAM — основное архитектурное требование. Полный базовый каталог Minecraft 26.2 означает контент, состояния и формы, а не требование воспроизвести архитектуру Minecraft или весь ванильный игровой процесс. + +## Как фиксировать результат + +Для каждой проверки сохранять: идентификатор, ревизию исходников, версии инструментов, команду или сценарий, используемые данные, фактический результат и путь к доказательству. Статусы: `не проверено`, `пройдено`, `не пройдено`, `заблокировано` с конкретной причиной. Сборка сама по себе не подтверждает рабочую сетевую игру или корректность импорта. + +Проверки ниже делятся на два рубежа. Первый позволяет получить работающую основу и быстро обнаруживать ошибки интеграции. Его прохождение не означает завершение полного MVP и не уменьшает исходный объём. + +## Рубеж A — минимальный сквозной этап + +### A-CORE: хранение и независимые миры + +- Ядро собирается и используется из отдельного Rust-процесса без HTTP, рендера, браузера и запуска сервера Minecraft. +- Созданы шаблон и два независимых мира на его основе. Изменение блока в первом мире не изменяет шаблон и второй мир. Замена исходного шаблона не меняет уже закреплённую неизменяемую версию. +- Удаление наследуемого блока записывает воздух в наложение, а не возвращает блок шаблона при повторном чтении. Проверить положительные и отрицательные координаты и границы секций. +- `world.edit` принимает пакет изменений целиком или отклоняет его целиком. Устаревшая ревизия, неизвестный блок и недопустимые координаты не оставляют частичных изменений. +- Повтор операции с тем же `operation_id` и тем же содержимым возвращает `replayed: true`, не увеличивает ревизию и не применяет изменение повторно. Повтор идентификатора с другим содержимым должен иметь документированное безопасное поведение; до его определения проверка не закрывается. +- `world.undo` восстанавливает предыдущее содержимое выбранной операции; конфликт с последующими правками обрабатывается по явно описанной политике. Проверить возвращение в наследуемое состояние и удаление наложений. +- `world.reset` восстанавливает выбранную версию шаблона и не повреждает другие миры. Изменение ревизии и судьба журнала операций документированы и проверены. +- `read_region` возвращает правильные ненулевые блоки в включённых границах. Область ровно в 262144 ячейки принимается, превышение лимита и переполнение арифметики границ отклоняются до большой аллокации. + +### A-E2E: два клиента, MCP и перезапуск + +Воспроизводимый сценарий выполняется на одной локальной установке из чистого каталога данных: + +1. Запустить сервер на `127.0.0.1:4000`, проверить `/api/health`, открыть два независимых браузерных сеанса и подключить MCP отдельным процессом. +2. Оба клиента входят в один мир. Каждый видит геометрию и другого игрока; движение, поворот, прыжок и столкновения работают в общей системе координат из контракта. +3. Первый клиент ломает и ставит доступный блок. Второй получает изменение без перезагрузки. Повторное подключение получает то же состояние и актуальную ревизию. +4. MCP читает область, находит блок через каталог, выполняет изменение с `expected_revision` и `operation_id`. Оба клиента видят правку. Повтор той же операции идемпотентен; намеренно устаревшая ревизия возвращает понятную ошибку. +5. MCP выполняет undo, и оба клиента видят восстановление. MCP получает снимок камеры; PNG открывается и показывает ожидаемый участок мира, а не пустое изображение. +6. Создать второй мир из шаблона, переключить туда одного клиента и изменить блок. Второй клиент, оставшийся в исходном мире, не получает чужие правки или состояния игроков. +7. Записать проверяемые позиции, значения блоков, имена миров, закреплённые шаблоны, идентификаторы блоков и ревизии. Завершить сервер, запустить с тем же каталогом данных и подключить клиентов и MCP заново. Всё записанное совпадает; подтверждённые durable-операции не потеряны. +8. Повторить восстановление после принудительного завершения процесса в контролируемом тесте. Повреждённый или незавершённый хвост записи не делает ранее подтверждённый мир нечитаемым. Если гарантия подтверждения отличается от гарантии `flush`, это заранее отражено в контракте. + +Доказательство: журнал действий и ответов протокола, результаты сверки до/после рестарта, снимок двух клиентов. Скрипт протокола дополняет визуальную проверку браузера, но не заменяет её. + +### A-SERVER: сервер определяет состояние игры + +- Нельзя войти в произвольный неизвестный мир, передать нечисловые/неограниченные координаты или использовать недопустимый `BlockId` и привести к сбою процесса. +- Клиент передаёт ввод; координаты, скорость, столкновения и результат действий определяет сервер. Поддельный пакет с готовой позицией не телепортирует игрока. +- Сервер проверяет дистанцию взаимодействия и доступность действия по состоянию игры. Игрок не ломает далёкие блоки и не редактирует мир, в котором не находится. +- Частота пакетов и размер сообщений ограничены. Поток некорректных, слишком больших и слишком частых сообщений одного соединения не создаёт неограниченную очередь и не лишает второй клиент возможности играть. +- Контрольный HTTP API отвергает отсутствующий и неверный токен. Токен не попадает в публичный manifest, клиентскую сборку, URL или обычные журналы. Файл токена создаётся с правами `0600`. +- Несовместимые версия протокола и manifest приводят к документированному отказу или обновлению до входа. Совпадение заявленного хеша не трактуется как доказательство доверенности программы клиента. +- После разрыва соединения игрок убирается из соответствующего мира; повторное подключение не создаёт бессрочного дубликата сущности. + +### A-CLIENT: играбельный браузерный клиент + +- Клиент запускается по адресу сервера, получает manifest и каталог, отображает блоки, свободное пространство, формы начального набора и игроков. +- Управление камерой, движение, прыжок, выбор блока, установка/удаление и переключение мира доступны без ручной отправки запросов в консоли разработчика. +- Клиент показывает отказ входа, потерю соединения и ошибки действий понятным сообщением; зависшее состояние не выдаётся за подтверждённое сервером. +- Геометрия обновляется после сетевых правок. Начальная загрузка, повторное соединение и смена мира не оставляют геометрию предыдущего мира. + +### A-MCP: настоящий отдельный MCP + +- Отдельный процесс MCP успешно проходит `initialize`, объявляет инструменты и выполняет `tools/call` через клиент MCP. Наличие одного `/api/control` не закрывает этот критерий. +- Доступны операции мира, поиск каталога, чтение, пакетная правка, undo, метрики, запуск арены, снимок камеры и объявленные в контракте операции сущностей. +- Схемы аргументов, результаты и ошибки соответствуют фактическому поведению. Недоступный сервер и неверный токен дают ошибку инструмента без зависания MCP-процесса. +- MCP использует тот же контроль ревизий, границ и допустимости данных, что и прочие административные обращения. Поиск ограничивает размер ответа, а чтение не выгружает целый большой мир в контекст. + +### A-SPLEEF: один полный матч + +- В арене на общей карте участвуют два клиента. Есть ожидание/подготовка, начало, активная игра и завершение; сервер сообщает фазу и оставшееся время. +- В активной игре разрешено ломать только допустимый слой арены. Установка блоков и правки вне области ограничены правилами режима. +- Падение ниже настроенной границы исключает игрока на сервере. При одном оставшемся участнике объявляется один победитель; при одновременном выбывании и разрыве соединения результат определён правилами. +- Завершение и повторный запуск восстанавливают карту и участников. Параллельная арена на том же шаблоне сохраняет собственное состояние. +- Нельзя дважды запустить уже активный матч и получить несколько таймеров или повторное начисление результата. + +### A-MEMORY: ограниченность памяти основы + +- При чтении числа различных секций, превышающего `cache_sections`, количество резидентных секций в кэше не превышает заданную ёмкость. Проверить также ёмкость 0 или её явно документированный отказ. +- Два и более мира с общей неизменяемой основой не получают по полной копии данных карты в RAM. Метаданные миров и их изменения учитываются отдельно. +- Выгрузка изменённой секции сохраняет её изменения; повторное чтение после вытеснения и после рестарта возвращает одинаковый результат. +- История операций хранится на диске. Длинная последовательность правок не требует держать весь журнал и все снимки состояний в RAM. +- Метрики позволяют увидеть хотя бы число миров, резидентных секций и заданную ёмкость кэша. RSS процесса измеряется снаружи; один счётчик кэша не выдаётся за расход памяти всего сервера. + +## Рубеж B — полный согласованный MVP + +Все проверки рубежа A обязательны. Дополнительно должны быть завершены следующие части; начальная демонстрация с несколькими блоками их не заменяет. + +### B-CONTENT: полный базовый каталог Minecraft 26.2 + +- Зафиксированы точная редакция/сборка 26.2, источник каталога и контрольные суммы входных данных. Полнота проверяется сравнением с этим набором, а не заранее придуманным количеством блоков. +- Автоматическая сверка покрывает каждый базовый блок, допустимые состояния, формы рендера и столкновений, а также каждый базовый тип сущности целевого набора. +- Канонические имена и свойства сохраняются без потери при регистрации, хранении, сетевой передаче и рестарте. Неизвестные состояния не заменяются воздухом молча. +- Каталог сущностей включает данные, необходимые для отображения, размещения, хранения и обмена мирами; поведенческие возможности каждого типа явно отмечены. Заглушка для всех типов не считается полным каталогом с рабочими формами. +- Отдельно проверяются отличающиеся от полного куба формы, ориентации, составные блоки, прозрачность и блоки с дополнительными данными. Клиент и сервер используют совместимые формы и свойства. +- Ресурсы воспроизводимо собираются из объявленных источников. Отсутствующий ресурс приводит к диагностике с конкретным идентификатором, а отчёт содержит полный список пробелов. + +### B-PACKAGES: единая система модулей и ресурсов + +- Один версионируемый формат пакета описывает модули, текстуры, шейдеры и прочие ресурсы, зависимости, совместимость и стороны исполнения. +- Сервер формирует manifest с точными версиями и хешами; клиент автоматически получает требуемые клиентские части, проверяет целостность и повторно использует локальный кэш. +- Проверены отсутствующий пакет, несовместимая версия, цикл зависимостей, повреждённая загрузка, прерывание/возобновление и изменение набора между подключениями. Частичная установка не активируется как полная. +- Серверные файлы и секреты не попадают в клиентскую выдачу. Пакеты не могут писать за пределы каталога установки через относительные пути или архивные записи. +- Расширение регистрирует новый контент через документированный интерфейс; его можно подключить без правки исходников ядра. Права исполнения, доступные API и ограничения ресурсов модуля определены и проверены. +- Проект запускается независимо от Shacraft Launcher. Интерфейс будущей интеграции документирован. Реальное изменение/проверка существующего лаунчера относится к отдельному интеграционному этапу и не блокирует локальный выпуск движка. + +### B-INTEROP: импорт и экспорт Minecraft ↔ Shacraft + +- Команды и форматы импорта/экспорта документированы. Есть небольшие эталонные миры целевой версии: несколько измерений, отрицательные координаты, состояния блоков, сущности, данные блок-сущностей и пользовательские данные. +- Импортированный мир открывается сервером и клиентом; выборочная и полная автоматическая сверка эталонов подтверждает координаты, состояния и поддерживаемые данные. +- Цикл `Minecraft → Shacraft → Minecraft` сохраняет поддерживаемые данные семантически. Сравниваются декодированные значения; побайтовое равенство сжатых файлов не требуется. +- Исходные неизвестные или неподдерживаемые данные сохраняются для обратного экспорта, если их смысл нельзя корректно перенести. Экспорт не уничтожает их незаметно после редактирования других частей мира. +- Изменения, сделанные через клиент и MCP, правильно отражаются в экспортированном мире. Отдельно проверяются удаление блока, новые состояния и сущности. +- Отчёт перечисляет сохранённые, преобразованные, неподдерживаемые и потерянные данные с координатой/идентификатором и причиной. Отсутствие данных не маскируется успешным статусом. +- Повреждённый файл, неполная область, неизвестная версия, слишком большая распакованная запись и отмена операции не портят источник и ранее существующий целевой мир. +- Конвертер обрабатывает мир порциями; потребление RAM не растёт до размера всего мира. Эталон, превышающий бюджет кэша, конвертируется успешно. + +### B-PERSISTENCE: устойчивое состояние сервера + +- На диске сохраняются миры, закреплённые версии шаблонов, реестр контента, правки, необходимые данные undo, конфигурация и правила арен, а также сохраняемые сущности. +- Политика восстановления активного матча после рестарта определена: восстановление или безопасный сброс. Результат не оставляет навсегда активную арену и не дублирует победу. +- Проверены прерывания в момент записи секций, метаданных и журнала. Восстановление выбирает целостную версию; данные, на которые уже получено durable-подтверждение, сохраняются согласно контракту. +- Версия формата хранения проверяется при открытии. Несовместимая версия вызывает понятный отказ или проверяемую миграцию с возможностью восстановления исходных данных. + +### B-MEMORY: доказанная экономия серверной RAM + +Benchmark запускается на фиксированном наборе данных и оборудовании. До измерения фиксируются лимиты, размер карты, количество миров и игроков, частота правок, объём активных областей и допустимый запас RSS; выбранные значения публикуются вместе с результатом. + +- Сравнить 1, 10 и 100 независимых миров одного большого шаблона: без игроков, с одинаковой активной областью и с различными активными областями. Допускается рост метаданных и изменённых данных; полная копия карты на мир отсутствует. +- Для каждого варианта записать RSS/p95/пик, резидентные секции и их байты, размер наложений, число игроков, сетевые очереди, скорость/задержку тика и размер данных на диске. +- После обхода областей размером больше кэша неактивные секции вытесняются. Повторные обходы и циклы создания/сброса миров не вызывают постоянного линейного роста удерживаемой памяти. +- Медленный клиент, длинный журнал, частые снимки MCP и параллельные импорты не обходят лимиты через очереди, буферы ответов и вспомогательные кэши. +- Целевые лимиты памяти и задержки тика соблюдаются на опубликованной нагрузке. Без заранее зафиксированного бюджета можно подтвердить ограниченность отдельных структур, но нельзя объявлять достигнутым конкретный масштаб сервера. + +### B-DELIVERY: воспроизводимый открытый проект + +- В репозитории есть исходники самостоятельного Rust-ядра, отдельного сервера, клиента, MCP, конвертера, пакетов базового контента и примера режима; границы зависимостей проверяемы сборкой. +- Чистая установка по README воспроизводит сборку, тесты и сценарий A-E2E. Конфигурация, порты, команды запуска, каталог данных и получение токена описаны явно. +- Документированы API расширений, протокол, формат пакетов, хранение, гарантии сохранности и ограничения совместимости; выбранная открытая лицензия присутствует в репозитории. +- Архив выпуска создан, повторно распакован в чистый каталог и проверен. В него не включены токены, локальные миры пользователя и зависимости, которые должны загружаться отдельно. +- Итоговый отчёт ссылается на доказательства для каждого обязательного критерия. Непроверенные или заблокированные требования перечислены явно и не называются завершёнными. + +## Что нужно уточнить в CONTRACT.md по ходу реализации + +Эти решения можно проработать без остановки первого сквозного этапа. До приёмки соответствующей части полного MVP они должны стать явными контрактами и тестами: + +- Версия протокола, идентификаторы ошибок, лимиты пакетов/координат/строк, восстановление клиента при пропуске ревизии и семантика `switch_world`. +- Момент durable-подтверждения, replay с различными аргументами, конфликты undo, ревизия после reset, формат и срок хранения истории. +- Источник точной версии каталога 26.2, схема форм и состояний, каталог/сохранение сущностей и дополнительных данных блоков. +- Формат пакета, граф зависимостей, публикация ресурсов сервером, интерфейс и пределы исполнения модулей, интеграция лаунчера. +- Настройки арены, допустимые действия, таймеры, ничья, отключение игрока и политика сохранения матча. +- Форматы и команды импорта/экспорта, сохранение неподдерживаемых данных и машиночитаемый отчёт о потерях. +- Общий бюджет RAM, лимиты помимо кэша секций, целевая нагрузка и параметры воспроизводимого benchmark. diff --git a/docs/COMPATIBILITY.md b/docs/COMPATIBILITY.md new file mode 100644 index 0000000..94742d4 --- /dev/null +++ b/docs/COMPATIBILITY.md @@ -0,0 +1,104 @@ +# Совместимость с Minecraft Java 26.2 + +Дата проверки источников: 14 сентября 2026 года. Это план и критерии приёмки; наличие этого документа не означает, что конвертер или полный каталог уже реализованы. + +## Цель и границы + +Shacraft остаётся самостоятельным движком на Rust. Совместимость означает полный каталог идентификаторов блоков, их допустимых состояний и типов сущностей целевой версии, отдельное покрытие форм, а также обмен пользовательскими мирами и постройками с сохранением исходных данных. Совпадение всей симуляции Minecraft — редстоуна, генератора мира, поведения мобов, жидкостей и команд — требует собственных модулей и не следует автоматически из наличия каталога. + +Целевая версия действительно существует: Mojang выпустила Java 26.2 16 июня 2026 года. В ней добавлены, среди прочего, sulfur/cinnabar и sulfur cube; поэтому каталог более ранней версии нельзя выдавать за 26.2. [Официальный релиз](https://www.minecraft.net/en-us/article/minecraft-java-edition-26-2). + +Первый рабочий срез может иметь небольшой демонстрационный каталог, однако в интерфейсе, README и отчётах он должен называться именно демонстрационным. Требование полного каталога остаётся незакрытым до проверки против зафиксированного источника. + +## Зафиксированный источник данных + +При проверке [официального launcher manifest](https://piston-meta.mojang.com/mc/game/version_manifest_v2.json) получена запись `id=26.2`, `type=release`, `releaseTime=2026-06-16T12:03:33+00:00`. Она ссылается на [метаданные версии](https://piston-meta.mojang.com/v1/packages/bc42e43dfe43d65a2f6c2c1dbb322c75134e51fe/26.2.json): + +- SHA-1 JSON метаданных: `bc42e43dfe43d65a2f6c2c1dbb322c75134e51fe`. +- Java runtime: major version `25`. +- [Официальный server.jar](https://piston-data.mojang.com/v1/objects/823e2250d24b3ddac457a60c92a6a941943fcd6a/server.jar): размер `60894273` байта, SHA-1 `823e2250d24b3ddac457a60c92a6a941943fcd6a`. + +SHA-1 здесь фиксирует артефакт по метаданным Mojang. Наш файл происхождения дополнительно должен содержать вычисленный SHA-256, дату, команду извлечения, Java version и хеш каждого итогового отчёта. + +План получения каталога: + +1. Скачать этот JAR в локальный игнорируемый кэш, проверить размер и хеш. Не включать его в репозиторий и дистрибутив Shacraft. +2. Проверить доступную точку входа и `--help` генератора данных именно этого артефакта, затем получить отчёты блоков и реестров. Команда для старых версий не считается проверенной командой для 26.2. Сам факт существования `blocks.json` в генераторе подтверждён Mojang; его структура меняется, что видно уже в [изменениях 26.3 Snapshot 2](https://www.minecraft.net/en-us/article/minecraft-26-3-snapshot-2). +3. Из отчёта блоков извлечь resource location, свойства, допустимые значения, все перечисленные состояния и состояние по умолчанию. Из реестров — типы сущностей, block entities, биомы и прочие необходимые для конвертации ключи. Если нужного поля нет в отчёте, пометить пробел и сделать отдельное воспроизводимое извлечение из исполняемого API целевой версии. +4. Получить `DataVersion` из целевого артефакта/мира, сохранить в манифесте. Номер ещё не извлечён в рамках этого исследования; не подменять его номером datapack/resource pack. +5. Хранить минимальный производный каталог и собственные правила адаптации. Текстуры, звуки, модели, шейдеры и декомпилированный код Minecraft в него не входят. Визуальные ресурсы Shacraft создаются отдельно либо подключаются из локальных ресурсов пользователя отдельным адаптером. + +Полнота проверяется равенством множеств канонических состояний и типов сущностей с отчётами, а не ожидаемым числом из статьи или ручного списка. Количества блоков/состояний/сущностей появятся только после фактического извлечения. + +## Контракт каталога и форм + +В `WorldStore` используется каноническая строка `namespace:block[key=value,...]`, где ключи свойств отсортированы. Числовой `BlockId` — внутренний идентификатор Shacraft; он не равен числовому идентификатору Minecraft и не переносится между независимыми реестрами без явной таблицы соответствия. `air` остаётся ID 0 по `CONTRACT.md`. + +Для совместимости необходимы разные признаки покрытия: + +- `identity`: идентификатор и все состояния известны и проверены. +- `render`: собственная геометрия отображает состояние; заглушка обозначена явно. +- `collision`: проверена форма столкновений для этого состояния и контекста. +- `behavior`: установлен модуль поведения, если он реализован. +- `roundtrip`: данные можно вернуть в конкретную версию и формат. + +Один флаг `supported` скрывал бы различия. Нельзя получать коллизию автоматически из текстур или из визуальной модели. Формы могут зависеть от соседей, сущности и состояния мира; лестницы, плиты, двери, заборы, стены, растения, жидкости и динамические блоки требуют раздельных тестов. Начать с собственных параметрических форм, затем сравнивать их с выборкой поведения целевой версии. Формы со сложным контекстом сохраняют статус `approximate` до проверки. + +Сущность может быть известна по идентификатору и полностью сохраняться для экспорта, даже если в Shacraft она пока отображается маркером и не имеет AI. В интерфейсе это должно быть видно. Неизвестный блок сохраняет исходную строку и NBT; отображение заглушкой не должно заменять его на `air` в хранилище. + +## Форматы и порядок поддержки + +**NBT — слой сериализации, а не единый формат мира.** Для Java нужен типизированный big-endian NBT. Типы чисел, массивов и списков необходимо сохранять: преобразование к обычному JSON теряет информацию для обратной записи. Это различие прямо показано в [API Prismarine NBT](https://github.com/PrismarineJS/prismarine-nbt), который можно использовать как независимый тестовый декодер, не как зависимость Rust runtime. + +**Sponge `.schem` v3 — первый полноценный адаптер построек.** Поддержать блоки и состояния, block entities, entities, 3D-биомы, размеры, offset, metadata и DataVersion. Спецификация предусматривает эти контейнеры и палитры; точный порядок байтов и кодирование брать из [первичной спецификации Sponge v3](https://github.com/SpongePowered/Schematic-Specification/blob/master/versions/schematic-3.md). Затем добавить v2 как отдельную ветку декодера. Старый MCEdit `.schematic` не считать тем же форматом и не распознавать по одному похожему расширению. + +**Vanilla structure `.nbt` — отдельный адаптер.** Нужно зафиксировать схему 26.2 на файлах, сохранённых structure block: размеры, палитра/альтернативные палитры, блоки, относительные координаты, block entity NBT и сущности. Сырой NBT-декодер не равен поддержке этой схемы. Неподдерживаемые альтернативные палитры нельзя молча свести к первой. + +**Anvil world directory — потоковый адаптер миров.** Обрабатывать `level.dat`, измерения, region-файлы, секции/палитры, биомы, block entities, отдельные файлы сущностей и POI, а также остальные файлы сохранения. Извлекать разделы по мере необходимости, с ограничением кэша, сохраняя оригинал на диске. Проверять chunk `DataVersion` наряду с версией мира. + +Существующая реализация [WorldEdit AnvilChunk18](https://github.com/EngineHub/WorldEdit/blob/dca10737fe81bd81bb6a096e1801cc2d94f1793b/worldedit-core/src/main/java/com/sk89q/worldedit/world/chunk/AnvilChunk18.java) подтверждает практическую работу с секционными палитрами `Name`/`Properties`; [McRegionReader](https://github.com/EngineHub/WorldEdit/blob/dca10737fe81bd81bb6a096e1801cc2d94f1793b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionReader.java) полезен для проверки контейнера. Эти реализации — независимые ориентиры, не доказательство полного соответствия 26.2. Код не копировать в ядро без отдельного решения о лицензии. + +Нужно явно проверить варианты сжатия, внешние chunk payload и крупные chunks на целевых fixtures. Поддержкой одного zlib нельзя объявлять поддержку всех Anvil-файлов: возможность LZ4-сжатия регионов описана в [официальном релизе Java 1.20.5](https://www.minecraft.net/es-es/article/minecraft-java-edition-1-20-5). Неизвестное сжатие даёт диагностируемый отказ, а не пустой chunk. + +## Режимы экспорта и сохранение оригинала + +**`exact` — проверенная точность данных.** По умолчанию используется та же версия и тот же формат, что у источника. Исходные файлы помещаются в неизменяемое дисковое хранилище с хешами; изменения Shacraft хранятся отдельно. Нетронутые файлы копируются побайтно. В изменённых файлах переписываются только поддерживаемые данные; остальные типизированные NBT-поля сохраняются. Контракт точности — семантическое равенство NBT за исключением явных правок и заранее объявленных производных полей, а не одинаковое сжатие и размещение секторов. + +При неизвестной версии, некорректной палитре, неподдерживаемой операции, несовместимой сущности, неразрешённом преобразовании metadata или отсутствии необходимых исходных данных `exact` прекращает экспорт до публикации результата. Особенно проверять замену блока с block entity, инвентари, пассажиров и UUID, а также зависимости от света, heightmaps, POI и scheduled ticks. Нельзя оставить заведомо неверные производные данные и назвать экспорт точным. Правило пересчёта/инвалидации каждого такого поля должно быть проверено целевым Minecraft. + +**`best-effort` — преобразование с отчётом.** Разрешает только явно заданные правила замены. Формирует `conversion-report.json`: версия источника/назначения, формат, координата/UUID, код причины, исходное значение, действие, количество и тяжесть. Неизвестные данные остаются в сопровождающем архиве, однако это не означает, что целевой Minecraft сможет их использовать. Отчёт различает `preserved`, `approximated`, `dropped`, `unsupported`. + +Для мира, созданного в Shacraft без Minecraft-оригинала, экспорт в `.schem` доступен после проверки всех данных на представимость. Создание полноценного Anvil-мира потребует отдельного генератора корректного окружения сохранения; одной записи массива блоков в `.mca` недостаточно. + +Исходный мир никогда не редактируется конвертером на месте. Вывод готовится во временном каталоге и переименовывается после успешной проверки. Прерванный импорт можно возобновить по журналу chunks и хешам. Экспорт получает согласованный снимок мира; параллельные игровые правки не должны давать смесь ревизий. + +## Необходимые дополнения к текущему API + +`CONTRACT.md` покрывает блоки и ревизии. В нём пока нет durable API произвольного NBT, биомов, сущностей и provenance. До заявления о lossless-конвертации добавить версионируемый compatibility sidecar со ссылками на неизменяемые blobs, типизированными NBT и изменениями по chunk/UUID. Он должен участвовать в атомарной фиксации ревизии вместе с блоками. + +Не перегружать строковый реестр блоков всем NBT мира и не держать sidecar целиком в RAM. Экспортёр/импортёр — отдельная библиотека/CLI над ядром; Java используется для получения эталонных данных и интеграционной проверки, но не для работы Shacraft-сервера. + +## Приёмочные проверки + +1. **Каталог:** равенство множеств всех состояний/типов целевому отчёту, один default на блок, канонизация независимо от порядка properties, сохранение неизвестного namespace, стабильность таблицы ID после перезапуска. +2. **NBT:** все типы, пустые списки, 64-битные значения, массивы, Unicode, неизвестные поля. Проверка encode/decode независимым декодером; обычный JSON не выступает эталоном. +3. **Палитры:** один блок на всю секцию, границы размера палитры и битовой упаковки, секции из воздуха, отрицательные X/Z/Y, границы chunks/regions, отсутствующие данные и повреждённые индексы. +4. **Без правок:** Minecraft/WorldEdit fixture → Shacraft → исходный формат. Нетронутые файлы побайтно идентичны; пересобранные структуры семантически равны по типизированному NBT. Третий декодер подтверждает результат. +5. **С правками:** поменять одно состояние, сломать контейнер, поставить лестницу/дверь, переместить поддерживаемую сущность. После экспорта меняются только предусмотренные данные; metadata, прочие inventories/entities/biomes и неизвестные теги сохраняются либо дают точный отказ. +6. **Открытие:** отдельная копия экспортированного мира открывается целевым Java 26.2 без ошибок декодирования/утраты chunks; фиксируются хеш JAR, логи и проверяемые координаты. Одного собственного roundtrip недостаточно — одинаковая ошибка в reader/writer может остаться незамеченной. +7. **Версии/потери:** unsupported DataVersion, модифицированный namespace, отсутствующая модель, неподдерживаемое NBT и cross-format export проверяют отказ `exact` и полный отчёт `best-effort`. +8. **Ресурсы:** большие sparse-миры, высокоэнтропийные секции, большие payload, повреждённые длины и сильное сжатие. Ограничить распакованный размер, глубину NBT, очередь и кэш; измерить пик RAM. Прерывание/повтор импорта и сбой финальной записи не повреждают исходник и сохранённые ревизии. + +Fixtures должны быть собственными маленькими мирами/постройками без сторонних карт и ассетов. Для версии и сжатия, которых нет в fixtures и внешней проверке, не ставить статус `verified`. + +## Этапы и условия завершения + +1. **Происхождение:** зафиксированный источник 26.2, воспроизводимый генератор, DataVersion, хеши и точные количества; отсутствие этих результатов оставляет полный каталог незавершённым. +2. **Реестр:** все состояния и entity IDs загружаются в Shacraft, сохраняются и ищутся; формы и behavior имеют отдельную карту покрытия. +3. **NBT + `.schem` v3:** типизированные данные и архив оригинала, оба направления, отчёт потерь, независимые roundtrip-тесты. +4. **Structure NBT + `.schem` v2:** схемы разделены и проверены на эталонах, включая offset/повороты только там, где они реализованы. +5. **Anvil 26.2:** потоковый импорт, возврат исходного мира, затем проверенные правки и создание новых экспортируемых миров; тесты с целевым Minecraft. +6. **Полное покрытие форм:** все состояния имеют проверенную визуальную форму и отдельно коллизию либо явный незакрытый дефект. Завершение списка ID само по себе не закрывает этот этап. +7. **Расширение версий:** добавлять другие DataVersion только через отдельные адаптеры/миграции и fixtures. Не обещать автоматический downgrade и произвольные модифицированные миры. + +Главные технические риски: дрейф схем между версиями, скрытые связи block entities/POI/ticks, формы с зависимостью от контекста, полная пересборка мира из частично поддерживаемых данных и непредсказуемые пики RAM при распаковке. Каждый риск выше привязан к конкретному отказу или приёмочному тесту, чтобы он не превратился в молчаливую потерю пользовательского мира. diff --git a/docs/CONTRACT.md b/docs/CONTRACT.md new file mode 100644 index 0000000..b797f11 --- /dev/null +++ b/docs/CONTRACT.md @@ -0,0 +1,42 @@ +# Implementation contract, v1 + +Core Rust API (public in shacraft_core): +`type Pos = [i32;3]; type BlockId = u32;` +`WorldStore::open(path: impl AsRef, cache_sections: usize) -> anyhow::Result` +`list_worlds(&self) -> Result>`; `create_world(&mut self, name: &str, template: Option<&str>) -> Result<()>`; `reset_world(&mut self, name: &str, expected_revision: u64, operation_id: &str) -> Result`. +`register_block(&mut self, canonical_state: &str) -> Result`; `registry(&self) -> &[String]` (air is 0). +`get_block(&mut self, world: &str, pos: Pos) -> Result`; `revision(&self, world: &str) -> Result`. +`edit(&mut self, world: &str, expected_revision: u64, operation_id: &str, changes: Vec) -> Result`. +`undo(&mut self, world: &str, expected_revision: u64, operation_id: &str, target_operation: &str) -> Result`. +`read_region(&mut self, world: &str, min: Pos, max: Pos) -> Result>` returns nonair cells, bounds inclusive, capped at 262144 cells. +`stats(&self) -> serde_json::Value`; `flush(&mut self) -> Result<()>`. +`BlockChange { pub pos: Pos, pub block: BlockId }` derives Serialize/Deserialize/Clone. +`EditResult { pub revision: u64, pub changed: usize, pub replayed: bool }` derives Serialize/Deserialize/Clone. +`WorldInfo { pub name: String, pub revision: u64, pub template: Option }` derives Serialize/Deserialize/Clone. +All durable changes atomic/recoverable; bounded section cache and disk operation history; independent forks pin immutable templates. Core has no networking/rendering. + +Stage 1 storage semantics (accepted before implementation): +- SQLite transactions with WAL and synchronous=FULL are preferred to an untested bespoke durability journal. Use a bounded SQLite cache and a bounded decoded section LRU; report both. Reject a second writer using an exclusive advisory lock file. Reject cache_sections=0 explicitly. +- create_world(template) snapshots the source's current effective contents using references to immutable section blobs. The source's subsequent edits/reset never alter the child. Snapshot metadata stays disk indexed; creating the first snapshot can cost O(number of stored section references), without expanding all blocks in RAM. +- reset returns the instance to its pinned base (or air for worlds without a base), increments revision monotonically, and has expected_revision/idempotency checks just like edit. A reset is a barrier for undo of older operations. +- Operation IDs scoped per world, bounded UTF-8 strings. Look up idempotency before comparing the current revision. Repeating identical payload returns the stored result with replayed=true; same ID with a different payload is an error. Results survive restart and resets. +- Transactions publish and are acknowledged only after durable commit. Registry additions are durable. All input validation precedes mutation. Revisions fit SQLite's positive signed 64-bit integer range; reject overflow. +- Undo is atomic and conflict aware. It may conservatively require current revision==the target edit's resulting revision (safe initial implementation, explicitly documented); selective nonoverlapping undo is a later extension. Never overwrite later writes or ABA cycles silently. Undoing an undo/reset is unsupported initially. +- Default transaction limit 32768 cells; read_region limit 262144 volume; inclusive bounds and checked arithmetic. Reject duplicate cell positions in one edit, unknown BlockId, nonexistent world, malformed name, negative/overflowing volume and excessive input before writing. +- World names match [A-Za-z0-9_-]{1,64}; block canonical names and registry cardinality are bounded. Minimum supported coordinate domain [-30000000,30000000] for every axis with checked validation. Air ID 0 cannot be reassigned. registry returns owned canonical identifiers shared by worlds; bounds and its RAM footprint are reported. +- Section encoding versioned and defensively decoded: uniform or minimal-width palette + packed indices. Deduplicate immutable blobs; references are stored in indexed SQL tables. GC of unreachable blobs is explicit and bounded; disk retention is documented separately from RAM accounting. +- Diagnostics distinguish decoded payload estimates, cache entries/capacity, SQLite cache configuration, registry bytes, disk data and measured OS RSS. Do not call estimated payload size total server RAM. + +Server protocol (JSON, WebSocket /ws): +Client first: {type:'join',name:'Player',world:'lobby',manifest_hash:'…'}. +Server welcome: {type:'welcome',id,world,revision,registry:[canonical block states],blocks:[{pos:[x,y,z],block:id}],players:[],spawn:[x,y,z],manifest_hash}. +Client inputs at <=30Hz: {type:'input',seq,yaw,pitch,forward,strafe,jump}; yaw/pitch radians; forward/strafe in [-1,1]. +Coordinates: +Y up; yaw=0 looks toward -Z; +X right. Position is feet. +Client actions: {type:'break',pos:[x,y,z]} or {type:'place',pos:[x,y,z],block:id}; {type:'switch_world',world}; {type:'chat',text}. +Server tick 20Hz: {type:'state',players:[{id,name,position:[x,y,z],yaw,pitch}],tick,ack:seq,match:{phase,remaining,winner}}. +Server edit: {type:'blocks',revision,changes:[{pos,block}]}. +Server error: {type:'error',message}; chat {type:'chat',name,text}. +Client fetch GET /api/manifest -> {protocol:1,hash,packages:[],...}; GET /api/worlds -> [{name,revision,template}]; GET /api/catalog -> [{id,state,color,solid,shape}]. Server serves client from / . +Control HTTP: GET /api/health, /api/metrics, /api/worlds, /api/catalog public; POST /api/control with Authorization: Bearer token and {method,params}, JSON return {result:...} or {error:'...'}. +Control methods: world.list, world.create {name,template?}, world.reset {world}, world.read {world,min,max}, world.edit {world,expected_revision,operation_id,changes}, world.undo {world,expected_revision,operation_id,target_operation}, catalog.search {query,limit}, metrics, arena.start {world}, camera.capture {world,min?,max?} (top-down PNG), entity.spawn {world,kind,position}, entity.list {world}. +Default server host 127.0.0.1 port 4000, token file data/control.token (0600). No publishing or production access. diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md new file mode 100644 index 0000000..5dee62a --- /dev/null +++ b/docs/DECISIONS.md @@ -0,0 +1,37 @@ +# Решения и границы + +## D001. Независимый Rust workspace + +Ядро — библиотека. Сервер, MCP и конвертер — отдельные исполняемые компоненты. Общий реестр, координаты и операции доступны через публичный API. Графика и внешние сервисы не загружаются сервером. + +## D002. Сначала хранение и сквозная проверка + +Начинаем с архитектуры памяти и долговечности; подключаем клиент до завершения полного каталога. Наличие небольшого работающего этапа не меняет конечных требований. Невыполненные пункты остаются в плане. + +## D003. Первый тестовый клиент — браузерный + +Первый проверочный клиент использует собственный WebGL2-рендерер без готового игрового движка. Это ускоряет проверку двух подключений и MCP на локальной машине. Ядро и сервер остаются Rust. Нативный Rust-клиент не считается реализованным таким клиентом; для самостоятельного нативного выпуска и интеграции с Launcher потребуется отдельный шаг. Клиент не должен навязывать формат хранения серверу. + +## D004. Серверу нельзя доверять заявлениям клиента о собственной целостности + +Манифесты/хеши проверяют совместимость и загруженные файлы. Сервер подтверждает игровые действия и передаёт только необходимые данные. Полный запрет модифицированных клиентов на контролируемом игроком устройстве не гарантируется протоколом самопроверки. Подписывание дистрибутива и интеграция лаунчера являются отдельными задачами. + +## D005. Совместимость с Minecraft — адаптер + +Свой формат мира не является копией Anvil. Версия конвертера и профиля экспорта явная. Неподдерживаемые данные нельзя молча заменять воздухом. Каталог имён не эквивалентен реализации формы, коллизии, поведения и round-trip сохранения. + +## D006. Безопасные локальные значения по умолчанию + +Сервер слушает localhost. Управляющий API требует отдельный токен; обычный игровой клиент его не получает. Все очереди и объёмы запросов имеют предел. Собственные тестовые каталоги отделены от реальных миров. + +## D007. Версии и контракты пока рабочие + +`CONTRACT.md` — начальная спецификация для параллельной разработки, не обещание стабильного публичного API. Изменения согласуются до зависимой реализации и отражаются в документации. Особенно важно проверить атомарность правок, фиксацию шаблонов, ограничения registry и сетевой синхронизации. + +## D008. Надёжное хранение на SQLite/WAL + +Для первой реализации выбираем SQLite с транзакциями, WAL и `synchronous=FULL`, ограниченным кэшем страниц и блокировкой второго писателя. Секции остаются собственными компактными бинарными данными; SQLite хранит ссылки, метаданные и историю. Это позволяет проверять игровой формат без одновременного изобретения механизма надёжных транзакций. Размер файла, объём WAL и собственная память SQLite учитываются отдельно. + +## D009. Консервативная отмена в первом этапе + +Undo разрешён только для последней ревизии, созданной целевой правкой. Любая последующая операция, включая возврат блока к прежнему значению, запрещает такой undo. Это строже будущей избирательной отмены, зато не допускает потери последующих изменений. Ограничение явно указывается в API; сброс арены также является границей истории. diff --git a/docs/MEMORY_AND_STORAGE.md b/docs/MEMORY_AND_STORAGE.md new file mode 100644 index 0000000..f5c902f --- /dev/null +++ b/docs/MEMORY_AND_STORAGE.md @@ -0,0 +1,188 @@ +# Память, хранение и восстановление Shacraft Core + +Статус: план новой реализации с принятыми решениями D008–D009. Этот файл задаёт инварианты и критерии проверки; он не утверждает, что перечисленные механизмы уже реализованы. Публичный API находится в [CONTRACT.md](CONTRACT.md), принятые решения — в [DECISIONS.md](DECISIONS.md). Дополнительные предложения отдельно перечислены в конце. + +## 1. Главная цель и граница обещаний + +Главная цель ядра — ограничить серверную RAM при большом числе независимых матчей на одинаковых картах. Неизменённые блоки общего шаблона хранятся один раз. Матч владеет только собственными изменениями. Данные неактивных областей и история операций остаются на диске. + +Нельзя заранее обещать процент экономии относительно Paper или конкретное число игроков на гигабайт. Экономия зависит от разнообразия блоков, числа одновременно активных секций, степени изменения карт, сущностей, клиентской видимости и нагрузки. Успех подтверждают воспроизводимые измерения вместе с функциональными ограничениями реализации. + +Бюджет процесса включает больше, чем блоки: кэш секций, временные декодированные секции, индекс хранилища, реестр блоков, метаданные миров, историю, очереди сети, игроков и сущности. Ограничение одного кэша не означает ограничения всей RAM. + +## 2. Адресация и формат секции + +Единица хранения — секция 16 × 16 × 16, то есть 4096 ячеек. Координаты мира остаются знаковыми `i32` из контракта. Для каждой оси использовать `div_euclid(16)` и `rem_euclid(16)`: блок `-1` попадает в секцию `-1`, локальную координату `15`. Индекс ячейки: `x + 16*z + 256*y`. Этот порядок фиксируется версией формата. + +В первом этапе секция выбирает компактную uniform/palette кодировку: + +- `Uniform(BlockId)`: все 4096 блоков одинаковы; массив индексов отсутствует. +- `Paletted`: уникальные глобальные `BlockId` и плотно упакованные индексы палитры. Для `P > 1` требуется `ceil(log2(P))` бит на ячейку. + +Будущее расширение `Dense` может хранить 4096 глобальных `u32`, если палитра вместе с индексами занимает больше места. Это требует явной версии/тега кодировки и пока не входит в принятый минимальный контракт. + +Без заголовков и выравнивания палитра занимает `4*P + 4096*ceil(log2(P))/8` байт. При двух состояниях это 520 байт, при 16 — 2112, при 256 — 5120. При 4096 уникальных состояниях получается 22528 байт, поэтому прямой массив в 16384 байта выгоднее. Это расчёт полезных данных, а не RSS и не размер объекта Rust. + +При чтении одного блока не нужно распаковывать всю секцию. Изменение использует временный массив на 4096 значений и затем заново выбирает кодировку. Палитра после редактирования должна удалять неиспользуемые состояния. Дисковое сжатие можно добавить после замеров; оно не заменяет ограничение кэшей. + +Декодер проверяет версию, длины, размер палитры, диапазон каждого индекса и существование глобальных `BlockId`. Повреждённая секция возвращает ошибку с её идентификатором; подмена повреждения воздухом запрещена. Кодировка имеет фиксированный порядок байтов и контроль целостности. Контрольная сумма обнаруживает повреждение, но не является защитой от намеренной подмены. + +## 3. Общий шаблон и независимые миры + +Шаблон — неизменяемый снимок состояния мира, а не ссылка на его текущее изменяемое состояние. Внутренние идентификаторы мира, снимка и секции отличаются от пользовательских имён. `WorldInfo.template` может показывать имя источника, но внутренние данные обязаны хранить точный `snapshot_id` и ревизию источника. + +Мир содержит ссылку на базовый снимок и собственную таблицу замен секций. Чтение ищет замену мира, затем секцию в снимке, затем возвращает воздух. Явная замена на полностью воздушную секцию обязательна: отсутствие замены означает наследование, поэтому удаление всех блоков шаблона нельзя представлять отсутствующей записью. + +При первом изменении секции из шаблона создаётся новая секция мира. Сама секция шаблона никогда не изменяется. Одинаковые ссылки на неизменяемые секции могут использовать один объект кэша. Возврат секции к точному содержимому шаблона позволяет удалить замену после проверки равенства. + +Практическая схема MVP: + +1. Неизменяемые секции хранятся по идентификаторам на диске. +2. Снимок содержит дисковый индекс `координата секции → идентификатор секции`. +3. Мир содержит `snapshot_id`, текущую ревизию и дисковый индекс собственных замен. +4. При `create_world(template=source)` снимок берётся строго из одной зафиксированной ревизии источника. Для неизменившегося источника ранее созданный снимок можно переиспользовать. +5. Если нового снимка ещё нет, индекс эффективных секций копируется потоково по ссылкам, внутри согласованной транзакции. Сами блоки и содержимое секций не копируются в RAM или на диск повторно. + +Создание нового снимка таким способом может потребовать времени и места, пропорциональных числу ссылок на секции. Обещать `O(1)` для первого fork нельзя. Более сложный постоянный индекс с разделяемыми страницами возможен позже, если измерения покажут необходимость. Не следует заменять эту схему бесконечной цепочкой родительских миров: глубокая цепочка ухудшает чтения и усложняет сборку мусора. + +Проверка изоляции: изменить источник после fork, изменить один из двух дочерних миров, перезапустить процесс и убедиться, что три состояния различаются именно ожидаемым образом. Снимок остаётся доступным, даже если источник сброшен или впоследствии удалён. + +## 4. Ограничение памяти + +`cache_sections` ограничивает число сохранённых в кэше неизменяемых секций. Ноль явно отклоняется при открытии, как принято в CONTRACT. Для первой реализации используется LRU декодированных секций: массив на 4096 `u32` имеет известный размер 16384 байта полезной нагрузки, а объекты и индекс LRU учитываются дополнительно. Хранение палитр непосредственно в кэше — последующая оптимизация; компактная дисковая кодировка сама по себе не уменьшает декодированный кэш. + +Дополнительно контролировать байты кэша: учитывать буфер секции, палитру и метаданные записи, показывать их отдельно от полезной нагрузки. Ключ кэша — идентификатор неизменяемой секции. Кэш только по имени мира и координате легко оставляет устаревшие данные после reset. + +Временные буферы транзакции не попадают в бесконечный «грязный» кэш. Обработка секций идёт ограниченными порциями; завершённые записи передаются дисковому транзакционному механизму. Полученный через API `Vec` уже занимает RAM, поэтому потоковая внутренняя запись не отменяет ограничения размера запроса. + +Историю операций, дедупликацию и индекс секций нельзя загружать целиком при старте. Используется дисковый SQL-индекс с ограниченным кэшем страниц SQLite. Настроить и измерять бюджет страниц и временные данные. Выбор библиотеки сам по себе не доказывает соблюдение бюджета. + +Метаданные не бесплатны. `registry() -> &[String]` из контракта предполагает реестр в RAM; `list_worlds()` создаёт полный список. Для MVP задать явные ограничения на число миров и состояний и длину строк, отразить их в документации. При выходе за эти границы потребуется API с постраничной выдачей. + +Принятые ограничения: + +- одна операция редактирования — не более 32768 уникальных позиций; +- `read_region` — максимум 262144 ячейки объёма, как в контракте; считать произведение через проверяемую широкую арифметику до выделения памяти; +- имя мира соответствует `[A-Za-z0-9_-]{1,64}`; координаты каждой оси находятся в диапазоне `[-30000000,30000000]`; +- сервер дополнительно ограничивает размер JSON до десериализации, частоту операций, сетевые очереди и число одновременных запросов; +- максимальное количество сущностей и объём очереди изменений задаются отдельно от кэша блоков. + +Для ещё не зафиксированного ограничения `operation_id` предлагается от 1 до 128 байт UTF-8. Точные пределы реестра и остальных очередей задаются в реализации и документируются. Размеры должны быть проверены на реальном workload. Ограничение `read_region` относится к объёму области, а не только к числу возвращённых непустых блоков. + +## 5. Дисковая история и атомарность + +Минимальные логические сущности хранилища: реестр блоков, миры, снимки, ссылки снимков на секции, замены секций миров, неизменяемые секции, операции и изменения операций. История хранит старые и новые состояния затронутых ячеек, тип операции, ревизии, идентификатор и отпечаток нормализованного запроса. История нужна для undo и идемпотентных повторов; обычный журнал сообщений сервера её не заменяет. + +Дедупликация находится на диске с уникальным ключом `(world_id, operation_id)`. Не держать все идентификаторы операций в `HashMap`. Для поиска отменяемой операции и последующих изменений ячеек нужны дисковые индексы, а не чтение всего журнала на каждый undo. + +Одна успешная мутация атомарно фиксирует: + +1. Новые секции и изменения ссылок мира. +2. Новую ревизию, если состояние мира изменилось. +3. Запись операции, её отпечаток и точный результат для повторной выдачи. +4. Данные истории, необходимые для безопасного undo. + +Подтверждение успеха клиенту отправляется после долговечной фиксации всех четырёх частей. Ошибка записи не должна оставлять новую ревизию с прежними блоками или блоки без записи дедупликации. Выделение нового `BlockId` также долговечно: переоткрытие хранилища не перенумеровывает реестр; `air` всегда имеет ID 0. + +По D008 используется SQLite с WAL и `synchronous=FULL`; собственный WAL не реализуется. Секции остаются собственным версионированным бинарным форматом внутри транзакционного хранилища. Проверить фактическое применение настроек соединения. Не приравнивать запись в буфер ОС к долговечному commit. Гарантии долговечности предполагают исправную файловую систему и устройство, корректно исполняющее запросы синхронизации. + +На одном пути хранилища допускается один владелец записи. `&mut WorldStore` защищает только конкретный объект Rust: второй процесс или второй независимо открытый объект отклоняется эксклюзивной advisory-блокировкой файла. Блокировка сохраняется весь срок жизни `WorldStore`; сам факт существования lock-файла не означает занятую блокировку. Второе открытие не должно молча порождать две независимые картины метаданных. + +## 6. Ревизии и идемпотентность + +Ревизия — монотонный номер состояния одного мира. Хотя API использует `u64`, хранение ограничено неотрицательным диапазоном SQLite `i64`: следующая ревизия выше `i64::MAX` возвращает явную ошибку. Оборот к нулю запрещён. Reset не возвращает ревизию к нулю и не позволяет старому запросу случайно пройти проверку нового состояния. + +Порядок обработки `edit` и `undo`: + +1. Проверить размеры, формат идентификаторов и нормализовать запрос. Повторяющиеся позиции в `changes` рекомендуется отвергать, чтобы не зависеть от порядка дублей. +2. Найти `(world_id, operation_id)` в долговечной таблице. При совпадающем отпечатке вернуть сохранённый `EditResult`, установив `replayed=true`. Это выполняется до сравнения с текущей ревизией: нормальный повтор после потерянного ответа должен работать. +3. При существующем идентификаторе с другим содержимым вернуть конфликт идемпотентности и ничего не менять. +4. Для новой операции проверить `expected_revision` в той же транзакции, что и изменение. При несовпадении вернуть конфликт с текущей ревизией. +5. Проверить все позиции и `BlockId`, применить и долговечно зафиксировать результат, затем отправить ответ и событие изменения. + +Отпечаток включает метод и все его значимые аргументы, включая `expected_revision`; порядок уникальных позиций канонизируется. Область уникальности идентификатора — стабильный внутренний ID мира, а не имя, которое в будущем может быть переиспользовано. + +Для операции, реально не изменяющей блоки, предлагается сохранять идемпотентный результат с `changed=0` без увеличения ревизии. Пустые и повторные запросы не должны бесконечно увеличивать номер состояния. Такая операция всё же требует долговечной записи дедупликации. + +Повтор возвращает ревизию исходной операции, которая может быть меньше текущей. Клиент не должен откатывать свою текущую ревизию по такому ответу. Ошибка после commit, но до доставки ответа, означает неопределённость для вызывающего кода: безопасный повтор использует тот же идентификатор и те же аргументы. + +Автоматическое удаление истории меняет гарантию дедупликации. До появления явной политики хранения ID и операций история долговечно сохраняется. Уменьшать её срок незаметно нельзя; ограничение RAM достигается дисковым хранением, а не забыванием уже подтверждённых запросов. + +## 7. Безопасный undo + +Undo — новая атомарная операция с собственной ревизией и `operation_id`; старый журнал не переписывается. Цель обязана принадлежать тому же миру и содержать реально применённые изменения. + +Принятое консервативное правило MVP (D009): отмена допустима, только если текущая ревизия мира совпадает с результирующей ревизией целевой правки. Последующая правка даже другой ячейки блокирует отмену. Одного сравнения текущего `BlockId` с `after` недостаточно: последовательность «камень → воздух → камень» возвращает тот же блок, но означает чужую более позднюю работу. Выборочная отмена непересекающихся изменений — последующее расширение с отдельными дисковыми индексами происхождения изменений. + +При конфликте хотя бы одной ячейки отмена целиком отклоняется с описанием конфликта. Частичная отмена не является поведением по умолчанию. Новая отмена уже отменённой операции получает конфликт; точный повтор того же undo возвращает сохранённый результат. Отмену самого undo можно добавить отдельно, после определения семантики; MVP должен явно сообщать об отсутствии поддержки. + +Reset ставит барьер истории для отмен: undo операций до reset отклоняется. Это удобно выразить монотонной `epoch` мира, записанной вместе с операциями. Дедупликационные записи прежней эпохи сохраняются: повтор старой операции возвращает старый результат, но не применяет её заново. + +## 8. Fork, reset и жизненный цикл + +Fork пинует снимок определённой ревизии источника в одной согласованной операции. Не делать последовательность «узнать ревизию → читать секции без защиты → создать мир»: между действиями источник может измениться. История источника не становится историей дочернего мира; ревизия нового мира может начинаться с нуля при сохранённой ссылке на ревизию снимка. + +Reset возвращает мир к его закреплённому шаблону, а мир без шаблона — к воздуху. Он атомарно удаляет собственные замены, увеличивает ревизию и эпоху, фиксирует операцию reset и инвалидирует соответствующие производные кэши. Снимки, закреплённые другими мирами, не меняются. + +Сервер на reset должен уведомить клиентов о необходимости нового снимка/синхронизации. Событие с пустым `changes` само по себе не удалит уже отображаемые блоки. Сервер также должен согласовать перенос игроков, сущности и состояние арены; ядро блоков не может решать это за игровой слой. + +Освобождение секций и снимков выполняется только после проверки долговечных ссылок из миров, снимков и нужной истории. Обход связей и удаление идут порциями. Нельзя удалять снимок только потому, что имя его источника больше не существует. До реализации проверенной сборки мусора безопаснее оставлять недостижимые данные на диске и показывать их объём в метриках. + +## 9. Сбои и восстановление + +После открытия хранилища проверить версию формата, согласованность метаданных и завершить восстановление до выдачи обслуживающих запросов. Незавершённая транзакция не видна. Завершённая и подтверждённая операция сохраняется после аварийного завершения процесса. + +Восстановление WAL выполняет SQLite. Приложение не переписывает и не обрезает его самостоятельно. Ошибки целостности базы, неизвестная версия формата секции и неправильная контрольная сумма требуют явной ошибки и сохранения файлов для диагностики. Не «лечить» такие ошибки удалением данных или созданием пустого мира. + +Checkpoint выполняется механизмом SQLite, чтобы авария оставляла согласованное состояние. Приложение не удаляет файлы WAL/SHM вручную. `flush()` возвращает ошибки синхронизации/checkpoint и не маскирует их; его точные гарантии должны быть совместимы с commit-before-ack, а не подменять его. + +Обязательные сценарии проверок: + +- остановка процесса до записи, посередине записи, после commit и до ответа; +- повтор операции после каждого такого сбоя; +- исчерпание дискового пространства и отказ записи/синхронизации; +- обрезанный хвост и повреждение середины журнала; +- падение во время snapshot, reset, регистрации блока и checkpoint; +- два открытия одного пути, отрицательные и граничные координаты; +- повтор ID с другим запросом, конфликт ревизии, undo с ABA и undo после reset; +- отказ открытия при кэше 0, вытеснение при 1 и небольшом обычном лимите, последующее переоткрытие. + +Тест с завершением процесса проверяет process-crash recovery, но не имитирует достоверно отключение питания или поведение аппаратного кэша диска. Эти границы нужно указывать рядом с результатом. + +## 10. Метрики и сравнение с Paper + +`stats()` должен возвращать стабильные именованные поля с единицами измерения. Полезный минимальный набор: `cache_sections`, `cache_limit_sections`, `cache_payload_bytes`, `cache_hits`, `cache_misses`, `cache_evictions`, `transient_peak_bytes`, `world_count`, `snapshot_count`, `registry_states`, `history_operations`, `storage_bytes`, `wal_bytes`, `dirty_overlay_sections`, `commit_latency_ms`, `recovery_duration_ms`. Счётчик bytes обязан указывать, измеряется ли фактическое выделение или оценка полезной нагрузки. + +Снаружи измерять RSS/PSS процесса, пиковую RAM, cgroup memory при наличии, CPU, чтение/запись диска, задержку тика и административных операций. Память файлового кэша ОС не следует автоматически объявлять «сэкономленной» или складывать с RSS без объяснения методики. + +Нагрузочные сценарии: + +1. Один и тот же шаблон и 1, 10, 100 независимых миров без изменений. +2. Те же миры с одинаковым фиксированным числом изменённых секций, затем с долей изменений 1%, 10% и 100%. +3. Последовательное и случайное движение активной области через карту больше кэша. +4. Длительное редактирование с растущей дисковой историей при постоянном активном наборе секций. +5. Чередование fork/reset и перезапусков, проверка содержимого после каждого этапа. + +Фиксировать seed, карту, число миров, игроков/ботов, дистанцию видимости, набор сущностей, длительность прогрева и замера. Сначала сравнивать варианты собственного ядра: прямой массив против палитры, копирование против общего шаблона, разные лимиты кэша. Это помогает связать эффект с конкретным решением. + +При сравнении с Paper записать точные версии серверов, Minecraft, Java и Rust, параметры JVM, оборудование, ОС, плагины, способ создания/копирования миров и одинаковый сценарий игроков. Отдельно показывать режим хранения одинаковых блоков и полноценный игровой сценарий. Если Shacraft не выполняет освещение, AI, redstone, генерацию или другие функции сценария Paper, прямо перечислить различия: такой замер не доказывает превосходство при равной функциональности. + +Публиковать исходные команды и сырые результаты, медиану и разброс нескольких прогонов, а также задержки и I/O рядом с RAM. Уменьшение памяти ценой неприемлемого дискового доступа или задержек — измеренный компромисс, а не автоматически успех. + +## 11. Проверка контрактов перед следующими этапами + +Часть первоначальных замечаний уже принята в CONTRACT.md и DECISIONS D008–D009; остальные относятся к будущим сетевым этапам: + +- Уточнить snapshot/revision в семантике `template`; полезно добавить их в `WorldInfo`. +- Обновить управляющий HTTP-метод `world.reset`, чтобы он тоже требовал уже принятые в Rust API `expected_revision` и `operation_id`. Для retry create/fork определить идемпотентность и ревизию источника. +- Зафиксировать правила no-op и точные пределы строк/реестра; остальных принятых инвариантов это не отменяет. +- Ввести машиночитаемые коды ошибок: неизвестный мир/блок, конфликт ревизии, конфликт ID, конфликт undo, превышение лимита, повреждённое хранилище, отказ записи. +- Уточнить события reset/resync, согласованный снимок `welcome` и доставку изменений после его ревизии; иначе клиент может пропустить изменение между снимком и подпиской. +- Для server inputs явно проверять конечность чисел, границы координат, размер сообщений, частоту запросов, дальность взаимодействия и права на каждый мир. Токен управления не должен попадать в публичные ответы или логи. +- `manifest_hash` подтверждает только заявленную версию ресурсов. Он не доказывает отсутствие модификаций клиента; игровая проверка действий остаётся на сервере. + +Порядок реализации: компактная секция и её проверки → долговечная атомарная операция и recovery → ревизии/дедупликация/undo → snapshot/fork/reset → ограниченный кэш и метрики → нагрузочные измерения. В каждом этапе сначала сохраняются корректность и восстановление, затем добавляется оптимизация. + +## Источники по долговечности + +[SQLite WAL](https://sqlite.org/wal.html) и [PRAGMA synchronous](https://sqlite.org/pragma.html) описывают синхронизацию WAL при каждом commit в режиме FULL. Это выбранная настройка; корректность нашей схемы и восстановления всё равно проверяется отдельно. diff --git a/docs/PLAN.md b/docs/PLAN.md new file mode 100644 index 0000000..067b5d3 --- /dev/null +++ b/docs/PLAN.md @@ -0,0 +1,79 @@ +# План разработки + +План составлен до реализации. Каждый этап заканчивается воспроизводимым результатом в локальном репозитории. `docs/STATUS.md` обновляется после проверок. Пункт не считается выполненным по наличию интерфейса, заглушки, каталога имён или успешной компиляции. + +## Этап 0. Сохранить контекст и проверить проектирование + +- Записать исходные требования, решения, контракты и критерии приёмки. +- Зафиксировать источники данных совместимости и ограничения лицензий. +- Разделить компоненты так, чтобы можно было независимо разрабатывать хранение, совместимость, сетевой сервер и клиент. +- Согласовать обработку ошибок, координаты, ревизии, ограничения операций и формат диагностики. + +Выход: документы в `docs/`; список решений без молчаливого сокращения объёма. + +## Этап 1. Ядро и доказуемое управление памятью + +- Rust workspace; координаты с корректными отрицательными значениями; реестр канонических состояний блоков. +- Секции 16×16×16: единообразное состояние или палитра и упакованные индексы. Отсутствие отдельного объекта на каждый блок. +- Собственный версионированный формат хранения, ограниченный кэш, выгрузка на диск; операции чтения не изменяют мир. +- Независимые экземпляры общей карты. Изменение/сброс одного экземпляра не влияет на другие; снимок основы имеет чёткую семантику. +- Атомарные правки с журналом восстановления, ревизиями и идемпотентностью. История и undo хранятся на диске с ограниченным потреблением RAM. +- Проверки границ секций, упаковки, изоляции миров, повторов, конфликтов, сохранения и восстановления после сбоя. Измерение хранения общей карты против копий. + +Выход: самостоятельная библиотека и воспроизводимые тесты/измерения. Это ещё не весь MVP. + +## Этап 2. Сервер и минимальный клиент — сквозной сценарий + +- Сервер владеет миром и симуляцией; фиксированный тик, серверные движение/столкновения и проверка взаимодействий. +- Версионированный протокол: подключение, начальный снимок, изменения блоков, игроки, переход между мирами, ошибки и восстановление подключения. +- Лимиты числа игроков, радиуса мира, сообщений, частоты действий и очередей. Медленный клиент не увеличивает память сервера без ограничения. +- Тестовый клиент: собственный рендерер, камера, управление, блоки, простые сущности, состояния соединения. Сервер остаётся без графики. +- Два независимых клиента видят одинаковые изменения. Перезапуск сохраняет мир. Подключение и выход не оставляют сущности и фоновые задачи. + +Выход: запускаемая локальная сетевая песочница. На первом проходе допустим небольшой набор материалов; это не выполнение требования полного каталога. + +## Этап 3. Control API и собственный MCP + +- Общий API чтения, редактирования, истории и диагностики; MCP — отдельный процесс с stdio. +- Поиск материалов, чтение ограниченной области, пакетное строительство и шаблоны, preview/commit, undo с защитой от чужих изменений. +- Ресурсы с системой координат и возможностями; структурированные ответы и понятные ошибки. +- Визуальная обратная связь: снимок с явно указанными типом отображения и ревизией; топографический preview не объявляется снимком игрового клиента. +- Тестовые миры, сущности, управление аренами и метрики. Авторизация управляющего API; токен не попадает в клиентский код и журнал. +- Реальная MCP-сессия: initialize → list → build → inspect/capture → undo; параллельный клиент видит результат. + +Выход: законченный цикл автоматизированного строительства с проверкой результата. + +## Этап 4. Контент и двусторонняя совместимость + +- Проверить полный источник каталога именно Java 26.2; генерировать воспроизводимо, хранить происхождение и версию данных. +- Реализовать формы, коллизии и отображение семейств блоков; отдельно учитывать неподдерживаемые особенности. Базовые определения сущностей и сохраняемые свойства. +- Импорт Anvil/NBT по частям с ограниченным бюджетом памяти; обработка измерений и дополнительного NBT. +- Экспорт мира в целевую версию; режим точности и явных замен, sidecar для неподдерживаемых данных, корректное инвалидирование происхождения после правок. +- `.schem` import/export; тестовые fixtures; повторное открытие экспортированных данных независимым читателем и, при доступности целевой игры, самой игрой. + +Выход: проверенный обмен заявленными данными с отчётом покрытия. Полный каталог имён без форм/данных не закрывает этот этап. + +## Этап 5. Единые пакеты и законченная миниигра + +- Версионированный манифест с зависимостями, клиентскими/серверными частями, размерами, хешами, лицензиями и возможностями. +- Небольшой собственный базовый набор ассетов. Проверяемая загрузка клиентских ресурсов и кэш; обязательные ресурсы отделены от необязательного качества. +- Первое расширение через единый формат, работающий клиентский и серверный сценарий. Декларативная конфигурация не называется полноценной средой произвольных модулей. +- Spleef: ожидание → отсчёт → игра → выбывание → победитель → сброс. Несколько независимых арен используют общую карту. +- Повторные матчи, отключения, вход во время игры, переход в лобби, сохранение настроек после перезапуска. + +Выход: друзья могут сыграть законченный матч, а другой разработчик — добавить документированное расширение. + +## Этап 6. Приёмка, измерения и поставка + +- Проверить все критерии `ACCEPTANCE.md`; отдельный список фактического покрытия и ограничений. +- Измерить RSS/пик памяти, хранение секций, кэши, сетевые очереди, p95/p99 тика, поведение после многократных матчей и перезапуска. +- Сравнение с Paper проводить только при доступном сопоставимом стенде. До этого публиковать собственные числа без заявлений о кратности выигрыша. +- Инструкции запуска, конфигурация MCP, описание API/формата пакетов, лицензии, контрольная сумма исходного архива, Git-коммит. +- Продакшен-публикация и изменение лаунчера — отдельная интеграционная работа после локальной проверки. + +Выход: воспроизводимый локальный релиз MVP с честным отчётом, исходным архивом и известными ограничениями. + +## Рабочий порядок после планирования + +Сначала этап 1. Параллельно с ним можно готовить протокол и клиент по согласованному контракту, изучать каталог/конвертер. Подключение этих частей, изменения общих интерфейсов и проверки выполняются последовательно. После каждого этапа — сохранить результат; не оставлять единственную копию в одноразовой среде. + diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md new file mode 100644 index 0000000..7bf7d9b --- /dev/null +++ b/docs/REQUIREMENTS.md @@ -0,0 +1,31 @@ +# Исходные требования + +Источник: переписка [«Динамическое распределение ресурсов»](https://chatgpt.com/share/6aa7eba7-d0e8-83ed-804a-818e7d94cef5), прочитанная с прокруткой 14 сентября 2026 года, и продолжение в Codex. Этот документ сохраняет требования, а не подтверждает реализацию из старой среды. + +## Зачем нужен проект + +У Shacraft два сервера на машине с 8 ГБ RAM: выживание на NeoForge (примерно 300 модов, включая Create) и миниигры на Paper 26.2. Исследование динамического распределения памяти привело к самостоятельному проекту воксельного движка. Перенос существующих NeoForge-модов не является требованием первой версии. + +## Обязательные направления + +- Самостоятельное открытое ядро на Rust с собственной архитектурой. Низкоуровневые библиотеки допустимы; готовый игровой движок не является основой. +- Серверная RAM — главный измеряемый критерий. Не выдавать Rust, остановку тиков или битовые палитры сами по себе за доказательство экономии относительно Minecraft. +- Независимые ядро, тестовый сервер, тестовый клиент и отдельный собственный MCP. Ядро не зависит от графики, сокетов, MCP и лаунчера. +- Общие неизменяемые карты, независимые изменения экземпляров, выгрузка неактивного состояния, ограничение кэшей, очередей и истории. Один процесс обслуживает несколько миров/арен. +- Полный базовый каталог блоков, состояний и типов сущностей Minecraft Java 26.2. Нужны формы, коллизии и сохраняемые свойства. Полнота каталога и точность механик проверяются отдельно; полная ванильная симуляция не была согласована как обязательная первая реализация. +- Импорт Minecraft → Shacraft и обратный экспорт Shacraft → Minecraft. Основной результат — мир; `.schem` для построек — дополнительный формат. Неизвестные данные сохраняются или явно отражаются в отчёте; тихие потери недопустимы. +- Единая модель расширений: общие определения, серверная и клиентская логика, текстуры, звуки и шейдеры в одном формате пакетов. Сервер объявляет необходимый набор; клиент загружает недостающее, проверяет версии и хеши, использует кэш. Серверные секреты и код с секретами не раздаются клиенту. +- Политика допустимых клиентских модификаций. Сервер проверяет действия и совместимость пакетов. Контрольная сумма, сообщённая самим клиентом, не доказывает неизменность клиента. +- В перспективе — запуск через Shacraft Launcher. Существующая инфраструктура является интеграционной целью, не зависимостью ядра. +- Оригинальные текстуры/звуки/модели Minecraft не включаются в распространяемые файлы. Лицензии контента учитываются отдельно от лицензии кода. + +## Качественный MCP + +Minecraft Builder MCP — источник опыта, а не сервер, который нужно переименовать. Нужны самостоятельные инструменты осмотра и поиска, пакетного строительства, сущностей и арен, визуальной проверки и диагностики. Основы: общая Control API, ожидаемые ревизии, идентификаторы операций, конфликтобезопасная отмена, ограниченные ответы, история на диске, ограничения нагрузки и авторизация. + +## Что считать фактом + +В старом чате заявлялись 1 196 блоков, 158 типов сущностей, семь тестов и частичная реализация. Эти числа необходимо заново проверить по источникам и локальным результатам. Не переносить их в новый отчёт как установленные факты. + +Пожелание «в 2–4 раза меньше RAM» обсуждалось как возможная цель, не достигнутый результат. Сравнивать нужно одинаковые карты, загруженные области, игроков и механики, учитывая весь процесс и необходимые сервисы. + diff --git a/docs/STATUS.md b/docs/STATUS.md new file mode 100644 index 0000000..bd1dc73 --- /dev/null +++ b/docs/STATUS.md @@ -0,0 +1,25 @@ +# Состояние проекта + +Обновлено: 2026-09-14. + +## Подтверждено локально + +- Создан отдельный Git-репозиторий `/home/emil/Desktop/shacraft-core`. +- Доступны Rust 1.96.0, Cargo 1.96.0, Node.js 22.22.3, Python 3.14.4. +- Созданы workspace manifest, директории компонентов и первоначальный контракт. Компилируемой реализации пока нет. +- Записаны требования, план и решения. Независимо проверяются план хранения, совместимость и критерии приёмки. + +## Ближайшие действия + +1. Завершить проверку документов и поправить контракт. +2. Реализовать и проверить библиотеку ядра (этап 1). +3. Добавить сервер/клиент и собственный MCP с проверкой сквозного сценария. + +## Правила продолжения + +- Перед работой прочитать REQUIREMENTS, PLAN, DECISIONS, CONTRACT и этот файл. +- Новые результаты подтверждать командами, тестами или видимым поведением; старый облачный отчёт не использовать как доказательство. +- Не помечать весь MVP готовым по завершению одного этапа. +- Отмечать точные ограничения контента, конвертера, клиента, модулей и безопасности. +- Команды запуска и проверки сохранять в репозитории; изменения коммитить в локальный Git после проверки. +- Не помещать токены, данные реальных игроков и проприетарные ассеты в Git и архивы.