Events?
SSTentacleSS opened this issue · 0 comments
SSTentacleSS commented
I just think if we had such events in some cases, imho
Why immediate?
Sometimes it is necessary that the server first perform at least operations on the code, and then only deal with the event (a bug is not a bug with Groups.player.size)
Events.on(EventType.PlayerLeave, event -> Groups.player.size) // Always +1, same but reverse with PlayerJoin
What does this solution offer
Events.imon(EventType.PlayerLeave, event -> Groups.player.size) // Always equal to the current number of players, as expected
Although this is apparently not relevant now, since this issue has already been resolved differently by changing the code
Once handlers and the ability for the developer to decide which event delete, manage their execution order and be able to remove them from the global context
Since current implementation only limits developers, even too much, that it becomes a choice whether to use reflection to get a private field or not
Events.java implementation
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.WeakHashMap;
import arc.func.Cons;
import arc.struct.Seq;
import arc.util.Log;
import arc.util.Timer;
@SuppressWarnings("unchecked")
public final class Events {
private static Map<Class<Object>, Seq<Cons<Object>>> listeners = new HashMap<>();
private static WeakHashMap<Cons<Object>, Cons<Object>> bindings = new WeakHashMap<>();
public static <T> void prepend(Class<T> eventClass, Cons<T> listener) {
if (!listeners.containsKey(eventClass))
listeners.put((Class<Object>) eventClass, new Seq<>());
var eventListeners = listeners.get(eventClass);
eventListeners.add((Cons<Object>) listener);
listeners.put(
(Class<Object>) eventClass,
eventListeners.reverse().add((Cons<Object>) listener).reverse()
);
}
public static <T> void append(Class<T> eventClass, Cons<T> listener) {
if (!listeners.containsKey(eventClass))
listeners.put((Class<Object>) eventClass, new Seq<>());
listeners
.get(eventClass)
.add((Cons<Object>) listener);
}
public static <T> void on(Class<T> eventClass, Cons<T> listener) {
append(eventClass, listener);
}
public static <T> void once(Class<T> eventClass, Cons<T> listener) {
append(eventClass, setupOnceListener(eventClass, listener));
}
public static <T> void immediateAppend(Class<T> eventClass, Cons<T> listener) {
append(eventClass, setupImmediateListener(listener));
}
public static <T> void immediatePrepend(Class<T> eventClass, Cons<T> listener) {
prepend(eventClass, setupImmediateListener(listener));
}
public static <T> void imon(Class<T> eventClass, Cons<T> listener) {
immediateAppend(eventClass, listener);
}
public static <T> void imonce(Class<T> eventClass, Cons<T> listener) {
immediateAppend(eventClass, setupOnceListener(eventClass, listener));
}
public static <T> void remove() {
listeners.clear();
}
public static <T> void remove(Class<T> eventClass) {
listeners.remove(eventClass);
}
public static <T> void remove(Cons<T> listener) {
if (bindings.containsKey(listener))
remove(bindings.get(listener));
else for (Class<? extends Object> eventClass : listeners.keySet()) {
if (listeners.get(eventClass).remove((Cons<Object>) listener))
return;
}
}
public static Map<Class<Object>, Seq<Cons<Object>>> getListeners() {
return listeners;
}
public static <T> Seq<Cons<Object>> getListeners(Class<T> eventClass) {
return listeners.get(eventClass);
}
public static <T> void emit(T eventInstance) {
Optional
.ofNullable(listeners.get(eventInstance.getClass()))
.ifPresent(listeners -> listeners.forEach(listener -> {
listener.get(eventInstance);
}));
}
public static <T> void emitAsync(T eventInstance) {
Timer.schedule(() -> emit(eventInstance), 0);
}
private static <T> Cons<T> setupImmediateListener(Cons<T> task) {
Cons<T> immediateTask = eventInstance -> Timer.schedule(() -> task.get(eventInstance), 0);
bindings.put((Cons<Object>) task, (Cons<Object>) immediateTask);
return immediateTask;
}
private static <T> Cons<T> setupOnceListener(Class<T> event, Cons<T> task) {
Cons<T> onceTask = eventInstance -> {
task.get(eventInstance);
Log.info(bindings.get(task));
remove(task);
};
bindings.put((Cons<Object>) task, (Cons<Object>) onceTask);
return onceTask;
}
}