Skip to content

Commit 564190e

Browse files
committed
Refactor: Plugin system
1 parent 24e7465 commit 564190e

File tree

11 files changed

+63
-155
lines changed

11 files changed

+63
-155
lines changed

build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,11 @@ allprojects {
8080
apply(plugin = "maven-publish")
8181
apply(plugin = "org.jetbrains.kotlin.jvm")
8282

83-
base.archivesName.set(modId)
8483
group = mavenGroup
8584
version = modVersion
8685

86+
base.archivesName = modId
87+
8788
repositories {
8889
maven("https://api.modrinth.com/maven")
8990
maven("https://jitpack.io")

common/build.gradle.kts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,3 @@ dependencies {
2929
modImplementation("baritone-api:baritone-api:1.10.2")
3030
modImplementation("baritone-api:baritone-unoptimized-fabric:1.10.2")
3131
}
32-
33-
tasks {
34-
// Prevent recursive libraries
35-
remapJar {
36-
enabled = false
37-
}
38-
}
39-

common/src/main/kotlin/com/lambda/command/commands/ReplayCommand.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ object ReplayCommand : LambdaCommand(
2727
required(greedyString("replay filepath")) { replayName ->
2828
suggests { _, builder ->
2929
val dir = FolderRegister.replay
30-
dir.listRecursive().forEach {
30+
dir.listRecursive { it.isFile }.forEach {
3131
builder.suggest(it.relativeTo(dir).path)
3232
}
3333
builder.buildFuture()
@@ -67,4 +67,4 @@ object ReplayCommand : LambdaCommand(
6767
}
6868
}
6969
}
70-
}
70+
}

common/src/main/kotlin/com/lambda/core/Loader.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import com.lambda.interaction.RotationManager
1010
import com.lambda.module.ModuleRegistry
1111
import com.lambda.plugin.PluginRegistry
1212
import com.lambda.util.Communication.ascii
13-
import com.lambda.util.FolderRegister.plugins
1413
import kotlin.system.measureTimeMillis
1514

1615
object Loader {
@@ -22,14 +21,13 @@ object Loader {
2221
LambdaFont.Loader,
2322
GuiConfigurable,
2423
FriendManager,
24+
PluginRegistry,
2525
)
2626

2727
fun initialize() {
2828
ascii.split("\n").forEach { LOG.info(it) }
2929
LOG.info("Initializing ${Lambda.MOD_NAME} ${Lambda.VERSION}")
3030

31-
PluginRegistry.load(plugins) // TODO: Find something else
32-
3331
val initTime = measureTimeMillis {
3432
loadables.forEach { loadable ->
3533
var info: String

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

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 53 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,34 @@
11
package com.lambda.plugin
22

33
import com.lambda.Lambda.LOG
4+
import com.lambda.core.Loadable
5+
import com.lambda.util.FolderRegister.listRecursive
6+
import com.lambda.util.FolderRegister.mods
47
import java.io.File
58
import java.lang.reflect.InvocationTargetException
69
import java.lang.reflect.Method
710
import java.net.URL
811
import java.util.jar.JarFile
912

10-
object PluginRegistry {
11-
private val knotClassLoader: ClassLoader = Thread.currentThread().contextClassLoader
12-
private val addUrlMethod: Method
13+
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.
19+
20+
Plugin: {}
21+
Stacktrace: {}
22+
""".trimIndent()
1323

14-
private var loadClass: Class<*>? = null
15-
private var loadMethod: Method? = null
16-
private var loadInstance: Any? = null // TODO: This won't work with constructors
24+
private val threadClassLoader: ClassLoader = Thread.currentThread().contextClassLoader
25+
private val addUrlMethod: Method
1726

1827
init {
1928
try {
20-
addUrlMethod = knotClassLoader.javaClass.getMethod("addUrlFwd", URL::class.java)
29+
addUrlMethod =
30+
threadClassLoader.javaClass.getMethod("addUrlFwd", URL::class.java) // TODO: Check for other methods if not found
31+
2132
addUrlMethod.isAccessible = true
2233
} catch (e: NoSuchMethodException) {
2334
throw RuntimeException("Failed to get the addURL method from the KnotClassLoader.")
@@ -28,64 +39,54 @@ object PluginRegistry {
2839
IllegalAccessException::class,
2940
IllegalArgumentException::class,
3041
InvocationTargetException::class)
31-
fun feedJarToKnot(jar: File) {
32-
addUrlMethod.invoke(knotClassLoader, jar.toURI().toURL())
42+
fun forceFeedJar(jar: File) {
43+
addUrlMethod.invoke(threadClassLoader, jar.toURI().toURL())
3344
}
3445

35-
private fun preLoadPlugin(file: File): PluginClassLoader? {
36-
runCatching { feedJarToKnot(file) }
37-
.onFailure {
38-
LOG.error("Failed to add the URL of the plugin $file", it)
39-
return null
40-
}
46+
private fun loadPlugin(file: File) {
47+
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+
}
4154

42-
LOG.debug("Added the URL of the plugin {} to the Knot class loader", file)
55+
LOG.debug("Added the URL of the plugin {} to the thread class loader", file)
4356

44-
val jar = JarFile(file)
45-
val loader = PluginClassLoader(jar, knotClassLoader)
57+
val jar = JarFile(file)
4658

47-
val mainClass = jar.manifest.mainAttributes.getValue("Main-Class")
48-
?: return null.also { LOG.error("The plugin $jar does not have a main class") }
59+
val mainClass = jar.manifest.mainAttributes.getValue("Main-Class")
60+
?: return LOG.error("The plugin $jar does not have a main class")
4961

50-
loadClass = loader.loadClass(mainClass)
62+
val loadClass = threadClassLoader.loadClass(mainClass)
5163

52-
loadInstance =
53-
loadClass?.declaredFields?.firstOrNull { it.name == "INSTANCE" }?.get(null)
54-
?: loadClass?.constructors?.firstOrNull()?.newInstance()
55-
?: return null.also { LOG.error("The plugin $jar does not have an object instance or a public constructor") }
64+
val loadInstance =
65+
loadClass.declaredFields.firstOrNull { it.name == "INSTANCE" }?.get(null)
66+
?: loadClass.constructors.firstOrNull()?.newInstance()
67+
?: return LOG.error("The plugin $jar does not have an object instance or a public constructor")
5668

57-
loadMethod =
58-
loadClass?.methods?.find { it.name == "load" } ?: return null
59-
.also { LOG.warn("The plugin $jar does not have a load method") }
69+
val loadMethod =
70+
loadClass?.methods?.find { it.name == "load" }
71+
?: return LOG.warn("The plugin $jar does not have a load method")
6072

61-
return loader
73+
loadMethod.invoke(loadInstance)
74+
}.onFailure { LOG.error(errorMessageFMT, file, Thread.currentThread().stackTrace.joinToString("\n")) }
6275
}
6376

64-
private fun loadPlugin(file: File) {
65-
runCatching {
66-
loadMethod?.invoke(loadInstance)
67-
}
68-
.onFailure {
69-
return LOG.error("""
70-
A serious error occurred while loading a plugin.
71-
This is likely a bug in the plugin itself but it could also be a bug in Lambda.
72-
If you are a developer, please check the plugin's main class and load method.
73-
If you are a regular user, please report this issue to Lambda team and the plugin developer.
74-
75-
Plugin: $file
76-
Stacktrace: ${Thread.currentThread().stackTrace.joinToString("\n")}
77-
""".trimIndent())
77+
override fun load(): String {
78+
val plugins = mods
79+
.listRecursive {
80+
it.isFile && it.extension == "jar"
81+
// Not a fan of creating a new JarFile instance every iteration
82+
&& JarFile(it).manifest.mainAttributes.getValue("Lambda-Plugin") == "true"
7883
}
79-
}
84+
val size = plugins.count()
8085

81-
fun preLoad(path: File): List<PluginClassLoader> {
82-
val plugins = path.walk().filter { it.isFile && it.extension == "jar" }.toList()
83-
return plugins.mapNotNull { preLoadPlugin(it) }
84-
}
86+
plugins.forEach(::loadPlugin)
87+
88+
val plural = if (size == 1) "" else "s"
8589

86-
fun load(path: File): String {
87-
val plugins = path.walk().filter { it.isFile && it.extension == "jar" }.toList()
88-
plugins.forEach { loadPlugin(it) }
89-
return "Loaded ${plugins.size} plugins"
90+
return "Loaded $size plugin$plural"
9091
}
9192
}

common/src/main/kotlin/com/lambda/plugin/api/Plugin.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.lambda.plugin.api
22

33
import com.lambda.util.Nameable
44

5+
// TODO: Make this api better
56
abstract class Plugin(
67
override val name: String,
78
val description: String,
@@ -12,6 +13,5 @@ abstract class Plugin(
1213
val loadBefore: List<String>? = null,
1314
val loadAfter: List<String>? = null,
1415
) : Nameable {
15-
open fun preLoad() {} // This is invoked before the game launches
1616
abstract fun load()
1717
}

common/src/main/kotlin/com/lambda/util/FolderRegister.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,24 @@ import java.net.InetSocketAddress
1313
*
1414
* @property minecraft The root directory of the Minecraft client.
1515
* @property lambda The directory for the Lambda client, located within the Minecraft directory.
16+
* @property mods The directory for storing mods, located within the Minecraft directory.
1617
* @property config The directory for storing configuration files, located within the Lambda directory.
1718
* @property packetLogs The directory for storing packet logs, located within the Lambda directory.
1819
* @property replay The directory for storing replay files, located within the Lambda directory.
19-
* @property plugins The directory for storing plugin files, located within the Lambda directory.
2020
*/
2121
object FolderRegister {
2222
val minecraft: File = mc.runDirectory
2323
val lambda: File = File(minecraft, "lambda")
24+
val mods: File = File(minecraft, "mods")
2425
val config: File = File(lambda, "config")
2526
val packetLogs: File = File(lambda, "packet-log")
2627
val replay: File = File(lambda, "replay")
27-
val plugins: File = File(lambda, "plugins")
2828

2929
fun File.createIfNotExists() {
3030
if (!exists()) { mkdirs() }
3131
}
3232

33-
fun File.listRecursive() = walk().filter { it.isFile }
33+
fun File.listRecursive(predicate: (File) -> Boolean = { true }) = walk().filter(predicate)
3434

3535
fun File.locationBoundDirectory(): File {
3636
val hostName = (mc.networkHandler?.connection?.address as? InetSocketAddress)?.hostName ?: "singleplayer"

fabric/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ val fabricLoaderVersion = property("fabric_loader_version").toString()
22
val fabricApiVersion = property("fabric_api_version").toString()
33
val kotlinFabricVersion = property("kotlin_fabric_version").toString()
44

5-
base.archivesName.set("${base.archivesName.get()}-fabric")
5+
base.archivesName = "${base.archivesName.get()}-fabric"
66

77
architectury {
88
platformSetupLoomIde()

fabric/src/main/kotlin/com/lambda/fabric/LambdaBootstrap.kt

Lines changed: 0 additions & 34 deletions
This file was deleted.

0 commit comments

Comments
 (0)