mirror of
https://github.com/PaperMC/Velocity.git
synced 2026-02-17 14:37:43 +01:00
Support multiple plugins loaded from the same JAR
This commit is contained in:
@@ -28,6 +28,7 @@ import com.velocitypowered.api.plugin.Plugin;
|
|||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -56,9 +57,6 @@ import javax.tools.StandardLocation;
|
|||||||
@SupportedAnnotationTypes({PLUGIN_ANNOTATION_CLASS, SUBSCRIBE_ANNOTATION_CLASS})
|
@SupportedAnnotationTypes({PLUGIN_ANNOTATION_CLASS, SUBSCRIBE_ANNOTATION_CLASS})
|
||||||
public class ApiAnnotationProcessor extends AbstractProcessor {
|
public class ApiAnnotationProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
private @Nullable String pluginClassFound;
|
|
||||||
private boolean warnedAboutMultiplePlugins;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SourceVersion getSupportedSourceVersion() {
|
public SourceVersion getSupportedSourceVersion() {
|
||||||
return SourceVersion.latestSupported();
|
return SourceVersion.latestSupported();
|
||||||
@@ -107,6 +105,7 @@ public class ApiAnnotationProcessor extends AbstractProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ProcessorUtils.contains(annotations, Plugin.class)) {
|
if (ProcessorUtils.contains(annotations, Plugin.class)) {
|
||||||
|
List<SerializedPluginDescription> found = new ArrayList<>();
|
||||||
for (Element element : roundEnv.getElementsAnnotatedWith(Plugin.class)) {
|
for (Element element : roundEnv.getElementsAnnotatedWith(Plugin.class)) {
|
||||||
if (element.getKind() != ElementKind.CLASS) {
|
if (element.getKind() != ElementKind.CLASS) {
|
||||||
processingEnv.getMessager()
|
processingEnv.getMessager()
|
||||||
@@ -117,17 +116,6 @@ public class ApiAnnotationProcessor extends AbstractProcessor {
|
|||||||
|
|
||||||
Name qualifiedName = ((TypeElement) element).getQualifiedName();
|
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);
|
Plugin plugin = element.getAnnotation(Plugin.class);
|
||||||
if (!SerializedPluginDescription.ID_PATTERN.matcher(plugin.id()).matches()) {
|
if (!SerializedPluginDescription.ID_PATTERN.matcher(plugin.id()).matches()) {
|
||||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
|
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
|
||||||
@@ -137,24 +125,25 @@ public class ApiAnnotationProcessor extends AbstractProcessor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All good, generate the velocity-plugin.json.
|
// All good, generate the velocity-plugin-info.json.
|
||||||
SerializedPluginDescription description = SerializedPluginDescription
|
SerializedPluginDescription description = SerializedPluginDescription
|
||||||
.from(plugin, qualifiedName.toString());
|
.from(plugin, qualifiedName.toString());
|
||||||
|
found.add(description);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
FileObject object = processingEnv.getFiler()
|
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())) {
|
try (Writer writer = new BufferedWriter(object.openWriter())) {
|
||||||
new Gson().toJson(description, writer);
|
new Gson().toJson(found, writer);
|
||||||
}
|
}
|
||||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
|
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
|
||||||
"Wrote velocity-plugin.json to " + object.toUri().toString());
|
"Wrote velocity-plugin-info.json to " + object.toUri().toString());
|
||||||
pluginClassFound = qualifiedName.toString();
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
processingEnv.getMessager()
|
processingEnv.getMessager()
|
||||||
.printMessage(Diagnostic.Kind.ERROR, "Unable to generate plugin file");
|
.printMessage(Diagnostic.Kind.ERROR, "Unable to generate plugin file");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,12 +49,10 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
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;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
@@ -100,7 +98,7 @@ public class VelocityPluginManager implements PluginManager {
|
|||||||
p -> p.toFile().isFile() && p.toString().endsWith(".jar"))) {
|
p -> p.toFile().isFile() && p.toString().endsWith(".jar"))) {
|
||||||
for (Path path : stream) {
|
for (Path path : stream) {
|
||||||
try {
|
try {
|
||||||
found.add(loader.loadPluginDescription(path));
|
found.addAll(loader.loadPluginCandidates(path));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Unable to load plugin {}", path, e);
|
logger.error("Unable to load plugin {}", path, e);
|
||||||
}
|
}
|
||||||
@@ -160,7 +158,7 @@ public class VelocityPluginManager implements PluginManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
PluginDescription realPlugin = loader.loadPlugin(candidate);
|
PluginDescription realPlugin = loader.materializePlugin(candidate);
|
||||||
VelocityPluginContainer container = new VelocityPluginContainer(realPlugin);
|
VelocityPluginContainer container = new VelocityPluginContainer(realPlugin);
|
||||||
pluginContainers.put(container, loader.createModule(container));
|
pluginContainers.put(container, loader.createModule(container));
|
||||||
loadedPluginsById.put(candidate.id(), container);
|
loadedPluginsById.put(candidate.id(), container);
|
||||||
|
|||||||
@@ -21,15 +21,16 @@ import com.google.inject.Module;
|
|||||||
import com.velocitypowered.api.plugin.PluginContainer;
|
import com.velocitypowered.api.plugin.PluginContainer;
|
||||||
import com.velocitypowered.api.plugin.PluginDescription;
|
import com.velocitypowered.api.plugin.PluginDescription;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface is used for loading plugins.
|
* This interface is used for loading plugins.
|
||||||
*/
|
*/
|
||||||
public interface PluginLoader {
|
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}
|
* Creates a {@link Module} for the provided {@link PluginContainer}
|
||||||
|
|||||||
@@ -31,16 +31,23 @@ import com.velocitypowered.proxy.plugin.PluginClassLoader;
|
|||||||
import com.velocitypowered.proxy.plugin.loader.PluginLoader;
|
import com.velocitypowered.proxy.plugin.loader.PluginLoader;
|
||||||
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.VelocityPluginDescription;
|
||||||
|
import io.leangen.geantyref.TypeToken;
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.JarInputStream;
|
import java.util.jar.JarInputStream;
|
||||||
@@ -49,27 +56,28 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||||||
public class JavaPluginLoader implements PluginLoader {
|
public class JavaPluginLoader implements PluginLoader {
|
||||||
|
|
||||||
private final Path baseDirectory;
|
private final Path baseDirectory;
|
||||||
|
private final Map<URI, PluginClassLoader> classLoaders = new HashMap<>();
|
||||||
|
|
||||||
public JavaPluginLoader(ProxyServer server, Path baseDirectory) {
|
public JavaPluginLoader(ProxyServer server, Path baseDirectory) {
|
||||||
this.baseDirectory = baseDirectory;
|
this.baseDirectory = baseDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PluginDescription loadPluginDescription(Path source) throws Exception {
|
public Collection<PluginDescription> loadPluginCandidates(Path source) throws Exception {
|
||||||
SerializedPluginDescription serialized = getSerializedPluginInfo(source);
|
List<SerializedPluginDescription> serialized = getSerializedPluginInfo(source);
|
||||||
if (serialized == null) {
|
if (serialized.isEmpty()) {
|
||||||
throw new InvalidPluginException("Did not find a valid velocity-plugin.json.");
|
throw new InvalidPluginException("Did not find a valid velocity-plugin-info.json.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SerializedPluginDescription.ID_PATTERN.matcher(serialized.getId()).matches()) {
|
List<PluginDescription> candidates = new ArrayList<>();
|
||||||
throw new InvalidPluginException("Plugin ID '" + serialized.getId() + "' is invalid.");
|
for (SerializedPluginDescription description : serialized) {
|
||||||
|
candidates.add(createCandidateDescription(description, source));
|
||||||
}
|
}
|
||||||
|
return candidates;
|
||||||
return createCandidateDescription(serialized, source);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PluginDescription loadPlugin(PluginDescription source) throws Exception {
|
public PluginDescription materializePlugin(PluginDescription source) throws Exception {
|
||||||
if (!(source instanceof JavaVelocityPluginDescriptionCandidate)) {
|
if (!(source instanceof JavaVelocityPluginDescriptionCandidate)) {
|
||||||
throw new IllegalArgumentException("Description provided isn't of the Java plugin loader");
|
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.");
|
throw new IllegalStateException("JAR path not provided.");
|
||||||
}
|
}
|
||||||
|
|
||||||
URL pluginJarUrl = jarFilePath.toUri().toURL();
|
URI pluginJarUri = jarFilePath.toUri();
|
||||||
PluginClassLoader loader = AccessController.doPrivileged(
|
URL pluginJarUrl = pluginJarUri.toURL();
|
||||||
|
PluginClassLoader loader = this.classLoaders.computeIfAbsent(pluginJarUri, (uri) -> {
|
||||||
|
PluginClassLoader classLoader = AccessController.doPrivileged(
|
||||||
(PrivilegedAction<PluginClassLoader>) () -> new PluginClassLoader(new URL[]{pluginJarUrl},
|
(PrivilegedAction<PluginClassLoader>) () -> new PluginClassLoader(new URL[]{pluginJarUrl},
|
||||||
source));
|
source));
|
||||||
loader.addToClassloaders();
|
classLoader.addToClassloaders();
|
||||||
|
return classLoader;
|
||||||
|
});
|
||||||
|
|
||||||
JavaVelocityPluginDescriptionCandidate candidate =
|
JavaVelocityPluginDescriptionCandidate candidate =
|
||||||
(JavaVelocityPluginDescriptionCandidate) source;
|
(JavaVelocityPluginDescriptionCandidate) source;
|
||||||
@@ -130,32 +142,43 @@ public class JavaPluginLoader implements PluginLoader {
|
|||||||
((VelocityPluginContainer) container).setInstance(instance);
|
((VelocityPluginContainer) container).setInstance(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private @Nullable SerializedPluginDescription getSerializedPluginInfo(Path source)
|
private List<SerializedPluginDescription> getSerializedPluginInfo(Path source)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
boolean foundOldVelocityPlugin = false;
|
||||||
boolean foundBungeeBukkitPluginFile = false;
|
boolean foundBungeeBukkitPluginFile = false;
|
||||||
try (JarInputStream in = new JarInputStream(
|
try (JarInputStream in = new JarInputStream(
|
||||||
new BufferedInputStream(Files.newInputStream(source)))) {
|
new BufferedInputStream(Files.newInputStream(source)))) {
|
||||||
JarEntry entry;
|
JarEntry entry;
|
||||||
while ((entry = in.getNextJarEntry()) != null) {
|
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)) {
|
try (Reader pluginInfoReader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
|
||||||
return VelocityServer.GENERAL_GSON.fromJson(pluginInfoReader,
|
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")) {
|
if (entry.getName().equals("plugin.yml") || entry.getName().equals("bungee.yml")) {
|
||||||
foundBungeeBukkitPluginFile = true;
|
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) {
|
if (foundBungeeBukkitPluginFile) {
|
||||||
throw new InvalidPluginException("The plugin file " + source.getFileName() + " appears to "
|
throw new InvalidPluginException("The plugin file " + source.getFileName() + " appears to "
|
||||||
+ "be a Bukkit or BungeeCord plugin. Velocity does not support Bukkit or BungeeCord "
|
+ "be a Bukkit or BungeeCord plugin. Velocity does not support Bukkit or BungeeCord "
|
||||||
+ "plugins.");
|
+ "plugins.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return List.of();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user