Перейти к содержимому

Первый плагин: счетчик тиков

Меняется. Следите: #280, #288, #321

В этом туториале вы добавите плагин, который считает тики сервера. В 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 на каждое измерение у каждого измерения свой мир, и плагин строится по одному разу в каждом

Склонируйте andreypfau/mcrs. В разделе README Quick start есть заметка про тулчейн и команды запуска.

Плагин - это структура, которая реализует 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, потому что сервер строит отдельную его копию для каждого мира измерения. В бинарнике сервера уже есть маленький плагин такой формы:

Самый маленький Clone-плагин в src/main.rs
#[derive(Clone)]
struct LogInsteadOfPanicPlugin;
impl Plugin for LogInsteadOfPanicPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(FallbackErrorHandler(log_instead_of_panic));
}
}

src/main.rs:129-136

Шаг 3: зарегистрируйте его через add_dimension_plugin

Заголовок раздела «Шаг 3: зарегистрируйте его через add_dimension_plugin»

Функция serve() в src/main.rs уже регистрирует один плагин правильным способом. Добавьте свой на следующей строке.

Где serve() регистрирует плагин измерения
fn serve() -> AppExit {
let mut app = App::new();
app.insert_resource(FallbackErrorHandler(log_instead_of_panic));
app.add_dimension_plugin(LogInsteadOfPanicPlugin);

src/main.rs:43-46

Тест показывает оба вызова и то, что сервер из них строит:

Один плагин в каждом мире и один только в верхнем
#[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! в свою систему.

Это Java-код для иллюстрации. Его нет в репозитории, и к mcrs он не относится.

public final class CountTicks extends JavaPlugin {
private long ticks;
@Override
public void onEnable() {
getServer().getScheduler().runTaskTimer(this, () -> ticks++, 0L, 1L);
}
}

Четыре вещи, которые могут понадобиться плагину, пока не готовы. Это публичные события, источники чанков, сохранение и измерения во время работы сервера.5678 Что еще не готово для плагинов, показывает страница Статус, а почему add_plugins не доходит до измерения, объясняет страница Измерения.

Дальше: Перехват чата берет первое публичное событие, чтобы отменить и переписать строку чата.

  1. Решения нет#321 Отслеживает подготовку крейтов протокола, сети и игровых данных к публикации. ↩

  2. В работе#288 Планирует примеры как члены workspace в examples/ с тестом дерева зависимостей для каждого. ↩

  3. Работает#280 Позволяет пользовательским плагинам работать в каждом мире измерения. ↩

  4. Работает#636 Поднимает отменяемое событие для строки чата перед ее рассылкой. ↩

  5. Частично#283 Отслеживает публичные события игрока, которые плагин может отменить. ↩

  6. В работе#281 Планирует источник чанков как заменяемую точку расширения с пустым и плоским источниками. ↩

  7. В работе#78 Отслеживает сохранение измененных чанков в файлы регионов. ↩

  8. В работе#546 Планирует создание и удаление миров измерений на ходу и перенос игроков между ними. ↩

Источники и проверка

Проверено: 26.4-snapshot-7, 51d45c26a, 2026-11-09

Переведено с версии 51d45c26a

Следить: #280, #288, #321