User Tools

Site Tools


Sidebar

Setup

Basics

Items and Item Groups

Blocks and Block Entities

Entities

World Generation

Miscellaneous

Advanced

Documentation

Examples


Fabric Сontributors

If you'd like to contribute to Fabric, you might be interested in these links:


Extremely Strange People


Wiki Meta

  • Wiki Meta - Starting point for contributing to the wiki
  • Wiki Agenda - See what is on the current agenda, and what other contributors are currently working on.
tutorial:events

Custom Events

Fabric API provides an event system that allows mods to react to events in the game. The purpose of adding events is to add hooks that satisfy common use cases and/or providing enhanced compatibility and performance between mods that hook into the same areas of the code. The use of events often substitutes the use of mixins. Fabric API provides events for some use cases, but not all, meaning you'll have to use other methods like Mixins to add a hook. You can then choose to implement the hook as an event.

In this tutorial, we'll look at creating your own event which is triggered when sheep are sheared. The process of creating an event is:

  • creating the event callback interface
  • triggering the event from a Mixin
  • creating a test implementation

Creating a Callback Interface

The callback interface describes what must be implemented by event listeners that will listen to your event. The callback interface also describes how the event will be called from our mixin. It is conventional to place an Event object as a field in the callback interface, which will identify our actual event.

For our Event implementation we will choose to use an array backed event. The array will contain all event listeners that are listening to the event. Our implementation will call the event listeners in order until one of them does not return ActionResult.PASS. This means that a listener can say “cancel this”, “approve this” or “don't care, leave it to the next event listener” using its return value. Using ActionResult as a return value is a conventional way to make event handlers cooperate in this fashion.

You'll need to create an interface that has an Event instance and method for response implementation. A basic setup for our sheep shear callback is:

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

Let's look at this more in depth. When the invoker is called, we iterate over all listeners:

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

We then call our method (in this case, interact) on the listener to get its response:

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

If the listener says we have to cancel (ActionResult.FAIL) or fully finish (ActionResult.SUCCESS), the callback returns the result and finishes the loop. ActionResult.PASS moves on to the next listener, and in most cases should result in success if there are no more listeners registered:

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

In the Fabric API, we add Javadoc comments to the top of callback classes to document what each ActionResult does. In our case, it might be:

/**
 * 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.
/**

Triggering the event from a Mixin

We now have the basic event skeleton, but we need to trigger it. Because we want to have the event called when a player attempts to shear a sheep, we call the event invoker in SheepEntity#interactMob when dropItems() is called (ie. sheep can be sheared and player is holding shears):

@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();
        }
    }
}

In this simple mixin, we call the event invoker (SheepShearCallback.EVENT.invoker().[…]), which then calls all active listeners to see what it should do. It returns an ActionResult based on this, and if the result is FAIL, we don't shear the sheep, drop items, or damage the player's item (info.cancel();). Make sure to register your mixin in your mixins.json file!

Testing Event with a Listener

Now we need to test our event. You can register a listener in your initialization method (or other areas if you prefer) and add custom logic there. Here's an example that drops a diamond instead of wool at the sheep's feet:

SheepShearCallback.EVENT.register((player, sheep) ->
{
    sheep.setSheared(true);
 
    // create diamond item entity at sheep position
    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;
});

Note that this event also sets the sheep to be sheared manually, as it is normally canceled if we return FAIL. If you don't need to cancel the event, make sure you return PASS so other listeners are allowed to operate as well. Failing to follow these “not spoken rules” may result in angry modders on your doorstep.

If you enter into your game and shear a sheep, a diamond should drop instead of wool.

tutorial/events.txt · Last modified: 2019/07/03 21:07 by jamieswhiteshirt