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:
Ursprung
3579fa644b
Commit
a155f91dfd
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren