3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-11-16 21:10:30 +01:00

Support multiple plugins loaded from the same JAR

Dieser Commit ist enthalten in:
Andrew Steinborn 2021-05-15 23:42:54 -04:00
Ursprung 3579fa644b
Commit a155f91dfd
4 geänderte Dateien mit 62 neuen und 51 gelöschten Zeilen

Datei anzeigen

@ -28,6 +28,7 @@ import com.velocitypowered.api.plugin.Plugin;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@ -56,9 +57,6 @@ import javax.tools.StandardLocation;
@SupportedAnnotationTypes({PLUGIN_ANNOTATION_CLASS, SUBSCRIBE_ANNOTATION_CLASS})
public class ApiAnnotationProcessor extends AbstractProcessor {
private @Nullable String pluginClassFound;
private boolean warnedAboutMultiplePlugins;
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
@ -107,6 +105,7 @@ public class ApiAnnotationProcessor extends AbstractProcessor {
}
if (ProcessorUtils.contains(annotations, Plugin.class)) {
List<SerializedPluginDescription> found = new ArrayList<>();
for (Element element : roundEnv.getElementsAnnotatedWith(Plugin.class)) {
if (element.getKind() != ElementKind.CLASS) {
processingEnv.getMessager()
@ -117,17 +116,6 @@ public class ApiAnnotationProcessor extends AbstractProcessor {
Name qualifiedName = ((TypeElement) element).getQualifiedName();
if (Objects.equals(pluginClassFound, qualifiedName.toString())) {
if (!warnedAboutMultiplePlugins) {
processingEnv.getMessager()
.printMessage(Diagnostic.Kind.WARNING,
"Velocity does not yet currently support multiple plugins. "
+ "We are using " + pluginClassFound + " for your plugin's main class.");
warnedAboutMultiplePlugins = true;
}
return false;
}
Plugin plugin = element.getAnnotation(Plugin.class);
if (!SerializedPluginDescription.ID_PATTERN.matcher(plugin.id()).matches()) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
@ -137,24 +125,25 @@ public class ApiAnnotationProcessor extends AbstractProcessor {
return false;
}
// All good, generate the velocity-plugin.json.
// All good, generate the velocity-plugin-info.json.
SerializedPluginDescription description = SerializedPluginDescription
.from(plugin, qualifiedName.toString());
found.add(description);
}
try {
FileObject object = processingEnv.getFiler()
.createResource(StandardLocation.CLASS_OUTPUT, "", "velocity-plugin.json");
.createResource(StandardLocation.CLASS_OUTPUT, "", "velocity-plugin-info.json");
try (Writer writer = new BufferedWriter(object.openWriter())) {
new Gson().toJson(description, writer);
new Gson().toJson(found, writer);
}
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
"Wrote velocity-plugin.json to " + object.toUri().toString());
pluginClassFound = qualifiedName.toString();
"Wrote velocity-plugin-info.json to " + object.toUri().toString());
} catch (IOException e) {
processingEnv.getMessager()
.printMessage(Diagnostic.Kind.ERROR, "Unable to generate plugin file");
}
}
}
return false;
}

Datei anzeigen

