Skip to content

Instantly share code, notes, and snippets.

@JohnnyJayJay
Last active September 12, 2022 03:40
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JohnnyJayJay/5ab455cd2c3c3c71a7cadcae563dde01 to your computer and use it in GitHub Desktop.
Save JohnnyJayJay/5ab455cd2c3c3c71a7cadcae563dde01 to your computer and use it in GitHub Desktop.
Event Waiter für Spigot und faule Menschen, die Inventar Menüs wollen
package com.github.johnnyjayjay.testplugin;
import org.bukkit.Bukkit;
import org.bukkit.event.Event;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* This is an adaptation of jagrosh's EventWaiter from jda utilities
* (https://github.com/JDA-Applications/JDA-Utilities) for Spigot.
* In order to work with Spigot's terrible event API, it has been restructured quite a bit.
*
* <p>The EventExpecter is capable of handling specialized forms of
* {@link org.bukkit.event.Event} that must meet criteria not normally specifiable
* without implementation of a {@link org.bukkit.event.Listener Listener}.
*
* <p>In contrast to the implementation in jda-utilities, this class uses the bukkit
* scheduler for timeouts instead of an own thread pool.
*
* <p>As a final note, if you intend to use the EventExpecter, it is highly recommended
* you reuse a single instance across your plugin.
*
* @author Johnny_JayJay / John Grosh (jagrosh)
*/
public final class EventExpecter {
private final Plugin plugin;
/**
* Constructs an EventExpecter for the given plugin instance.
*
* @param plugin The plugin that uses this expecter.
* @throws java.lang.IllegalArgumentException If the plugin is {@code null}
*/
public EventExpecter(Plugin plugin) {
checkNotNull(plugin, "Plugin");
this.plugin = plugin;
}
/**
* Returns an instance of the builder-like class {@link ExpectationBuilder} to set up an event expectation conveniently.
*
* @param eventType The {@link java.lang.Class} of the Event to wait for.
* @param <T> The type of Event to wait for.
* @return A new instance of {@link ExpectationBuilder}.
*/
public <T extends Event> ExpectationBuilder<T> expect(Class<T> eventType) {
return new ExpectationBuilder<>(eventType);
}
public static final class ExpectationListener<T extends Event> implements Listener {
private final Predicate<T> condition;
private final Consumer<T> action;
private ExpectationListener(Predicate<T> condition, Consumer<T> action) {
this.condition = condition;
this.action = action;
}
public void attempt(T event) {
if (condition.test(event)) {
action.accept(event);
HandlerList.unregisterAll(this);
}
}
}
/**
* Class that is mainly used for chaining convenience when calling {@link #expect(Class)}.
*
* @param <T> The type of the event to expect.
*/
public class ExpectationBuilder<T extends Event> {
private final Class<T> eventType;
private Predicate<T> condition = (e) -> true;
private Consumer<T> action = (e) -> {
};
private long timeout = -1;
private Runnable timeoutAction = () -> {
};
private ExpectationBuilder(Class<T> eventType) {
this.eventType = eventType;
}
/**
* Sets the condition for this expectation. If the Predicate returns true for a corresponding event,
* the Consumer set in {@link #thenAccept(Consumer)} will be called and the Expectation will be completed.
*
* @param condition The Predicate to test when Events of the provided type are thrown.
* @return this.
*/
public ExpectationBuilder<T> takeIf(Predicate<T> condition) {
this.condition = condition;
return this;
}
/**
* Sets the Consumer that will be called if the right event happens.
*
* @param action The Consumer to perform an action when the condition Predicate returns {@code true}.
* @return this.
*/
public ExpectationBuilder<T> thenAccept(Consumer<T> action) {
this.action = action;
return this;
}
/**
* Sets a timeout for the Expectation, i.e. after the the specified amount of ticks, the Expecter
* will stop waiting for the event. By default, there is no timeout.
*
* @param ticks The amount of ticks to wait for, or {@code -1} if there is no timeout.
* @return this.
*/
public ExpectationBuilder<T> timeoutAfter(long ticks) {
this.timeout = ticks;
return this;
}
/**
* Sets the action to perform if the Expectation times out.
*
* @param action The Runnable to run if the time runs out before a correct Event is thrown.
* The Runnable will be executed on the Bukkit server thread.
* @return this.
*/
public ExpectationBuilder<T> runOnTimeout(Runnable action) {
this.timeoutAction = action;
return this;
}
/**
* Finishes the expectation setup and registers a listener. Schedules a timeout if one was specified.
*
* @return the listener for this expectation (can be used to trigger manually or to unregister early)
* @throws IllegalArgumentException if eventType, condition, action or timeOutAction are {@code null}
*/
@SuppressWarnings("unchecked")
public ExpectationListener<T> register() {
checkNotNull(eventType, "The provided class type");
checkNotNull(condition, "The provided condition predicate");
checkNotNull(action, "The provided action consumer");
checkNotNull(timeoutAction, "The provided timeout action");
ExpectationListener<T> listener = new ExpectationListener<>(condition, action);
Bukkit.getPluginManager().registerEvent(eventType, listener, EventPriority.NORMAL, (lis, event) -> ((ExpectationListener<T>) lis).attempt((T) event), plugin);
if (timeout > 0) {
Bukkit.getScheduler().runTaskLater(plugin, () -> {
HandlerList.unregisterAll(listener);
timeoutAction.run();
}, timeout);
}
return listener;
}
}
private static void check(boolean condition, String message) {
if (!condition)
throw new IllegalArgumentException(message);
}
private static void checkNotNull(Object argument, String name) {
check(argument != null, name + " may not be null");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment