From d644dfaa27fbd547437ee58865183ad5fc15f03b Mon Sep 17 00:00:00 2001
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
Date: Thu, 23 May 2024 13:13:02 -0700
Subject: [PATCH] Allow using PluginLoader classpath API from Bukkit plugins
 (#10758)

Allows using the PluginLoader API without any of the other changes imposed by switching to a paper-plugin.yml.

Used by setting paper-plugin-loader in plugin.yml to the class name of your PluginLoader.

Also allows skipping the libraries field by setting paper-skip-libraries to true (by default both libraries and jars provided by the PluginLoader are added to the classpath).
---
 ...plugin-to-use-Paper-PluginLoader-API.patch |  91 ++++++++++++
 ...plugin-to-use-Paper-PluginLoader-API.patch | 130 ++++++++++++++++++
 patches/server/Paper-Plugins.patch            |  14 +-
 3 files changed, 230 insertions(+), 5 deletions(-)
 create mode 100644 patches/api/Allow-Bukkit-plugin-to-use-Paper-PluginLoader-API.patch
 create mode 100644 patches/server/Allow-Bukkit-plugin-to-use-Paper-PluginLoader-API.patch

diff --git a/patches/api/Allow-Bukkit-plugin-to-use-Paper-PluginLoader-API.patch b/patches/api/Allow-Bukkit-plugin-to-use-Paper-PluginLoader-API.patch
new file mode 100644
index 0000000000..71dc63761f
--- /dev/null
+++ b/patches/api/Allow-Bukkit-plugin-to-use-Paper-PluginLoader-API.patch
@@ -0,0 +1,91 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
+Date: Tue, 21 May 2024 13:18:00 -0700
+Subject: [PATCH] Allow Bukkit plugin to use Paper PluginLoader API
+
+
+diff --git a/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java b/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java
++++ b/src/main/java/org/bukkit/plugin/PluginDescriptionFile.java
+@@ -0,0 +0,0 @@ public final class PluginDescriptionFile implements io.papermc.paper.plugin.conf
+     private Set<PluginAwareness> awareness = ImmutableSet.of();
+     private String apiVersion = null;
+     private List<String> libraries = ImmutableList.of();
++    // Paper start - plugin loader api
++    private String paperPluginLoader;
++    @org.jetbrains.annotations.ApiStatus.Internal @org.jetbrains.annotations.Nullable
++    public String getPaperPluginLoader() {
++        return this.paperPluginLoader;
++    }
++    // Paper end - plugin loader api
+     // Paper start - oh my goddddd
+     /**
+      * Don't use this.
+@@ -0,0 +0,0 @@ public final class PluginDescriptionFile implements io.papermc.paper.plugin.conf
+         } else {
+             libraries = ImmutableList.<String>of();
+         }
++        // Paper start - plugin loader api
++        if (map.containsKey("paper-plugin-loader")) {
++            this.paperPluginLoader = map.get("paper-plugin-loader").toString();
++        }
++
++        /*
++        Allow skipping the Bukkit/Spigot 'libraries' list. By default, both the 'libraries'
++        list and the 'paper-plugin-loader' will contribute libraries. It may be desired to only
++        use one or the other. (i.e. 'libraries' on Spigot and 'paper-plugin-loader' on Paper)
++        */
++        if (map.containsKey("paper-skip-libraries")) {
++            String skip = map.get("paper-skip-libraries").toString();
++            if (skip.equalsIgnoreCase("true")) {
++                this.libraries = ImmutableList.of();
++            }
++        }
++        // Paper end - plugin loader api
+ 
+         try {
+             lazyPermissions = (Map<?, ?>) map.get("permissions");
+diff --git a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java
++++ b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java
+@@ -0,0 +0,0 @@ public class LibraryLoader
+     @Nullable
+     public ClassLoader createLoader(@NotNull PluginDescriptionFile desc)
+     {
+-        if ( desc.getLibraries().isEmpty() )
++        // Paper start - plugin loader api
++        return this.createLoader(desc, null);
++    }
++    @Nullable
++    public ClassLoader createLoader(@NotNull PluginDescriptionFile desc, java.util.@Nullable List<java.nio.file.Path> paperLibraryPaths) {
++        if ( desc.getLibraries().isEmpty() && paperLibraryPaths == null )
++        // Paper end - plugin loader api
+         {
+             return null;
+         }
+@@ -0,0 +0,0 @@ public class LibraryLoader
+         }
+ 
+         DependencyResult result;
+-        try
++        if (!dependencies.isEmpty()) try // Paper - plugin loader api
+         {
+             result = repository.resolveDependencies( session, new DependencyRequest( new CollectRequest( (Dependency) null, dependencies, repositories ), null ) );
+         } catch ( DependencyResolutionException ex )
+         {
+             throw new RuntimeException( "Error resolving libraries", ex );
+-        }
++        } else result = null; // Paper - plugin loader api
+ 
+         List<URL> jarFiles = new ArrayList<>();
+         List<java.nio.file.Path> jarPaths = new ArrayList<>(); // Paper - remap libraries
+-        for ( ArtifactResult artifact : result.getArtifactResults() )
++        // Paper start - plugin loader api
++        if (paperLibraryPaths != null) jarPaths.addAll(paperLibraryPaths);
++        if (result != null) for ( ArtifactResult artifact : result.getArtifactResults() )
++        // Paper end - plugin loader api
+         {
+             // Paper start - remap libraries
+             jarPaths.add(artifact.getArtifact().getFile().toPath());
diff --git a/patches/server/Allow-Bukkit-plugin-to-use-Paper-PluginLoader-API.patch b/patches/server/Allow-Bukkit-plugin-to-use-Paper-PluginLoader-API.patch
new file mode 100644
index 0000000000..3a93272174
--- /dev/null
+++ b/patches/server/Allow-Bukkit-plugin-to-use-Paper-PluginLoader-API.patch
@@ -0,0 +1,130 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
+Date: Tue, 21 May 2024 13:18:15 -0700
+Subject: [PATCH] Allow Bukkit plugin to use Paper PluginLoader API
+
+
+diff --git a/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java b/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java
++++ b/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java
+@@ -0,0 +0,0 @@ public class PaperClasspathBuilder implements PluginClasspathBuilder {
+     }
+ 
+     public PaperPluginClassLoader buildClassLoader(Logger logger, Path source, JarFile jarFile, PaperPluginMeta configuration) {
+-        PaperLibraryStore paperLibraryStore = new PaperLibraryStore();
+-        for (ClassPathLibrary library : this.libraries) {
+-            library.register(paperLibraryStore);
+-        }
+-
+-        List<Path> paths = paperLibraryStore.getPaths();
+-        if (PluginInitializerManager.instance().pluginRemapper != null) {
+-            paths = PluginInitializerManager.instance().pluginRemapper.remapLibraries(paths);
+-        }
++        List<Path> paths = this.buildLibraryPaths(true);
+         URL[] urls = new URL[paths.size()];
+         for (int i = 0; i < paths.size(); i++) {
+             Path path = paths.get(i);
+@@ -0,0 +0,0 @@ public class PaperClasspathBuilder implements PluginClasspathBuilder {
+             throw new RuntimeException(exception);
+         }
+     }
++
++    public List<Path> buildLibraryPaths(final boolean remap) {
++        PaperLibraryStore paperLibraryStore = new PaperLibraryStore();
++        for (ClassPathLibrary library : this.libraries) {
++            library.register(paperLibraryStore);
++        }
++
++        List<Path> paths = paperLibraryStore.getPaths();
++        if (remap && PluginInitializerManager.instance().pluginRemapper != null) {
++            paths = PluginInitializerManager.instance().pluginRemapper.remapLibraries(paths);
++        }
++        return paths;
++    }
+ }
+diff --git a/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProvider.java b/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProvider.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProvider.java
++++ b/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProvider.java
+@@ -0,0 +0,0 @@ public class SpigotPluginProvider implements PluginProvider<JavaPlugin>, Provide
+     private final PluginDescriptionFile description;
+     private final JarFile jarFile;
+     private final Logger logger;
++    private final List<Path> paperLibraryPaths;
+     private final ComponentLogger componentLogger;
+     private ProviderStatus status;
+     private DependencyContext dependencyContext;
+ 
+-    SpigotPluginProvider(Path path, JarFile file, PluginDescriptionFile description) {
++    SpigotPluginProvider(Path path, JarFile file, PluginDescriptionFile description, List<Path> paperLibraryPaths) {
+         this.path = path;
+         this.jarFile = file;
+         this.description = description;
+         this.logger = PaperPluginLogger.getLogger(description);
++        this.paperLibraryPaths = paperLibraryPaths;
+         this.componentLogger = ComponentLogger.logger(this.logger.getName());
+     }
+ 
+@@ -0,0 +0,0 @@ public class SpigotPluginProvider implements PluginProvider<JavaPlugin>, Provide
+ 
+             final PluginClassLoader loader;
+             try {
+-                loader = new PluginClassLoader(this.getClass().getClassLoader(), this.description, dataFolder, this.path.toFile(), LIBRARY_LOADER.createLoader(this.description), this.jarFile, this.dependencyContext); // Paper
++                loader = new PluginClassLoader(this.getClass().getClassLoader(), this.description, dataFolder, this.path.toFile(), LIBRARY_LOADER.createLoader(this.description, this.paperLibraryPaths), this.jarFile, this.dependencyContext); // Paper
+             } catch (InvalidPluginException ex) {
+                 throw ex;
+             } catch (Throwable ex) {
+diff --git a/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProviderFactory.java b/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProviderFactory.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProviderFactory.java
++++ b/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProviderFactory.java
+@@ -0,0 +0,0 @@
+ package io.papermc.paper.plugin.provider.type.spigot;
+ 
++import com.destroystokyo.paper.utils.PaperPluginLogger;
++import io.papermc.paper.plugin.bootstrap.PluginProviderContextImpl;
+ import io.papermc.paper.plugin.entrypoint.classloader.BytecodeModifyingURLClassLoader;
++import io.papermc.paper.plugin.entrypoint.classloader.PaperSimplePluginClassLoader;
++import io.papermc.paper.plugin.loader.PaperClasspathBuilder;
++import io.papermc.paper.plugin.loader.PluginLoader;
+ import io.papermc.paper.plugin.provider.configuration.serializer.constraints.PluginConfigConstraints;
+ import io.papermc.paper.plugin.provider.type.PluginTypeFactory;
++import io.papermc.paper.plugin.provider.util.ProviderUtil;
+ import io.papermc.paper.util.MappingEnvironment;
++import java.util.List;
++import java.util.logging.Logger;
++import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
+ import org.bukkit.plugin.InvalidDescriptionException;
+ import org.bukkit.plugin.PluginDescriptionFile;
+ import org.bukkit.plugin.java.LibraryLoader;
+@@ -0,0 +0,0 @@ class SpigotPluginProviderFactory implements PluginTypeFactory<SpigotPluginProvi
+             throw new InvalidDescriptionException("Restricted name, cannot use 0x20 (space character) in a plugin name.");
+         }
+ 
+-        return new SpigotPluginProvider(source, file, configuration);
++        final List<Path> paperLibraryPaths;
++        if (configuration.getPaperPluginLoader() != null) {
++            final Logger logger = PaperPluginLogger.getLogger(configuration);
++            PaperClasspathBuilder builder = new PaperClasspathBuilder(PluginProviderContextImpl.create(
++                configuration, ComponentLogger.logger(logger.getName()), source
++            ));
++
++            try (
++                PaperSimplePluginClassLoader simplePluginClassLoader = new PaperSimplePluginClassLoader(source, file, configuration, this.getClass().getClassLoader())
++            ) {
++                PluginLoader loader = ProviderUtil.loadClass(configuration.getPaperPluginLoader(), PluginLoader.class, simplePluginClassLoader);
++                loader.classloader(builder);
++            } catch (IOException e) {
++                throw new RuntimeException(e);
++            }
++
++            paperLibraryPaths = builder.buildLibraryPaths(false);
++        } else {
++            paperLibraryPaths = null;
++        }
++
++        return new SpigotPluginProvider(source, file, configuration, paperLibraryPaths);
+     }
+ 
+     @Override
diff --git a/patches/server/Paper-Plugins.patch b/patches/server/Paper-Plugins.patch
index 0a1af54ed8..7520c4fa63 100644
--- a/patches/server/Paper-Plugins.patch
+++ b/patches/server/Paper-Plugins.patch
@@ -936,13 +936,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        this.libraryLoader = libraryLoader;
 +
 +        this.logger = logger;
-+        if (this.configuration.hasOpenClassloader()) {
++        if (this.configuration().hasOpenClassloader()) {
 +            this.group = PaperClassLoaderStorage.instance().registerOpenGroup(this);
 +        }
 +    }
 +
++    private PaperPluginMeta configuration() {
++        return (PaperPluginMeta) this.configuration;
++    }
++
 +    public void refreshClassloaderDependencyTree(DependencyContext dependencyContext) {
-+         if (this.configuration.hasOpenClassloader()) {
++         if (this.configuration().hasOpenClassloader()) {
 +             return;
 +         }
 +         if (this.group != null) {
@@ -1094,7 +1098,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 @@ -0,0 +0,0 @@
 +package io.papermc.paper.plugin.entrypoint.classloader;
 +
-+import io.papermc.paper.plugin.provider.configuration.PaperPluginMeta;
++import io.papermc.paper.plugin.configuration.PluginMeta;
 +import io.papermc.paper.plugin.util.NamespaceChecker;
 +import org.jetbrains.annotations.ApiStatus;
 +
@@ -1120,13 +1124,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        ClassLoader.registerAsParallelCapable();
 +    }
 +
-+    protected final PaperPluginMeta configuration;
++    protected final PluginMeta configuration;
 +    protected final Path source;
 +    protected final Manifest jarManifest;
 +    protected final URL jarUrl;
 +    protected final JarFile jar;
 +
-+    public PaperSimplePluginClassLoader(Path source, JarFile file, PaperPluginMeta configuration, ClassLoader parentLoader) throws IOException {
++    public PaperSimplePluginClassLoader(Path source, JarFile file, PluginMeta configuration, ClassLoader parentLoader) throws IOException {
 +        super(source.getFileName().toString(), new URL[]{source.toUri().toURL()}, parentLoader);
 +
 +        this.source = source;