@ -49,12 +49,10 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -100,7 +98,7 @@ public class VelocityPluginManager implements PluginManager {
p -> p.toFile().isFile() && p.toString().endsWith(".jar"))) {
for (Path path : stream) {
try {
found.add(loader.loadPluginDescription(path));
found.addAll(loader.loadPluginCandidates(path));
} catch (Exception e) {
logger.error("Unable to load plugin {}", path, e);
}
@ -160,7 +158,7 @@ public class VelocityPluginManager implements PluginManager {
}
try {
PluginDescription realPlugin = loader.loadPlugin(candidate);
PluginDescription realPlugin = loader.materializePlugin(candidate);
VelocityPluginContainer container = new VelocityPluginContainer(realPlugin);
pluginContainers.put(container, loader.createModule(container));
loadedPluginsById.put(candidate.id(), container);

Datei anzeigen

@ -21,15 +21,16 @@ import com.google.inject.Module;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginDescription;
import java.nio.file.Path;
import java.util.Collection;
/**
* This interface is used for loading plugins.
*/
public interface PluginLoader {
PluginDescription loadPluginDescription(Path source) throws Exception;
Collection<PluginDescription> loadPluginCandidates(Path source) throws Exception;
PluginDescription loadPlugin(PluginDescription source) throws Exception;
PluginDescription materializePlugin(PluginDescription source) throws Exception;
/**
* Creates a {@link Module} for the provided {@link PluginContainer}

Datei anzeigen

@ -31,16 +31,23 @@ import com.velocitypowered.proxy.plugin.PluginClassLoader;
import com.velocitypowered.proxy.plugin.loader.PluginLoader;
import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer;
import com.velocitypowered.proxy.plugin.loader.VelocityPluginDescription;
import io.leangen.geantyref.TypeToken;
import java.io.BufferedInputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
@ -49,27 +56,28 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public class JavaPluginLoader implements PluginLoader {
private final Path baseDirectory;
private final Map<URI, PluginClassLoader> classLoaders = new HashMap<>();
public JavaPluginLoader(ProxyServer server, Path baseDirectory) {
this.baseDirectory = baseDirectory;
}
@Override
public PluginDescription loadPluginDescription(Path source) throws Exception {
SerializedPluginDescription serialized = getSerializedPluginInfo(source);
if (serialized == null) {
throw new InvalidPluginException("Did not find a valid velocity-plugin.json.");
public Collection<PluginDescription> loadPluginCandidates(Path source) throws Exception {
List<SerializedPluginDescription> serialized = getSerializedPluginInfo(source);
if (serialized.isEmpty()) {
throw new InvalidPluginException("Did not find a valid velocity-plugin-info.json.");
}
if (!SerializedPluginDescription.ID_PATTERN.matcher(serialized.getId()).matches()) {
throw new InvalidPluginException("Plugin ID '" + serialized.getId() + "' is invalid.");
List<PluginDescription> candidates = new ArrayList<>();
for (SerializedPluginDescription description : serialized) {
candidates.add(createCandidateDescription(description, source));
}
return createCandidateDescription(serialized, source);
return candidates;
}
@Override
public PluginDescription loadPlugin(PluginDescription source) throws Exception {
public PluginDescription materializePlugin(PluginDescription source) throws Exception {
if (!(source instanceof JavaVelocityPluginDescriptionCandidate)) {
throw new IllegalArgumentException("Description provided isn't of the Java plugin loader");
}
@ -79,11 +87,15 @@ public class JavaPluginLoader implements PluginLoader {
throw new IllegalStateException("JAR path not provided.");
}
URL pluginJarUrl = jarFilePath.toUri().toURL();
PluginClassLoader loader = AccessController.doPrivileged(
URI pluginJarUri = jarFilePath.toUri();
URL pluginJarUrl = pluginJarUri.toURL();
PluginClassLoader loader = this.classLoaders.computeIfAbsent(pluginJarUri, (uri) -> {
PluginClassLoader classLoader = AccessController.doPrivileged(
(PrivilegedAction<PluginClassLoader>) () -> new PluginClassLoader(new URL[]{pluginJarUrl},
source));
loader.addToClassloaders();
classLoader.addToClassloaders();
return classLoader;
});
JavaVelocityPluginDescriptionCandidate candidate =
(JavaVelocityPluginDescriptionCandidate) source;
@ -130,32 +142,43 @@ public class JavaPluginLoader implements PluginLoader {
((VelocityPluginContainer) container).setInstance(instance);
}
private @Nullable SerializedPluginDescription getSerializedPluginInfo(Path source)
private List<SerializedPluginDescription> getSerializedPluginInfo(Path source)
throws Exception {
boolean foundOldVelocityPlugin = false;
boolean foundBungeeBukkitPluginFile = false;
try (JarInputStream in = new JarInputStream(
new BufferedInputStream(Files.newInputStream(source)))) {
JarEntry entry;
while ((entry = in.getNextJarEntry()) != null) {
if (entry.getName().equals("velocity-plugin.json")) {
if (entry.getName().equals("velocity-plugin-info.json")) {
try (Reader pluginInfoReader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
return VelocityServer.GENERAL_GSON.fromJson(pluginInfoReader,
SerializedPluginDescription.class);
new TypeToken<List<SerializedPluginDescription>>() {}.getType());
}
}
if (entry.getName().equals("velocity-plugin.json")) {
foundOldVelocityPlugin = true;
}
if (entry.getName().equals("plugin.yml") || entry.getName().equals("bungee.yml")) {
foundBungeeBukkitPluginFile = true;
}
}
if (foundOldVelocityPlugin) {
throw new InvalidPluginException("The plugin file " + source.getFileName() + " appears to "
+ "be developed for an older version of Velocity. Please obtain a newer version of the "
+ "plugin.");
}
if (foundBungeeBukkitPluginFile) {
throw new InvalidPluginException("The plugin file " + source.getFileName() + " appears to "
+ "be a Bukkit or BungeeCord plugin. Velocity does not support Bukkit or BungeeCord "
+ "plugins.");
}
return null;
return List.of();
}
}