Как писать эту документацию
Добавить или поправить страницу этого сайта может любой. Здесь рассказано, для чего нужна страница, какие в ней поля и компоненты, как пометить возможность, которой еще нет, и что проверить перед PR. Сайт лежит в docs-site/, а как запустить его локально, написано в README.
Три вида документации
Заголовок раздела «Три вида документации»Канон: docs/, PERF.md, DECISIONS.md |
Этот сайт | README и лендинг | |
|---|---|---|---|
| Для кого | ИИ-агенты, авторы спецификаций | плагинщики из JVM, контрибьюторы, Rust-разработчики, админы | наблюдатели, игроки, любопытные разработчики |
| Вопрос читателя | Что именно требует правило? | Как сделать X и почему так? | Что это и для меня ли? |
| Глубина | полная | 1-4 экрана, дальше ссылка в канон | экран на секцию |
Канон ради сайта никогда не правят, поэтому страница объясняет канон и ссылается на него. Когда читателю нужно точное правило, дайте ссылку и не копируйте правило. Когда идея для новичка новая, перескажите ее на 1-2 экрана. Причину решения тоже не пересказывайте: поставьте ссылку на issue с меткой decision и добавьте одну строку своими словами.
Из заметок по генерации мира, то есть из docs/worldgen.md, structures.md, surface.md, scattering.md, heightmap.md и aquifer.md, не копируйте ничего. В них есть имена кода игры.
У каждого факта один источник
Заголовок раздела «У каждого факта один источник»Не пишите число по памяти. Версия игры записана в assets/minecraft/version.json, цифры производительности в PERF.md, поведение в docs/spec/, решения в issue.
Счетчик, который повторяют две страницы, и id снапшота игры лежат в src/data/facts.yml. Страница пишет такое значение как {fact('crates')} после строки import { fact } from '~/lib/facts.mjs';, а status.yml пишет его как {crates}. Id snapshot читается из assets/minecraft/version.json. У каждой записи есть поле how, в нем сказано, как это значение измерить. Число, которое называет только одна страница, остается в ее тексте.
В русском после числа меняется форма слова (“122 крейта”, “122 крейтов”), поэтому при смене числа русские страницы правят вручную. Нужные места находит grep -rn "fact('crates')" src/content/docs/ru.
Плашка вверху главной страницы называет коммит origin/main, с которым сверялось последнее обновление документации. Коммит и его дата записаны в facts.yml как docs_commit и docs_commit_date, и обновление документации двигает оба значения. npm test падает, если дата не совпадает с датой этого коммита. При каждом заходе плашка спрашивает у GitHub, сколько коммитов появилось в main после этого коммита, и прячет строку, если GitHub не ответил.
Одна страница, один файл
Заголовок раздела «Одна страница, один файл»Основной язык - английский. Страница лежит в docs-site/src/content/docs/en/<группа>/<slug>.mdx, а ее русская версия - по тому же пути в ru/. Группы называются start, plugins, concepts, crates, server, client, reference, contributing и project. Сайдбар строится по каталогам, а порядок внутри группы задает sidebar.order.
Frontmatter
Заголовок раздела «Frontmatter»Страница начинается с блока полей, его называют frontmatter. Схема блока лежит в docs-site/src/content.config.ts, и неверное поле ломает сборку.
---title: Your first plugindescription: One sentence.sidebar: order: 3kind: explanation # tutorial | how-to | reference | explanation | landingaudience: [jvm, rust] # любые из jvm, contributor, rust, admin, player, observerstability: changing # stable | changing | wiptracking: [283, 636] # номера issue или PR, обязательно если не stablesources: # пути от корня репозитория, issue:N, pr:N - crates/mcrs_minecraft_chat_server/src/chat_plugin.rs - issue:283verified: snapshot: 26.4-snapshot-7 commit: 51d45c26a date: 2026-11-09translated_from: 51d45c26a # только в ru-файлах---| Поле | Что в нем писать |
|---|---|
kind |
Тип страницы, и он у страницы один. Туториал ведет читателя за руку, how-to решает одну задачу, reference перечисляет факты, а explanation отвечает на вопрос “почему”. |
sources |
Все файлы репозитория, на которые опирается страница. Страница без sources ревью не проходит. Берите пути из самих файлов, а не из текста старых issue, потому что крейты переименовывают. |
verified |
Снапшот, коммит и дата, когда вы последний раз сверили страницу с кодом. Не копируйте это поле с другой страницы. |
Баннер стабильности вверху страницы и блок “Источники и проверка” внизу сайт рисует из frontmatter, поэтому руками их не пишите.
В русском файле есть только title, description, sidebar и translated_from. Поля kind, audience, stability, tracking, sources и verified берутся из английской страницы, и русский файл с любым из них ломает сборку.
Компоненты
Заголовок раздела «Компоненты»Импортируйте нужные компоненты сразу после frontmatter:
import Status from '~/components/Status.astro';import StatusRows from '~/components/StatusRows.astro';import Example from '~/components/Example.astro';import Demo from '~/components/Demo.astro';import { Aside, Tabs, TabItem, Card, CardGrid, LinkCard } from '@astrojs/starlight/components';| Компонент | Для чего |
|---|---|
<Status issue={78} /> |
Бейдж состояния со ссылкой на issue, pull request или документ. |
<StatusRows areas="network,persistence" /> |
Строки из docs-site/src/data/status.yml для перечисленных id. |
<Example file="path" from="fn run" to="..." title="..." /> |
Фрагмент файла репозитория, который вырезается при сборке по текстовым якорям. |
<Demo src="/docs/demos/cows.html" title="..." /> |
Интерактивная страница в iframe. |
Tabs с syncKey="jvm" |
Paper и mcrs рядом. |
Каждая issue и каждый pull request на сайте идут через Status. Не пишите #78 текстом и не дописывайте состояние словами рядом с бейджем. Состояние issue берется из docs-site/src/data/issues.yml, и это одно из значений works, partial, wip, undecided или declined. Поэтому state рядом с issue ломает сборку, как и issue, которой нет в реестре. У pull request состояние свое, потому что оно относится к одному утверждению, и влитый pull request пишется как <Status pr={636} state="works" />. Бейдж может вести и на документ: doc="docs/spec/login.md".
Код из репозитория никогда не вставляйте в MDX руками. Берите Example: он вырезает код из файла при сборке, поэтому страница не покажет код, которого уже нет. Код, которого в репозитории нет, например сниппет для Paper, идет в обычный блок с меткой java.
Example находит фрагмент по двум текстовым якорям, from и to. Якорь, которого нет в файле или который найден дважды, ломает сборку, поэтому выбирайте якоря внимательно:
- В
fromберите самый короткий текст, который уникален в файле и переживет переименование соседей. Хорошо подходит сигнатура вродеpub fn add_command,fn run_echo(илиimpl Plugin for CountTicks. Не ставьте якорь на комментарий и на строку вида}. - Без
toфрагментом будет блок, который открывается в строкеfrom, до закрывающей его скобки. - Добавьте
to, когда нужен кусок функции или несколько элементов подряд. Тогда фрагмент кончается на первой строке послеfrom, в которой естьto. - Скобки в строковых литералах сбивают счет скобок, в этом случае тоже задайте
to. - Оба якоря могут занимать несколько строк. Разделяйте строки через
\nвнутри{"..."}.
Номера строк для ссылки под блоком считаются по якорям.
Ссылки между страницами абсолютные, с base и слешем в конце: /docs/en/start/status/, а в русских файлах /docs/ru/.... Ссылка на файл репозитория выглядит как https://github.com/andreypfau/mcrs/blob/main/<путь>.
Чего еще нет: пишем и помечаем
Заголовок раздела «Чего еще нет: пишем и помечаем»Вот честная строка про загрузчик плагинов: “Загрузчика jar, wasm или скриптов нет, и решения про него нет”. На этом она заканчивается, потому что проект отклонил только фасад и Bukkit-подобный API, а больше ничего не решил.1
Владелец решил, что возможность, которой еще нет, можно подробно описывать уже сейчас, если ее описывает какой-то источник: issue, спецификация, файл в docs/ или код. Обмана в этом нет, потому что пометка говорит, что возможности пока нет. Когда она появится, поправьте текст и снимите пометку.
- Целая страница о такой возможности получает
stability: wipиtracking. - Блок внутри стабильной страницы получает
<Aside type="caution" title="WIP">со ссылкой на issue или бейдж<Status issue={N} />. - Не выдумывайте. Опирайтесь на issue (
gh issue view N -R andreypfau/mcrs), спецификацию, файл вdocs/или код. Если источника нет, напишите “решения пока нет” и больше ничего. - Не превращайте цель в утверждение. Пишите “направление, не обязательство”.
- Гайд по установке и запуску делайте коротким: несколько строк, ссылка на Quick start в README и на технические заметки, пометка “подробный гайд появится к альфе”. Не записывайте шаги по догадке, не прогнав их.
У возможности есть состояние, у страницы - стабильность
Заголовок раздела «У возможности есть состояние, у страницы - стабильность»Страница про плагины может быть changing, потому что issue еще изменит механизм, а внутри нее одна строка помечена works, а другая wip. Первая пометка - стабильность страницы, две другие - состояния возможностей.
Состояние возможности показывает Status в тексте и в таблицах. Состояние issue - это одна строка в docs-site/src/data/issues.yml, и вместе с ней меняются все бейджи этой issue на обоих языках.
| Состояние | Когда ставить |
|---|---|
works |
Есть тест, который это запускает, или замер в PERF.md. Одного коммита мало. |
partial |
Работает часть, а остальное в issue или PR. Ссылка обязательна. |
wip |
В коде этого нет или оно не закончено, и есть issue. “Направление, не обязательство”. |
undecided |
Issue нет, или на ней стоит метка needs-decision. |
declined |
Только когда закрыта issue с меткой decision. |
Стабильность страницы - это баннер, который сайт рисует по полю stability. У страницы stable баннера нет, страница changing пишет “Меняется, следите: #N”, а страница wip пишет, что в коде этого еще нет.
Доки в том же PR
Заголовок раздела «Доки в том же PR»Если вы меняете код, на который опирается страница, правьте страницу в том же PR. В шаблоне PR для этого есть чекбокс.
Workflow docs запускается на каждом PR. Его скрипт, docs-site/scripts/check-sources.mjs, сравнивает измененные файлы с sources каждой страницы и пишет предупреждение по каждой странице, которую задевает ваш PR. Сборку предупреждение никогда не роняет. Когда увидите его, откройте страницу и проверьте ее. Потом поправьте текст и блок verified или напишите в PR, почему правки не нужны. Ту же проверку можно запустить локально: npm run check-sources в docs-site/.
Тот же workflow запускается каждый понедельник в 06:00 UTC и вручную. Этот запуск выполняет node docs-site/scripts/check-sources.mjs --stale и пишет в сводку job отчет из трех разделов:
Доки догоняют main пачками. Для этого запустите скилл mcrs-update-docs в агенте для кода. Он проходит pull request, влитые после самого старого verified.commit, и обновляет страницы, строки статуса, реестр issue и новости. Потом он двигает docs_commit и убирает признаки AI-текста из того, что сам поменял, сверяясь со свежей копией руководства Википедии.
- Английские страницы, у которых после
verified.commitпоявились коммиты вsources. - Русские страницы, которые отстали от английских.
- Пути из
sources, которых не существует.
Коммит, которого нет в истории, попадает в отчет как unknown commit. Отчет сборку никогда не роняет, а вот несуществующий путь в sources роняет npm test.
Еще еженедельная задача запускает check-issues, который сверяет состояния в issues.yml с GitHub.
Эти проверки читают имена файлов и ничего не знают о смысле. Они не заметят, что состояние issue изменилось после даты verified страницы, поэтому раз в месяц владелец обходит страницу Статус.
Английский и русский
Заголовок раздела «Английский и русский»Русский файл лежит по тому же пути и повторяет структуру и порядок английского, а содержания, которого нет в английском, в нем нет. Если русского файла нет, сайт показывает английскую страницу с пометкой, что она еще не переведена.
Поле translated_from: <коммит> называет коммит английской страницы, с которой вы переводили. Когда английская страница уходит вперед, разницу видно в git log между двумя коммитами. Еженедельный отчет считает русскую страницу отставшей, если у английской после translated_from есть коммит, которого нет у русского файла. Коммит, который правит оба языка сразу, отставанием не считается, поэтому после совместной правки translated_from двигать не нужно.
Устоявшиеся англицизмы, такие как тик, чанк, крейт, плагин и снапшот, в русском тексте остаются, а при первом упоминании английский термин стоит в скобках. Заголовки issue остаются английскими.
Шаблон объяснялки
Заголовок раздела «Шаблон объяснялки»У интерактивной объяснялки вроде демки с коровами 12 обязательных частей. Объяснялку, в которой нет хотя бы одной, не публикуют.
- Одно утверждение: одно предложение о том, что сравнивается.
- Две стороны на общих часах: ваниль слева, mcrs справа, один индекс кадра, никаких отдельных таймеров. На узком экране они идут друг под другом в том же порядке.
- Одинаковый вход: один сид, одна сцена, одно число сущностей. Если вход разный, напишите почему.
- Декларативные данные сценария: кадры в массиве или JSON рядом с файлом. Рендер про сценарий не знает.
- Плеер: пауза, шаг вперед, шаг назад, сначала, счетчик “шаг N из M”, клавиши-стрелки, остановка на последнем кадре.
- Подпись к каждому кадру: одна-две фразы с номером тика.
- Таблица отличий: 3-6 строк, после анимации.
- Мост для читателей из JVM: одна строка “в Paper это выглядело бы так: …”.
- Источник: ссылки на правило спецификации, на
docs/*.mdс разделом и на issue. Механизм в разработке получает бейджStatus. - Честная сноска: что упрощено, чего в игре сегодня нет (пишите “цель, не готово: #N”) и что не замер.
- Доступность: светлая и темная тема,
prefers-reduced-motionбез автоплея,aria-labelу SVG, фокус на кнопках, контраст текста не ниже 4.5 к 1. - Мета:
lang, версия снапшота и дата, когда вы сверили объяснялку с кодом.
Цвета сравнения постоянные: ваниль - охра #b8741a, mcrs - бирюза #1d7a70.
У объяснялки четыре правила честности. Механизм не равен замеру. Показывайте то, что реализовано, а цель помечайте. Игрушечная модель называет свои упрощения. Поведение ванили берется из спецификации или из оракула и никогда из памяти автора.
Чеклист честности
Заголовок раздела «Чеклист честности»Пройдите по этому списку перед каждым PR, который добавляет или меняет страницу.
- У каждого числа рядом стоят машина, сценарий и ссылка на
PERF.mdили issue. Цифры берутся только изPERF.md, вместе с машиной (M4 Max, macOS) и сценарием. - “Работает” пишется только тогда, когда это показывает тест, коммит или запись в
PERF.md. Иначе пишите “частично” или “wip”. - Состояние и цель визуально различаются: бейдж, ярлык или Aside.
- Каждое “нет” ссылается на issue или решение.
- Нет чисел, снятых не на тихой машине, и нет сравнений с ванилью, Paper или Horizon в цифрах.
- У утверждения о чужом проекте есть источник, иначе его не пишут. Для продакшена называем только Paper, Folia, Minestom и Velocity. Rust-проекты идут одной нейтральной строкой без оценок.
- Версия игры и счетчики берутся из
fact(...)и никогда не набираются руками:{fact('snapshot')}читаетassets/minecraft/version.json, а{fact('crates')}читаетfacts.yml. - У страницы с меняющимся содержимым есть дата и коммит последней проверки.
- “Мы” не относится к большей группе, чем есть на самом деле. Никаких заявлений про “сообщество”.
- Рядом с каждой командой запуска стоит предупреждение о безопасности: у сервера нет аутентификации, поэтому не открывайте его порт наружу.
- Цели и issue подаются как “направление, не обязательство”.
В двух фактах черновики ошибаются часто. Во-первых, конфликты между системами Bevy компилятор не проверяет. Доступ к данным проверяет планировщик, когда строит граф. Во-вторых, проект отклонил только фасад и Bukkit-подобный API, а о загрузчике плагинов в этом решении ничего нет.1
Clean-room в доках
Заголовок раздела «Clean-room в доках»Правила clean-room для кода действуют и в документации. Не пишите имена классов, методов и полей кода игры, кроме ResourceKey, TagKey, BlockPos, SectionPos, ColumnPos и PalettedContainer. Публичные API Paper, Bukkit, Velocity и Minestom называть можно. Не пишите, что mcrs - порт. Сами правила объясняет страница Clean-room на практике.
Пишите для человека, который читает страницу один раз, сверху вниз, и никогда не открывал репозиторий. Что именно выдает машинный текст, лучше всего описано в Википедии: Признаки сгенерированности текста для русского и Signs of AI writing для английского. Обе страницы правят почти каждый день, потому что с каждой новой моделью шаблоны меняются. Перед тем как писать или проверять страницу, откройте актуальную версию и сверьте с ней свой текст.
Правила ниже - общая выжимка, сделанная 2026-11-10, и она устареет. Если правила расходятся со страницами Википедии или не упоминают шаблон, который там есть, верьте Википедии. Из-за таких шаблонов читатель перестает доверять тексту, кто бы его ни написал.
Объясняйте, а не перечисляйте утверждения
Заголовок раздела «Объясняйте, а не перечисляйте утверждения»- Начните страницу с того, что читатель из нее получит и для кого она. Двух-трех предложений хватит.
- Сначала пример, потом правило, которое он показывает. Примером может быть фрагмент кода, команда с выводом или конкретный случай.
- У правила должна быть причина рядом. Связывайте предложения словами “потому что”, “поэтому”, “но”, “когда”. Если подряд идут короткие несвязанные утверждения, читателю приходится восстанавливать логику самому.
- Термин объясните в нескольких словах там, где он встречается на странице впервые, или дайте ссылку на глоссарий. Не рассчитывайте, что читатель знает Bevy.
- Пишите полными предложениями. В абзаце одна тема и обычно от 2 до 5 предложений. Длина предложений должна меняться.
- Обращайтесь к читателю на “вы”. Активный залог, настоящее время.
- Заголовок говорит, о чем раздел или что читатель в нем делает: “Игрок - это строка в таблице”, “Запустите сервер”. Заголовки “Обзор” и “Введение” не нужны.
- Нумерованный список нужен для шагов, которые читатель выполняет, таблица - для значений, которые он ищет, маркированный список - для коротких однородных пунктов. Все остальное пишется прозой.
- Aside нужен для предупреждения или для текста, который можно пропустить. Если на странице больше двух-трех Aside, их никто не замечает.
- Issue или pull request - это источник, а источник уходит в сноску, как в Википедии. Напишите предложение так, чтобы факт был в нем самом, и поставьте
[^357]после точки. В конце файла напишите[^357]: <Status issue={357} /> Одна строка о том, что эта issue решает или отслеживает.Читатель, который не откроет issue, ничего не теряет. В тексте не пишите “см. #357”, “решение #357 говорит” или “отслеживается в #357”. - В ячейке таблицы состояние и есть содержимое, поэтому там
<Status issue={357} />остается в ячейке. В журнале изменений на странице новостей ссылки на pull request остаются в записях. - Путь к файлу ставьте в конец предложения или в отдельное предложение. Не разрывайте мысль скобками.
Шаблоны, которые надо убрать
Заголовок раздела «Шаблоны, которые надо убрать»| Шаблон | Пример | Как писать |
|---|---|---|
| Противопоставление тому, чего никто не утверждал, в любом порядке | “Дело не в новом синтаксисе, а в другом способе мыслить.” “Это таблица, а не объект.” | Скажите, что это. “Не” и “вместо” оставьте для ошибки, которую читатель и правда может сделать. |
| Двоеточие как барабанная дробь | “Правило короткое: не полагайтесь на порядок.” | “Не полагайтесь на порядок.” |
| Тройка по привычке | “быстро, безопасно и просто” | Назовите столько пунктов, сколько есть: один, два или пять. |
| Предложение о том, что следующее важно | “Важно отметить, что…” | Удалите его и скажите суть. |
| Итог в конце | “Что это значит”, “Вкратце”, пересказ страницы списком | Страница заканчивается там, где кончилось содержание. Одной ссылки “Дальше” достаточно. |
| Раздутые и рекламные слова | “ключевой”, “мощный”, “бесшовный”, “надежный”, “обеспечивает”, “демонстрирует”, “подчеркивает”, “отражает” | Простое слово или число. |
| Глагол на месте простого “это” или “есть” | “выступает”, “служит”, “предлагает” | “это”, “у X есть”. |
| Хвост с деепричастием | “…, обеспечивая одинаковый результат” | Новое предложение о том, что происходит и почему. |
| Разные слова для одного и того же | “измерение”, потом “мир”, потом “реальность” | Один термин на одну вещь на всем сайте. |
| Список с жирными заголовками | “- Скорость: работает быстро” | Таблица или обычные предложения. |
| Размытые утверждения | “многие разработчики”, “широко используется” | Источник, число или ничего. |
| Жирный шрифт по всему абзацу | Жирным выделяйте только предупреждение, которое нельзя пропустить. |
Механика
Заголовок раздела «Механика»- Английский текст пишется простым языком, для читателя, который не носитель.
- Конкретика: у утверждения есть ссылка или число.
- Только ASCII-минус, без длинного и среднего тире. Кавычки прямые.
- Без жесткого переноса: абзац - одна строка.
- Страница занимает 1-4 экрана. Если страница разрослась, разбейте ее или дайте ссылку на канон.
Русский текст
Заголовок раздела «Русский текст»- Пишите так, как русскоязычный разработчик объяснил бы тему коллеге. Переводите смысл абзаца, а не структуру английских фраз.
- Глаголы вместо цепочек существительных, активный залог.
- Без канцелярита (“является”, “осуществлять”, “данный”, “в рамках”, “представляет собой”).
- Без штампов: “стоит отметить”, “важно понимать”, “играет ключевую роль”, “таким образом”, “в заключение”.
- Без повисших деепричастных оборотов (“используя этот метод, результаты улучшаются”).
- Без диапазона “от X до Y”, когда его концы не лежат на одной шкале. Перечислите пункты.
- После двоеточия строчная буква. В заголовке с заглавной буквы пишется только первое слово.
- Буква с двумя точками не используется: везде пишем “е”.
Перед PR
Заголовок раздела «Перед PR»- Frontmatter соответствует схеме, у страниц
wipиchangingестьtracking. - Якорь каждого
Exampleнаходит свой код, и фрагмент показывает то, о чем говорит текст рядом. - Ссылки ведут на страницы, которые есть в дереве.
npm run buildвdocs-site/проходит.- В странице нет длинных тире, нет буквы с двумя точками и нет имен из кода игры.
Footnotes
Заголовок раздела «Footnotes»Источники и проверка
Проверено: 26.4-snapshot-7, 3540f457b, 2026-11-10
Переведено с версии 3540f457b