Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-17 05:20:14 +01:00
Fix regressions with plugin dependency loading from #272
This is a quick and dirty fix because it's late. I'll need to commit a better fix later.
Dieser Commit ist enthalten in:
Ursprung
3b6f8e2421
Commit
37994449d7
@ -16,6 +16,7 @@ import com.velocitypowered.api.plugin.meta.PluginDependency;
|
|||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer;
|
import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer;
|
||||||
|
import com.velocitypowered.proxy.plugin.loader.VelocityPluginDescription;
|
||||||
import com.velocitypowered.proxy.plugin.loader.java.JavaPluginLoader;
|
import com.velocitypowered.proxy.plugin.loader.java.JavaPluginLoader;
|
||||||
import com.velocitypowered.proxy.plugin.util.PluginDependencyUtils;
|
import com.velocitypowered.proxy.plugin.util.PluginDependencyUtils;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -25,12 +26,13 @@ import java.nio.file.Path;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
@ -68,7 +70,7 @@ public class VelocityPluginManager implements PluginManager {
|
|||||||
.newDirectoryStream(directory, p -> p.toFile().isFile() && p.toString().endsWith(".jar"))) {
|
.newDirectoryStream(directory, p -> p.toFile().isFile() && p.toString().endsWith(".jar"))) {
|
||||||
for (Path path : stream) {
|
for (Path path : stream) {
|
||||||
try {
|
try {
|
||||||
found.add(loader.loadPlugin(path));
|
found.add(loader.loadPluginDescription(path));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Unable to load plugin {}", path, e);
|
logger.error("Unable to load plugin {}", path, e);
|
||||||
}
|
}
|
||||||
@ -80,31 +82,29 @@ public class VelocityPluginManager implements PluginManager {
|
|||||||
return;
|
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<PluginDescription> sortedPlugins = PluginDependencyUtils.sortCandidates(found);
|
List<PluginDescription> sortedPlugins = PluginDependencyUtils.sortCandidates(found);
|
||||||
|
|
||||||
|
Set<String> loadedPluginsById = new HashSet<>();
|
||||||
Map<PluginContainer, Module> pluginContainers = new HashMap<>();
|
Map<PluginContainer, Module> pluginContainers = new HashMap<>();
|
||||||
// Now load the plugins
|
// Now load the plugins
|
||||||
pluginLoad:
|
pluginLoad:
|
||||||
for (PluginDescription plugin : sortedPlugins) {
|
for (PluginDescription candidate : sortedPlugins) {
|
||||||
// Verify dependencies
|
// Verify dependencies
|
||||||
for (PluginDependency dependency : plugin.getDependencies()) {
|
for (PluginDependency dependency : candidate.getDependencies()) {
|
||||||
if (!dependency.isOptional() && !isLoaded(dependency.getId())) {
|
if (!dependency.isOptional() && !loadedPluginsById.contains(dependency.getId())) {
|
||||||
logger.error("Can't load plugin {} due to missing dependency {}", plugin.getId(),
|
logger.error("Can't load plugin {} due to missing dependency {}", candidate.getId(),
|
||||||
dependency.getId());
|
dependency.getId());
|
||||||
continue pluginLoad;
|
continue pluginLoad;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
VelocityPluginContainer container = new VelocityPluginContainer(plugin);
|
PluginDescription realPlugin = loader.loadPlugin(candidate);
|
||||||
|
VelocityPluginContainer container = new VelocityPluginContainer(realPlugin);
|
||||||
pluginContainers.put(container, loader.createModule(container));
|
pluginContainers.put(container, loader.createModule(container));
|
||||||
|
loadedPluginsById.add(realPlugin.getId());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Can't create module for plugin {}", plugin.getId(), e);
|
logger.error("Can't create module for plugin {}", candidate.getId(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,9 @@ import java.nio.file.Path;
|
|||||||
*/
|
*/
|
||||||
public interface PluginLoader {
|
public interface PluginLoader {
|
||||||
|
|
||||||
PluginDescription loadPlugin(Path source) throws Exception;
|
PluginDescription loadPluginDescription(Path source) throws Exception;
|
||||||
|
|
||||||
|
PluginDescription loadPlugin(PluginDescription source) throws Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link Module} for the provided {@link PluginContainer}
|
* Creates a {@link Module} for the provided {@link PluginContainer}
|
||||||
|
@ -38,7 +38,7 @@ public class JavaPluginLoader implements PluginLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PluginDescription loadPlugin(Path source) throws Exception {
|
public PluginDescription loadPluginDescription(Path source) throws Exception {
|
||||||
Optional<SerializedPluginDescription> serialized = getSerializedPluginInfo(source);
|
Optional<SerializedPluginDescription> serialized = getSerializedPluginInfo(source);
|
||||||
|
|
||||||
if (!serialized.isPresent()) {
|
if (!serialized.isPresent()) {
|
||||||
@ -50,13 +50,23 @@ public class JavaPluginLoader implements PluginLoader {
|
|||||||
throw new InvalidPluginException("Plugin ID '" + pd.getId() + "' is invalid.");
|
throw new InvalidPluginException("Plugin ID '" + pd.getId() + "' is invalid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return createCandidateDescription(pd, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PluginDescription loadPlugin(PluginDescription source) throws Exception {
|
||||||
|
if (!(source instanceof JavaVelocityPluginDescriptionCandidate)) {
|
||||||
|
throw new IllegalArgumentException("Description provided isn't of the Java plugin loader");
|
||||||
|
}
|
||||||
PluginClassLoader loader = new PluginClassLoader(
|
PluginClassLoader loader = new PluginClassLoader(
|
||||||
new URL[]{source.toUri().toURL()}
|
new URL[]{source.getSource().get().toUri().toURL()}
|
||||||
);
|
);
|
||||||
loader.addToClassloaders();
|
loader.addToClassloaders();
|
||||||
|
|
||||||
Class mainClass = loader.loadClass(pd.getMain());
|
JavaVelocityPluginDescriptionCandidate candidate =
|
||||||
return createDescription(pd, source, mainClass);
|
(JavaVelocityPluginDescriptionCandidate) source;
|
||||||
|
Class mainClass = loader.loadClass(candidate.getMainClass());
|
||||||
|
return createDescription(candidate, mainClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -127,15 +137,16 @@ public class JavaPluginLoader implements PluginLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private VelocityPluginDescription createDescription(SerializedPluginDescription description,
|
private VelocityPluginDescription createCandidateDescription(
|
||||||
Path source, Class mainClass) {
|
SerializedPluginDescription description,
|
||||||
|
Path source) {
|
||||||
Set<PluginDependency> dependencies = new HashSet<>();
|
Set<PluginDependency> dependencies = new HashSet<>();
|
||||||
|
|
||||||
for (SerializedPluginDescription.Dependency dependency : description.getDependencies()) {
|
for (SerializedPluginDescription.Dependency dependency : description.getDependencies()) {
|
||||||
dependencies.add(toDependencyMeta(dependency));
|
dependencies.add(toDependencyMeta(dependency));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new JavaVelocityPluginDescription(
|
return new JavaVelocityPluginDescriptionCandidate(
|
||||||
description.getId(),
|
description.getId(),
|
||||||
description.getName(),
|
description.getName(),
|
||||||
description.getVersion(),
|
description.getVersion(),
|
||||||
@ -144,6 +155,22 @@ public class JavaPluginLoader implements PluginLoader {
|
|||||||
description.getAuthors(),
|
description.getAuthors(),
|
||||||
dependencies,
|
dependencies,
|
||||||
source,
|
source,
|
||||||
|
description.getMain()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VelocityPluginDescription createDescription(
|
||||||
|
JavaVelocityPluginDescriptionCandidate description,
|
||||||
|
Class mainClass) {
|
||||||
|
return new JavaVelocityPluginDescription(
|
||||||
|
description.getId(),
|
||||||
|
description.getName().orElse(null),
|
||||||
|
description.getVersion().orElse(null),
|
||||||
|
description.getDescription().orElse(null),
|
||||||
|
description.getUrl().orElse(null),
|
||||||
|
description.getAuthors(),
|
||||||
|
description.getDependencies(),
|
||||||
|
description.getSource().orElse(null),
|
||||||
mainClass
|
mainClass
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.velocitypowered.proxy.plugin.loader.java;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.plugin.meta.PluginDependency;
|
||||||
|
import com.velocitypowered.proxy.plugin.loader.VelocityPluginDescription;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
class JavaVelocityPluginDescriptionCandidate extends VelocityPluginDescription {
|
||||||
|
|
||||||
|
private final String mainClass;
|
||||||
|
|
||||||
|
JavaVelocityPluginDescriptionCandidate(String id, @Nullable String name, @Nullable String version,
|
||||||
|
@Nullable String description, @Nullable String url,
|
||||||
|
@Nullable List<String> authors, Collection<PluginDependency> dependencies, Path source,
|
||||||
|
String mainClass) {
|
||||||
|
super(id, name, version, description, url, authors, dependencies, source);
|
||||||
|
this.mainClass = checkNotNull(mainClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getMainClass() {
|
||||||
|
return mainClass;
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import com.velocitypowered.api.plugin.PluginDescription;
|
|||||||
import com.velocitypowered.api.plugin.meta.PluginDependency;
|
import com.velocitypowered.api.plugin.meta.PluginDependency;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -28,17 +29,20 @@ public class PluginDependencyUtils {
|
|||||||
* @throws IllegalStateException if there is a circular loop in the dependency graph
|
* @throws IllegalStateException if there is a circular loop in the dependency graph
|
||||||
*/
|
*/
|
||||||
public static List<PluginDescription> sortCandidates(List<PluginDescription> candidates) {
|
public static List<PluginDescription> sortCandidates(List<PluginDescription> candidates) {
|
||||||
|
List<PluginDescription> sortedCandidates = new ArrayList<>(candidates);
|
||||||
|
sortedCandidates.sort(Comparator.comparing(PluginDescription::getId));
|
||||||
|
|
||||||
// Create a graph and populate it with plugin dependencies. Specifically, each graph has plugin
|
// 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
|
// nodes, and edges that represent the dependencies that plugin relies on. Non-existent plugins
|
||||||
// are ignored.
|
// are ignored.
|
||||||
MutableGraph<PluginDescription> graph = GraphBuilder.directed()
|
MutableGraph<PluginDescription> graph = GraphBuilder.directed()
|
||||||
.allowsSelfLoops(false)
|
.allowsSelfLoops(false)
|
||||||
.expectedNodeCount(candidates.size())
|
.expectedNodeCount(sortedCandidates.size())
|
||||||
.build();
|
.build();
|
||||||
Map<String, PluginDescription> candidateMap = Maps.uniqueIndex(candidates,
|
Map<String, PluginDescription> candidateMap = Maps.uniqueIndex(sortedCandidates,
|
||||||
PluginDescription::getId);
|
PluginDescription::getId);
|
||||||
|
|
||||||
for (PluginDescription description : candidates) {
|
for (PluginDescription description : sortedCandidates) {
|
||||||
graph.addNode(description);
|
graph.addNode(description);
|
||||||
|
|
||||||
for (PluginDependency dependency : description.getDependencies()) {
|
for (PluginDependency dependency : description.getDependencies()) {
|
||||||
|
@ -69,8 +69,8 @@ class PluginDependencyUtilsTest {
|
|||||||
void sortCandidatesMultiplePluginsDependentOnOne() throws Exception {
|
void sortCandidatesMultiplePluginsDependentOnOne() throws Exception {
|
||||||
List<PluginDescription> plugins = ImmutableList.of(HAS_DEPENDENCY_3, HAS_DEPENDENCY_1,
|
List<PluginDescription> plugins = ImmutableList.of(HAS_DEPENDENCY_3, HAS_DEPENDENCY_1,
|
||||||
NO_DEPENDENCY);
|
NO_DEPENDENCY);
|
||||||
List<PluginDescription> expected = ImmutableList.of(NO_DEPENDENCY, HAS_DEPENDENCY_3,
|
List<PluginDescription> expected = ImmutableList.of(NO_DEPENDENCY, HAS_DEPENDENCY_1,
|
||||||
HAS_DEPENDENCY_1);
|
HAS_DEPENDENCY_3);
|
||||||
assertEquals(expected, PluginDependencyUtils.sortCandidates(plugins));
|
assertEquals(expected, PluginDependencyUtils.sortCandidates(plugins));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren