Фильтры событий

Теперь, когда вы потратили немного времени на работу с событиями, вы, вероятно, заметили, что есть несколько предварительных условий, которые вы обычно проверяете при написании слушателя событий. Фильтры событий - это группа аннотаций, которые помогают вам автоматически проверять аспекты события, и если проверка не успешна, ваш слушатель не будет вызываться. Это позволяет вашему слушателю быть посвященным логике вашего обработчика, а не предварительным условиям, в результате чего получается более чистый код.

Фильтры событий бывают двух типов: фильтры типов событий и фильтры параметров.

Фильтры типов события - это аннотации методов, которые применяются к вашему методу слушателя, а также аннотация Listener и предоставляют несколько фильтров, основанных на типе или состоянии события.

Параметр фильтров проверяет объекты, содержащиеся в событии, такие как cause. Они входят в еще две разновидности: параметр источника и параметр фильтра. Каждый дополнительный параметр должен иметь одну аннотацию источника и необязательно может включать любое количество аннотаций фильтра.

Фильтры типа событий

** @ Include / @ Exclude ** Эти два параметра используются в сочетании с прослушиванием событий супертипа, таких как AffectEntityEvent, что бы сузить круг событий, которые вы получаете как подмножество событий расширяющее прослушиваемое событие.

Например:

@Listener
@Exclude(InteractBlockEvent.Primary.class)
public void onInteract(InteractBlockEvent event) {
    // do something
}

Этот слушатель обычно будет вызван для всех событий, расширяющих InteractBlockEvent. Тем не менее, аннотация Exclude не позволит вашему слушателю быть вызванным для события InteractBlockEvent.Primary (оставив только событие InteractBlockEvent.Secondary).

Например с Include может быть:

@Listener
@Include({DamageEntityEvent.class, DestructEntityEvent.class})
public void onEvent(EntityEvent event) {
    // do something
}

Этот слушатель обычно вызывается для всех EntityEvents, однако аннотация Include сужает его только для получения DamageEntityEvent и DestructEntityEvents.

@IsCancelled Эта аннотация позволяет фильтровать события по состоянию их отмены в то время, когда обычно вызывается ваш слушатель событий. По умолчанию ваш слушатель событий не будет вызываться, если событие было отменено предыдущим слушателем событий. Однако вы можете изменить это поведение на одно из трех состояний в зависимости от значения Tristate в аннотации IsCancelled.

  • Tristate.FALSE является поведением по умолчанию, если аннотация IsCancelled отсутствует и ваш слушатель не будет вызываться когда событие было отменено.

  • Tristate.UNDEFINED приведет к вызову вашего слушателя не смотря на состояние отмены события.

  • Tristate.TRUE вызовет слушателя только в том случае, если событие было отменено предыдущим слушателем событий.

Parameter Filters

Parameter filters allow you to filter based on objects within the event. These annotations come in two types, sources and filters. Each additional parameter for your listener method, beyond the normal event parameter, requires exactly one source annotation and may optionally have any number of filter annotations.

Parameter Source Annotations

Parameter source annotations tell the event system where it should look for this parameter’s value. This may be in the events cause or in a member of the event object itself.

@First This parameter source annotation tells the event system to find the first object in the event’s cause which matches the type of your parameter (This is equivalent to Cause#first(Class)). If no object is found matching this parameter then your listener is not called.

In this example your listener will only be called if there is a player in the event’s cause, and the player parameter will be set to the first player present the cause.

@Listener
public void onInteract(InteractBlockEvent.Secondary event, @First Player player) {
    // do something
}

@Last This is similar to @First however it instead makes a call to Cause#last(Class).

@Listener
public void onInteract(InteractBlockEvent.Secondary event, @Last Player player) {
    // do something
}

@Before This parameter source annotation tells the event system to find the object before the one of the type specified by the annotation parameter (This is equivalent to Cause#before(Class)). Additionally, the found object must match the type of the parameter. If no object is found meeting these criteria, then your listener is not called.

In this example your listener will only be called if there is a player located before a plugin container in the event’s cause. The player parameter will be set to that player.

@Listener
public void onInteract(InteractBlockEvent.Secondary event, @Before(PluginContainer.class) Player player) {
    // do something
}

@After This is similar to @Before, but it instead uses Cause#after(Class).

@All This parameter source annotation requires that the annotated parameter be an array type. The returned array will be equivalent to the contents of calling Cause#allOf(Class). By default if the returned array would be empty then the validation fails however this can be disabled by setting ignoreEmpty=false.

In this example your listener will always be called, although the players array may be empty if the event’s cause contained no players.

@Listener
public void onInteract(InteractBlockEvent.Secondary event, @All(ignoreEmpty=false) Player[] players) {
    // do something
}

@Root This parameter source annotation will fetch the root object of the cause, equivalent to Cause#root(). It also performs an additional check that the type of the root object matches the type of your parameter.

@Named This parameter source annotation tells the event system to find the object with the name specified by the annotation parameter (This is equivalent to Cause#get(String, Class)). Additionally, the found object must match the type of the parameter. If no object is found meeting these criteria, then your listener is not called.

In this example your listener will only be called if there is a player associated with the name NamedCause.OWNER . The player parameter will be set to that player.

@Listener
public void onInteract(InteractBlockEvent.Secondary event, @Named(NamedCause.OWNER) Player player) {
    // do something
}

@Getter This parameter source annotation will fetch a getter on the event with the specified name. If the specified getter returns an Optional, @Getter will automatically unwrap the Optional.

In this example, we attempt to retrieve the value of getUseItemResult using the @Getter annotation.

@Listener
public void onInteract(InteractBlockEvent.Secondary event, @Getter("getUseItemResult") Tristate result) {
    // do something
}

Parameter Filter Annotations

Parameter filter annotations add additional validation to objects returned from parameter source annotations. As with all event filters if any of these validations fail then your listener will not be called.

@Supports This parameter filter may be applied to any parameter type which is a DataHolder. It takes a class extending DataManipulator as its parameter and validates that the annotated DataHolder supports the given DataManipulator. This validation is equivalent to CompositeValueStore#supports(Class<? extends H>).

In this example the listener will be called only if there is an entity in the event’s cause, and if that entity supports the data manipulator FlyingData.

@Listener
public void onInteract(InteractBlockEvent.Secondary event, @First @Supports(FlyingData.class) Entity entity) {
    // do something
}

@Has This parameter filter is similar to the @Supports parameter filter except that it additionally validates that the DataHolder contains an instance of the given DataManipulator.

In this example the listener will be called only if there is an entity in the event’s cause, and if that entity has an instance of FlyingData available.

@Listener
public void onInteract(InteractBlockEvent.Secondary event, @First @Has(FlyingData.class) Entity entity) {
    // do something
}

Примечание

Both @Has and @Supports have an optional parameter inverse which can be set to cause validation to fail if the does have, or does support, the target DataManipulator.