From e1bfcfc365668c5c20021e9a6fd11bc8fafae3d0 Mon Sep 17 00:00:00 2001 From: Owain van Brakel Date: Mon, 25 May 2020 05:26:51 +0200 Subject: [PATCH] externalpluginmanager: Mimic PluginManager.scanAndInstantiate closer --- .../client/plugins/ExternalPluginManager.java | 132 +++++++++++------- .../client/plugins/PluginManager.java | 4 +- 2 files changed, 86 insertions(+), 50 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginManager.java index d9ad5a4917..27c8dd2629 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginManager.java @@ -24,6 +24,10 @@ */ package net.runelite.client.plugins; +import com.google.common.collect.Lists; +import com.google.common.graph.GraphBuilder; +import com.google.common.graph.Graphs; +import com.google.common.graph.MutableGraph; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.inject.Binder; import com.google.inject.CreationException; @@ -400,9 +404,66 @@ public class ExternalPluginManager private void scanAndInstantiate(List plugins, boolean init, boolean initConfig) { RuneLiteSplashScreen.stage(.66, "Loading external plugins"); + MutableGraph> graph = GraphBuilder + .directed() + .build(); + for (Plugin plugin : plugins) + { + Class clazz = plugin.getClass(); + PluginDescriptor pluginDescriptor = clazz.getAnnotation(PluginDescriptor.class); + + try + { + if (pluginDescriptor == null) + { + if (Plugin.class.isAssignableFrom(clazz)) + { + log.warn("Class {} is a plugin, but has no plugin descriptor", clazz); + } + continue; + } + else if (!Plugin.class.isAssignableFrom(clazz)) + { + log.warn("Class {} has plugin descriptor, but is not a plugin", clazz); + continue; + } + else if (!pluginTypes.contains(pluginDescriptor.type())) + { + continue; + } + } + catch (EnumConstantNotPresentException e) + { + log.warn("{} has an invalid plugin type of {}", clazz, e.getMessage()); + continue; + } + + @SuppressWarnings("unchecked") Class pluginClass = (Class) clazz; + graph.addNode(pluginClass); + } + + // Build plugin graph + for (Class pluginClazz : graph.nodes()) + { + net.runelite.client.plugins.PluginDependency[] pluginDependencies = pluginClazz.getAnnotationsByType(net.runelite.client.plugins.PluginDependency.class); + + for (net.runelite.client.plugins.PluginDependency pluginDependency : pluginDependencies) + { + graph.putEdge(pluginClazz, pluginDependency.value()); + } + } + + if (Graphs.hasCycle(graph)) + { + throw new RuntimeException("Plugin dependency graph contains a cycle!"); + } + + List>> sortedPlugins = PluginManager.topologicalGroupSort(graph); + sortedPlugins = Lists.reverse(sortedPlugins); AtomicInteger loaded = new AtomicInteger(); - List scannedPlugins = new CopyOnWriteArrayList<>(); + + final long start = System.currentTimeMillis(); // some plugins get stuck on IO, so add some extra threads ExecutorService exec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2, @@ -412,57 +473,30 @@ public class ExternalPluginManager try { - for (Plugin plugin : plugins) + List scannedPlugins = new CopyOnWriteArrayList<>(); + sortedPlugins.forEach(group -> { - Class clazz = plugin.getClass(); - PluginDescriptor pluginDescriptor = clazz.getAnnotation(PluginDescriptor.class); - - try - { - if (pluginDescriptor == null) - { - if (Plugin.class.isAssignableFrom(clazz)) - { - log.warn("Class {} is a plugin, but has no plugin descriptor", clazz); - } - continue; - } - else if (!Plugin.class.isAssignableFrom(clazz)) - { - log.warn("Class {} has plugin descriptor, but is not a plugin", clazz); - continue; - } - else if (!pluginTypes.contains(pluginDescriptor.type())) - { - continue; - } - } - catch (EnumConstantNotPresentException e) - { - log.warn("{} has an invalid plugin type of {}", clazz, e.getMessage()); - continue; - } - List> curGroup = new ArrayList<>(); - curGroup.add(exec.submit(() -> - { - Plugin plugininst; - try + group.forEach(pluginClazz -> + curGroup.add(exec.submit(() -> { - //noinspection unchecked - plugininst = instantiate(scannedPlugins, (Class) plugin.getClass(), init, initConfig); - scannedPlugins.add(plugininst); - } - catch (PluginInstantiationException e) - { - log.warn("Error instantiating plugin!", e); - return; - } + Plugin plugininst; + try + { + //noinspection unchecked + plugininst = instantiate(scannedPlugins, (Class) pluginClazz, init, initConfig); + scannedPlugins.add(plugininst); + } + catch (PluginInstantiationException e) + { + log.warn("Error instantiating plugin!", e); + return; + } - loaded.getAndIncrement(); + loaded.getAndIncrement(); - RuneLiteSplashScreen.stage(.67, .75, "Loading external plugins", loaded.get(), scannedPlugins.size()); - })); + RuneLiteSplashScreen.stage(.67, .75, "Loading external plugins", loaded.get(), scannedPlugins.size()); + }))); curGroup.forEach(future -> { try @@ -474,7 +508,9 @@ public class ExternalPluginManager log.warn("Could not instantiate external plugin", e); } }); - } + }); + + log.info("External plugin instantiation took {}ms", System.currentTimeMillis() - start); } finally { 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 5a8e57b425..5788b916f3 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 @@ -666,7 +666,7 @@ public class PluginManager * @param * @return */ - private List> topologicalGroupSort(Graph graph) + static List> topologicalGroupSort(Graph graph) { final Set root = graph.nodes().stream() .filter(node -> graph.inDegree(node) == 0) @@ -696,7 +696,7 @@ public class PluginManager return dependencyGroups; } - private void incrementChildren(Graph graph, Map dependencyCount, T n, int val) + private static void incrementChildren(Graph graph, Map dependencyCount, T n, int val) { if (!dependencyCount.containsKey(n) || dependencyCount.get(n) < val) {