Readd @Subscribe, for unconditionally active subscriptions in plugins

This commit is contained in:
Lucwousin
2019-11-16 11:17:20 +01:00
parent b7bd7c5a08
commit 4fa3a73eb9
10 changed files with 147 additions and 27 deletions

View File

@@ -24,13 +24,13 @@
*/ */
package net.runelite.api.hooks; package net.runelite.api.hooks;
import net.runelite.api.MainBufferProvider;
import net.runelite.api.events.Event;
import net.runelite.api.widgets.WidgetItem;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelEvent;
import net.runelite.api.MainBufferProvider;
import net.runelite.api.events.Event;
import net.runelite.api.widgets.WidgetItem;
/** /**
* Interface of callbacks the injected client uses to send events * Interface of callbacks the injected client uses to send events
@@ -42,14 +42,14 @@ public interface Callbacks
* *
* @param event the event * @param event the event
*/ */
<T> void post(Class<T> eventClass, Event event); <T extends Event, E extends T> void post(Class<T> eventClass, E event);
/** /**
* Post a deferred event, which gets delayed until the next cycle. * Post a deferred event, which gets delayed until the next cycle.
* *
* @param event the event * @param event the event
*/ */
<T> void postDeferred(Class<T> eventClass, Event event); <T extends Event, E extends T> void postDeferred(Class<T> eventClass, E event);
/** /**
* Called each client cycle. * Called each client cycle.

View File

@@ -156,13 +156,13 @@ public class Hooks implements Callbacks
} }
@Override @Override
public <T> void post(Class<T> eventClass, Event event) public <T extends Event, E extends T> void post(Class<T> eventClass, E event)
{ {
eventBus.post(eventClass, event); eventBus.post(eventClass, event);
} }
@Override @Override
public <T> void postDeferred(Class<T> eventClass, Event event) public <T extends Event, E extends T> void postDeferred(Class<T> eventClass, E event)
{ {
deferredEventBus.post(eventClass, event); deferredEventBus.post(eventClass, event);
} }

View File

@@ -29,7 +29,7 @@ public class EventBus implements EventBusInterface
private OpenOSRSConfig openOSRSConfig; private OpenOSRSConfig openOSRSConfig;
@NonNull @NonNull
private <T> Relay<Object> getSubject(Class<T> eventClass) private <T extends Event> Relay<Object> getSubject(Class<T> eventClass)
{ {
return subjectList.computeIfAbsent(eventClass, k -> PublishRelay.create().toSerialized()); return subjectList.computeIfAbsent(eventClass, k -> PublishRelay.create().toSerialized());
} }
@@ -49,7 +49,7 @@ public class EventBus implements EventBusInterface
@Override @Override
// Subscribe on lifecycle (for example from plugin startUp -> shutdown) // Subscribe on lifecycle (for example from plugin startUp -> shutdown)
public <T> void subscribe(Class<T> eventClass, @NonNull Object lifecycle, @NonNull Consumer<T> action) public <T extends Event> void subscribe(Class<T> eventClass, @NonNull Object lifecycle, @NonNull Consumer<T> action)
{ {
if (subscriptionList.containsKey(lifecycle) && eventClass.equals(subscriptionList.get(lifecycle))) if (subscriptionList.containsKey(lifecycle) && eventClass.equals(subscriptionList.get(lifecycle)))
{ {
@@ -74,7 +74,7 @@ public class EventBus implements EventBusInterface
} }
@Override @Override
public <T> void subscribe(Class<T> eventClass, @NonNull Object lifecycle, @NonNull Consumer<T> action, int takeUntil) public <T extends Event> void subscribe(Class<T> eventClass, @NonNull Object lifecycle, @NonNull Consumer<T> action, int takeUntil)
{ {
if (subscriptionList.containsKey(lifecycle) && eventClass.equals(subscriptionList.get(lifecycle))) if (subscriptionList.containsKey(lifecycle) && eventClass.equals(subscriptionList.get(lifecycle)))
{ {
@@ -113,7 +113,7 @@ public class EventBus implements EventBusInterface
} }
@Override @Override
public <T> void post(Class<T> eventClass, @NonNull Event event) public <T extends Event> void post(Class<? extends T> eventClass, @NonNull T event)
{ {
getSubject(eventClass).accept(event); getSubject(eventClass).accept(event);
} }

View File

@@ -6,11 +6,11 @@ import net.runelite.api.events.Event;
public interface EventBusInterface public interface EventBusInterface
{ {
<T> void subscribe(Class<T> eventClass, @NonNull Object lifecycle, @NonNull Consumer<T> action); <T extends Event> void subscribe(Class<T> eventClass, @NonNull Object lifecycle, @NonNull Consumer<T> action);
<T> void subscribe(Class<T> eventClass, @NonNull Object lifecycle, @NonNull Consumer<T> action, int takeUntil); <T extends Event> void subscribe(Class<T> eventClass, @NonNull Object lifecycle, @NonNull Consumer<T> action, int takeUntil);
void unregister(@NonNull Object lifecycle); void unregister(@NonNull Object lifecycle);
<T> void post(Class<T> eventClass, @NonNull Event event); <T extends Event> void post(Class<? extends T> eventClass, @NonNull T event);
} }

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2018, Abex
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.eventbus;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a method as an event subscriber.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Subscribe {}

View File

@@ -61,7 +61,7 @@ public class ExternalPluginLoader
BASE.mkdirs(); BASE.mkdirs();
} }
public void scanAndLoad() void scanAndLoad()
{ {
if (!OpenOSRSConfig.enablePlugins()) if (!OpenOSRSConfig.enablePlugins())
{ {
@@ -111,7 +111,7 @@ public class ExternalPluginLoader
return; return;
} }
if (loadedPlugins.size() != 1) if (loadedPlugins.size() > 1)
{ {
close(loader); close(loader);
log.warn("You can not have more than one plugin per jar"); log.warn("You can not have more than one plugin per jar");
@@ -119,8 +119,6 @@ public class ExternalPluginLoader
} }
Plugin plugin = loadedPlugins.get(0); Plugin plugin = loadedPlugins.get(0);
plugin.file = pluginFile;
plugin.loader = loader;
// Initialize default configuration // Initialize default configuration
Injector injector = plugin.getInjector(); Injector injector = plugin.getInjector();

View File

@@ -24,17 +24,27 @@
*/ */
package net.runelite.client.plugins; package net.runelite.client.plugins;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Binder; import com.google.inject.Binder;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Module; import com.google.inject.Module;
import java.io.File; import io.reactivex.functions.Consumer;
import java.lang.reflect.Method;
import java.util.Set;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Value;
import net.runelite.api.events.Event;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
public abstract class Plugin implements Module public abstract class Plugin implements Module
{ {
protected Injector injector; private final Set<Subscription> annotatedSubscriptions = findSubscriptions();
private final Object annotatedSubsLock = new Object();
public File file; @Getter(AccessLevel.PROTECTED)
public PluginClassLoader loader; protected Injector injector;
@Override @Override
public void configure(Binder binder) public void configure(Binder binder)
@@ -49,8 +59,47 @@ public abstract class Plugin implements Module
{ {
} }
public final Injector getInjector() @SuppressWarnings("unchecked")
final void addAnnotatedSubscriptions(EventBus eventBus)
{ {
return injector; annotatedSubscriptions.forEach(sub -> eventBus.subscribe(sub.type, annotatedSubsLock, sub.method));
}
final void removeAnnotatedSubscriptions(EventBus eventBus)
{
eventBus.unregister(annotatedSubsLock);
}
private Set<Subscription> findSubscriptions()
{
ImmutableSet.Builder<Subscription> builder = ImmutableSet.builder();
for (Method method : this.getClass().getDeclaredMethods())
{
if (method.getAnnotation(Subscribe.class) == null)
continue;
assert method.getParameterCount() == 1 : "Methods annotated with @Subscribe should have only one parameter";
Class<?> type = method.getParameterTypes()[0];
assert Event.class.isAssignableFrom(type) : "Parameters of methods annotated with @Subscribe should implement net.runelite.api.events.Event";
assert method.getReturnType() == void.class : "Methods annotated with @Subscribe should have a void return type";
method.setAccessible(true);
Subscription sub = new Subscription(type.asSubclass(Event.class), event -> method.invoke(this, event));
builder.add(sub);
}
return builder.build();
}
@Value
private static class Subscription
{
private final Class type;
private final Consumer method;
} }
} }

