Readd @Subscribe, for unconditionally active subscriptions in plugins
This commit is contained in:
@@ -24,13 +24,13 @@
|
||||
*/
|
||||
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.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
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
|
||||
@@ -42,14 +42,14 @@ public interface Callbacks
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
|
||||
@@ -156,13 +156,13 @@ public class Hooks implements Callbacks
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class EventBus implements EventBusInterface
|
||||
private OpenOSRSConfig openOSRSConfig;
|
||||
|
||||
@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());
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public class EventBus implements EventBusInterface
|
||||
|
||||
@Override
|
||||
// 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)))
|
||||
{
|
||||
@@ -74,7 +74,7 @@ public class EventBus implements EventBusInterface
|
||||
}
|
||||
|
||||
@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)))
|
||||
{
|
||||
@@ -113,7 +113,7 @@ public class EventBus implements EventBusInterface
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ import net.runelite.api.events.Event;
|
||||
|
||||
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);
|
||||
|
||||
<T> void post(Class<T> eventClass, @NonNull Event event);
|
||||
<T extends Event> void post(Class<? extends T> eventClass, @NonNull T event);
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
@@ -61,7 +61,7 @@ public class ExternalPluginLoader
|
||||
BASE.mkdirs();
|
||||
}
|
||||
|
||||
public void scanAndLoad()
|
||||
void scanAndLoad()
|
||||
{
|
||||
if (!OpenOSRSConfig.enablePlugins())
|
||||
{
|
||||
@@ -111,7 +111,7 @@ public class ExternalPluginLoader
|
||||
return;
|
||||
}
|
||||
|
||||
if (loadedPlugins.size() != 1)
|
||||
if (loadedPlugins.size() > 1)
|
||||
{
|
||||
close(loader);
|
||||
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.file = pluginFile;
|
||||
plugin.loader = loader;
|
||||
|
||||
// Initialize default configuration
|
||||
Injector injector = plugin.getInjector();
|
||||
|
||||
@@ -24,17 +24,27 @@
|
||||
*/
|
||||
package net.runelite.client.plugins;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.Injector;
|
||||
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
|
||||
{
|
||||
protected Injector injector;
|
||||
private final Set<Subscription> annotatedSubscriptions = findSubscriptions();
|
||||
private final Object annotatedSubsLock = new Object();
|
||||
|
||||
public File file;
|
||||
public PluginClassLoader loader;
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
protected Injector injector;
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,6 +376,8 @@ public class PluginManager
|
||||
}
|
||||
});
|
||||
|
||||
plugin.addAnnotatedSubscriptions(eventBus);
|
||||
|
||||
log.debug("Plugin {} is now running", plugin.getClass().getSimpleName());
|
||||
if (!isOutdated && sceneTileManager != null)
|
||||
{
|
||||
@@ -386,7 +388,6 @@ public class PluginManager
|
||||
}
|
||||
}
|
||||
|
||||
// eventBus.register(plugin);
|
||||
schedule(plugin);
|
||||
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());
|
||||
eventBus.post(PluginChanged.class, new PluginChanged(plugin, false));
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ public class DeferredEventBus extends EventBus
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
@@ -46,10 +46,13 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.events.Event;
|
||||
import net.runelite.client.RuneLite;
|
||||
import net.runelite.client.RuneLiteModule;
|
||||
import net.runelite.client.config.Config;
|
||||
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 org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@@ -108,7 +111,6 @@ public class PluginManagerTest
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user