From aec6ad036ba949cc7c81bab6ca39ae991e3c799e Mon Sep 17 00:00:00 2001 From: Bukkit/Spigot Date: Sun, 15 Mar 2020 18:59:32 +1100 Subject: [PATCH] #474: Add ability to set other plugin names as provided API so others can still depend on it By: Phoenix616 --- .../command/defaults/PluginsCommand.java | 4 ++ .../bukkit/plugin/PluginDescriptionFile.java | 44 +++++++++++- .../bukkit/plugin/SimplePluginManager.java | 72 +++++++++++++++++-- 3 files changed, 112 insertions(+), 8 deletions(-) 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; * {@link #getName()} * The unique name of plugin * + * provides + * {@link #getProvides()} + * The plugin APIs which this plugin provides + * * version * {@link #getVersion()} * A plugin revision identifier @@ -130,6 +134,7 @@ import org.yaml.snakeyaml.nodes.Tag; *

* 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 List provides = 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: + *

provides:
+     *- OtherPluginName
+     *- OldPluginName
+ * + * @return immutable list of the plugin APIs which this plugin provides + */ + @NotNull + public List getProvides() { + return provides; + } + /** * Gives the version of the plugin. * @@ -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 { Map 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