diff --git a/src/main/java/org/violetmoon/zeta/event/bus/ZetaEventBus.java b/src/main/java/org/violetmoon/zeta/event/bus/ZetaEventBus.java index 001a5e4..ceaa8f5 100644 --- a/src/main/java/org/violetmoon/zeta/event/bus/ZetaEventBus.java +++ b/src/main/java/org/violetmoon/zeta/event/bus/ZetaEventBus.java @@ -169,6 +169,9 @@ private Listeners getListenersFor(Method method) { * Pausefrogeline */ private class Listeners { + + private final Map handles = new LinkedHashMap<>(); + private record Subscriber(@Nullable Object receiver, Class owningClazz, Method method) { @Override public boolean equals(Object object) { @@ -200,7 +203,6 @@ MethodHandle unreflect() { } } - private final Map handles = new LinkedHashMap<>(); void subscribe(@Nullable Object receiver, Class owningClazz, Method method) { try { diff --git a/src/main/java/org/violetmoon/zeta/event/bus/wip/ForgeZetaBus.java b/src/main/java/org/violetmoon/zeta/event/bus/wip/ForgeZetaBus.java new file mode 100644 index 0000000..b88db00 --- /dev/null +++ b/src/main/java/org/violetmoon/zeta/event/bus/wip/ForgeZetaBus.java @@ -0,0 +1,109 @@ +package org.violetmoon.zeta.event.bus.wip; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.eventbus.api.IEventBus; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.violetmoon.zeta.Zeta; + +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; + +public class ForgeZetaBus extends ZetaBus { + + private final Map, Function> forgeToZetaMap = new HashMap<>(); + private final Map, Function> zetaToForgeMap = new HashMap<>(); + //ForgeZAddReloadListener.class, (Function) ForgeZAddReloadListener::new + + private final IEventBus forgeBus; + + /** + * @param subscriberAnnotation The annotation that subscribe()/unsubscribe() will pay attention to. + * @param eventRoot The superinterface of all events fired on this bus. + */ + public ForgeZetaBus(Zeta z, Class subscriberAnnotation, Class eventRoot, @Nullable Logger logSpam) { + super(z, subscriberAnnotation, eventRoot, logSpam); + this.forgeBus = MinecraftForge.EVENT_BUS; + } + + public void registerEventMappings(Class zetaEvent, + Function forgeToZeta, + Class forgeEvent, + Function zetaToForge) { + forgeToZetaMap.put(zetaEvent, forgeToZeta); + zetaToForgeMap.put(forgeEvent, zetaToForge); + } + + // takes a method that takes a zeta event and turns into one that takes a forge event + private Consumer remapMethod(MethodHandle zetaEventConsumer, Class zetaEventClass) { + Function forgeToZetaFunc = forgeToZetaMap.get(zetaEventClass); + return createForgeConsumer(zetaEventConsumer, forgeToZetaFunc); + } + + private Consumer createForgeConsumer(MethodHandle zetaEventConsumer, Function forgeToZetaFunc) { + return event -> { + try { + zetaEventConsumer.invoke(forgeToZetaFunc.apply(event)); + } catch (Throwable e) { + throw new RuntimeException(e); + } + }; + } + + @Override + protected void addListener(Method method, Object receiver, Class owningClazz) { + if (method.getParameterCount() != 1) + throw arityERR(method); + + Class eventType = method.getParameterTypes()[0]; + if (!eventRoot.isAssignableFrom(eventType)) + throw typeERR(method); + + MethodHandle handle; + try { + handle = MethodHandles.publicLookup().unreflect(method); + } catch (Exception e) { + throw new RuntimeException(e); + } + + //fill in the "this" parameter + if (receiver != null) + handle = handle.bindTo(receiver); + + forgeBus.addListener(remapMethod(handle, (Class) eventType)); + } + + @Override + protected void removeListener(Method m, Object receiver, Class owningClazz) { + + } + + + @Override + public T fire(@NotNull T event) { + forgeBus.post(remapEvent(event)); + return event; + } + + private Event remapEvent(@NotNull T event) { + Function zetaToForgeFunc = zetaToForgeMap.get(event.getClass()); + return createForgeEvent(event, zetaToForgeFunc); + } + + private Event createForgeEvent(@NotNull E event, Function function) { + return function.apply((T) event); + } + + @Override + public T fire(@NotNull T event, Class firedAs) { + return null; + } +} diff --git a/src/main/java/org/violetmoon/zeta/event/bus/wip/ZetaBus.java b/src/main/java/org/violetmoon/zeta/event/bus/wip/ZetaBus.java new file mode 100644 index 0000000..df92d0a --- /dev/null +++ b/src/main/java/org/violetmoon/zeta/event/bus/wip/ZetaBus.java @@ -0,0 +1,125 @@ +package org.violetmoon.zeta.event.bus.wip; + +import com.google.common.base.Preconditions; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.violetmoon.zeta.Zeta; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.stream.Stream; + +public abstract class ZetaBus { + + protected final Class subscriberAnnotation; + protected final Class eventRoot; + protected final Zeta z; + protected final @Nullable Logger logSpam; + + /** + * @param subscriberAnnotation The annotation that subscribe()/unsubscribe() will pay attention to. + * @param eventRoot The superinterface of all events fired on this bus. + */ + public ZetaBus(Zeta z, Class subscriberAnnotation, Class eventRoot, @Nullable Logger logSpam) { + Preconditions.checkArgument(eventRoot.isInterface(), "Event roots should be an interface"); + + this.z = z; + this.subscriberAnnotation = subscriberAnnotation; + this.eventRoot = eventRoot; + this.logSpam = logSpam; + } + + /** + * If the parameter is a Class: subscribes all static methods from it (and its superclasses) to the event bus. + * Otherwise, subscribes all non-static methods on that object (and its superclasses) to the event bus. + * (Note that the event bus will hold a reference to this object.) + */ + public ZetaBus subscribe(@NotNull Object target) { + Preconditions.checkNotNull(target, "null passed to subscribe"); + + Object receiver; + Class owningClazz; + if (target instanceof Class clazz) { + receiver = null; + owningClazz = clazz; + } else { + receiver = target; + owningClazz = target.getClass(); + } + + streamAnnotatedMethods(owningClazz, receiver == null) + .forEach(m -> addListener(m, receiver, owningClazz)); + return this; + } + + protected abstract void addListener(Method m, Object receiver, Class owningClazz); + + /** + * If the parameter is a Class: unsubscribes all static methods from it (and its superclasses) from the event bus. + * Otherwise, unsubscribes all non-static methods on that object (and its superclasses) from the event bus. + */ + public ZetaBus unsubscribe(@NotNull Object target) { + Preconditions.checkNotNull(target, "null passed to unsubscribe"); + + Object receiver; + Class owningClazz; + if (target instanceof Class clazz) { + receiver = null; + owningClazz = clazz; + } else { + receiver = target; + owningClazz = target.getClass(); + } + + streamAnnotatedMethods(owningClazz, receiver == null) + .forEach(m -> removeListener(m, receiver, owningClazz)); + return this; + } + + protected abstract void removeListener(Method m, Object receiver, Class owningClazz); + + /** + * Fires an event on the event bus. Each subscriber will be visited in order. + */ + public abstract T fire(@NotNull T event); + + /** + * Fires an event on the event bus. Each subscriber will be visited in order. + * Listeners for "firedAs" will be invoked, instead of listeners for the event's own class. + *

+ * (The generic should be Class<? super T & ? extends E>, but unfortunately, javac.) + */ + public abstract T fire(@NotNull T event, Class firedAs); + + /** + * Grabs methods from this class (and its superclasses, recursively) that are annotated with this bus's + * annotation; and of the requested staticness. + */ + private Stream streamAnnotatedMethods(Class owningClazz, boolean wantStatic) { + return Arrays.stream(owningClazz.getMethods()) + .filter(m -> m.isAnnotationPresent(subscriberAnnotation) && ((m.getModifiers() & Modifier.STATIC) != 0) == wantStatic); + } + + protected RuntimeException arityERR(Method method) { + return methodProblem("Method annotated with @" + subscriberAnnotation.getSimpleName() + + " should take 1 parameter.", method, null); + } + + protected RuntimeException typeERR(Method method) { + return methodProblem("Method annotated with @" + subscriberAnnotation.getSimpleName() + + " should take an implementor of " + eventRoot.getSimpleName() + ".", method, null); + } + + protected RuntimeException unreflectERR(Method method, Throwable cause) { + return methodProblem("Exception unreflecting a @" + subscriberAnnotation.getSimpleName() + + " method, is it public?", method, cause); + } + + protected static RuntimeException methodProblem(String problem, Method method, @Nullable Throwable cause) { + return new RuntimeException("%s%nMethod class: %s%nMethod name: %s".formatted( + problem, method.getDeclaringClass().getName(), method.getName()), cause); + } +} diff --git a/src/main/java/org/violetmoon/zetaimplforge/event/load/Test.java b/src/main/java/org/violetmoon/zetaimplforge/event/load/Test.java deleted file mode 100644 index 13e9efa..0000000 --- a/src/main/java/org/violetmoon/zetaimplforge/event/load/Test.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.violetmoon.zetaimplforge.event.load; - -import net.minecraftforge.event.AddReloadListenerEvent; -import net.minecraftforge.eventbus.EventBus; -import net.minecraftforge.eventbus.api.Event; -import org.jetbrains.annotations.NotNull; -import org.violetmoon.zeta.event.bus.IZetaLoadEvent; -import org.violetmoon.zeta.event.bus.ZetaEventBus; - -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; - -// This made sense when i wrote it. Now i have no clue -// Please somebody finish this -public class Test { - - private static final Map, Function> FORGE_TO_ZETA = Map.of( - ForgeZAddReloadListener.class, (Function) ForgeZAddReloadListener::new - ); - - public static Consumer remap(Consumer zetaEventConsumer, Class cl) { - Function forgeToZeta = (Function) FORGE_TO_ZETA.get(cl); - return getEventConsumer(zetaEventConsumer, forgeToZeta); - } - - @NotNull - private static Consumer getEventConsumer(Consumer zetaEventConsumer, Function forgeToZeta) { - return event -> zetaEventConsumer.accept(forgeToZeta.apply(event)); - } - - - public static class ExampleZetaBus{ - private EventBus forgeBus; - - public void addListener(Consumer zetaEventConsumer, Class cl){ - forgeBus.addListener(remap(zetaEventConsumer, cl)); - } - - } - -}