Первый плагин: счетчик тиков
В этом туториале вы добавите плагин, который считает тики сервера. В mcrs нет ни загрузчика плагинов, ни jar-файлов, об этом рассказывает страница Это не Paper API. Поэтому “установить плагин” здесь значит добавить код на Rust в программу, которая запускает сервер. Плагин лежит в вашей копии репозитория mcrs, рядом с бинарником сервера.
Восемь терминов этой страницы
Заголовок раздела «Восемь терминов этой страницы»Если Rust и Bevy вам в новинку, сначала прочитайте эту таблицу. Полный минимум есть на странице Rust для Java-разработчика.
| Слово | Ближайшая идея из Java | Что значит здесь |
|---|---|---|
| plugin (плагин) | JavaPlugin с onEnable |
структура с методом build, который добавляет системы и ресурсы в приложение |
| system (система) | задача планировщика или обработчик события | функция, которую Bevy запускает по расписанию, например в FixedUpdate |
| resource (ресурс) | поле-одиночка сервера | одно значение на мир, читается через Res<T> или ResMut<T> |
| observer (наблюдатель) | слушатель события | функция, которая срабатывает при событии; пишется с параметром On<Event> |
| query (запрос) | поток (stream) по всем игрокам с фильтром | параметр системы, который обходит сущности с указанными компонентами |
&mut |
изменяемая ссылка | заем, который может менять значение; одновременно существует только один |
Clone |
clone() |
копия значения; плагину измерения она нужна, почему - сказано в шаге 2 |
| dimension world (мир измерения) | свой объект World на каждое измерение |
у каждого измерения свой мир, и плагин строится по одному разу в каждом |
Шаг 1: склонируйте репозиторий
Заголовок раздела «Шаг 1: склонируйте репозиторий»Склонируйте andreypfau/mcrs. В разделе README Quick start есть заметка про тулчейн и команды запуска.
Шаг 2: напишите плагин
Заголовок раздела «Шаг 2: напишите плагин»Плагин - это структура, которая реализует Plugin. Плагин ниже регистрирует ресурс Ticks, в котором хранится число, и одну систему. Система работает в FixedUpdate и прибавляет к числу единицу на каждом тике сервера.
#[derive(Resource, Default)]struct Ticks<const ALL: bool>(u32);
#[derive(Message)]struct Ticked;
#[derive(Clone)]struct CountTicks<const ALL: bool>;
impl<const ALL: bool> Plugin for CountTicks<ALL> { fn name(&self) -> &str { if ALL { "count every tick" } else { "count overworld ticks" } }
fn build(&self, app: &mut App) { app.init_resource::<Ticks<ALL>>().add_systems( FixedUpdate, |mut ticks: ResMut<Ticks<ALL>>, mut ticked: MessageWriter<Ticked>| { ticks.0 += 1; if ALL { ticked.write(Ticked); } }, ); app.add_message::<Ticked>(); }}crates/mcrs_minecraft_server/tests/it/dimension_plugins.rs:10-40
Код взят из тестового файла репозитория, где плагин считает тики в верхнем мире и в Незере. Чтобы запустить два варианта плагина, тест использует константный обобщенный параметр ALL. Читайте код как простой CountTicks и не обращайте внимания на ALL.
Плагин должен реализовать Clone, потому что сервер строит отдельную его копию для каждого мира измерения. В бинарнике сервера уже есть маленький плагин такой формы:
#[derive(Clone)]struct LogInsteadOfPanicPlugin;
impl Plugin for LogInsteadOfPanicPlugin { fn build(&self, app: &mut App) { app.insert_resource(FallbackErrorHandler(log_instead_of_panic)); }}Шаг 3: зарегистрируйте его через add_dimension_plugin
Заголовок раздела «Шаг 3: зарегистрируйте его через add_dimension_plugin»Функция serve() в src/main.rs уже регистрирует один плагин правильным способом. Добавьте свой на следующей строке.
fn serve() -> AppExit { let mut app = App::new(); app.insert_resource(FallbackErrorHandler(log_instead_of_panic)); app.add_dimension_plugin(LogInsteadOfPanicPlugin);Тест показывает оба вызова и то, что сервер из них строит:
#[test]fn dimension_plugins_run_in_every_world_or_in_the_world_of_their_key() { let mut app = host_app::make_host_app(); app.add_dimension_plugin(CountTicks::<true>) .add_dimension_plugin(Rate(1)) .add_dimension_plugin_to(OVERWORLD, SeesSpawn) .add_dimension_plugin_to(OVERWORLD, AddsCountTicks) .add_dimension_plugin_to(OVERWORLD, CountTicks::<false>) .add_dimension_plugin_to(OVERWORLD, Rate(2));crates/mcrs_minecraft_server/tests/it/dimension_plugins.rs:90-98
Шаг 4: запустите тест, который проверяет счет
Заголовок раздела «Шаг 4: запустите тест, который проверяет счет»Тестовый плагин ничего не печатает. Тест проверяет, что счетчик больше нуля в обоих мирах, через ticks::<true>(...) в том же файле. Команда ниже запускает этот тест, и тот же прием использует тест чата.4
cargo test -p mcrs_minecraft_server --test it dimension_plugins::Для этой страницы мы команду не запускали. В репозитории нет строки лога для этого плагина, поэтому ожидаемого лога здесь нет. Если он вам нужен, добавьте вызов tracing::info! в свою систему.
Тот же плагин в Paper
Заголовок раздела «Тот же плагин в Paper»Это Java-код для иллюстрации. Его нет в репозитории, и к mcrs он не относится.
public final class CountTicks extends JavaPlugin { private long ticks;
@Override public void onEnable() { getServer().getScheduler().runTaskTimer(this, () -> ticks++, 0L, 1L); }}Идея та же, а различий три. Плагин mcrs компилируется в программу сервера, тогда как плагин Paper поставляется в jar. Счетчик хранится как ресурс в мире, а в Paper он лежит в поле вашего класса. Задача здесь - система, которую планирует Bevy, и запускается она по одному разу в каждом мире измерения.
Чего плагинам пока не хватает
Заголовок раздела «Чего плагинам пока не хватает»Четыре вещи, которые могут понадобиться плагину, пока не готовы. Это публичные события, источники чанков, сохранение и измерения во время работы сервера.5678 Что еще не готово для плагинов, показывает страница Статус, а почему add_plugins не доходит до измерения, объясняет страница Измерения.
Дальше: Перехват чата берет первое публичное событие, чтобы отменить и переписать строку чата.
Footnotes
Заголовок раздела «Footnotes»-
Решения нет#321 Отслеживает подготовку крейтов протокола, сети и игровых данных к публикации. ↩
-
В работе#288 Планирует примеры как члены workspace в
examples/с тестом дерева зависимостей для каждого. ↩ -
Работает#280 Позволяет пользовательским плагинам работать в каждом мире измерения. ↩
-
Работает#636 Поднимает отменяемое событие для строки чата перед ее рассылкой. ↩
-
Частично#283 Отслеживает публичные события игрока, которые плагин может отменить. ↩
-
В работе#281 Планирует источник чанков как заменяемую точку расширения с пустым и плоским источниками. ↩
-
В работе#78 Отслеживает сохранение измененных чанков в файлы регионов. ↩
-
В работе#546 Планирует создание и удаление миров измерений на ходу и перенос игроков между ними. ↩
Источники и проверка
Проверено: 26.4-snapshot-7, 51d45c26a, 2026-11-09
Переведено с версии 51d45c26a