diff --git a/api/src/main/java/net/md_5/bungee/api/plugin/PluginDescription.java b/api/src/main/java/net/md_5/bungee/api/plugin/PluginDescription.java index 619dc516..9c9daa74 100644 --- a/api/src/main/java/net/md_5/bungee/api/plugin/PluginDescription.java +++ b/api/src/main/java/net/md_5/bungee/api/plugin/PluginDescription.java @@ -1,5 +1,6 @@ package net.md_5.bungee.api.plugin; +import java.util.Set; import lombok.AllArgsConstructor; import lombok.Data; @@ -27,6 +28,10 @@ public class PluginDescription * Plugin author. */ private String author; + /** + * Plugin hard dependencies. + */ + private Set depends; public PluginDescription() { diff --git a/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java b/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java index e5a86dae..cdfaae1e 100644 --- a/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java +++ b/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java @@ -11,6 +11,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Stack; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.logging.Level; @@ -136,21 +137,86 @@ public class PluginManager */ public void enablePlugins() { + Map pluginStatuses = new HashMap<>(); for ( Map.Entry entry : plugins.entrySet() ) { Plugin plugin = entry.getValue(); + if ( !this.enablePlugin( pluginStatuses, new Stack(), plugin ) ) + { + ProxyServer.getInstance().getLogger().warning( "Failed to enable " + entry.getKey() ); + } + } + } + + private boolean enablePlugin(Map pluginStatuses, Stack dependStack, Plugin plugin) + { + if ( pluginStatuses.containsKey(plugin) ) + { + return pluginStatuses.get(plugin); + } + + // success status + boolean status = true; + + // try to load dependencies first + for ( String dependName : plugin.getDescription().getDepends() ) + { + Plugin depend = this.plugins.get(dependName); + Boolean dependStatus = depend != null ? pluginStatuses.get(depend) : Boolean.FALSE; + + if ( dependStatus == null ) { + if ( dependStack.contains(depend) ) + { + StringBuilder dependencyGraph = new StringBuilder(); + for ( Plugin element : dependStack ) + { + dependencyGraph.append(element.getDescription().getName()).append(" -> "); + } + dependencyGraph.append(plugin.getDescription().getName()).append(" -> ").append(dependName); + ProxyServer.getInstance().getLogger().log( Level.WARNING, "Circular dependency detected: " + dependencyGraph ); + status = false; + } else + { + dependStack.push( plugin ); + dependStatus = this.enablePlugin( pluginStatuses, dependStack, depend ); + dependStack.pop(); + } + } + + if ( dependStatus == Boolean.FALSE ) + { + ProxyServer.getInstance().getLogger().log( Level.WARNING, "{0} (required by {1}) is unavailable", new Object[] + { + depend.getDescription().getName(), plugin.getDescription().getName() + } ); + status = false; + } + + if ( !status ) + { + break; + } + } + + // do actual loading + if ( status ) + { try { plugin.onEnable(); ProxyServer.getInstance().getLogger().log( Level.INFO, "Enabled plugin {0} version {1} by {2}", new Object[] { - entry.getKey(), plugin.getDescription().getVersion(), plugin.getDescription().getAuthor() + plugin.getDescription().getName(), plugin.getDescription().getVersion(), plugin.getDescription().getAuthor() } ); } catch ( Exception ex ) { - ProxyServer.getInstance().getLogger().log( Level.WARNING, "Exception encountered when loading plugin: " + entry.getKey(), ex ); + ProxyServer.getInstance().getLogger().log( Level.WARNING, "Exception encountered when loading plugin: " + plugin.getDescription().getName(), ex ); + status = false; } } + + pluginStatuses.put( plugin, status ); + return status; } /**