From 72b2ac5705c894f7e30f7296c73e310caee82b5d Mon Sep 17 00:00:00 2001 From: Max Weber Date: Thu, 15 Jul 2021 11:41:09 -0600 Subject: [PATCH 1/3] rl-client: add plugin conflict support --- .../client/plugins/PluginDescriptor.java | 15 +++++ .../client/plugins/PluginManager.java | 63 +++++++++++++++++++ .../client/plugins/config/ConfigPanel.java | 2 +- .../client/plugins/config/ConfigPlugin.java | 4 +- .../config/PluginConfigurationDescriptor.java | 11 ++++ .../client/plugins/config/PluginListItem.java | 2 +- .../plugins/config/PluginListPanel.java | 6 +- .../plugins/config/PluginToggleButton.java | 25 +++++++- 8 files changed, 121 insertions(+), 7 deletions(-) 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 0a3336d203..a555c36cf6 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 @@ -53,6 +53,18 @@ public @interface PluginDescriptor */ String[] tags() default {}; + /** + * A list of plugin names that are mutually exclusive with this plugin. Any plugins + * with a name or conflicts value that matches this will be disabled when this plugin + * is started + */ + String[] conflicts() default {}; + + /** + * If this plugin should be defaulted to on. Plugin-Hub plugins should always + * have this set to true (the default), since having them off by defaults means + * the user has to install the plugin, then separately enable it, which is confusing. + */ boolean enabledByDefault() default true; /** @@ -62,6 +74,9 @@ public @interface PluginDescriptor boolean developerPlugin() default false; + /** + * If this plugin should be loaded when there is no {@link net.runelite.api.Client} + */ boolean loadWhenOutdated() default false; boolean loadInSafeMode() default true; 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 1549e5cb74..c9c88b669a 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 @@ -47,7 +47,9 @@ import java.lang.invoke.MethodType; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Optional; @@ -380,6 +382,19 @@ public class PluginManager return false; } + List conflicts = conflictsForPlugin(plugin); + for (Plugin conflict : conflicts) + { + if (isPluginEnabled(conflict)) + { + setPluginEnabled(conflict, false); + } + if (activePlugins.contains(conflict)) + { + stopPlugin(conflict); + } + } + activePlugins.add(plugin); try @@ -445,6 +460,18 @@ public class PluginManager final PluginDescriptor pluginDescriptor = plugin.getClass().getAnnotation(PluginDescriptor.class); final String keyName = Strings.isNullOrEmpty(pluginDescriptor.configName()) ? plugin.getClass().getSimpleName() : pluginDescriptor.configName(); configManager.setConfiguration(RuneLiteConfig.GROUP_NAME, keyName.toLowerCase(), String.valueOf(enabled)); + + if (enabled) + { + List conflicts = conflictsForPlugin(plugin); + for (Plugin conflict : conflicts) + { + if (isPluginEnabled(conflict)) + { + setPluginEnabled(conflict, false); + } + } + } } public boolean isPluginEnabled(Plugin plugin) @@ -638,4 +665,40 @@ public class PluginManager } return l; } + + public List conflictsForPlugin(Plugin plugin) + { + Set conflicts; + { + PluginDescriptor desc = plugin.getClass().getAnnotation(PluginDescriptor.class); + conflicts = new HashSet<>(Arrays.asList(desc.conflicts())); + conflicts.add(desc.name()); + } + + return plugins.stream() + .filter(p -> + { + if (p == plugin) + { + return false; + } + + PluginDescriptor desc = p.getClass().getAnnotation(PluginDescriptor.class); + if (conflicts.contains(desc.name())) + { + return true; + } + + for (String conflict : desc.conflicts()) + { + if (conflicts.contains(conflict)) + { + return true; + } + } + + return false; + }) + .collect(Collectors.toList()); + } } 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 f451debee8..aaa2a42b67 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 @@ -181,7 +181,7 @@ class ConfigPanel extends PluginPanel topPanelBackButton.setToolTipText("Back"); topPanel.add(topPanelBackButton, BorderLayout.WEST); - pluginToggle = new PluginToggleButton(); + pluginToggle = new PluginToggleButton(pluginConfig.getConflicts()); topPanel.add(pluginToggle, BorderLayout.EAST); title = new JLabel(); title.setForeground(Color.WHITE); 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 41372e3895..c8b8feefb5 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 @@ -75,11 +75,11 @@ public class ConfigPlugin extends Plugin pluginListPanel.addFakePlugin(new PluginConfigurationDescriptor( "RuneLite", "RuneLite client settings", new String[]{"client", "notification", "size", "position", "window", "chrome", "focus", "font", "overlay", "tooltip", "infobox"}, - null, runeLiteConfig, configManager.getConfigDescriptor(runeLiteConfig) + runeLiteConfig, configManager.getConfigDescriptor(runeLiteConfig) ), new PluginConfigurationDescriptor( "Chat Color", "Recolor chat text", new String[]{"colour", "messages"}, - null, chatColorConfig, configManager.getConfigDescriptor(chatColorConfig) + chatColorConfig, configManager.getConfigDescriptor(chatColorConfig) )); pluginListPanel.rebuildPluginList(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java index 279991656b..1cba92649f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java @@ -24,8 +24,10 @@ */ package net.runelite.client.plugins.config; +import java.util.List; import javax.annotation.Nullable; import javax.swing.JMenuItem; +import lombok.RequiredArgsConstructor; import lombok.Value; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigDescriptor; @@ -35,6 +37,7 @@ import net.runelite.client.plugins.Plugin; import net.runelite.client.util.LinkBrowser; @Value +@RequiredArgsConstructor class PluginConfigurationDescriptor { private final String name; @@ -52,11 +55,19 @@ class PluginConfigurationDescriptor @Nullable private final ConfigDescriptor configDescriptor; + @Nullable + private final List conflicts; + boolean hasConfigurables() { return configDescriptor != null && !configDescriptor.getItems().stream().allMatch(item -> item.getItem().hidden()); } + PluginConfigurationDescriptor(String name, String description, String[] tags, Config config, ConfigDescriptor configDescriptor) + { + this(name, description, tags, null, config, configDescriptor, null); + } + /** * Creates a menu item for linking to a support page for the plugin * diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java index 767fddbc1a..ce49b44a14 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java @@ -166,7 +166,7 @@ class PluginListItem extends JPanel implements SearchablePlugin addLabelPopupMenu(nameLabel, configMenuItem, pluginConfig.createSupportMenuItem(), uninstallItem); add(nameLabel, BorderLayout.CENTER); - onOffToggle = new PluginToggleButton(); + onOffToggle = new PluginToggleButton(pluginConfig.getConflicts()); buttonPanel.add(onOffToggle); if (pluginConfig.getPlugin() != null) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java index a17e6af0c6..14a6ad1171 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java @@ -202,6 +202,9 @@ class PluginListPanel extends PluginPanel PluginDescriptor descriptor = plugin.getClass().getAnnotation(PluginDescriptor.class); Config config = pluginManager.getPluginConfigProxy(plugin); ConfigDescriptor configDescriptor = config == null ? null : configManager.getConfigDescriptor(config); + List conflicts = pluginManager.conflictsForPlugin(plugin).stream() + .map(Plugin::getName) + .collect(Collectors.toList()); return new PluginConfigurationDescriptor( descriptor.name(), @@ -209,7 +212,8 @@ class PluginListPanel extends PluginPanel descriptor.tags(), plugin, config, - configDescriptor); + configDescriptor, + conflicts); }) ) .map(desc -> diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java index 8de213ec56..f125eec9f8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java @@ -27,6 +27,7 @@ package net.runelite.client.plugins.config; import java.awt.Dimension; import java.awt.image.BufferedImage; +import java.util.List; import javax.swing.ImageIcon; import javax.swing.JToggleButton; import net.runelite.client.util.ImageUtil; @@ -51,12 +52,32 @@ class PluginToggleButton extends JToggleButton )); } - public PluginToggleButton() + public PluginToggleButton(List conflicts) { super(OFF_SWITCHER); setSelectedIcon(ON_SWITCHER); SwingUtil.removeButtonDecorations(this); setPreferredSize(new Dimension(25, 0)); - SwingUtil.addModalTooltip(this, "Disable plugin", "Enable plugin"); + + String conflictString = ""; + if (conflicts != null && !conflicts.isEmpty()) + { + StringBuilder sb = new StringBuilder("
Conflicts with "); + for (int i = 0; i < conflicts.size() - 2; i++) + { + sb.append(conflicts.get(i)); + sb.append(", "); + } + if (conflicts.size() > 2) + { + sb.append(conflicts.get(conflicts.size() - 2)); + sb.append(" and "); + } + + sb.append(conflicts.get(conflicts.size() - 1)); + conflictString = sb.toString(); + } + + SwingUtil.addModalTooltip(this, "Disable plugin", "Enable plugin" + conflictString); } } From 62a9e4f79b3a402ccdd31681b665176cd4f04a03 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Sat, 11 Sep 2021 19:33:34 -0600 Subject: [PATCH 2/3] Revert "rl-client: add plugin conflict support" This reverts commit 72b2ac5705c894f7e30f7296c73e310caee82b5d. --- .../client/plugins/PluginDescriptor.java | 15 ----- .../client/plugins/PluginManager.java | 63 ------------------- .../client/plugins/config/ConfigPanel.java | 2 +- .../client/plugins/config/ConfigPlugin.java | 4 +- .../config/PluginConfigurationDescriptor.java | 11 ---- .../client/plugins/config/PluginListItem.java | 2 +- .../plugins/config/PluginListPanel.java | 6 +- .../plugins/config/PluginToggleButton.java | 25 +------- 8 files changed, 7 insertions(+), 121 deletions(-) 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 a555c36cf6..0a3336d203 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 @@ -53,18 +53,6 @@ public @interface PluginDescriptor */ String[] tags() default {}; - /** - * A list of plugin names that are mutually exclusive with this plugin. Any plugins - * with a name or conflicts value that matches this will be disabled when this plugin - * is started - */ - String[] conflicts() default {}; - - /** - * If this plugin should be defaulted to on. Plugin-Hub plugins should always - * have this set to true (the default), since having them off by defaults means - * the user has to install the plugin, then separately enable it, which is confusing. - */ boolean enabledByDefault() default true; /** @@ -74,9 +62,6 @@ public @interface PluginDescriptor boolean developerPlugin() default false; - /** - * If this plugin should be loaded when there is no {@link net.runelite.api.Client} - */ boolean loadWhenOutdated() default false; boolean loadInSafeMode() default true; 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 c9c88b669a..1549e5cb74 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 @@ -47,9 +47,7 @@ import java.lang.invoke.MethodType; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Optional; @@ -382,19 +380,6 @@ public class PluginManager return false; } - List conflicts = conflictsForPlugin(plugin); - for (Plugin conflict : conflicts) - { - if (isPluginEnabled(conflict)) - { - setPluginEnabled(conflict, false); - } - if (activePlugins.contains(conflict)) - { - stopPlugin(conflict); - } - } - activePlugins.add(plugin); try @@ -460,18 +445,6 @@ public class PluginManager final PluginDescriptor pluginDescriptor = plugin.getClass().getAnnotation(PluginDescriptor.class); final String keyName = Strings.isNullOrEmpty(pluginDescriptor.configName()) ? plugin.getClass().getSimpleName() : pluginDescriptor.configName(); configManager.setConfiguration(RuneLiteConfig.GROUP_NAME, keyName.toLowerCase(), String.valueOf(enabled)); - - if (enabled) - { - List conflicts = conflictsForPlugin(plugin); - for (Plugin conflict : conflicts) - { - if (isPluginEnabled(conflict)) - { - setPluginEnabled(conflict, false); - } - } - } } public boolean isPluginEnabled(Plugin plugin) @@ -665,40 +638,4 @@ public class PluginManager } return l; } - - public List conflictsForPlugin(Plugin plugin) - { - Set conflicts; - { - PluginDescriptor desc = plugin.getClass().getAnnotation(PluginDescriptor.class); - conflicts = new HashSet<>(Arrays.asList(desc.conflicts())); - conflicts.add(desc.name()); - } - - return plugins.stream() - .filter(p -> - { - if (p == plugin) - { - return false; - } - - PluginDescriptor desc = p.getClass().getAnnotation(PluginDescriptor.class); - if (conflicts.contains(desc.name())) - { - return true; - } - - for (String conflict : desc.conflicts()) - { - if (conflicts.contains(conflict)) - { - return true; - } - } - - return false; - }) - .collect(Collectors.toList()); - } } 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 aaa2a42b67..f451debee8 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 @@ -181,7 +181,7 @@ class ConfigPanel extends PluginPanel topPanelBackButton.setToolTipText("Back"); topPanel.add(topPanelBackButton, BorderLayout.WEST); - pluginToggle = new PluginToggleButton(pluginConfig.getConflicts()); + pluginToggle = new PluginToggleButton(); topPanel.add(pluginToggle, BorderLayout.EAST); title = new JLabel(); title.setForeground(Color.WHITE); 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 c8b8feefb5..41372e3895 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 @@ -75,11 +75,11 @@ public class ConfigPlugin extends Plugin pluginListPanel.addFakePlugin(new PluginConfigurationDescriptor( "RuneLite", "RuneLite client settings", new String[]{"client", "notification", "size", "position", "window", "chrome", "focus", "font", "overlay", "tooltip", "infobox"}, - runeLiteConfig, configManager.getConfigDescriptor(runeLiteConfig) + null, runeLiteConfig, configManager.getConfigDescriptor(runeLiteConfig) ), new PluginConfigurationDescriptor( "Chat Color", "Recolor chat text", new String[]{"colour", "messages"}, - chatColorConfig, configManager.getConfigDescriptor(chatColorConfig) + null, chatColorConfig, configManager.getConfigDescriptor(chatColorConfig) )); pluginListPanel.rebuildPluginList(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java index 1cba92649f..279991656b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java @@ -24,10 +24,8 @@ */ package net.runelite.client.plugins.config; -import java.util.List; import javax.annotation.Nullable; import javax.swing.JMenuItem; -import lombok.RequiredArgsConstructor; import lombok.Value; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigDescriptor; @@ -37,7 +35,6 @@ import net.runelite.client.plugins.Plugin; import net.runelite.client.util.LinkBrowser; @Value -@RequiredArgsConstructor class PluginConfigurationDescriptor { private final String name; @@ -55,19 +52,11 @@ class PluginConfigurationDescriptor @Nullable private final ConfigDescriptor configDescriptor; - @Nullable - private final List conflicts; - boolean hasConfigurables() { return configDescriptor != null && !configDescriptor.getItems().stream().allMatch(item -> item.getItem().hidden()); } - PluginConfigurationDescriptor(String name, String description, String[] tags, Config config, ConfigDescriptor configDescriptor) - { - this(name, description, tags, null, config, configDescriptor, null); - } - /** * Creates a menu item for linking to a support page for the plugin * diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java index ce49b44a14..767fddbc1a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java @@ -166,7 +166,7 @@ class PluginListItem extends JPanel implements SearchablePlugin addLabelPopupMenu(nameLabel, configMenuItem, pluginConfig.createSupportMenuItem(), uninstallItem); add(nameLabel, BorderLayout.CENTER); - onOffToggle = new PluginToggleButton(pluginConfig.getConflicts()); + onOffToggle = new PluginToggleButton(); buttonPanel.add(onOffToggle); if (pluginConfig.getPlugin() != null) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java index 14a6ad1171..a17e6af0c6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java @@ -202,9 +202,6 @@ class PluginListPanel extends PluginPanel PluginDescriptor descriptor = plugin.getClass().getAnnotation(PluginDescriptor.class); Config config = pluginManager.getPluginConfigProxy(plugin); ConfigDescriptor configDescriptor = config == null ? null : configManager.getConfigDescriptor(config); - List conflicts = pluginManager.conflictsForPlugin(plugin).stream() - .map(Plugin::getName) - .collect(Collectors.toList()); return new PluginConfigurationDescriptor( descriptor.name(), @@ -212,8 +209,7 @@ class PluginListPanel extends PluginPanel descriptor.tags(), plugin, config, - configDescriptor, - conflicts); + configDescriptor); }) ) .map(desc -> diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java index f125eec9f8..8de213ec56 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java @@ -27,7 +27,6 @@ package net.runelite.client.plugins.config; import java.awt.Dimension; import java.awt.image.BufferedImage; -import java.util.List; import javax.swing.ImageIcon; import javax.swing.JToggleButton; import net.runelite.client.util.ImageUtil; @@ -52,32 +51,12 @@ class PluginToggleButton extends JToggleButton )); } - public PluginToggleButton(List conflicts) + public PluginToggleButton() { super(OFF_SWITCHER); setSelectedIcon(ON_SWITCHER); SwingUtil.removeButtonDecorations(this); setPreferredSize(new Dimension(25, 0)); - - String conflictString = ""; - if (conflicts != null && !conflicts.isEmpty()) - { - StringBuilder sb = new StringBuilder("
Conflicts with "); - for (int i = 0; i < conflicts.size() - 2; i++) - { - sb.append(conflicts.get(i)); - sb.append(", "); - } - if (conflicts.size() > 2) - { - sb.append(conflicts.get(conflicts.size() - 2)); - sb.append(" and "); - } - - sb.append(conflicts.get(conflicts.size() - 1)); - conflictString = sb.toString(); - } - - SwingUtil.addModalTooltip(this, "Disable plugin", "Enable plugin" + conflictString); + SwingUtil.addModalTooltip(this, "Disable plugin", "Enable plugin"); } } From 24511aea4e50474768ab751e8214676af14e6dd8 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Thu, 15 Jul 2021 11:41:09 -0600 Subject: [PATCH 3/3] rl-client: add plugin conflict support --- .../client/plugins/PluginDescriptor.java | 15 +++++ .../client/plugins/PluginManager.java | 63 +++++++++++++++++++ .../client/plugins/config/ConfigPanel.java | 1 + .../client/plugins/config/ConfigPlugin.java | 4 +- .../config/PluginConfigurationDescriptor.java | 11 ++++ .../client/plugins/config/PluginListItem.java | 3 +- .../plugins/config/PluginListPanel.java | 6 +- .../plugins/config/PluginToggleButton.java | 38 ++++++++++- 8 files changed, 136 insertions(+), 5 deletions(-) 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 0a3336d203..a555c36cf6 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 @@ -53,6 +53,18 @@ public @interface PluginDescriptor */ String[] tags() default {}; + /** + * A list of plugin names that are mutually exclusive with this plugin. Any plugins + * with a name or conflicts value that matches this will be disabled when this plugin + * is started + */ + String[] conflicts() default {}; + + /** + * If this plugin should be defaulted to on. Plugin-Hub plugins should always + * have this set to true (the default), since having them off by defaults means + * the user has to install the plugin, then separately enable it, which is confusing. + */ boolean enabledByDefault() default true; /** @@ -62,6 +74,9 @@ public @interface PluginDescriptor boolean developerPlugin() default false; + /** + * If this plugin should be loaded when there is no {@link net.runelite.api.Client} + */ boolean loadWhenOutdated() default false; boolean loadInSafeMode() default true; 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 1549e5cb74..c9c88b669a 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 @@ -47,7 +47,9 @@ import java.lang.invoke.MethodType; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Optional; @@ -380,6 +382,19 @@ public class PluginManager return false; } + List conflicts = conflictsForPlugin(plugin); + for (Plugin conflict : conflicts) + { + if (isPluginEnabled(conflict)) + { + setPluginEnabled(conflict, false); + } + if (activePlugins.contains(conflict)) + { + stopPlugin(conflict); + } + } + activePlugins.add(plugin); try @@ -445,6 +460,18 @@ public class PluginManager final PluginDescriptor pluginDescriptor = plugin.getClass().getAnnotation(PluginDescriptor.class); final String keyName = Strings.isNullOrEmpty(pluginDescriptor.configName()) ? plugin.getClass().getSimpleName() : pluginDescriptor.configName(); configManager.setConfiguration(RuneLiteConfig.GROUP_NAME, keyName.toLowerCase(), String.valueOf(enabled)); + + if (enabled) + { + List conflicts = conflictsForPlugin(plugin); + for (Plugin conflict : conflicts) + { + if (isPluginEnabled(conflict)) + { + setPluginEnabled(conflict, false); + } + } + } } public boolean isPluginEnabled(Plugin plugin) @@ -638,4 +665,40 @@ public class PluginManager } return l; } + + public List conflictsForPlugin(Plugin plugin) + { + Set conflicts; + { + PluginDescriptor desc = plugin.getClass().getAnnotation(PluginDescriptor.class); + conflicts = new HashSet<>(Arrays.asList(desc.conflicts())); + conflicts.add(desc.name()); + } + + return plugins.stream() + .filter(p -> + { + if (p == plugin) + { + return false; + } + + PluginDescriptor desc = p.getClass().getAnnotation(PluginDescriptor.class); + if (conflicts.contains(desc.name())) + { + return true; + } + + for (String conflict : desc.conflicts()) + { + if (conflicts.contains(conflict)) + { + return true; + } + } + + return false; + }) + .collect(Collectors.toList()); + } } 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 f451debee8..7015d4b1c7 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 @@ -211,6 +211,7 @@ class ConfigPanel extends PluginPanel if (pluginConfig.getPlugin() != null) { + pluginToggle.setConflicts(pluginConfig.getConflicts()); pluginToggle.setSelected(pluginManager.isPluginEnabled(pluginConfig.getPlugin())); pluginToggle.addItemListener(i -> { 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 41372e3895..c8b8feefb5 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 @@ -75,11 +75,11 @@ public class ConfigPlugin extends Plugin pluginListPanel.addFakePlugin(new PluginConfigurationDescriptor( "RuneLite", "RuneLite client settings", new String[]{"client", "notification", "size", "position", "window", "chrome", "focus", "font", "overlay", "tooltip", "infobox"}, - null, runeLiteConfig, configManager.getConfigDescriptor(runeLiteConfig) + runeLiteConfig, configManager.getConfigDescriptor(runeLiteConfig) ), new PluginConfigurationDescriptor( "Chat Color", "Recolor chat text", new String[]{"colour", "messages"}, - null, chatColorConfig, configManager.getConfigDescriptor(chatColorConfig) + chatColorConfig, configManager.getConfigDescriptor(chatColorConfig) )); pluginListPanel.rebuildPluginList(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java index 279991656b..1cba92649f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java @@ -24,8 +24,10 @@ */ package net.runelite.client.plugins.config; +import java.util.List; import javax.annotation.Nullable; import javax.swing.JMenuItem; +import lombok.RequiredArgsConstructor; import lombok.Value; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigDescriptor; @@ -35,6 +37,7 @@ import net.runelite.client.plugins.Plugin; import net.runelite.client.util.LinkBrowser; @Value +@RequiredArgsConstructor class PluginConfigurationDescriptor { private final String name; @@ -52,11 +55,19 @@ class PluginConfigurationDescriptor @Nullable private final ConfigDescriptor configDescriptor; + @Nullable + private final List conflicts; + boolean hasConfigurables() { return configDescriptor != null && !configDescriptor.getItems().stream().allMatch(item -> item.getItem().hidden()); } + PluginConfigurationDescriptor(String name, String description, String[] tags, Config config, ConfigDescriptor configDescriptor) + { + this(name, description, tags, null, config, configDescriptor, null); + } + /** * Creates a menu item for linking to a support page for the plugin * diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java index 767fddbc1a..64a4cc41b0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java @@ -69,7 +69,7 @@ class PluginListItem extends JPanel implements SearchablePlugin private final List keywords = new ArrayList<>(); private final JToggleButton pinButton; - private final JToggleButton onOffToggle; + private final PluginToggleButton onOffToggle; static { @@ -167,6 +167,7 @@ class PluginListItem extends JPanel implements SearchablePlugin add(nameLabel, BorderLayout.CENTER); onOffToggle = new PluginToggleButton(); + onOffToggle.setConflicts(pluginConfig.getConflicts()); buttonPanel.add(onOffToggle); if (pluginConfig.getPlugin() != null) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java index a17e6af0c6..14a6ad1171 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java @@ -202,6 +202,9 @@ class PluginListPanel extends PluginPanel PluginDescriptor descriptor = plugin.getClass().getAnnotation(PluginDescriptor.class); Config config = pluginManager.getPluginConfigProxy(plugin); ConfigDescriptor configDescriptor = config == null ? null : configManager.getConfigDescriptor(config); + List conflicts = pluginManager.conflictsForPlugin(plugin).stream() + .map(Plugin::getName) + .collect(Collectors.toList()); return new PluginConfigurationDescriptor( descriptor.name(), @@ -209,7 +212,8 @@ class PluginListPanel extends PluginPanel descriptor.tags(), plugin, config, - configDescriptor); + configDescriptor, + conflicts); }) ) .map(desc -> diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java index 8de213ec56..637fa189a6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java @@ -27,6 +27,7 @@ package net.runelite.client.plugins.config; import java.awt.Dimension; import java.awt.image.BufferedImage; +import java.util.List; import javax.swing.ImageIcon; import javax.swing.JToggleButton; import net.runelite.client.util.ImageUtil; @@ -51,12 +52,47 @@ class PluginToggleButton extends JToggleButton )); } + private String conflictString = ""; + public PluginToggleButton() { super(OFF_SWITCHER); setSelectedIcon(ON_SWITCHER); SwingUtil.removeButtonDecorations(this); setPreferredSize(new Dimension(25, 0)); - SwingUtil.addModalTooltip(this, "Disable plugin", "Enable plugin"); + addItemListener(l -> updateTooltip()); + updateTooltip(); + } + + private void updateTooltip() + { + setToolTipText(isSelected() ? "Disable plugin" : "Enable plugin" + conflictString); + } + + public void setConflicts(List conflicts) + { + if (conflicts != null && !conflicts.isEmpty()) + { + StringBuilder sb = new StringBuilder("
Conflicts with "); + for (int i = 0; i < conflicts.size() - 2; i++) + { + sb.append(conflicts.get(i)); + sb.append(", "); + } + if (conflicts.size() > 2) + { + sb.append(conflicts.get(conflicts.size() - 2)); + sb.append(" and "); + } + + sb.append(conflicts.get(conflicts.size() - 1)); + conflictString = sb.toString(); + } + else + { + conflictString = ""; + } + + updateTooltip(); } }