Merge branch 'master' into ver/4.3.0

This commit is contained in:
dfsek 2021-02-17 10:37:14 -07:00
commit d670fc904f
9 changed files with 107 additions and 17 deletions

View File

@ -1,4 +0,0 @@
package com.dfsek.terra.api.core.event;
public interface Event {
}

View File

@ -1,7 +1,14 @@
package com.dfsek.terra.api.core.event; package com.dfsek.terra.api.core.event;
import com.dfsek.terra.api.core.event.events.Event;
public interface EventManager { public interface EventManager {
void callEvent(Event event); /**
* Call an event, and return the execution status.
* @param event Event to pass to all registered EventListeners.
* @return False if the event is cancellable and has been cancelled, otherwise true.
*/
boolean callEvent(Event event);
void registerListener(EventListener listener); void registerListener(EventListener listener);
} }

View File

@ -1,6 +1,9 @@
package com.dfsek.terra.api.core.event; package com.dfsek.terra.api.core.event;
import com.dfsek.terra.api.core.TerraPlugin; import com.dfsek.terra.api.core.TerraPlugin;
import com.dfsek.terra.api.core.event.annotations.Priority;
import com.dfsek.terra.api.core.event.events.Cancellable;
import com.dfsek.terra.api.core.event.events.Event;
import com.dfsek.terra.api.util.ReflectionUtil; import com.dfsek.terra.api.util.ReflectionUtil;
import java.io.PrintWriter; import java.io.PrintWriter;
@ -8,12 +11,14 @@ import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class TerraEventManager implements EventManager { public class TerraEventManager implements EventManager {
private final Map<Class<? extends Event>, Map<EventListener, List<Method>>> listeners = new HashMap<>(); private final Map<Class<? extends Event>, List<ListenerHolder>> listeners = new HashMap<>();
private final TerraPlugin main; private final TerraPlugin main;
public TerraEventManager(TerraPlugin main) { public TerraEventManager(TerraPlugin main) {
@ -21,26 +26,27 @@ public class TerraEventManager implements EventManager {
} }
@Override @Override
public void callEvent(Event event) { public boolean callEvent(Event event) {
if(!listeners.containsKey(event.getClass())) return; listeners.getOrDefault(event.getClass(), Collections.emptyList()).forEach(listenerHolder -> {
listeners.get(event.getClass()).forEach((eventListener, methods) -> methods.forEach(method -> {
try { try {
method.invoke(eventListener, event); listenerHolder.method.invoke(listenerHolder.listener, event);
} catch(InvocationTargetException e) { } catch(InvocationTargetException e) {
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
e.getTargetException().printStackTrace(new PrintWriter(writer)); e.getTargetException().printStackTrace(new PrintWriter(writer));
main.getLogger().warning("Exception occurred during event handling:"); main.getLogger().warning("Exception occurred during event handling:");
main.getLogger().warning(writer.toString()); main.getLogger().warning(writer.toString());
main.getLogger().warning("Report this to the maintainers of " + eventListener.getClass().getCanonicalName()); main.getLogger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
} catch(Exception e) { } catch(Exception e) {
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer)); e.printStackTrace(new PrintWriter(writer));
main.getLogger().warning("Exception occurred during event handling:"); main.getLogger().warning("Exception occurred during event handling:");
main.getLogger().warning(writer.toString()); main.getLogger().warning(writer.toString());
main.getLogger().warning("Report this to the maintainers of " + eventListener.getClass().getCanonicalName()); main.getLogger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
} }
}) }
); );
if(event instanceof Cancellable) return !((Cancellable) event).isCancelled();
return true;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -53,8 +59,34 @@ public class TerraEventManager implements EventManager {
if(method.getParameterCount() != 1) continue; // Check that parameter count is only 1. if(method.getParameterCount() != 1) continue; // Check that parameter count is only 1.
Class<?> eventParam = method.getParameterTypes()[0]; Class<?> eventParam = method.getParameterTypes()[0];
if(!Event.class.isAssignableFrom(eventParam)) continue; // Check that parameter is an Event. if(!Event.class.isAssignableFrom(eventParam)) continue; // Check that parameter is an Event.
Priority p = method.getAnnotation(Priority.class);
int priority = p == null ? 0 : p.value();
method.setAccessible(true); method.setAccessible(true);
listeners.computeIfAbsent((Class<? extends Event>) eventParam, e -> new HashMap<>()).computeIfAbsent(listener, l -> new ArrayList<>()).add(method);
List<ListenerHolder> holders = listeners.computeIfAbsent((Class<? extends Event>) eventParam, e -> new ArrayList<>());
holders.add(new ListenerHolder(method, listener, priority));
holders.sort(Comparator.comparingInt(ListenerHolder::getPriority)); // Sort priorities.
}
}
private static final class ListenerHolder {
private final Method method;
private final EventListener listener;
private final int priority;
private ListenerHolder(Method method, EventListener listener, int priority) {
this.method = method;
this.listener = listener;
this.priority = priority;
}
public int getPriority() {
return priority;
} }
} }
} }

