Fix plugin loading exception
This commit is contained in:
parent
9f6ea9d3d4
commit
228c508df0
@ -0,0 +1,160 @@
|
|||||||
|
package org.patchbukkit.loader;
|
||||||
|
|
||||||
|
import io.papermc.paper.plugin.configuration.PluginMeta;
|
||||||
|
import io.papermc.paper.plugin.provider.classloader.ConfiguredPluginClassLoader;
|
||||||
|
import io.papermc.paper.plugin.provider.classloader.PluginClassLoaderGroup;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
import org.bukkit.plugin.InvalidDescriptionException;
|
||||||
|
import org.bukkit.plugin.PluginDescriptionFile;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class PatchBukkitPluginClassLoader
|
||||||
|
extends URLClassLoader
|
||||||
|
implements ConfiguredPluginClassLoader
|
||||||
|
{
|
||||||
|
|
||||||
|
private final PluginDescriptionFile description;
|
||||||
|
private final File dataFolder;
|
||||||
|
private final File file;
|
||||||
|
private JavaPlugin plugin;
|
||||||
|
|
||||||
|
static {
|
||||||
|
ClassLoader.registerAsParallelCapable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PatchBukkitPluginClassLoader(ClassLoader parent, File file)
|
||||||
|
throws MalformedURLException, InvalidDescriptionException {
|
||||||
|
super(new URL[] { file.toURI().toURL() }, parent);
|
||||||
|
this.file = file;
|
||||||
|
this.description = loadDescription(file);
|
||||||
|
this.dataFolder = new File(file.getParentFile(), description.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PluginDescriptionFile loadDescription(File file)
|
||||||
|
throws InvalidDescriptionException {
|
||||||
|
try (JarFile jar = new JarFile(file)) {
|
||||||
|
JarEntry entry = jar.getJarEntry("paper-plugin.yml");
|
||||||
|
if (entry == null) {
|
||||||
|
entry = jar.getJarEntry("plugin.yml");
|
||||||
|
}
|
||||||
|
if (entry == null) {
|
||||||
|
throw new InvalidDescriptionException(
|
||||||
|
"Jar does not contain plugin.yml"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
try (InputStream stream = jar.getInputStream(entry)) {
|
||||||
|
return new PluginDescriptionFile(stream);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new InvalidDescriptionException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child-first class loading: try to load from plugin JAR before delegating to parent.
|
||||||
|
* This is critical for plugin classes to be loaded by this classloader.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Class<?> loadClass(String name, boolean resolve)
|
||||||
|
throws ClassNotFoundException {
|
||||||
|
synchronized (getClassLoadingLock(name)) {
|
||||||
|
// First, check if already loaded
|
||||||
|
Class<?> c = findLoadedClass(name);
|
||||||
|
|
||||||
|
if (c == null) {
|
||||||
|
// For plugin-specific classes, try to load from JAR first (child-first)
|
||||||
|
// For JDK and server classes, delegate to parent
|
||||||
|
if (
|
||||||
|
!name.startsWith("java.") &&
|
||||||
|
!name.startsWith("jdk.") &&
|
||||||
|
!name.startsWith("sun.") &&
|
||||||
|
!name.startsWith("javax.")
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
// Try to find in plugin JAR first
|
||||||
|
c = findClass(name);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
// Not in JAR, delegate to parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not found in JAR (or is a system class), delegate to parent
|
||||||
|
if (c == null) {
|
||||||
|
c = getParent().loadClass(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolve) {
|
||||||
|
resolveClass(c);
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PluginMeta getConfiguration() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> loadClass(
|
||||||
|
String name,
|
||||||
|
boolean resolve,
|
||||||
|
boolean checkGlobal,
|
||||||
|
boolean checkLibraries
|
||||||
|
) throws ClassNotFoundException {
|
||||||
|
return loadClass(name, resolve);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(JavaPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
if (!dataFolder.exists()) {
|
||||||
|
dataFolder.mkdirs();
|
||||||
|
}
|
||||||
|
plugin.init(
|
||||||
|
org.bukkit.Bukkit.getServer(),
|
||||||
|
description,
|
||||||
|
dataFolder,
|
||||||
|
file,
|
||||||
|
this,
|
||||||
|
description,
|
||||||
|
com.destroystokyo.paper.utils.PaperPluginLogger.getLogger(
|
||||||
|
description
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public JavaPlugin getPlugin() {
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public PluginClassLoaderGroup getGroup() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PluginDescriptionFile getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getDataFolder() {
|
||||||
|
return dataFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,16 +4,32 @@ import io.papermc.paper.plugin.provider.util.ProviderUtil;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
public class PluginLoader {
|
public class PluginLoader {
|
||||||
|
|
||||||
public JavaPlugin createPlugin(String mainClass) {
|
private static final Logger LOGGER = Logger.getLogger("PatchBukkit");
|
||||||
|
|
||||||
|
public static JavaPlugin createPlugin(String jarPath, String mainClass) {
|
||||||
try {
|
try {
|
||||||
|
File jarFile = new File(jarPath);
|
||||||
|
if (!jarFile.exists()) {
|
||||||
|
LOGGER.severe("Plugin file does not exist: " + jarPath);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
PatchBukkitPluginClassLoader classLoader =
|
||||||
|
new PatchBukkitPluginClassLoader(
|
||||||
|
PluginLoader.class.getClassLoader(),
|
||||||
|
jarFile
|
||||||
|
);
|
||||||
|
|
||||||
return ProviderUtil.loadClass(
|
return ProviderUtil.loadClass(
|
||||||
mainClass,
|
mainClass,
|
||||||
JavaPlugin.class,
|
JavaPlugin.class,
|
||||||
this.getClass().getClassLoader()
|
classLoader
|
||||||
);
|
);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@ -1,13 +1,9 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use j4rs::{ClasspathEntry, InvocationArg, Jvm, JvmBuilder};
|
use j4rs::{InvocationArg, Jvm, JvmBuilder};
|
||||||
|
|
||||||
pub fn initialize_jvm(
|
pub fn initialize_jvm(j4rs_folder: &PathBuf) -> Result<Jvm, String> {
|
||||||
classpath_entries: Vec<ClasspathEntry>,
|
|
||||||
j4rs_folder: &PathBuf,
|
|
||||||
) -> Result<Jvm, String> {
|
|
||||||
let jvm = JvmBuilder::new()
|
let jvm = JvmBuilder::new()
|
||||||
.classpath_entries(classpath_entries)
|
|
||||||
.with_base_path(j4rs_folder)
|
.with_base_path(j4rs_folder)
|
||||||
.build()
|
.build()
|
||||||
.map_err(|err| format!("JVM failed to init: {:?}", err))?;
|
.map_err(|err| format!("JVM failed to init: {:?}", err))?;
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use j4rs::ClasspathEntry;
|
|
||||||
use pumpkin::plugin::Context;
|
use pumpkin::plugin::Context;
|
||||||
use pumpkin_api_macros::{plugin_impl, plugin_method};
|
use pumpkin_api_macros::{plugin_impl, plugin_method};
|
||||||
|
|
||||||
@ -60,17 +59,13 @@ async fn on_load_inner(_plugin: &mut MyPlugin, server: Arc<Context>) -> Result<(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let classpath_entries = jar_paths
|
|
||||||
.iter()
|
|
||||||
.map(|entry| ClasspathEntry::new(entry))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Manage embedded resources
|
// Manage embedded resources
|
||||||
cleanup_stale_files(&dirs.j4rs);
|
cleanup_stale_files(&dirs.j4rs);
|
||||||
sync_embedded_resources(&dirs.j4rs)?;
|
sync_embedded_resources(&dirs.j4rs)?;
|
||||||
|
|
||||||
// Initialize JVM and PatchBukkit server
|
// Initialize JVM and PatchBukkit server
|
||||||
let jvm = initialize_jvm(classpath_entries, &dirs.j4rs)?;
|
let jvm = initialize_jvm(&dirs.j4rs)?;
|
||||||
setup_patchbukkit_server(&jvm)?;
|
setup_patchbukkit_server(&jvm)?;
|
||||||
|
|
||||||
plugin_manager
|
plugin_manager
|
||||||
|
|||||||
@ -158,7 +158,10 @@ impl PluginManager {
|
|||||||
let result = jvm.invoke(
|
let result = jvm.invoke(
|
||||||
&loader,
|
&loader,
|
||||||
"createPlugin",
|
"createPlugin",
|
||||||
&[InvocationArg::try_from(&plugin.main_class)?],
|
&[
|
||||||
|
InvocationArg::try_from(&plugin.path.to_string_lossy().to_string())?,
|
||||||
|
InvocationArg::try_from(&plugin.main_class)?,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user