Minestom/src/main/java/net/minestom/server/event/EventNode.java

358 lines
14 KiB
Java
Raw Normal View History

2021-06-02 08:17:21 +02:00
package net.minestom.server.event;
import net.minestom.server.event.trait.CancellableEvent;
2021-06-03 21:39:38 +02:00
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagReadable;
2021-08-16 23:42:02 +02:00
import org.jetbrains.annotations.ApiStatus;
2021-06-08 14:15:30 +02:00
import org.jetbrains.annotations.Contract;
2021-06-02 10:19:23 +02:00
import org.jetbrains.annotations.NotNull;
2021-06-03 21:39:38 +02:00
import org.jetbrains.annotations.Nullable;
2021-06-02 08:17:21 +02:00
2021-08-16 23:42:02 +02:00
import java.util.List;
import java.util.Set;
import java.util.function.BiPredicate;
2021-06-03 20:46:11 +02:00
import java.util.function.Consumer;
2021-06-02 08:17:21 +02:00
import java.util.function.Predicate;
2021-06-08 22:58:48 +02:00
/**
* Represents a single node in an event graph.
* <p>
* A node may contain any number of children and/or listeners. When an event is called,
* the node will filter it based on the parameters given at creation and then propagate
* it down to child nodes and listeners if it passes.
*
* @param <T> The event type accepted by this node
*/
2021-10-22 01:55:55 +02:00
public sealed interface EventNode<T extends Event> permits EventNodeImpl {
2021-06-02 08:17:21 +02:00
2021-06-08 22:58:48 +02:00
/**
* Creates an event node which accepts any event type with no filtering.
*
* @param name The name of the node
* @return An event node with no filtering
*/
2021-06-08 14:15:30 +02:00
@Contract(value = "_ -> new", pure = true)
2021-08-16 23:42:02 +02:00
static @NotNull EventNode<Event> all(@NotNull String name) {
2021-06-04 04:10:13 +02:00
return type(name, EventFilter.ALL);
2021-06-02 11:29:16 +02:00
}
2021-06-08 22:58:48 +02:00
/**
* Creates an event node which accepts any event of the given type. The type is provided
* by the {@link EventFilter}.
* <p>
* For example, you could create an event filter which only accepts player events with the following
* <p><pre>
* var playerEventNode = EventNode.type("demo", EventFilter.PLAYER);
* </pre>
*
2021-06-09 06:52:00 +02:00
* @param name The name of the event node
2021-06-08 22:58:48 +02:00
* @param filter The event type filter to apply
2021-06-09 06:52:00 +02:00
* @param <E> The resulting event type of the node
2021-06-08 22:58:48 +02:00
* @return A node with just an event type filter
*/
2021-06-08 14:15:30 +02:00
@Contract(value = "_, _ -> new", pure = true)
2021-08-16 23:42:02 +02:00
static <E extends Event, V> @NotNull EventNode<E> type(@NotNull String name,
@NotNull EventFilter<E, V> filter) {
2021-06-04 04:39:45 +02:00
return create(name, filter, null);
2021-06-03 21:59:27 +02:00
}
2021-06-08 22:58:48 +02:00
/**
* Creates an event node which accepts any event of the given type which passes
* the provided condition. The condition is based on the event object itself.
* <p>
* For example, you could create an event filter which only accepts player events
* where the player is in the pos x/z quadrant of the world.
2021-06-10 14:41:44 +02:00
* <p><pre>{@code
2021-06-08 22:58:48 +02:00
* var playerInPosXZNode = EventNode.event("abc", EventFilter.PLAYER, event -> {
* var position = event.getPlayer().getPosition();
* return position.getX() > 0 && position.getZ() > 0;
* });
2021-06-10 14:41:44 +02:00
* }</pre>
2021-06-08 22:58:48 +02:00
*
2021-06-09 06:52:00 +02:00
* @param name The name of the event node
* @param filter The event type filter to apply
2021-06-08 22:58:48 +02:00
* @param predicate The event condition
2021-06-09 06:52:00 +02:00
* @param <E> The resulting event type of the node
2021-06-08 22:58:48 +02:00
* @return A node with an event type filter as well as a condition on the event.
*/
2021-06-08 14:15:30 +02:00
@Contract(value = "_, _, _ -> new", pure = true)
2021-08-16 23:42:02 +02:00
static <E extends Event, V> @NotNull EventNode<E> event(@NotNull String name,
@NotNull EventFilter<E, V> filter,
@NotNull Predicate<E> predicate) {
2021-06-04 04:39:45 +02:00
return create(name, filter, (e, h) -> predicate.test(e));
}
2021-06-08 22:58:48 +02:00
/**
* Creates an event node which accepts any event of the given type which passes
* the provided condition. The condition is based on the event object as well as
* the event handler type defined in the {@link EventFilter}.
* <p>
* For example, you could create an event filter which only accepts player events
* where the player is in the pos x/z quadrant of the world.
2021-06-10 14:41:44 +02:00
* <p><pre>{@code
2021-06-08 22:58:48 +02:00
* var playerInPosXZNode = EventNode.type("abc", EventFilter.PLAYER, (event, player) -> {
* var position = player.getPosition();
* return position.getX() > 0 && position.getZ() > 0;
* });
2021-06-10 14:41:44 +02:00
* }</pre>
2021-06-08 22:58:48 +02:00
*
2021-06-09 06:52:00 +02:00
* @param name The name of the event node
* @param filter The event type filter to apply
2021-06-08 22:58:48 +02:00
* @param predicate The event condition
2021-06-09 06:52:00 +02:00
* @param <E> The resulting event type of the node
* @param <V> The handler type of the event filter
2021-06-08 22:58:48 +02:00
* @return A node with an event type filter as well as a condition on the event.
*/
@Contract(value = "_, _, _ -> new", pure = true)
2021-08-16 23:42:02 +02:00
static <E extends Event, V> @NotNull EventNode<E> type(@NotNull String name,
@NotNull EventFilter<E, V> filter,
@NotNull BiPredicate<E, V> predicate) {
2021-06-08 22:58:48 +02:00
return create(name, filter, predicate);
}
/**
* Creates an event node which accepts any event of the given type which passes
* the provided condition. The condition is based on the event handler defined
* by the {@link EventFilter}.
* <p>
* For example, you could create an event filter which only accepts player events
* where the player is in creative mode.
* <p><pre>
* var playerIsCreative = EventNode.value("abc", EventFilter.PLAYER, Player::isCreative);
* </pre>
*
2021-06-09 06:52:00 +02:00
* @param name The name of the event node
* @param filter The event type filter to apply
2021-06-08 22:58:48 +02:00
* @param predicate The event condition
2021-06-09 06:52:00 +02:00
* @param <E> The resulting event type of the node
* @param <V> The handler type of the event filter
2021-06-08 22:58:48 +02:00
* @return A node with an event type filter as well as a condition on the event.
*/
2021-06-08 14:15:30 +02:00
@Contract(value = "_, _, _ -> new", pure = true)
2021-08-16 23:42:02 +02:00
static <E extends Event, V> @NotNull EventNode<E> value(@NotNull String name,
@NotNull EventFilter<E, V> filter,
@NotNull Predicate<V> predicate) {
2021-06-04 04:39:45 +02:00
return create(name, filter, (e, h) -> predicate.test(h));
}
2021-06-08 22:58:48 +02:00
/**
* Creates an event node which accepts any event of the given type which has a handler who
* has the given tag.
2021-06-09 06:52:00 +02:00
* <p>
2021-06-08 22:58:48 +02:00
* The {@link EventFilter}'s resulting event type must be {@link TagReadable}.
*
2021-06-09 06:52:00 +02:00
* @param name The name of the event node
2021-06-08 22:58:48 +02:00
* @param filter The event type filter to apply
2021-06-09 06:52:00 +02:00
* @param tag The tag which must be contained on the event handler
* @param <E> The resulting event type of the node
2021-06-08 22:58:48 +02:00
* @return A node with an event type filter as well as a handler with the provided tag
*/
2021-06-08 14:15:30 +02:00
@Contract(value = "_, _, _ -> new", pure = true)
2021-08-16 23:42:02 +02:00
static <E extends Event> @NotNull EventNode<E> tag(@NotNull String name,
@NotNull EventFilter<E, ? extends TagReadable> filter,
@NotNull Tag<?> tag) {
2021-06-04 04:39:45 +02:00
return create(name, filter, (e, h) -> h.hasTag(tag));
2021-06-03 21:39:38 +02:00
}
2021-06-08 22:58:48 +02:00
/**
* Creates an event node which accepts any event of the given type which has a handler who
* has an applicable tag. An applicable tag means that it passes the given condition.
*
2021-06-09 06:52:00 +02:00
* @param name The name of the event node
* @param filter The event type filter to apply
* @param tag The tag which must be contained on the event handler
2021-06-08 22:58:48 +02:00
* @param consumer The condition to test against the tag, if it exists.
2021-06-09 06:52:00 +02:00
* @param <E> The resulting event type of the node
2021-06-08 22:58:48 +02:00
* @return A node with an event type filter as well as a handler with the provided tag
*/
2021-06-08 14:15:30 +02:00
@Contract(value = "_, _, _, _ -> new", pure = true)
2021-08-16 23:42:02 +02:00
static <E extends Event, V> @NotNull EventNode<E> tag(@NotNull String name,
@NotNull EventFilter<E, ? extends TagReadable> filter,
@NotNull Tag<V> tag,
@NotNull Predicate<@Nullable V> consumer) {
2021-06-04 04:39:45 +02:00
return create(name, filter, (e, h) -> consumer.test(h.getTag(tag)));
}
private static <E extends Event, V> EventNode<E> create(@NotNull String name,
@NotNull EventFilter<E, V> filter,
@Nullable BiPredicate<E, V> predicate) {
2021-08-16 23:42:02 +02:00
//noinspection unchecked
return new EventNodeImpl<>(name, filter, predicate != null ? (e, o) -> predicate.test(e, (V) o) : null);
}
2021-06-02 19:50:23 +02:00
2021-06-08 22:58:48 +02:00
/**
2021-08-20 05:21:53 +02:00
* Calls an event starting from this node.
2021-06-08 22:58:48 +02:00
*
2021-08-20 05:21:53 +02:00
* @param event the event to call
2021-06-08 22:58:48 +02:00
*/
2021-08-19 06:45:23 +02:00
default void call(@NotNull T event) {
2021-08-20 05:21:53 +02:00
//noinspection unchecked
getHandle((Class<T>) event.getClass()).call(event);
2021-08-19 06:45:23 +02:00
}
2021-08-20 05:21:53 +02:00
/**
* Gets the handle of an event type.
*
* @param handleType the handle type
* @param <E> the event type
2021-08-22 05:08:46 +02:00
* @return the handle linked to {@code handleType}
2021-08-20 05:21:53 +02:00
*/
2021-08-24 15:25:11 +02:00
@ApiStatus.Experimental
2021-08-20 05:21:53 +02:00
<E extends T> @NotNull ListenerHandle<E> getHandle(@NotNull Class<E> handleType);
2021-06-08 22:58:48 +02:00
/**
* Execute a cancellable event with a callback to execute if the event is successful.
* Event conditions and propagation is the same as {@link #call(Event)}.
*
2021-06-09 06:52:00 +02:00
* @param event The event to execute
2021-06-08 22:58:48 +02:00
* @param successCallback A callback if the event is not cancelled
*/
2021-08-16 23:42:02 +02:00
default void callCancellable(@NotNull T event, @NotNull Runnable successCallback) {
2021-06-04 03:48:51 +02:00
call(event);
if (!(event instanceof CancellableEvent) || !((CancellableEvent) event).isCancelled()) {
successCallback.run();
}
}
2021-06-08 14:15:30 +02:00
@Contract(pure = true)
2021-08-16 23:42:02 +02:00
@NotNull Class<T> getEventType();
2021-06-08 14:15:30 +02:00
@Contract(pure = true)
2021-08-16 23:42:02 +02:00
@NotNull String getName();
@Contract(pure = true)
int getPriority();
2021-06-08 13:47:10 +02:00
2021-06-08 14:15:30 +02:00
@Contract(value = "_ -> this")
2021-08-16 23:42:02 +02:00
@NotNull EventNode<T> setPriority(int priority);
2021-06-08 13:47:10 +02:00
2021-06-08 14:15:30 +02:00
@Contract(pure = true)
2021-08-16 23:42:02 +02:00
@Nullable EventNode<? super T> getParent();
2021-06-04 03:48:51 +02:00
2021-06-08 22:58:48 +02:00
/**
* Returns an unmodifiable view of the children in this node.
*
* @see #addChild(EventNode)
* @see #removeChild(EventNode)
*/
2021-06-08 14:15:30 +02:00
@Contract(pure = true)
2021-08-16 23:42:02 +02:00
@NotNull Set<@NotNull EventNode<T>> getChildren();
2021-06-04 03:48:51 +02:00
2021-06-08 22:58:48 +02:00
/**
* Locates all child nodes with the given name and event type recursively starting at this node.
*
2021-06-09 06:52:00 +02:00
* @param name The event node name to filter for
2021-06-08 22:58:48 +02:00
* @param eventType The event node type to filter for
* @return All matching event nodes
*/
2021-06-08 16:55:21 +02:00
@Contract(pure = true)
2021-08-16 23:42:02 +02:00
<E extends T> @NotNull List<EventNode<E>> findChildren(@NotNull String name, Class<E> eventType);
2021-06-04 03:48:51 +02:00
2021-06-08 22:58:48 +02:00
/**
* Locates all child nodes with the given name and event type recursively starting at this node.
*
* @param name The event name to filter for
* @return All matching event nodes
*/
2021-06-08 16:55:21 +02:00
@Contract(pure = true)
2021-08-16 23:42:02 +02:00
default @NotNull List<EventNode<T>> findChildren(@NotNull String name) {
return findChildren(name, getEventType());
2021-06-04 03:48:51 +02:00
}
2021-06-08 22:58:48 +02:00
/**
* Replaces all children matching the given name and type recursively starting from this node.
* <p>
* Node: The callee may not be replaced by this call.
*
2021-06-09 06:52:00 +02:00
* @param name The event name to filter for
2021-06-08 22:58:48 +02:00
* @param eventType The event node type to filter for
* @param eventNode The replacement node
*/
2021-08-16 23:42:02 +02:00
<E extends T> void replaceChildren(@NotNull String name, @NotNull Class<E> eventType, @NotNull EventNode<E> eventNode);
2021-06-08 22:58:48 +02:00
/**
* Replaces all children matching the given name and type recursively starting from this node.
* <p>
* Node: The callee may not be replaced by this call.
*
2021-06-09 06:52:00 +02:00
* @param name The node name to filter for
2021-06-08 22:58:48 +02:00
* @param eventNode The replacement node
*/
2021-08-16 23:42:02 +02:00
default void replaceChildren(@NotNull String name, @NotNull EventNode<T> eventNode) {
replaceChildren(name, getEventType(), eventNode);
}
2021-06-08 22:58:48 +02:00
/**
* Recursively removes children with the given name and type starting at this node.
*
2021-06-09 06:52:00 +02:00
* @param name The node name to filter for
2021-06-08 22:58:48 +02:00
* @param eventType The node type to filter for
*/
2021-08-16 23:42:02 +02:00
void removeChildren(@NotNull String name, @NotNull Class<? extends T> eventType);
2021-06-08 22:58:48 +02:00
/**
* Recursively removes children with the given name starting at this node.
*
* @param name The node name to filter for
*/
2021-08-24 11:49:44 +02:00
default void removeChildren(@NotNull String name) {
removeChildren(name, getEventType());
}
2021-06-08 22:58:48 +02:00
/**
* Directly adds a child node to this node.
*
* @param child The child to add
* @return this, can be used for chaining
*/
2021-06-08 14:15:30 +02:00
@Contract(value = "_ -> this")
2021-08-16 23:42:02 +02:00
@NotNull EventNode<T> addChild(@NotNull EventNode<? extends T> child);
2021-06-02 09:50:18 +02:00
2021-06-08 22:58:48 +02:00
/**
* Directly removes the given child from this node.
*
* @param child The child to remove
* @return this, can be used for chaining
*/
2021-06-08 14:15:30 +02:00
@Contract(value = "_ -> this")
2021-08-16 23:42:02 +02:00
@NotNull EventNode<T> removeChild(@NotNull EventNode<? extends T> child);
2021-06-08 14:15:30 +02:00
@Contract(value = "_ -> this")
2021-08-16 23:42:02 +02:00
@NotNull EventNode<T> addListener(@NotNull EventListener<? extends T> listener);
2021-06-08 14:15:30 +02:00
@Contract(value = "_, _ -> this")
2021-08-17 02:33:48 +02:00
default <E extends T> @NotNull EventNode<T> addListener(@NotNull Class<E> eventType, @NotNull Consumer<@NotNull E> listener) {
return addListener(EventListener.of(eventType, listener));
}
2021-06-08 14:15:30 +02:00
@Contract(value = "_ -> this")
2021-08-16 23:42:02 +02:00
@NotNull EventNode<T> removeListener(@NotNull EventListener<? extends T> listener);
2021-08-22 00:41:30 +02:00
/**
* Maps a specific object to a node.
* <p>
* Be aware that such structure have huge performance penalty as they will
* always require a map lookup. Use only at last resort.
*
* @param node the node to map
* @param value the mapped value
*/
2021-08-17 02:58:12 +02:00
@ApiStatus.Experimental
2021-08-16 23:42:02 +02:00
void map(@NotNull EventNode<? extends T> node, @NotNull Object value);
2021-08-22 00:41:30 +02:00
/**
* Undo {@link #map(EventNode, Object)}
*
* @param value the value to unmap
* @return true if the value has been unmapped, false if nothing happened
*/
2021-08-17 02:58:12 +02:00
@ApiStatus.Experimental
2021-08-16 23:42:02 +02:00
boolean unmap(@NotNull Object value);
2021-08-17 02:58:12 +02:00
@ApiStatus.Experimental
void register(@NotNull EventBinding<? extends T> binding);
2021-08-17 02:58:12 +02:00
@ApiStatus.Experimental
void unregister(@NotNull EventBinding<? extends T> binding);
2021-06-02 08:17:21 +02:00
}