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