diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java index 83e9b69bd..02f3391d2 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java @@ -17,6 +17,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; @@ -68,6 +69,11 @@ public class VelocityPluginManager implements PluginManager { return; } + // Sort the loaded plugins twice. First, sort the already-loaded plugins by their IDs, so as + // to make the topographic sort deterministic (since the order will differ depending on the + // first node chosen in the graph, which is the first plugin we found). Afterwards, we execute + // a depth-first search over the loaded plugins. + found.sort(Comparator.comparing(PluginDescription::getId)); List sortedPlugins = PluginDependencyUtils.sortCandidates(found); // Now load the plugins diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/util/PluginDependencyUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/util/PluginDependencyUtils.java index 93c5b341d..5fb735afb 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/util/PluginDependencyUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/util/PluginDependencyUtils.java @@ -26,12 +26,16 @@ public class PluginDependencyUtils { * @throws IllegalStateException if there is a circular loop in the dependency graph */ public static List sortCandidates(List candidates) { - // Create our graph, we're going to be using this for Kahn's algorithm. - MutableGraph graph = GraphBuilder.directed().allowsSelfLoops(false).build(); + // Create a graph and populate it with plugin dependencies. Specifically, each graph has plugin + // nodes, and edges that represent the dependencies that plugin relies on. Non-existent plugins + // are ignored. + MutableGraph graph = GraphBuilder.directed() + .allowsSelfLoops(false) + .expectedNodeCount(candidates.size()) + .build(); Map candidateMap = Maps .uniqueIndex(candidates, d -> d == null ? null : d.getId()); - // Add edges for (PluginDescription description : candidates) { graph.addNode(description); @@ -44,6 +48,7 @@ public class PluginDependencyUtils { } } + // Now we do the depth-first search. List sorted = new ArrayList<>(); Map marks = new HashMap<>();