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;
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.

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

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();
}
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();

View File

@@ -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;
}
}

View File

@@ -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));

View File

@@ -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));
}

View File

@@ -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;
}
}