View File

@ -0,0 +1,38 @@
package com.dfsek.terra.api.core.event.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotated listener methods will have a specific priority set.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Priority {
/**
* Highest possible priority. Listeners with this priority will always be invoked first.
*/
int HIGHEST = Integer.MAX_VALUE;
/**
* Lowest possible priority. Listeners with this priority will always be invoked last.
*/
int LOWEST = Integer.MIN_VALUE;
/**
* Default priority.
*/
int NORMAL = 0;
/**
* High priority.
*/
int HIGH = 1;
/**
* Low Priority.
*/
int LOW = -1;
/**
* @return Priority of this event. Events are executed from lowest to highest priorities.
*/
int value();
}

View File

@ -0,0 +1,11 @@
package com.dfsek.terra.api.core.event.events;
/**
* Events that implement this interface may be cancelled.
*
* Cancelling an event is assumed to stop the execution of whatever action triggered the event.
*/
public interface Cancellable extends Event {
boolean isCancelled();
void setCancelled();
}

View File

@ -0,0 +1,4 @@
package com.dfsek.terra.api.core.event.events;
public interface Event {
}

View File

@ -1,6 +1,6 @@
package com.dfsek.terra.api.core.event.events.config; package com.dfsek.terra.api.core.event.events.config;
import com.dfsek.terra.api.core.event.Event; import com.dfsek.terra.api.core.event.events.Event;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
public abstract class ConfigPackLoadEvent implements Event { public abstract class ConfigPackLoadEvent implements Event {

View File

@ -1,6 +1,6 @@
package com.dfsek.terra.api.core.event.events.world; package com.dfsek.terra.api.core.event.events.world;
import com.dfsek.terra.api.core.event.Event; import com.dfsek.terra.api.core.event.events.Event;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
/** /**

View File

@ -2,7 +2,8 @@ package event;
import com.dfsek.tectonic.loading.TypeRegistry; import com.dfsek.tectonic.loading.TypeRegistry;
import com.dfsek.terra.api.core.TerraPlugin; import com.dfsek.terra.api.core.TerraPlugin;
import com.dfsek.terra.api.core.event.Event; import com.dfsek.terra.api.core.event.annotations.Priority;
import com.dfsek.terra.api.core.event.events.Event;
import com.dfsek.terra.api.core.event.EventListener; import com.dfsek.terra.api.core.event.EventListener;
import com.dfsek.terra.api.core.event.EventManager; import com.dfsek.terra.api.core.event.EventManager;
import com.dfsek.terra.api.core.event.TerraEventManager; import com.dfsek.terra.api.core.event.TerraEventManager;
@ -127,6 +128,7 @@ public class EventTest {
} }
static class TestListener2 implements EventListener { static class TestListener2 implements EventListener {
@Priority(Priority.LOWEST)
public void doThing(TestEvent event) { public void doThing(TestEvent event) {
System.out.println("Event value 2: " + event.value); System.out.println("Event value 2: " + event.value);
} }