From c3fae863563da3d265307f92775d6291ac99d9a2 Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Wed, 10 Jan 2018 19:11:17 +0100 Subject: [PATCH] Add ability to disable/enable all plugins Add ability to fully disable and enable each plugin. Fully disabling plugin means removing it from scheduler and eventBus and fully stopping it. That improves performance, because it basically removes overhead of any plugin that is disabled. Fixes: #280 Signed-off-by: Tomas Slusny --- .../java/net/runelite/client/RuneLite.java | 5 +- .../client/account/SessionManager.java | 6 +- .../net/runelite/client/config/Config.java | 1 - .../runelite/client/config/ConfigManager.java | 72 ++------- .../client/plugins/PluginDescriptor.java | 2 + .../client/plugins/PluginManager.java | 148 ++++++++++++++++-- .../client/plugins/config/ConfigPanel.java | 7 +- .../client/plugins/config/ConfigPlugin.java | 6 +- .../net/runelite/client/ui/PluginToolbar.java | 2 + .../client/ui/overlay/OverlayRenderer.java | 22 +-- 10 files changed, 182 insertions(+), 89 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index 4fb9e71652..d1d875885d 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -151,7 +151,7 @@ public class RuneLite // Initialize Discord service discordService.init(); - // Load default configuration + // Load user configuration configManager.load(); // Register event listeners @@ -159,6 +159,7 @@ public class RuneLite eventBus.register(menuManager); eventBus.register(chatMessageManager); eventBus.register(gui); + eventBus.register(pluginManager); // Setup the notifier notifier = new Notifier(properties.getTitle(), gui.getTrayIcon()); @@ -172,7 +173,7 @@ public class RuneLite // Plugins have provided their config, so set default config // to main settings - configManager.loadDefault(); + pluginManager.loadDefaultPluginConfiguration(); // Start plugins pluginManager.startCorePlugins(); diff --git a/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java b/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java index ee74cc1fae..f72fcf0b93 100644 --- a/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java +++ b/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java @@ -41,10 +41,10 @@ import javax.inject.Inject; import javax.inject.Singleton; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import net.runelite.client.RuneLite; -import net.runelite.client.config.ConfigManager; import net.runelite.api.events.SessionClose; import net.runelite.api.events.SessionOpen; +import net.runelite.client.RuneLite; +import net.runelite.client.config.ConfigManager; import net.runelite.http.api.account.AccountClient; import net.runelite.http.api.account.OAuthResponse; import net.runelite.http.api.ws.messages.LoginResponse; @@ -261,4 +261,4 @@ public class SessionManager closeSession(); deleteSession(); } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/config/Config.java b/runelite-client/src/main/java/net/runelite/client/config/Config.java index 4c82282d7d..6c95e9941a 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/Config.java +++ b/runelite-client/src/main/java/net/runelite/client/config/Config.java @@ -26,5 +26,4 @@ package net.runelite.client.config; public interface Config { - } diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java index 66637c58f4..8f1a97a1f1 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java @@ -25,8 +25,6 @@ package net.runelite.client.config; import com.google.common.eventbus.EventBus; -import com.google.inject.Injector; -import com.google.inject.Key; import java.awt.Color; import java.awt.Dimension; import java.io.File; @@ -37,8 +35,8 @@ import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; -import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.Properties; import java.util.concurrent.ScheduledExecutorService; @@ -46,10 +44,9 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; +import net.runelite.api.events.ConfigChanged; import net.runelite.client.RuneLite; import net.runelite.client.account.AccountSession; -import net.runelite.api.events.ConfigChanged; -import net.runelite.client.plugins.PluginManager; import net.runelite.http.api.config.ConfigClient; import net.runelite.http.api.config.ConfigEntry; import net.runelite.http.api.config.Configuration; @@ -66,9 +63,6 @@ public class ConfigManager @Inject ScheduledExecutorService executor; - @Inject - PluginManager pluginManager; - private AccountSession session; private ConfigClient client; private File propertiesFile; @@ -81,42 +75,6 @@ public class ConfigManager this.propertiesFile = getPropertiesFile(); } - public ConfigManager(EventBus eventBus, AccountSession session) - { - this.eventBus = eventBus; - switchSession(session); - } - - public List getConfigProxies() - { - List injectors = new ArrayList<>(); - injectors.add(RuneLite.getInjector()); - pluginManager.getPlugins().forEach(pl -> injectors.add(pl.getInjector())); - - List list = new ArrayList<>(); - for (Injector injector : injectors) - { - for (Key key : injector.getAllBindings().keySet()) - { - Class type = key.getTypeLiteral().getRawType(); - if (Config.class.isAssignableFrom(type)) - { - Config config = (Config) injector.getInstance(key); - list.add(config); - } - } - } - return list; - } - - public void loadDefault() - { - for (Object config : getConfigProxies()) - { - setDefaultConfiguration(config, false); - } - } - public final void switchSession(AccountSession session) { if (session == null) @@ -133,7 +91,6 @@ public class ConfigManager this.propertiesFile = getPropertiesFile(); load(); // load profile specific config - loadDefault(); // set defaults over anything not set } private File getPropertiesFile() @@ -183,8 +140,18 @@ public class ConfigManager for (ConfigEntry entry : configuration.getConfig()) { log.debug("Loading configuration value from client {}: {}", entry.getKey(), entry.getValue()); + final String[] split = entry.getKey().split("\\."); + final String groupName = split[0]; + final String key = split[1]; + final String value = entry.getValue(); + final String oldValue = (String) properties.setProperty(entry.getKey(), value); - properties.setProperty(entry.getKey(), entry.getValue()); + ConfigChanged configChanged = new ConfigChanged(); + configChanged.setGroup(groupName); + configChanged.setKey(key); + configChanged.setOldValue(oldValue); + configChanged.setNewValue(value); + eventBus.post(configChanged); } try @@ -343,16 +310,11 @@ public class ConfigManager List items = Arrays.stream(inter.getMethods()) .filter(m -> m.getParameterCount() == 0) - .sorted((m1, m2) - -> Integer.compare( - m1.getDeclaredAnnotation(ConfigItem.class).position(), - m2.getDeclaredAnnotation(ConfigItem.class).position() - ) - ) + .sorted(Comparator.comparingInt(m -> m.getDeclaredAnnotation(ConfigItem.class).position())) .map(m -> new ConfigItemDescriptor( - m.getDeclaredAnnotation(ConfigItem.class), - m.getReturnType() - )) + m.getDeclaredAnnotation(ConfigItem.class), + m.getReturnType() + )) .collect(Collectors.toList()); return new ConfigDescriptor(group, items); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginDescriptor.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginDescriptor.java index 19599c815c..d7d0756985 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginDescriptor.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginDescriptor.java @@ -37,6 +37,8 @@ public @interface PluginDescriptor { String name(); + boolean enabledByDefault() default true; + boolean developerPlugin() default false; boolean loadWhenOutdated() default false; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java index 491b173ed9..9fa6b69d2b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java @@ -26,12 +26,14 @@ package net.runelite.client.plugins; import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; import com.google.common.reflect.ClassPath; import com.google.common.reflect.ClassPath.ClassInfo; import com.google.inject.Binder; import com.google.inject.CreationException; import com.google.inject.Inject; import com.google.inject.Injector; +import com.google.inject.Key; import com.google.inject.Module; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -40,11 +42,18 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ScheduledExecutorService; import javax.inject.Singleton; import javax.swing.SwingUtilities; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import net.runelite.api.events.SessionClose; +import net.runelite.api.events.SessionOpen; import net.runelite.client.RuneLite; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.events.PluginChanged; import net.runelite.client.task.Schedule; import net.runelite.client.task.ScheduledMethod; @@ -65,10 +74,94 @@ public class PluginManager @Inject Scheduler scheduler; + @Inject + ConfigManager configManager; + + @Inject + ScheduledExecutorService executor; + @Setter boolean isOutdated; private final List plugins = new CopyOnWriteArrayList<>(); + private final List activePlugins = new CopyOnWriteArrayList<>(); + private final String runeliteGroupName = RuneLiteConfig.class + .getAnnotation(ConfigGroup.class).keyName(); + + @Subscribe + public void onSessionOpen(SessionOpen event) + { + refreshPlugins(); + } + + @Subscribe + public void onSessionClose(SessionClose event) + { + refreshPlugins(); + } + + private void refreshPlugins() + { + loadDefaultPluginConfiguration(); + getPlugins() + .forEach(plugin -> executor.submit(() -> + { + try + { + if (!startPlugin(plugin)) + { + stopPlugin(plugin); + } + } + catch (PluginInstantiationException e) + { + log.warn("Error during starting/stopping plugin {}. {}", plugin.getClass().getSimpleName(), e); + } + })); + } + + public List getPluginConfigProxies() + { + List injectors = new ArrayList<>(); + injectors.add(RuneLite.getInjector()); + getPlugins().forEach(pl -> injectors.add(pl.getInjector())); + + List list = new ArrayList<>(); + for (Injector injector : injectors) + { + for (Key key : injector.getAllBindings().keySet()) + { + Class type = key.getTypeLiteral().getRawType(); + if (Config.class.isAssignableFrom(type)) + { + Config config = (Config) injector.getInstance(key); + list.add(config); + } + } + } + return list; + } + + public void loadDefaultPluginConfiguration() + { + for (Object config : getPluginConfigProxies()) + { + configManager.setDefaultConfiguration(config, false); + } + + for (Plugin plugin : getPlugins()) + { + final String keyName = plugin.getClass().getSimpleName().toLowerCase(); + final String value = configManager.getConfiguration(runeliteGroupName, keyName); + + if (value == null) + { + final PluginDescriptor pluginDescriptor = plugin.getClass().getAnnotation(PluginDescriptor.class); + final boolean enabled = pluginDescriptor == null || pluginDescriptor.enabledByDefault(); + configManager.setConfiguration(runeliteGroupName, keyName, String.valueOf(enabled)); + } + } + } public void loadCorePlugins() throws IOException { @@ -86,7 +179,7 @@ public class PluginManager } catch (PluginInstantiationException ex) { - log.warn("Unable to start plugin {}", plugin.getClass().getSimpleName(), ex); + log.warn("Unable to start plugin {}. {}", plugin.getClass().getSimpleName(), ex); plugins.remove(plugin); } } @@ -136,11 +229,11 @@ public class PluginManager Plugin plugin; try { - plugin = instantiate(pluginDescriptor, (Class) clazz); + plugin = instantiate((Class) clazz); } catch (PluginInstantiationException ex) { - log.warn("error instantiating plugin!", ex); + log.warn("Error instantiating plugin!", ex); continue; } @@ -150,8 +243,15 @@ public class PluginManager return scannedPlugins; } - void startPlugin(Plugin plugin) throws PluginInstantiationException + public synchronized boolean startPlugin(Plugin plugin) throws PluginInstantiationException { + if (activePlugins.contains(plugin) || !isPluginEnabled(plugin)) + { + return false; + } + + activePlugins.add(plugin); + try { // plugins always start in the event thread @@ -169,22 +269,30 @@ public class PluginManager log.debug("Plugin {} is now running", plugin.getClass().getSimpleName()); eventBus.register(plugin); - eventBus.post(new PluginChanged(plugin, true)); schedule(plugin); + eventBus.post(new PluginChanged(plugin, true)); } catch (InterruptedException | InvocationTargetException ex) { throw new PluginInstantiationException(ex); } + + return true; } - void stopPlugin(Plugin plugin) throws PluginInstantiationException + public synchronized boolean stopPlugin(Plugin plugin) throws PluginInstantiationException { + if (!activePlugins.contains(plugin) || isPluginEnabled(plugin)) + { + return false; + } + + activePlugins.remove(plugin); + try { unschedule(plugin); eventBus.unregister(plugin); - eventBus.post(new PluginChanged(plugin, false)); // plugins always stop in the event thread SwingUtilities.invokeAndWait(() -> @@ -199,19 +307,37 @@ public class PluginManager } }); + log.debug("Plugin {} is now stopped", plugin.getClass().getSimpleName()); + eventBus.post(new PluginChanged(plugin, false)); + } catch (InterruptedException | InvocationTargetException ex) { throw new PluginInstantiationException(ex); } + + return true; } - Plugin instantiate(PluginDescriptor pluginDescriptor, Class clazz) throws PluginInstantiationException + public void setPluginEnabled(Plugin plugin, boolean enabled) + { + final String keyName = plugin.getClass().getSimpleName().toLowerCase(); + configManager.setConfiguration(runeliteGroupName, keyName, String.valueOf(enabled)); + } + + public boolean isPluginEnabled(Plugin plugin) + { + final String keyName = plugin.getClass().getSimpleName().toLowerCase(); + final String value = configManager.getConfiguration(runeliteGroupName, keyName); + return Boolean.valueOf(value); + } + + private Plugin instantiate(Class clazz) throws PluginInstantiationException { Plugin plugin; try { - plugin = (Plugin) clazz.newInstance(); + plugin = clazz.newInstance(); } catch (InstantiationException | IllegalAccessException ex) { @@ -222,7 +348,7 @@ public class PluginManager { Module pluginModule = (Binder binder) -> { - binder.bind((Class) clazz).toInstance(plugin); + binder.bind(clazz).toInstance(plugin); binder.install(plugin); }; Injector pluginInjector = RuneLite.getInjector().createChildInjector(pluginModule); @@ -234,7 +360,7 @@ public class PluginManager throw new PluginInstantiationException(ex); } - log.debug("Loaded plugin {}", pluginDescriptor.name()); + log.debug("Loaded plugin {}", clazz.getSimpleName()); return plugin; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java index a4b2cd59e4..1580473440 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java @@ -66,6 +66,7 @@ import net.runelite.client.config.ConfigDescriptor; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigItemDescriptor; import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.PluginManager; import net.runelite.client.ui.PluginPanel; @Slf4j @@ -74,14 +75,16 @@ public class ConfigPanel extends PluginPanel private static final int TEXT_FIELD_WIDTH = 7; private static final int SPINNER_FIELD_WIDTH = 6; + private final PluginManager pluginManager; private final ConfigManager configManager; private final JTextField searchBar = new JTextField(); private Map children = new TreeMap<>(); private int scrollBarPosition = 0; - public ConfigPanel(ConfigManager configManager) + public ConfigPanel(PluginManager pluginManager, ConfigManager configManager) { super(); + this.pluginManager = pluginManager; this.configManager = configManager; searchBar.getDocument().addDocumentListener(new DocumentListener() @@ -112,7 +115,7 @@ public class ConfigPanel extends PluginPanel final void rebuildPluginList() { Map newChildren = new TreeMap<>(); - configManager.getConfigProxies() + pluginManager.getPluginConfigProxies() .stream() // Convert config proxies to pair of config descriptors and config proxies .map(c -> new AbstractMap.SimpleEntry<>(configManager.getConfigDescriptor(c), c)) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java index 02d3210c56..94978d843a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java @@ -32,6 +32,7 @@ import net.runelite.client.config.ConfigManager; import net.runelite.client.events.PluginChanged; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.PluginManager; import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; @@ -47,13 +48,16 @@ public class ConfigPlugin extends Plugin @Inject private ConfigManager configManager; + @Inject + PluginManager pluginManager; + private ConfigPanel configPanel; private NavigationButton navButton; @Override protected void startUp() throws Exception { - configPanel = new ConfigPanel(configManager); + configPanel = new ConfigPanel(pluginManager, configManager); navButton = new NavigationButton( "Configuration", diff --git a/runelite-client/src/main/java/net/runelite/client/ui/PluginToolbar.java b/runelite-client/src/main/java/net/runelite/client/ui/PluginToolbar.java index 6dd3e9944c..a9534d63e5 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/PluginToolbar.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/PluginToolbar.java @@ -59,6 +59,7 @@ public class PluginToolbar extends JToolBar buttons.add(button); add(button); revalidate(); + repaint(); } public void removeNavigation(NavigationButton button) @@ -66,6 +67,7 @@ public class PluginToolbar extends JToolBar buttons.remove(button); remove(button); revalidate(); + repaint(); } private void onClick(NavigationButton button) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java index 7be716baf4..462dfaa9c3 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java @@ -32,9 +32,10 @@ import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.BufferedImage; -import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -44,11 +45,11 @@ import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.GameState; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.ResizeableChanged; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; -import net.runelite.api.events.GameStateChanged; import net.runelite.client.events.PluginChanged; -import net.runelite.api.events.ResizeableChanged; import net.runelite.client.plugins.PluginManager; import net.runelite.client.ui.overlay.infobox.InfoBoxOverlay; import net.runelite.client.ui.overlay.tooltip.TooltipOverlay; @@ -75,7 +76,7 @@ public class OverlayRenderer @Inject TooltipOverlay tooltipOverlay; - private final List overlays = new ArrayList<>(); + private final List overlays = new CopyOnWriteArrayList<>(); private BufferedImage surface; private Graphics2D surfaceGraphics; @@ -107,16 +108,7 @@ public class OverlayRenderer @Subscribe public void onPluginChanged(PluginChanged event) { - if (event.isLoaded()) - { - overlays.addAll(event.getPlugin().getOverlays()); - } - else - { - overlays.removeAll(event.getPlugin().getOverlays()); - } - - sortOverlays(overlays); + refreshPlugins(); } private void refreshPlugins() @@ -126,8 +118,10 @@ public class OverlayRenderer .concat( pluginManager.getPlugins() .stream() + .filter(plugin -> pluginManager.isPluginEnabled(plugin)) .flatMap(plugin -> plugin.getOverlays().stream()), Stream.of(infoBoxOverlay, tooltipOverlay)) + .filter(Objects::nonNull) .collect(Collectors.toList())); sortOverlays(overlays); }