PluginManager { public List<Plugin> loadPlugins(List<Class<?>> plugins, BiConsumer<Integer, Integer> onPluginLoaded) throws PluginInstantiationException { MutableGraph<Class<? extends Plugin>> graph = GraphBuilder .directed() .build(); for (Class<?> clazz : plugins) { PluginDescriptor pluginDescriptor = clazz.getAnnotation(PluginDescriptor.class); if (pluginDescriptor == null) { if (clazz.getSuperclass() == Plugin.class) { log.warn("Class {} is a plugin, but has no plugin descriptor", clazz); } continue; } if (clazz.getSuperclass() != Plugin.class) { log.warn("Class {} has plugin descriptor, but is not a plugin", clazz); continue; } if (!pluginDescriptor.loadWhenOutdated() && isOutdated) { continue; } if (pluginDescriptor.developerPlugin() && !developerMode) { continue; } if (safeMode && !pluginDescriptor.loadInSafeMode()) { log.debug("Disabling {} due to safe mode", clazz); configManager.unsetConfiguration(RuneLiteConfig.GROUP_NAME, clazz.getSimpleName().toLowerCase()); continue; } Class<Plugin> pluginClass = (Class<Plugin>) clazz; graph.addNode(pluginClass); } for (Class<? extends Plugin> pluginClazz : graph.nodes()) { PluginDependency[] pluginDependencies = pluginClazz.getAnnotationsByType(PluginDependency.class); for (PluginDependency pluginDependency : pluginDependencies) { if (graph.nodes().contains(pluginDependency.value())) { graph.putEdge(pluginClazz, pluginDependency.value()); } } } if (Graphs.hasCycle(graph)) { throw new PluginInstantiationException("Plugin dependency graph contains a cycle!"); } List<Class<? extends Plugin>> sortedPlugins = topologicalSort(graph); sortedPlugins = Lists.reverse(sortedPlugins); int loaded = 0; List<Plugin> newPlugins = new ArrayList<>(); for (Class<? extends Plugin> pluginClazz : sortedPlugins) { Plugin plugin; try { plugin = instantiate(this.plugins, (Class<Plugin>) pluginClazz); newPlugins.add(plugin); this.plugins.add(plugin); } catch (PluginInstantiationException ex) { log.warn("Error instantiating plugin!", ex); } loaded++; if (onPluginLoaded != null) { onPluginLoaded.accept(loaded, sortedPlugins.size()); } } return newPlugins; } @Inject @VisibleForTesting  PluginManager(
		@Named("developerMode") final boolean developerMode,
		@Named("safeMode") final boolean safeMode,
		final EventBus eventBus,
		final Scheduler scheduler,
		final ConfigManager configManager,
		final ScheduledExecutorService executor,
		final Provider<GameEventManager> sceneTileManager); @Subscribe void onSessionOpen(SessionOpen event); @Subscribe void onSessionClose(SessionClose event); Config getPluginConfigProxy(Plugin plugin); List<Config> getPluginConfigProxies(Collection<Plugin> plugins); void loadDefaultPluginConfiguration(Collection<Plugin> plugins); void startPlugins(); void loadCorePlugins(); List<Plugin> loadPlugins(List<Class<?>> plugins, BiConsumer<Integer, Integer> onPluginLoaded); boolean startPlugin(Plugin plugin); boolean stopPlugin(Plugin plugin); void setPluginEnabled(Plugin plugin, boolean enabled); boolean isPluginEnabled(Plugin plugin); void add(Plugin plugin); void remove(Plugin plugin); Collection<Plugin> getPlugins();  }
@Test public void testLoadPlugins() throws Exception { PluginManager pluginManager = new PluginManager(false, false, null, null, null, null, null); pluginManager.setOutdated(true); pluginManager.loadCorePlugins(); Collection<Plugin> plugins = pluginManager.getPlugins(); long expected = pluginClasses.stream() .map(cl -> cl.getAnnotation(PluginDescriptor.class)) .filter(Objects::nonNull) .filter(PluginDescriptor::loadWhenOutdated) .count(); assertEquals(expected, plugins.size()); pluginManager = new PluginManager(false, false, null, null, null, null, null); pluginManager.loadCorePlugins(); plugins = pluginManager.getPlugins(); EventBus eventBus = new EventBus(); plugins.forEach(eventBus::register); expected = pluginClasses.stream() .map(cl -> cl.getAnnotation(PluginDescriptor.class)) .filter(Objects::nonNull) .filter(pd -> !pd.developerPlugin()) .count(); assertEquals(expected, plugins.size()); }