aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJason Penilla <[email protected]>2024-04-28 14:55:10 -0700
committerGitHub <[email protected]>2024-04-28 14:55:10 -0700
commit05308fc2992958a695194a6ec8c5815316015deb (patch)
tree35e6b1ecf9b7c536e76d1d4913bb176fc0c6df98
parent7f2d5315fd85fda43b5a5e7040c3a47a68a052b5 (diff)
downloadPaper-05308fc2992958a695194a6ec8c5815316015deb.tar.gz
Paper-05308fc2992958a695194a6ec8c5815316015deb.zip
Remap plugin libraries with namespace set to spigot (#10610)
* Remap plugin libraries with namespace set to spigot * Remap plugin libraries with namespace set to spigot
-rw-r--r--patches/api/0474-Add-hook-to-remap-library-jars.patch38
-rw-r--r--patches/server/0020-Plugin-remapping.patch126
-rw-r--r--patches/server/0022-Remap-reflection-calls-in-plugins-using-internals.patch255
-rw-r--r--patches/server/1046-Modify-library-loader-jars-bytecode.patch261
4 files changed, 401 insertions, 279 deletions
diff --git a/patches/api/0474-Add-hook-to-remap-library-jars.patch b/patches/api/0474-Add-hook-to-remap-library-jars.patch
new file mode 100644
index 0000000000..fe1609629e
--- /dev/null
+++ b/patches/api/0474-Add-hook-to-remap-library-jars.patch
@@ -0,0 +1,38 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jason Penilla <[email protected]>
+Date: Sun, 28 Apr 2024 13:51:08 -0700
+Subject: [PATCH] Add hook to remap library jars
+
+
+diff --git a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java
+index 5b0203e908f84c531886b8ea8faeb591eb045636..8e1b6be2462aaa692efa1f72986921a6dc357196 100644
+--- a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java
++++ b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java
+@@ -47,6 +47,7 @@ public class LibraryLoader
+ private final DefaultRepositorySystemSession session;
+ private final List<RemoteRepository> repositories;
+ public static java.util.function.BiFunction<URL[], ClassLoader, URLClassLoader> LIBRARY_LOADER_FACTORY; // Paper - rewrite reflection in libraries
++ public static java.util.function.Function<List<java.nio.file.Path>, List<java.nio.file.Path>> REMAPPER; // Paper - remap libraries
+
+ public LibraryLoader(@NotNull Logger logger)
+ {
+@@ -111,9 +112,18 @@ public class LibraryLoader
+ }
+
+ List<URL> jarFiles = new ArrayList<>();
++ List<java.nio.file.Path> jarPaths = new ArrayList<>(); // Paper - remap libraries
+ for ( ArtifactResult artifact : result.getArtifactResults() )
+ {
+- File file = artifact.getArtifact().getFile();
++ // Paper start - remap libraries
++ jarPaths.add(artifact.getArtifact().getFile().toPath());
++ }
++ if (REMAPPER != null) {
++ jarPaths = REMAPPER.apply(jarPaths);
++ }
++ for (java.nio.file.Path path : jarPaths) {
++ File file = path.toFile();
++ // Paper end - remap libraries
+
+ URL url;
+ try
diff --git a/patches/server/0020-Plugin-remapping.patch b/patches/server/0020-Plugin-remapping.patch
index 7f868edfd0..9cfcaa768c 100644
--- a/patches/server/0020-Plugin-remapping.patch
+++ b/patches/server/0020-Plugin-remapping.patch
@@ -66,22 +66,24 @@ index 65fb16941fa7e3a9b300696fb6bd2b562bca48cd..5ffd1d7c130e01a4a7516b361e48bfaf
+ mainClass.set(null as String?)
+}
diff --git a/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java b/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java
-index 708e5bb9bbf0476fcc2c4b92c6830b094703b43e..bb1cfa8ea8b11fc36ea72c8e382b8554bccd0ce5 100644
+index 708e5bb9bbf0476fcc2c4b92c6830b094703b43e..6f14cb9a73faa1d0ae2939d08809d9f6c2a99e1d 100644
--- a/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java
+++ b/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java
-@@ -6,10 +6,10 @@ import io.papermc.paper.plugin.entrypoint.Entrypoint;
+@@ -6,10 +6,12 @@ import io.papermc.paper.plugin.entrypoint.Entrypoint;
import io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler;
import io.papermc.paper.plugin.provider.PluginProvider;
import io.papermc.paper.plugin.provider.type.paper.PaperPluginParent;
+import io.papermc.paper.pluginremap.PluginRemapper;
++import java.util.function.Function;
import joptsimple.OptionSet;
import net.minecraft.server.dedicated.DedicatedServer;
import org.bukkit.configuration.file.YamlConfiguration;
-import org.bukkit.craftbukkit.CraftServer;
++import org.bukkit.plugin.java.LibraryLoader;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
-@@ -25,10 +25,14 @@ public class PluginInitializerManager {
+@@ -25,10 +27,15 @@ public class PluginInitializerManager {
private static PluginInitializerManager impl;
private final Path pluginDirectory;
private final Path updateDirectory;
@@ -93,10 +95,11 @@ index 708e5bb9bbf0476fcc2c4b92c6830b094703b43e..bb1cfa8ea8b11fc36ea72c8e382b8554
+ this.pluginRemapper = Boolean.getBoolean("paper.disable-plugin-rewriting")
+ ? null
+ : PluginRemapper.create(pluginDirectory);
++ LibraryLoader.REMAPPER = this.pluginRemapper == null ? Function.identity() : this.pluginRemapper::remapLibraries;
}
private static PluginInitializerManager parse(@NotNull final OptionSet minecraftOptionSet) throws Exception {
-@@ -96,6 +100,7 @@ public class PluginInitializerManager {
+@@ -96,6 +103,7 @@ public class PluginInitializerManager {
public static void load(OptionSet optionSet) throws Exception {
// We have to load the bukkit configuration inorder to get the update folder location.
io.papermc.paper.plugin.PluginInitializerManager pluginSystem = io.papermc.paper.plugin.PluginInitializerManager.init(optionSet);
@@ -104,6 +107,31 @@ index 708e5bb9bbf0476fcc2c4b92c6830b094703b43e..bb1cfa8ea8b11fc36ea72c8e382b8554
// Register the default plugin directory
io.papermc.paper.plugin.util.EntrypointUtil.registerProvidersFromSource(io.papermc.paper.plugin.provider.source.DirectoryProviderSource.INSTANCE, pluginSystem.pluginDirectoryPath());
+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 f38ecd7f65dc24e4a3f0bc675e3730287ac353f1..f576060c8fe872772bbafe2016fc9b83a3c095f1 100644
+--- a/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java
++++ b/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java
+@@ -1,5 +1,6 @@
+ package io.papermc.paper.plugin.loader;
+
++import io.papermc.paper.plugin.PluginInitializerManager;
+ import io.papermc.paper.plugin.bootstrap.PluginProviderContext;
+ import io.papermc.paper.plugin.loader.library.ClassPathLibrary;
+ import io.papermc.paper.plugin.loader.library.PaperLibraryStore;
+@@ -45,9 +46,12 @@ public class PaperClasspathBuilder implements PluginClasspathBuilder {
+ }
+
+ List<Path> paths = paperLibraryStore.getPaths();
++ if (PluginInitializerManager.instance().pluginRemapper != null) {
++ paths = PluginInitializerManager.instance().pluginRemapper.remapLibraries(paths);
++ }
+ URL[] urls = new URL[paths.size()];
+ for (int i = 0; i < paths.size(); i++) {
+- Path path = paperLibraryStore.getPaths().get(i);
++ Path path = paths.get(i);
+ try {
+ urls[i] = path.toUri().toURL();
+ } catch (MalformedURLException e) {
diff --git a/src/main/java/io/papermc/paper/plugin/provider/source/DirectoryProviderSource.java b/src/main/java/io/papermc/paper/plugin/provider/source/DirectoryProviderSource.java
index 226f457db6c1461c943c157b2b91e7450abc9dc6..0846d3a904e470ae1920c5c8be3df9c5dfc3de27 100644
--- a/src/main/java/io/papermc/paper/plugin/provider/source/DirectoryProviderSource.java
@@ -347,10 +375,10 @@ index 0000000000000000000000000000000000000000..3a5bb5d2a45654385ca0bc15c81ef953
+}
diff --git a/src/main/java/io/papermc/paper/pluginremap/PluginRemapper.java b/src/main/java/io/papermc/paper/pluginremap/PluginRemapper.java
new file mode 100644
-index 0000000000000000000000000000000000000000..6f1e4ae352dcc6aacd9703b1653701f93974d1bd
+index 0000000000000000000000000000000000000000..a1ce1307b2834f2415bdddbf42d80e2d69a480e1
--- /dev/null
+++ b/src/main/java/io/papermc/paper/pluginremap/PluginRemapper.java
-@@ -0,0 +1,371 @@
+@@ -0,0 +1,433 @@
+package io.papermc.paper.pluginremap;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
@@ -397,6 +425,7 @@ index 0000000000000000000000000000000000000000..6f1e4ae352dcc6aacd9703b1653701f9
+ public static final boolean DEBUG_LOGGING = Boolean.getBoolean("Paper.PluginRemapperDebug");
+ private static final String PAPER_REMAPPED = ".paper-remapped";
+ private static final String UNKNOWN_ORIGIN = "unknown-origin";
++ private static final String LIBRARIES = "libraries";
+ private static final String EXTRA_PLUGINS = "extra-plugins";
+ private static final String REMAP_CLASSPATH = "remap-classpath";
+ private static final String REVERSED_MAPPINGS = "mappings/reversed";
@@ -407,6 +436,7 @@ index 0000000000000000000000000000000000000000..6f1e4ae352dcc6aacd9703b1653701f9
+ private final RemappedPluginIndex remappedPlugins;
+ private final RemappedPluginIndex extraPlugins;
+ private final UnknownOriginRemappedPluginIndex unknownOrigin;
++ private final UnknownOriginRemappedPluginIndex libraries;
+ private @Nullable CompletableFuture<IMappingFile> reversedMappings;
+
+ public PluginRemapper(final Path pluginsDir) {
@@ -418,6 +448,7 @@ index 0000000000000000000000000000000000000000..6f1e4ae352dcc6aacd9703b1653701f9
+ this.remappedPlugins = new RemappedPluginIndex(remappedPlugins, false);
+ this.extraPlugins = new RemappedPluginIndex(this.remappedPlugins.dir().resolve(EXTRA_PLUGINS), true);
+ this.unknownOrigin = new UnknownOriginRemappedPluginIndex(this.remappedPlugins.dir().resolve(UNKNOWN_ORIGIN));
++ this.libraries = new UnknownOriginRemappedPluginIndex(this.remappedPlugins.dir().resolve(LIBRARIES));
+ }
+
+ public static @Nullable PluginRemapper create(final Path pluginsDir) {
@@ -446,6 +477,7 @@ index 0000000000000000000000000000000000000000..6f1e4ae352dcc6aacd9703b1653701f9
+ this.remappedPlugins.write();
+ this.extraPlugins.write();
+ this.unknownOrigin.write(clean);
++ this.libraries.write(clean);
+ }
+
+ // Called on startup and reload
@@ -465,6 +497,29 @@ index 0000000000000000000000000000000000000000..6f1e4ae352dcc6aacd9703b1653701f9
+ this.save(false);
+ }
+
++ public List<Path> remapLibraries(final List<Path> libraries) {
++ final List<CompletableFuture<Path>> tasks = new ArrayList<>();
++ for (final Path lib : libraries) {
++ if (!lib.getFileName().toString().endsWith(".jar")) {
++ if (DEBUG_LOGGING) {
++ LOGGER.info("Library '{}' is not a jar.", libraries);
++ }
++ tasks.add(CompletableFuture.completedFuture(lib));
++ continue;
++ }
++ final @Nullable Path cached = this.libraries.getIfPresent(lib);
++ if (cached != null) {
++ if (DEBUG_LOGGING) {
++ LOGGER.info("Library '{}' has not changed since last remap.", libraries);
++ }
++ tasks.add(CompletableFuture.completedFuture(cached));
++ continue;
++ }
++ tasks.add(this.remapLibrary(this.libraries, lib));
++ }
++ return waitForAll(tasks);
++ }
++
+ public Path rewritePlugin(final Path plugin) {
+ // Already remapped
+ if (plugin.getParent().equals(this.remappedPlugins.dir())
@@ -585,6 +640,20 @@ index 0000000000000000000000000000000000000000..6f1e4ae352dcc6aacd9703b1653701f9
+ }, executor).thenCompose(f -> f);
+ }
+
++ private CompletableFuture<Path> remapPlugin(
++ final RemappedPluginIndex index,
++ final Path inputFile
++ ) {
++ return this.remap(index, inputFile, false);
++ }
++
++ private CompletableFuture<Path> remapLibrary(
++ final RemappedPluginIndex index,
++ final Path inputFile
++ ) {
++ return this.remap(index, inputFile, true);
++ }
++
+ /**
+ * Returns the remapped file if remapping was necessary, otherwise null.
+ *
@@ -592,7 +661,11 @@ index 0000000000000000000000000000000000000000..6f1e4ae352dcc6aacd9703b1653701f9
+ * @param inputFile input file
+ * @return remapped file, or inputFile if no remapping was necessary
+ */
-+ private CompletableFuture<Path> remapPlugin(final RemappedPluginIndex index, final Path inputFile) {
++ private CompletableFuture<Path> remap(
++ final RemappedPluginIndex index,
++ final Path inputFile,
++ final boolean library
++ ) {
+ final Path destination = index.input(inputFile);
+
+ try (final FileSystem fs = FileSystems.newFileSystem(inputFile, new HashMap<>())) {
@@ -608,18 +681,35 @@ index 0000000000000000000000000000000000000000..6f1e4ae352dcc6aacd9703b1653701f9
+ } else {
+ ns = null;
+ }
-+ if (ns != null && (ns.equals(InsertManifestAttribute.MOJANG_NAMESPACE) || ns.equals(InsertManifestAttribute.MOJANG_PLUS_YARN_NAMESPACE))) {
-+ if (DEBUG_LOGGING) {
-+ LOGGER.info("Plugin '{}' is already Mojang mapped.", inputFile);
++ final boolean mojangMappedManifest = ns != null && (ns.equals(InsertManifestAttribute.MOJANG_NAMESPACE) || ns.equals(InsertManifestAttribute.MOJANG_PLUS_YARN_NAMESPACE));
++ if (library) {
++ if (mojangMappedManifest) {
++ if (DEBUG_LOGGING) {
++ LOGGER.info("Library '{}' is already Mojang mapped.", inputFile);
++ }
++ index.skip(inputFile);
++ return CompletableFuture.completedFuture(inputFile);
++ } else if (ns == null) {
++ if (DEBUG_LOGGING) {
++ LOGGER.info("Library '{}' does not specify a mappings namespace (not remapping).", inputFile);
++ }
++ index.skip(inputFile);
++ return CompletableFuture.completedFuture(inputFile);
+ }
-+ index.skip(inputFile);
-+ return CompletableFuture.completedFuture(inputFile);
-+ } else if (ns == null && Files.exists(fs.getPath(PluginFileType.PAPER_PLUGIN_YML))) {
-+ if (DEBUG_LOGGING) {
-+ LOGGER.info("Plugin '{}' is a Paper plugin with no namespace specified.", inputFile);
++ } else {
++ if (mojangMappedManifest) {
++ if (DEBUG_LOGGING) {
++ LOGGER.info("Plugin '{}' is already Mojang mapped.", inputFile);
++ }
++ index.skip(inputFile);
++ return CompletableFuture.completedFuture(inputFile);
++ } else if (ns == null && Files.exists(fs.getPath(PluginFileType.PAPER_PLUGIN_YML))) {
++ if (DEBUG_LOGGING) {
++ LOGGER.info("Plugin '{}' is a Paper plugin with no namespace specified.", inputFile);
++ }
++ index.skip(inputFile);
++ return CompletableFuture.completedFuture(inputFile);
+ }
-+ index.skip(inputFile);
-+ return CompletableFuture.completedFuture(inputFile);
+ }
+ } catch (final IOException ex) {
+ throw new RuntimeException("Failed to open plugin jar " + inputFile, ex);
@@ -643,7 +733,7 @@ index 0000000000000000000000000000000000000000..6f1e4ae352dcc6aacd9703b1653701f9
+ } catch (final Exception ex) {
+ throw new RuntimeException("Failed to remap plugin jar '" + inputFile + "'", ex);
+ }
-+ LOGGER.info("Done remapping plugin '{}' in {}ms.", inputFile, System.currentTimeMillis() - start);
++ LOGGER.info("Done remapping {} '{}' in {}ms.", library ? "library" : "plugin", inputFile, System.currentTimeMillis() - start);
+ return destination;
+ }, this.threadPool);
+ }
diff --git a/patches/server/0022-Remap-reflection-calls-in-plugins-using-internals.patch b/patches/server/0022-Remap-reflection-calls-in-plugins-using-internals.patch
index f24134e203..1ac8ec3f81 100644
--- a/patches/server/0022-Remap-reflection-calls-in-plugins-using-internals.patch
+++ b/patches/server/0022-Remap-reflection-calls-in-plugins-using-internals.patch
@@ -43,6 +43,197 @@ index 893ad5e7c2d32ccd64962d95d146bbd317c28ab8..3d73ea0e63c97b2b08e719b7be7af389
if (mojName == null && MOJANG_TO_OBF.containsKey(name)) {
mojName = name;
}
+diff --git a/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/BytecodeModifyingURLClassLoader.java b/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/BytecodeModifyingURLClassLoader.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..405416dc3d1c8c58b4e0c880d8751ca319188f62
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/BytecodeModifyingURLClassLoader.java
+@@ -0,0 +1,185 @@
++package io.papermc.paper.plugin.entrypoint.classloader;
++
++import io.papermc.paper.pluginremap.reflect.ReflectionRemapper;
++import java.io.IOException;
++import java.io.InputStream;
++import java.io.UncheckedIOException;
++import java.net.JarURLConnection;
++import java.net.URL;
++import java.net.URLClassLoader;
++import java.security.CodeSigner;
++import java.security.CodeSource;
++import java.util.Map;
++import java.util.concurrent.ConcurrentHashMap;
++import java.util.function.Function;
++import java.util.jar.Attributes;
++import java.util.jar.Manifest;
++import org.checkerframework.checker.nullness.qual.Nullable;
++import org.objectweb.asm.ClassReader;
++import org.objectweb.asm.ClassVisitor;
++import org.objectweb.asm.ClassWriter;
++
++import static java.util.Objects.requireNonNullElse;
++
++public final class BytecodeModifyingURLClassLoader extends URLClassLoader {
++ static {
++ ClassLoader.registerAsParallelCapable();
++ }
++
++ private static final Object MISSING_MANIFEST = new Object();
++
++ private final Function<byte[], byte[]> modifier;
++ private final Map<String, Object> manifests = new ConcurrentHashMap<>();
++
++ public BytecodeModifyingURLClassLoader(
++ final URL[] urls,
++ final ClassLoader parent,
++ final Function<byte[], byte[]> modifier
++ ) {
++ super(urls, parent);
++ this.modifier = modifier;
++ }
++
++ public BytecodeModifyingURLClassLoader(
++ final URL[] urls,
++ final ClassLoader parent
++ ) {
++ this(urls, parent, bytes -> {
++ final ClassReader classReader = new ClassReader(bytes);
++ final ClassWriter classWriter = new ClassWriter(classReader, 0);
++ final ClassVisitor visitor = ReflectionRemapper.visitor(classWriter);
++ if (visitor == classWriter) {
++ return bytes;
++ }
++ classReader.accept(visitor, 0);
++ return classWriter.toByteArray();
++ });
++ }
++
++ @Override
++ protected Class<?> findClass(final String name) throws ClassNotFoundException {
++ final Class<?> result;
++ final String path = name.replace('.', '/').concat(".class");
++ final URL url = this.findResource(path);
++ if (url != null) {
++ try {
++ result = this.defineClass(name, url);
++ } catch (final IOException e) {
++ throw new ClassNotFoundException(name, e);
++ }
++ } else {
++ result = null;
++ }
++ if (result == null) {
++ throw new ClassNotFoundException(name);
++ }
++ return result;
++ }
++
++ private Class<?> defineClass(String name, URL url) throws IOException {
++ int i = name.lastIndexOf('.');
++ if (i != -1) {
++ String pkgname = name.substring(0, i);
++ // Check if package already loaded.
++ final @Nullable Manifest man = this.manifestFor(url);
++ if (this.getAndVerifyPackage(pkgname, man, url) == null) {
++ try {
++ if (man != null) {
++ this.definePackage(pkgname, man, url);
++ } else {
++ this.definePackage(pkgname, null, null, null, null, null, null, null);
++ }
++ } catch (IllegalArgumentException iae) {
++ // parallel-capable class loaders: re-verify in case of a
++ // race condition
++ if (this.getAndVerifyPackage(pkgname, man, url) == null) {
++ // Should never happen
++ throw new AssertionError("Cannot find package " +
++ pkgname);
++ }
++ }
++ }
++ }
++ final byte[] bytes;
++ try (final InputStream is = url.openStream()) {
++ bytes = is.readAllBytes();
++ }
++
++ final byte[] modified = this.modifier.apply(bytes);
++
++ final CodeSource cs = new CodeSource(url, (CodeSigner[]) null);
++ return this.defineClass(name, modified, 0, modified.length, cs);
++ }
++
++ private Package getAndVerifyPackage(
++ String pkgname,
++ Manifest man, URL url
++ ) {
++ Package pkg = getDefinedPackage(pkgname);
++ if (pkg != null) {
++ // Package found, so check package sealing.
++ if (pkg.isSealed()) {
++ // Verify that code source URL is the same.
++ if (!pkg.isSealed(url)) {
++ throw new SecurityException(
++ "sealing violation: package " + pkgname + " is sealed");
++ }
++ } else {
++ // Make sure we are not attempting to seal the package
++ // at this code source URL.
++ if ((man != null) && this.isSealed(pkgname, man)) {
++ throw new SecurityException(
++ "sealing violation: can't seal package " + pkgname +
++ ": already loaded");
++ }
++ }
++ }
++ return pkg;
++ }
++
++ private boolean isSealed(String name, Manifest man) {
++ Attributes attr = man.getAttributes(name.replace('.', '/').concat("/"));
++ String sealed = null;
++ if (attr != null) {
++ sealed = attr.getValue(Attributes.Name.SEALED);
++ }
++ if (sealed == null) {
++ if ((attr = man.getMainAttributes()) != null) {
++ sealed = attr.getValue(Attributes.Name.SEALED);
++ }
++ }
++ return "true".equalsIgnoreCase(sealed);
++ }
++
++ private @Nullable Manifest manifestFor(final URL url) throws IOException {
++ Manifest man = null;
++ if (url.getProtocol().equals("jar")) {
++ try {
++ final Object computedManifest = this.manifests.computeIfAbsent(jarName(url), $ -> {
++ try {
++ final Manifest m = ((JarURLConnection) url.openConnection()).getManifest();
++ return requireNonNullElse(m, MISSING_MANIFEST);
++ } catch (final IOException e) {
++ throw new UncheckedIOException(e);
++ }
++ });
++ if (computedManifest instanceof Manifest found) {
++ man = found;
++ }
++ } catch (final UncheckedIOException e) {
++ throw e.getCause();
++ } catch (final IllegalArgumentException e) {
++ throw new IOException(e);
++ }
++ }
++ return man;
++ }
++
++ private static String jarName(final URL sourceUrl) {
++ final int exclamationIdx = sourceUrl.getPath().lastIndexOf('!');
++ if (exclamationIdx != -1) {
++ return sourceUrl.getPath().substring(0, exclamationIdx);
++ }
++ throw new IllegalArgumentException("Could not find jar for URL " + sourceUrl);
++ }
++}
diff --git a/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperClassloaderBytecodeModifier.java b/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperClassloaderBytecodeModifier.java
index f9a2c55a354c877749db3f92956de802ae575788..39182cdd17473da0123dc7172dce507eab29fedc 100644
--- a/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperClassloaderBytecodeModifier.java
@@ -66,6 +257,70 @@ index f9a2c55a354c877749db3f92956de802ae575788..39182cdd17473da0123dc7172dce507e
+ return classWriter.toByteArray();
}
}
+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 f576060c8fe872772bbafe2016fc9b83a3c095f1..82032370e7896b621e37ee3726016440e177619f 100644
+--- a/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java
++++ b/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java
+@@ -2,12 +2,11 @@ package io.papermc.paper.plugin.loader;
+
+ import io.papermc.paper.plugin.PluginInitializerManager;
+ import io.papermc.paper.plugin.bootstrap.PluginProviderContext;
++import io.papermc.paper.plugin.entrypoint.classloader.BytecodeModifyingURLClassLoader;
++import io.papermc.paper.plugin.entrypoint.classloader.PaperPluginClassLoader;
+ import io.papermc.paper.plugin.loader.library.ClassPathLibrary;
+ import io.papermc.paper.plugin.loader.library.PaperLibraryStore;
+-import io.papermc.paper.plugin.entrypoint.classloader.PaperPluginClassLoader;
+ import io.papermc.paper.plugin.provider.configuration.PaperPluginMeta;
+-import org.jetbrains.annotations.NotNull;
+-
+ import java.io.IOException;
+ import java.net.MalformedURLException;
+ import java.net.URL;
+@@ -17,6 +16,7 @@ import java.util.ArrayList;
+ import java.util.List;
+ import java.util.jar.JarFile;
+ import java.util.logging.Logger;
++import org.jetbrains.annotations.NotNull;
+
+ public class PaperClasspathBuilder implements PluginClasspathBuilder {
+
+@@ -60,7 +60,8 @@ public class PaperClasspathBuilder implements PluginClasspathBuilder {
+ }
+
+ try {
+- return new PaperPluginClassLoader(logger, source, jarFile, configuration, this.getClass().getClassLoader(), new URLClassLoader(urls, getClass().getClassLoader()));
++ final URLClassLoader libraryLoader = new BytecodeModifyingURLClassLoader(urls, this.getClass().getClassLoader());
++ return new PaperPluginClassLoader(logger, source, jarFile, configuration, this.getClass().getClassLoader(), libraryLoader);
+ } catch (IOException exception) {
+ throw new RuntimeException(exception);
+ }
+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 bdd9bc8a414719b9f1d6f01f90539ddb8603a878..31f05a7336ea124d24a5059652a2950a9f672758 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
+@@ -1,9 +1,11 @@
+ package io.papermc.paper.plugin.provider.type.spigot;
+
++import io.papermc.paper.plugin.entrypoint.classloader.BytecodeModifyingURLClassLoader;
+ import io.papermc.paper.plugin.provider.configuration.serializer.constraints.PluginConfigConstraints;
+ import io.papermc.paper.plugin.provider.type.PluginTypeFactory;
+ import org.bukkit.plugin.InvalidDescriptionException;
+ import org.bukkit.plugin.PluginDescriptionFile;
++import org.bukkit.plugin.java.LibraryLoader;
+ import org.yaml.snakeyaml.error.YAMLException;
+
+ import java.io.IOException;
+@@ -15,6 +17,10 @@ import java.util.jar.JarFile;
+
+ class SpigotPluginProviderFactory implements PluginTypeFactory<SpigotPluginProvider, PluginDescriptionFile> {
+
++ static {
++ LibraryLoader.LIBRARY_LOADER_FACTORY = BytecodeModifyingURLClassLoader::new;
++ }
++
+ @Override
+ public SpigotPluginProvider build(JarFile file, PluginDescriptionFile configuration, Path source) throws InvalidDescriptionException {
+ // Copied from SimplePluginManager#loadPlugins
diff --git a/src/main/java/io/papermc/paper/pluginremap/reflect/PaperReflection.java b/src/main/java/io/papermc/paper/pluginremap/reflect/PaperReflection.java
new file mode 100644
index 0000000000000000000000000000000000000000..c653d1f5af8b407cfba715e6027dbb695046892a
diff --git a/patches/server/1046-Modify-library-loader-jars-bytecode.patch b/patches/server/1046-Modify-library-loader-jars-bytecode.patch
deleted file mode 100644
index 126560a15e..0000000000
--- a/patches/server/1046-Modify-library-loader-jars-bytecode.patch
+++ /dev/null
@@ -1,261 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jason Penilla <[email protected]>
-Date: Sun, 28 Apr 2024 11:12:14 -0700
-Subject: [PATCH] Modify library loader jars bytecode
-
-
-diff --git a/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/BytecodeModifyingURLClassLoader.java b/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/BytecodeModifyingURLClassLoader.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..405416dc3d1c8c58b4e0c880d8751ca319188f62
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/BytecodeModifyingURLClassLoader.java
-@@ -0,0 +1,185 @@
-+package io.papermc.paper.plugin.entrypoint.classloader;
-+
-+import io.papermc.paper.pluginremap.reflect.ReflectionRemapper;
-+import java.io.IOException;
-+import java.io.InputStream;
-+import java.io.UncheckedIOException;
-+import java.net.JarURLConnection;
-+import java.net.URL;
-+import java.net.URLClassLoader;
-+import java.security.CodeSigner;
-+import java.security.CodeSource;
-+import java.util.Map;
-+import java.util.concurrent.ConcurrentHashMap;
-+import java.util.function.Function;
-+import java.util.jar.Attributes;
-+import java.util.jar.Manifest;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.objectweb.asm.ClassReader;
-+import org.objectweb.asm.ClassVisitor;
-+import org.objectweb.asm.ClassWriter;
-+
-+import static java.util.Objects.requireNonNullElse;
-+
-+public final class BytecodeModifyingURLClassLoader extends URLClassLoader {
-+ static {
-+ ClassLoader.registerAsParallelCapable();
-+ }
-+
-+ private static final Object MISSING_MANIFEST = new Object();
-+
-+ private final Function<byte[], byte[]> modifier;
-+ private final Map<String, Object> manifests = new ConcurrentHashMap<>();
-+
-+ public BytecodeModifyingURLClassLoader(
-+ final URL[] urls,
-+ final ClassLoader parent,
-+ final Function<byte[], byte[]> modifier
-+ ) {
-+ super(urls, parent);
-+ this.modifier = modifier;
-+ }
-+
-+ public BytecodeModifyingURLClassLoader(
-+ final URL[] urls,
-+ final ClassLoader parent
-+ ) {
-+ this(urls, parent, bytes -> {
-+ final ClassReader classReader = new ClassReader(bytes);
-+ final ClassWriter classWriter = new ClassWriter(classReader, 0);
-+ final ClassVisitor visitor = ReflectionRemapper.visitor(classWriter);
-+ if (visitor == classWriter) {
-+ return bytes;
-+ }
-+ classReader.accept(visitor, 0);
-+ return classWriter.toByteArray();
-+ });
-+ }
-+
-+ @Override
-+ protected Class<?> findClass(final String name) throws ClassNotFoundException {
-+ final Class<?> result;
-+ final String path = name.replace('.', '/').concat(".class");
-+ final URL url = this.findResource(path);
-+ if (url != null) {
-+ try {
-+ result = this.defineClass(name, url);
-+ } catch (final IOException e) {
-+ throw new ClassNotFoundException(name, e);
-+ }
-+ } else {
-+ result = null;
-+ }
-+ if (result == null) {
-+ throw new ClassNotFoundException(name);
-+ }
-+ return result;
-+ }
-+
-+ private Class<?> defineClass(String name, URL url) throws IOException {
-+ int i = name.lastIndexOf('.');
-+ if (i != -1) {
-+ String pkgname = name.substring(0, i);
-+ // Check if package already loaded.
-+ final @Nullable Manifest man = this.manifestFor(url);
-+ if (this.getAndVerifyPackage(pkgname, man, url) == null) {
-+ try {
-+ if (man != null) {
-+ this.definePackage(pkgname, man, url);
-+ } else {
-+ this.definePackage(pkgname, null, null, null, null, null, null, null);
-+ }
-+ } catch (IllegalArgumentException iae) {
-+ // parallel-capable class loaders: re-verify in case of a
-+ // race condition
-+ if (this.getAndVerifyPackage(pkgname, man, url) == null) {
-+ // Should never happen
-+ throw new AssertionError("Cannot find package " +
-+ pkgname);
-+ }
-+ }
-+ }
-+ }
-+ final byte[] bytes;
-+ try (final InputStream is = url.openStream()) {
-+ bytes = is.readAllBytes();
-+ }
-+
-+ final byte[] modified = this.modifier.apply(bytes);
-+
-+ final CodeSource cs = new CodeSource(url, (CodeSigner[]) null);
-+ return this.defineClass(name, modified, 0, modified.length, cs);
-+ }
-+
-+ private Package getAndVerifyPackage(
-+ String pkgname,
-+ Manifest man, URL url
-+ ) {
-+ Package pkg = getDefinedPackage(pkgname);
-+ if (pkg != null) {
-+ // Package found, so check package sealing.
-+ if (pkg.isSealed()) {
-+ // Verify that code source URL is the same.
-+ if (!pkg.isSealed(url)) {
-+ throw new SecurityException(
-+ "sealing violation: package " + pkgname + " is sealed");
-+ }
-+ } else {
-+ // Make sure we are not attempting to seal the package
-+ // at this code source URL.
-+ if ((man != null) && this.isSealed(pkgname, man)) {
-+ throw new SecurityException(
-+ "sealing violation: can't seal package " + pkgname +
-+ ": already loaded");
-+ }
-+ }
-+ }
-+ return pkg;
-+ }
-+
-+ private boolean isSealed(String name, Manifest man) {
-+ Attributes attr = man.getAttributes(name.replace('.', '/').concat("/"));
-+ String sealed = null;
-+ if (attr != null) {
-+ sealed = attr.getValue(Attributes.Name.SEALED);
-+ }
-+ if (sealed == null) {
-+ if ((attr = man.getMainAttributes()) != null) {
-+ sealed = attr.getValue(Attributes.Name.SEALED);
-+ }
-+ }
-+ return "true".equalsIgnoreCase(sealed);
-+ }
-+
-+ private @Nullable Manifest manifestFor(final URL url) throws IOException {
-+ Manifest man = null;
-+ if (url.getProtocol().equals("jar")) {
-+ try {
-+ final Object computedManifest = this.manifests.computeIfAbsent(jarName(url), $ -> {
-+ try {
-+ final Manifest m = ((JarURLConnection) url.openConnection()).getManifest();
-+ return requireNonNullElse(m, MISSING_MANIFEST);
-+ } catch (final IOException e) {
-+ throw new UncheckedIOException(e);
-+ }
-+ });
-+ if (computedManifest instanceof Manifest found) {
-+ man = found;
-+ }
-+ } catch (final UncheckedIOException e) {
-+ throw e.getCause();
-+ } catch (final IllegalArgumentException e) {
-+ throw new IOException(e);
-+ }
-+ }
-+ return man;
-+ }
-+
-+ private static String jarName(final URL sourceUrl) {
-+ final int exclamationIdx = sourceUrl.getPath().lastIndexOf('!');
-+ if (exclamationIdx != -1) {
-+ return sourceUrl.getPath().substring(0, exclamationIdx);
-+ }
-+ throw new IllegalArgumentException("Could not find jar for URL " + sourceUrl);
-+ }
-+}
-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 f38ecd7f65dc24e4a3f0bc675e3730287ac353f1..ca6cb891e9da9d7e08f1a82fab212d2063cc9ef6 100644
---- a/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java
-+++ b/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java
-@@ -1,12 +1,11 @@
- package io.papermc.paper.plugin.loader;
-
- import io.papermc.paper.plugin.bootstrap.PluginProviderContext;
-+import io.papermc.paper.plugin.entrypoint.classloader.BytecodeModifyingURLClassLoader;
-+import io.papermc.paper.plugin.entrypoint.classloader.PaperPluginClassLoader;
- import io.papermc.paper.plugin.loader.library.ClassPathLibrary;
- import io.papermc.paper.plugin.loader.library.PaperLibraryStore;
--import io.papermc.paper.plugin.entrypoint.classloader.PaperPluginClassLoader;
- import io.papermc.paper.plugin.provider.configuration.PaperPluginMeta;
--import org.jetbrains.annotations.NotNull;
--
- import java.io.IOException;
- import java.net.MalformedURLException;
- import java.net.URL;
-@@ -16,6 +15,7 @@ import java.util.ArrayList;
- import java.util.List;
- import java.util.jar.JarFile;
- import java.util.logging.Logger;
-+import org.jetbrains.annotations.NotNull;
-
- public class PaperClasspathBuilder implements PluginClasspathBuilder {
-
-@@ -56,7 +56,8 @@ public class PaperClasspathBuilder implements PluginClasspathBuilder {
- }
-
- try {
-- return new PaperPluginClassLoader(logger, source, jarFile, configuration, this.getClass().getClassLoader(), new URLClassLoader(urls, getClass().getClassLoader()));
-+ final URLClassLoader libraryLoader = new BytecodeModifyingURLClassLoader(urls, this.getClass().getClassLoader());
-+ return new PaperPluginClassLoader(logger, source, jarFile, configuration, this.getClass().getClassLoader(), libraryLoader);
- } catch (IOException exception) {
- throw new RuntimeException(exception);
- }
-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 bdd9bc8a414719b9f1d6f01f90539ddb8603a878..31f05a7336ea124d24a5059652a2950a9f672758 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
-@@ -1,9 +1,11 @@
- package io.papermc.paper.plugin.provider.type.spigot;
-
-+import io.papermc.paper.plugin.entrypoint.classloader.BytecodeModifyingURLClassLoader;
- import io.papermc.paper.plugin.provider.configuration.serializer.constraints.PluginConfigConstraints;
- import io.papermc.paper.plugin.provider.type.PluginTypeFactory;
- import org.bukkit.plugin.InvalidDescriptionException;
- import org.bukkit.plugin.PluginDescriptionFile;
-+import org.bukkit.plugin.java.LibraryLoader;
- import org.yaml.snakeyaml.error.YAMLException;
-
- import java.io.IOException;
-@@ -15,6 +17,10 @@ import java.util.jar.JarFile;
-
- class SpigotPluginProviderFactory implements PluginTypeFactory<SpigotPluginProvider, PluginDescriptionFile> {
-
-+ static {
-+ LibraryLoader.LIBRARY_LOADER_FACTORY = BytecodeModifyingURLClassLoader::new;
-+ }
-+
- @Override
- public SpigotPluginProvider build(JarFile file, PluginDescriptionFile configuration, Path source) throws InvalidDescriptionException {
- // Copied from SimplePluginManager#loadPlugins