-
-
Notifications
You must be signed in to change notification settings - Fork 115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add documentation for event filters #379
Merged
Merged
Changes from 9 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
b64ab56
Start documentation for event filters
Deamon5550 b4b9aee
Add event-filters to index
Deamon5550 31beebf
Split Events page in several sections
Saladoc b44b112
Fix the reference on main class page
Saladoc d71df29
First pass at some documentation of Cause
Deamon5550 c3cd82a
Clean up a few things on the cause page
Deamon5550 5a36f56
Update event filter page for final event filters
Deamon5550 22b572c
Fix Dot Points
Inscrutable 7e631cf
Update listeners page after event filter changes
Deamon5550 020601a
Document @Before, @After, and @Named
Aaron1011 51dff61
Fix ending bold symbols
Aaron1011 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
============ | ||
Event Causes | ||
============ | ||
|
||
Events are great for attaching additional logic to game actions, but | ||
they have the drawback of providing next to no context as to what has **caused** that event to | ||
occur. The ``Cause`` object allows providing and receiving additional contextual information about | ||
the event. This contextual information can then used to modify the behavior of your event listener. | ||
|
||
For example, a world protection plugin needs information on what player has caused a | ||
ChangeBlockEvent to occur before they can decide if the event should be cancelled or not. | ||
Rather than go with the traditional route of creating a multitude of subevents for the different source | ||
conditions this information is instead provided in the ``Cause`` of the event. | ||
|
||
Any event which extends ``CauseTracked`` provides a ``Cause`` object which can be interogated | ||
for the information pertaining to why the event was fired. The Cause object can be retrived from | ||
an event by simply calling ``event.getCause()``. | ||
|
||
Retrieving objects from a Cause | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
Structurally, a ``Cause`` object contains a sequential list of objects. There are several methods of | ||
retrieving information from a Cause object which we will discuss here, for a more complete | ||
listing please see the javadocs **link**. | ||
|
||
.. note:: | ||
|
||
The objects within a cause are ordered such that the first object is the most immediate | ||
cause of the event, and subsequent objects are of decreasing importance and/or may only | ||
provide contextual information. | ||
|
||
``Cause#root()`` returns the first object within the cause, if it exists. This object is the most | ||
immediate or direct cause of the event. | ||
|
||
``Cause#first(Class)`` returns the first object in the cause chain whose type is either the | ||
same as or is a subtype of the given class. For example given a cause which contained a player | ||
followed by an entity ``[Player, Entity, ...]`` | ||
|
||
.. code-block:: java | ||
|
||
@Listener | ||
public void onEvent(ExampleCauseEvent event) { | ||
Cause cause = event.getCause(); // [Player, Entity] | ||
Optional<Player> firstPlayer = cause.first(Player.class); // 1 | ||
Optionml<Entity> firstEntity = cause.first(Entity.class); // 2 | ||
} | ||
|
||
Both optionals would contain the player object as it's type directly matched request for a | ||
Player type and it matched the request for an Entity type as Player is a subtype of Entity. | ||
|
||
``Cause#last(Class)`` is similar to *Cause#first(Class)* except it returns the last value in | ||
the cause chain matching the type. | ||
|
||
Continuing from the example above, if we instead changed it to call ``Cause.last(Class)`` the first | ||
optional would contain the player object still, but the second optional would now contain | ||
the entity that we passed in the second position of the cause. | ||
|
||
``Cause.has(Class)`` returns a boolean value and can be used to check if a cause chain | ||
contains any object matching the provided type. | ||
|
||
``Cause.all()`` simply returns all objects within the cause allowing more advanced handling. | ||
|
||
Named Causes | ||
~~~~~~~~~~~~ | ||
|
||
Sometimes the ordering of objects within the cause isn't enough to get the proper idea of what | ||
an object represents in relation to the event. This is where ``NamedCause`` comes in. Named | ||
causes provide a method for tagging objects within a cause with a **unique** name allowing them | ||
to be easily identified and requested. Some examples of use cases for named causes is the | ||
`Notifier` of a GrowBlockEvent or the `Source` of a DamageEntityEvent. | ||
|
||
**Retrieving a named entry from a cause** | ||
|
||
.. code-block:: java | ||
|
||
@Listener | ||
public void onGrow(GrowBlockEvent event) { | ||
Optional<Player> notifier = event.getCause().get(NamedCause.NOTIFIER); | ||
} | ||
|
||
This example makes use of ``Cause.get(String name)`` which can be used to retrive the object | ||
associated with a name if it is present within the cause chain. Additionally | ||
``Cause.getNamedCauses()`` provides a ``Map<String, Object>`` which can be used to find all | ||
present names and their associated objects. | ||
|
||
.. note:: | ||
|
||
Some common identifying names for ``NamedCause``\ s are present as static fields in the | ||
``NamedCause`` class. Identifiers which are specific to certain events can often be found | ||
as static fields on the event class, for example ``DamageEntityEvent.SOURCE``. | ||
|
||
Creating custom Causes | ||
~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
Creating a cause to use when firing an event is extremely easy. The hardest part is deciding | ||
what information to include in the cause. If you're firing an event from your plugin which is | ||
usually triggered through other means perhaps you want to include your plugin container so | ||
other plugins know that the event comes from your plugin. Or if you are firing the event on | ||
behalf of a player due to some action it's usually a good idea to include that player in | ||
the cause. | ||
|
||
.. note:: | ||
|
||
Cause objects are immutable therefore cannot be modified once created. | ||
|
||
In the simplest case where you don't want to provide any information you can simply use | ||
``Cause.of()`` to get a cause which contains no objects. | ||
|
||
Using ``Cause.of(Object...)`` or ``Cause.ofNullable(Object...)`` you can construct a cause | ||
from a series of objects. The objects will be added to the cause chain in the order that they | ||
are passed to the method, so the first object parameter will become the root cause. | ||
|
||
If you already have a cause object and would like to append some more objects to the | ||
chain you can use ``Cause.with(Object...)``. This constructs a new Cause object containing | ||
first the objects already present in the original cause, then followed by the additional | ||
objects that you provided. | ||
|
||
Finally if you wish to add an object to a cause with a defined named first call | ||
``NamedCause.of(String name, Object object)`` and then pass the returned ``NamedCause`` instance | ||
to the cause chain as you would a normal object. Simply calling ``Cause.of(name, object)`` | ||
will result in a cause chain containing two objects (the String name, and then the object), | ||
rather than one object with an attached name. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
====================== | ||
Creating Custom Events | ||
====================== | ||
|
||
You can write your own event classes and dispatch those events using the method described above. | ||
|
||
An event class must either implement the ``Event`` interface or extend the ``AbstractEvent`` class. | ||
|
||
If you want your event to be cancellable, the class must also implement ``Cancellable``. | ||
|
||
Example: Custom Event Class | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
.. code-block:: java | ||
|
||
import org.spongepowered.api.entity.player.Player; | ||
import org.spongepowered.api.event.impl.AbstractEvent; | ||
import org.spongepowered.api.event.Cancellable; | ||
|
||
public class PrivateMessageEvent extends AbstractEvent implements Cancellable { | ||
|
||
private boolean cancelled = false; | ||
|
||
private Player sender; | ||
private Player recipient; | ||
|
||
private String message; | ||
|
||
public Player getSender() { | ||
return sender; | ||
} | ||
|
||
public Player getRecipient() { | ||
return recipient; | ||
} | ||
|
||
public String getMessage() { | ||
return message; | ||
} | ||
|
||
@Override | ||
public boolean isCancelled() { | ||
return cancelled; | ||
} | ||
|
||
@Override | ||
public void setCancelled(boolean cancel) { | ||
cancelled = cancel; | ||
} | ||
|
||
public PrivateMessageEvent(Player sender, Player recipient, String message) { | ||
this.sender = sender; | ||
this.recipient = recipient; | ||
this.message = message; | ||
} | ||
} | ||
|
||
|
||
Example: Fire Custom Event | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
.. code-block:: java | ||
|
||
game.getEventManager().post(new PrivateMessageEvent(playerA, playerB, "Hello World!")); | ||
|
||
|
||
Example: Listen for Custom Event | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
.. code-block:: java | ||
|
||
import org.spongepowered.api.text.Texts; | ||
import org.spongepowered.api.text.chat.ChatTypes; | ||
|
||
@Listener | ||
public void onPrivateMessage(PrivateMessageEvent event) { | ||
if(event.getMessage().equals("hi i am from planetminecraft")) { | ||
event.setCancelled(true); | ||
return; | ||
} | ||
|
||
String senderName = event.getSender().getName(); | ||
event.getRecipient().sendMessage(ChatTypes.CHAT, Texts.of("PM from " + senderName + ": " + event.getMessage())); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
============= | ||
Event Filters | ||
============= | ||
|
||
Now that you've spent a bit of time working with events you've probably noticed that there are several preconditions that you | ||
very commonly check while writing an event listener. Event filters are a group of annotations that assist you by allowing you | ||
to automatically validate aspects of the event, and if the validation fails then your listener will not be called. This allows | ||
your listener to be dedicated to the logic of your handler, rather than the preconditions, resulting in cleaner code. | ||
|
||
Event filters come in two varieties, event type filters and parameter filters. | ||
|
||
Event type filters are method annotations that are applied to your listener method along with the ``@Listener`` annotation and | ||
provide several filters based on the type or state of the event. | ||
|
||
Parameter filters validate objects contained within the event such as the cause. They come in a further two varieties | ||
parameter sources and parameter filters. Each additional parameter must have one source annotation, and optionally may include | ||
any number of filter annotations. | ||
|
||
Event Type Filters | ||
================== | ||
|
||
**@Include/@Exclude** | ||
These two parameters are used in conjunction with listening for supertype events such as ``EntityEvent`` while narrowing the | ||
events that you receive to just a subset of the events extending the event you're listening for. | ||
|
||
For example: | ||
|
||
.. code-block:: java | ||
|
||
@Listener | ||
@Exclude(InteractBlockEvent.Primary.class) | ||
public void onInteract(InteractBlockEvent event) { | ||
// do something | ||
} | ||
|
||
This listener would normally be called for all events extending InteractBlockEvent. However, the ``Exclude`` annotation | ||
will prevent your listener from being called for the ``InteractBlockEvent.Primary`` event (leaving just the | ||
``InteractBlockEvent.Secondary`` event). | ||
|
||
An example with ``@Include`` could be: | ||
|
||
.. code-block:: java | ||
|
||
@Listener | ||
@Include({DamageEntityEvent.class, DestructEntityEvent.class}) | ||
public void onEvent(EntityEvent event) { | ||
// do something | ||
} | ||
|
||
This listener would normally be called for all EntityEvents, however the ``Include`` annotation narrows it to | ||
only recieve ``DamageEntityEvent`` and ``DestructEntityEvent``\ s. | ||
|
||
**@IsCancelled** | ||
This annotation allows filtering events by their cancellation state at the time that your event listener would normally be | ||
called. By default your event listener will not be called if the event has been cancelled by a previous event listener. | ||
However you can change this behavior to one of three states depending on the ``Tristate`` value in the ``@IsCancelled`` | ||
annotation. | ||
|
||
- ``Tristate.FALSE`` is the default behavior if the ``IsCancelled`` annotation is not present, and will not call your | ||
listener if the event has been cancelled. | ||
- ``Tristate.UNDEFINED`` will cause your listener to be called regardless of the cancellation state of the event. | ||
- ``Tristate.TRUE`` will cause your listener to be called only if the event has been cancelled by a previous event listener. | ||
|
||
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.** | ||
|
||
.. code-block:: java | ||
|
||
@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<?>)``. | ||
|
||
.. code-block:: java | ||
|
||
@Listener | ||
public void onInteract(InteractBlockEvent.Secondary event, @Last Player player) { | ||
// do something | ||
} | ||
|
||
**@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#all(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.** | ||
|
||
.. code-block:: java | ||
|
||
@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. | ||
|
||
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 | ||
ype. This validation is equivalent to ``DataHolder#supports(Class<? extends DataManipulator>)``. | ||
|
||
**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``.** | ||
|
||
.. code-block:: java | ||
|
||
@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``. This validation is equivalent to | ||
``DataHolder#hasData(Class<? extends 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.** | ||
|
||
.. code-block:: java | ||
|
||
@Listener | ||
public void onInteract(InteractBlockEvent.Secondary event, @First @Has(FlyingData.class) Entity entity) { | ||
// do something | ||
} | ||
|
||
.. note:: | ||
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. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe remove the brackets?