View File

@@ -376,6 +376,8 @@ public class PluginManager
} }
}); });
plugin.addAnnotatedSubscriptions(eventBus);
log.debug("Plugin {} is now running", plugin.getClass().getSimpleName()); log.debug("Plugin {} is now running", plugin.getClass().getSimpleName());
if (!isOutdated && sceneTileManager != null) if (!isOutdated && sceneTileManager != null)
{ {
@@ -386,7 +388,6 @@ public class PluginManager
} }
} }
// eventBus.register(plugin);
schedule(plugin); schedule(plugin);
eventBus.post(PluginChanged.class, new PluginChanged(plugin, true)); eventBus.post(PluginChanged.class, new PluginChanged(plugin, true));
} }
@@ -424,6 +425,8 @@ public class PluginManager
} }
}); });
plugin.removeAnnotatedSubscriptions(eventBus);
log.debug("Plugin {} is now stopped", plugin.getClass().getSimpleName()); log.debug("Plugin {} is now stopped", plugin.getClass().getSimpleName());
eventBus.post(PluginChanged.class, new PluginChanged(plugin, false)); eventBus.post(PluginChanged.class, new PluginChanged(plugin, false));

View File

@@ -47,7 +47,7 @@ public class DeferredEventBus extends EventBus
} }
@Override @Override
public <T> void post(Class<T> eventClass, @NonNull Event event) public <T extends Event> void post(Class<? extends T> eventClass, @NonNull T event)
{ {
pendingEvents.add(new ImmutablePair<>(eventClass, event)); pendingEvents.add(new ImmutablePair<>(eventClass, event));
} }

View File

@@ -46,10 +46,13 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.events.Event;
import net.runelite.client.RuneLite; import net.runelite.client.RuneLite;
import net.runelite.client.RuneLiteModule; import net.runelite.client.RuneLiteModule;
import net.runelite.client.config.Config; import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigItem;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
@@ -108,7 +111,6 @@ public class PluginManagerTest
} }
} }
@SuppressWarnings("unchecked")
@Test @Test
public void testLoadPlugins() throws Exception public void testLoadPlugins() throws Exception
{ {
@@ -190,4 +192,33 @@ public class PluginManagerTest
} }
} }
@Test
public void testEventbusAnnotations() throws PluginInstantiationException
{
EventBus eventbus = new EventBus();
PluginManager pluginManager = new PluginManager(true, eventbus, null, null, null, null)
{
@Override
public boolean isPluginEnabled(Plugin plugin)
{
return true;
}
};
class TestEvent implements Event {}
class TestPlugin extends Plugin
{
private boolean thisShouldBeTrue = false;
@Subscribe
private void doSomething(TestEvent event)
{
thisShouldBeTrue = true;
}
}
TestPlugin plugin = new TestPlugin();
pluginManager.startPlugin(plugin);
eventbus.post(TestEvent.class, new TestEvent());
assert plugin.thisShouldBeTrue;
}
} }