Table of Contents

Пользовательские события(ивенты)

Fabric API предоставляет систему, которая позволяет модам реагировать на события, происходящие в игре. События - это перехватчики, которые удовлетворяют общим случаям использования и/или обеспечивают повышенную совместимость и производительность между модами, которые подключаются к одним и тем же областям кода. Использование событий часто заменяет использование миксинов. Fabric API предоставляет события для важных областей в кодовой базе Minecraft, в которых могут быть заинтересованы несколько моддеров. В некоторых областях нет хуков, поэтому вы можете выбрать использование миксина или создать свое собственное событие.

В этом туториале мы рассмотрим создание события, которое запускается при стрижке овец. Процесс создания события заключается в:

Создание интерфейса обратного вызова

Интерфейс обратного вызова описывает, что должно быть реализовано слушателями событий, которые будут прослушивать ваше событие. Интерфейс обратного вызова также описывает, как событие будет вызываться из нашего миксина. Обычно объект события помещается в качестве поля в интерфейсе обратного вызова, который будет идентифицировать наше фактическое событие.

Для нашей реализации события мы выберем использовать событие с поддержкой массива. Массив будет содержать все прослушиватели событий, которые прослушивают событие. Наша реализация будет вызывать прослушиватели событий по порядку до тех пор, пока один из них не вернет ActionResult.PASS. то означает, что слушатель может сказать “отменить это”, “одобрить это” или “мне все равно, оставьте это следующему слушателю событий”, используя его возвращаемое значение. Использование ActionResult в качестве возвращаемого значения - это обычный способ заставить обработчик событий взаимодействовать таким образом.

Вам нужно будет создать интерфейс, который имеет экземпляр Event и метод для реализации ответа. Базовая настройка для нашего обратного вызова стрижки овец - это:

  1. public interface SheepShearCallback {
  2.  
  3. Event<SheepShearCallback> EVENT = EventFactory.createArrayBacked(SheepShearCallback.class,
  4. (listeners) -> (player, sheep) -> {
  5. for (SheepShearCallback listener : listeners) {
  6. ActionResult result = listener.interact(player, sheep);
  7.  
  8. if(result != ActionResult.PASS) {
  9. return result;
  10. }
  11. }
  12.  
  13. return ActionResult.PASS;
  14. });
  15.  
  16. ActionResult interact(PlayerEntity player, SheepEntity sheep);
  17. }

Давайте рассмотрим это более подробно. Когда вызывается вызывающий, мы перебираем всех слушателей:

(listeners) -> (player, sheep) -> {
    for (SheepShearCallback listener : listeners) {

Затем мы вызываем наш метод (в данном случае interact) для слушателя, чтобы получить его ответ:

ActionResult result = listener.interact(player, sheep);

Если слушатель говорит, что мы должны отменить (ActionResult.FAIL) или полностью завершить (ActionResult.SUCCESS), обратный вызов возвращает результат и завершает цикл. ActionResult.PASS переходит к следующему слушателю и в большинстве случаев должно привести к успеху, если больше не зарегистрировано слушателей:

// ....
    if(result != ActionResult.PASS) {
        return result;
    }
}
 
return ActionResult.PASS;

В Fabric API, мы добавляем комментарии Javadoc в начало классов обратного вызова, чтобы документировать, что делает каждый ActionResult. В нашем случае это может быть:

/**
 * Callback for shearing a sheep.
 * Called before the sheep is sheared, items are dropped, and items are damaged.
 * Upon return:
 * - SUCCESS cancels further processing and continues with normal shearing behavior.
 * - PASS falls back to further processing and defaults to SUCCESS if no other listeners are available
 * - FAIL cancels further processing and does not shear the sheep.
/**

Запуск события из миксина

Теперь у нас есть базовый скелет события, но нам нужно его запустить. Поскольку мы хотим, чтобы событие вызывалось, когда игрок пытается подстричь овцу, мы вызываем средство вызова события в SheepEntity#interactMob при вызове dropItems() (т.е. овец можно стричь, и игрок держит ножницы):

@Mixin(SheepEntity.class)
public class SheepShearMixin {
 
    @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/SheepEntity;dropItems()V"), method = "interactMob", cancellable = true)
    private void onShear(final PlayerEntity player, final Hand hand, final CallbackInfoReturnable<Boolean> info) {
        ActionResult result = SheepShearCallback.EVENT.invoker().interact(player, (SheepEntity) (Object) this);
 
        if(result == ActionResult.FAIL) {
            info.cancel();
        }
    }
}

В этом простом миксине мы вызываем средство вызова события (SheepShearCallback.EVENT.invoker().[…]), оторый затем вызывает всех активных слушателей, чтобы посмотреть, что он должен делать. На основе этого он возвращает ActionResult, и если результат - FAIL(Неудача), мы не стригем овец, не роняем предметы, не повреждаем предметы игрока (info.cancel();). Обязательно зарегистрируйте свой миксин в вашем файле mixins.json!

Тестовое событие со слушателем

Теперь нам нужно протестировать наше событие. Вы можете зарегистрировать прослушиватель в своем методе инициализации (или в других областях, если предпочитаете) и добавить туда пользовательскую логику. Вот пример, который бросает алмаз вместо шерсти к ногам овцы:

SheepShearCallback.EVENT.register((player, sheep) -> {
    sheep.setSheared(true);
 
    // создание алмаза в виде дропа в позиции овцы
    ItemStack stack = new ItemStack(Items.DIAMOND);
    ItemEntity itemEntity = new ItemEntity(player.world, sheep.x, sheep.y, sheep.z, stack);
    player.world.spawnEntity(itemEntity);
 
    return ActionResult.FAIL;
});

Обратите внимание, что это событие также настраивает стрижку овец вручную, так как обычно оно отменяется, если мы возвращаем FAIL. Если вам не нужно отменять событие, убедитесь, что вы вернули PASS , чтобы другие слушатели также могли работать. Несоблюдение этих “невысказанных правил” может привести к появлению разгневанных моддеров на вашем пороге.

Если вы зайдёте в игру и подстрижёте овцу, вместо шерсти должен упасть алмаз.