Skip to content

Commit 512ca91

Browse files
committed
Removed knot class loader hack
1 parent 564190e commit 512ca91

File tree

2 files changed

+94
-43
lines changed

2 files changed

+94
-43
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.lambda.plugin
2+
3+
import java.io.*
4+
import java.util.jar.JarFile
5+
6+
/**
7+
* A class for loading classes from a JAR file.
8+
*
9+
* @property file The JAR file from which to load classes
10+
* @property parent The parent ClassLoader to delegate to.
11+
*/
12+
internal class PluginLoader(
13+
private val file: JarFile,
14+
parent: ClassLoader,
15+
) : ClassLoader(parent), Closeable {
16+
private val classes = mutableMapOf<String, ByteArray>()
17+
18+
init {
19+
file.entries().asSequence().forEach { entry ->
20+
if (entry.isDirectory) return@forEach
21+
22+
val bytes = file.getInputStream(entry).use { it.readBytes() }
23+
24+
if (entry.name.endsWith(".class")) {
25+
classes[
26+
entry.name.removeSuffix(".class")
27+
.replace('/', '.')
28+
] = bytes
29+
}
30+
}
31+
}
32+
33+
/**
34+
* Loads the class with the specified name.
35+
*
36+
* @param name The binary name of the class.
37+
* @return The resulting class object.
38+
*/
39+
public override fun findClass(name: String): Class<*> {
40+
val clazz = classes[name] ?: throw ClassNotFoundException(name)
41+
42+
return defineClass(name, clazz, 0, clazz.size)
43+
}
44+
45+
/**
46+
* Closes the JAR file.
47+
*/
48+
override fun close() { file.close() }
49+
}

common/src/main/kotlin/com/lambda/plugin/PluginRegistry.kt

Lines changed: 45 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,73 +5,75 @@ import com.lambda.core.Loadable
55
import com.lambda.util.FolderRegister.listRecursive
66
import com.lambda.util.FolderRegister.mods
77
import java.io.File
8-
import java.lang.reflect.InvocationTargetException
9-
import java.lang.reflect.Method
10-
import java.net.URL
118
import java.util.jar.JarFile
129

1310
object PluginRegistry : Loadable {
14-
private val errorMessageFMT = """
15-
A serious error occurred while loading a plugin.
16-
This is likely a bug in the plugin itself but it could also be a bug in Lambda.
17-
If you are a developer, please check the plugin's main class and load method.
18-
If you are a regular user, please report this issue to Lambda team and the plugin developer.
11+
private val classLoaderError = """
12+
An error occurred while retrieving the thread class loader.
13+
This likely mean that newer versions of the mod loader
14+
have changed the way mods are loaded, affecting the plugin system.
15+
Please report this error to the Lambda developers with the following information:
16+
- The version of Lambda
17+
- The version of the mod loader (ex. Fabric, Forge, etc.)
18+
- The version of Minecraft you are using
19+
- The version of Java you are using
1920
20-
Plugin: {}
21-
Stacktrace: {}
21+
Plugin: %s
22+
Stacktrace:
23+
%s
2224
""".trimIndent()
2325

24-
private val threadClassLoader: ClassLoader = Thread.currentThread().contextClassLoader
25-
private val addUrlMethod: Method
26-
27-
init {
28-
try {
29-
addUrlMethod =
30-
threadClassLoader.javaClass.getMethod("addUrlFwd", URL::class.java) // TODO: Check for other methods if not found
31-
32-
addUrlMethod.isAccessible = true
33-
} catch (e: NoSuchMethodException) {
34-
throw RuntimeException("Failed to get the addURL method from the KnotClassLoader.")
35-
}
36-
}
37-
38-
@Throws(
39-
IllegalAccessException::class,
40-
IllegalArgumentException::class,
41-
InvocationTargetException::class)
42-
fun forceFeedJar(jar: File) {
43-
addUrlMethod.invoke(threadClassLoader, jar.toURI().toURL())
44-
}
26+
private val loadingError = """
27+
An error occurred while loading a plugin.
28+
If you are a developer, please check the plugin's main class and load method.
29+
If you are a regular user, please report this issue to the plugin developer.
30+
31+
Plugin: %s
32+
Error: %s
33+
Stacktrace:
34+
%s
35+
""".trimIndent()
4536

4637
private fun loadPlugin(file: File) {
4738
runCatching {
48-
// Someone got a better idea?
49-
// Don't like nested try-catch blocks
50-
runCatching { forceFeedJar(file) }
51-
.onFailure {
52-
LOG.error("Failed to feed the plugin {} to the thread class loader", file)
53-
}
54-
55-
LOG.debug("Added the URL of the plugin {} to the thread class loader", file)
56-
5739
val jar = JarFile(file)
5840

5941
val mainClass = jar.manifest.mainAttributes.getValue("Main-Class")
6042
?: return LOG.error("The plugin $jar does not have a main class")
6143

62-
val loadClass = threadClassLoader.loadClass(mainClass)
44+
val loader = PluginLoader(jar,
45+
Thread.currentThread().contextClassLoader ?:
46+
return classLoaderError.format(
47+
file,
48+
Thread.currentThread().stackTrace.joinToString("\n")
49+
)
50+
.split("\n")
51+
.forEach(LOG::error))
52+
53+
val loadClass = loader.loadClass(mainClass)
6354

6455
val loadInstance =
6556
loadClass.declaredFields.firstOrNull { it.name == "INSTANCE" }?.get(null)
6657
?: loadClass.constructors.firstOrNull()?.newInstance()
6758
?: return LOG.error("The plugin $jar does not have an object instance or a public constructor")
68-
6959
val loadMethod =
7060
loadClass?.methods?.find { it.name == "load" }
7161
?: return LOG.warn("The plugin $jar does not have a load method")
7262

7363
loadMethod.invoke(loadInstance)
74-
}.onFailure { LOG.error(errorMessageFMT, file, Thread.currentThread().stackTrace.joinToString("\n")) }
64+
}.onFailure {
65+
val threadDump = Thread.getAllStackTraces().entries.joinToString("\n") {
66+
it.key.toString() + it.value.joinToString("\n") { "\tat $it" }
67+
}
68+
69+
loadingError.format(
70+
file,
71+
it.message,
72+
threadDump
73+
)
74+
.split("\n")
75+
.forEach(LOG::error)
76+
}
7577
}
7678

7779
override fun load(): String {

0 commit comments

Comments
 (0)