diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/PluginsCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/PluginsCommand.java index 11fbd0e0c9..bcb576a427 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/PluginsCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/PluginsCommand.java @@ -45,6 +45,10 @@ public class PluginsCommand extends BukkitCommand { pluginList.append(plugin.isEnabled() ? ChatColor.GREEN : ChatColor.RED); pluginList.append(plugin.getDescription().getName()); + + if (plugin.getDescription().getProvides().size() > 0) { + pluginList.append(" (").append(String.join(", ", plugin.getDescription().getProvides())).append(")"); + } } return "(" + plugins.length + "): " + pluginList.toString(); diff --git a/paper-api/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java b/paper-api/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java index cb31a6d5ad..70d508190d 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java +++ b/paper-api/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java @@ -64,6 +64,10 @@ import org.yaml.snakeyaml.nodes.Tag; *
provides
version
* A plugin.yml example:
*name: Inferno + *provides: [Hell] *version: 1.4.1 *description: This plugin is so 31337. You can set yourself on fire. *# We could place every author in the authors list, but chose not to for illustrative purposes @@ -218,6 +223,7 @@ public final class PluginDescriptionFile { }; String rawName = null; private String name = null; + private Listprovides = ImmutableList.of(); private String main = null; private String classLoaderOf = null; private List depend = ImmutableList.of(); @@ -299,6 +305,37 @@ public final class PluginDescriptionFile { return name; } + /** + * Gives the list of other plugin APIs which this plugin provides. + * These are usable for other plugins to depend on. + * + *
+ *- Must consist of all alphanumeric characters, underscores, hyphon, + * and period (a-z,A-Z,0-9, _.-). Any other character will cause the + * plugin.yml to fail loading. + *
- A different plugin providing the same one or using it as their name + * will not result in the plugin to fail loading. + *
- Case sensitive. + *
- An entry of this list can be referenced in {@link #getDepend()}, + * {@link #getSoftDepend()}, and {@link #getLoadBefore()}. + *
provides
must be in YAML list + * format. + *+ * In the plugin.yml, this entry is named
provides
. + *+ * Example: + *
+ * + * @return immutable list of the plugin APIs which this plugin provides + */ + @NotNull + public Listprovides: + *- OtherPluginName + *- OldPluginNamegetProvides() { + return provides; + } + /** * Gives the version of the plugin. * @@ -459,7 +496,7 @@ public final class PluginDescriptionFile { * plugin in the network, * all plugins in that network will fail. - *
@@ -923,6 +960,8 @@ public final class PluginDescriptionFile { throw new InvalidDescriptionException(ex, "name is of wrong type"); } + provides = makePluginNameList(map, "provides"); + try { version = map.get("version").toString(); } catch (NullPointerException ex) { @@ -1080,6 +1119,9 @@ public final class PluginDescriptionFile { Mapdepend
must be in must be independ
must be in YAML list * format. *map = new HashMap (); map.put("name", name); + if (provides != null) { + map.put("provides", provides); + } map.put("main", main); map.put("version", version); map.put("order", order.toString()); diff --git a/paper-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/paper-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java index 844adf5147..d92a24acff 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java +++ b/paper-api/src/main/java/org/bukkit/plugin/SimplePluginManager.java @@ -123,6 +123,7 @@ public final class SimplePluginManager implements PluginManager { Map plugins = new HashMap (); Set loadedPlugins = new HashSet (); + Map pluginsProvided = new HashMap<>(); Map > dependencies = new HashMap >(); Map > softDependencies = new HashMap >(); @@ -165,6 +166,38 @@ public final class SimplePluginManager implements PluginManager { )); } + String removedProvided = pluginsProvided.remove(description.getName()); + if (removedProvided != null) { + server.getLogger().warning(String.format( + "Ambiguous plugin name `%s'. It is also provided by `%s'", + description.getName(), + removedProvided + )); + } + + for (String provided : description.getProvides()) { + File pluginFile = plugins.get(provided); + if (pluginFile != null) { + server.getLogger().warning(String.format( + "`%s provides `%s' while this is also the name of `%s' in `%s'", + file.getPath(), + provided, + pluginFile.getPath(), + directory.getPath() + )); + } else { + String replacedPlugin = pluginsProvided.put(provided, description.getName()); + if (replacedPlugin != null) { + server.getLogger().warning(String.format( + "`%s' is provided by both `%s' and `%s'", + provided, + description.getName(), + replacedPlugin + )); + } + } + } + Collection softDependencySet = description.getSoftDepend(); if (softDependencySet != null && !softDependencySet.isEmpty()) { if (softDependencies.containsKey(description.getName())) { @@ -224,7 +257,7 @@ public final class SimplePluginManager implements PluginManager { dependencyIterator.remove(); // We have a dependency not found - } else if (!plugins.containsKey(dependency)) { + } else if (!plugins.containsKey(dependency) && !pluginsProvided.containsKey(dependency)) { missingDependency = false; pluginIterator.remove(); softDependencies.remove(plugin); @@ -249,7 +282,7 @@ public final class SimplePluginManager implements PluginManager { String softDependency = softDependencyIterator.next(); // Soft depend is no longer around - if (!plugins.containsKey(softDependency)) { + if (!plugins.containsKey(softDependency) && !pluginsProvided.containsKey(softDependency)) { softDependencyIterator.remove(); } } @@ -265,8 +298,14 @@ public final class SimplePluginManager implements PluginManager { missingDependency = false; try { - result.add(loadPlugin(file)); - loadedPlugins.add(plugin); + Plugin loadedPlugin = loadPlugin(file); + if (loadedPlugin != null) { + result.add(loadedPlugin); + loadedPlugins.add(loadedPlugin.getName()); + loadedPlugins.addAll(loadedPlugin.getDescription().getProvides()); + } else { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'"); + } continue; } catch (InvalidPluginException ex) { server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex); @@ -290,8 +329,14 @@ public final class SimplePluginManager implements PluginManager { pluginIterator.remove(); try { - result.add(loadPlugin(file)); - loadedPlugins.add(plugin); + Plugin loadedPlugin = loadPlugin(file); + if (loadedPlugin != null) { + result.add(loadedPlugin); + loadedPlugins.add(loadedPlugin.getName()); + loadedPlugins.addAll(loadedPlugin.getDescription().getProvides()); + } else { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'"); + } break; } catch (InvalidPluginException ex) { server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex); @@ -352,6 +397,9 @@ public final class SimplePluginManager implements PluginManager { if (result != null) { plugins.add(result); lookupNames.put(result.getDescription().getName(), result); + for (String provided : result.getDescription().getProvides()) { + lookupNames.putIfAbsent(provided, result); + } } return result; @@ -796,7 +844,17 @@ public final class SimplePluginManager implements PluginManager { Preconditions.checkArgument(plugin != null, "plugin"); Preconditions.checkArgument(depend != null, "depend"); - return dependencyGraph.nodes().contains(plugin.getName()) && Graphs.reachableNodes(dependencyGraph, plugin.getName()).contains(depend.getName()); + if (dependencyGraph.nodes().contains(plugin.getName())) { + if (Graphs.reachableNodes(dependencyGraph, plugin.getName()).contains(depend.getName())) { + return true; + } + for (String provided : depend.getProvides()) { + if (Graphs.reachableNodes(dependencyGraph, plugin.getName()).contains(provided)) { + return true; + } + } + } + return false; } @Override