Readd @Subscribe, for unconditionally active subscriptions in plugins
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
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();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user