diff --git a/.idea/markdown.xml b/.idea/markdown.xml
new file mode 100644
index 0000000..c61ea33
--- /dev/null
+++ b/.idea/markdown.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/kotlin/dev/zerozipp/loader/Agent.kt b/src/main/kotlin/dev/zerozipp/loader/Agent.kt
index e0b8e11..6a27b56 100644
--- a/src/main/kotlin/dev/zerozipp/loader/Agent.kt
+++ b/src/main/kotlin/dev/zerozipp/loader/Agent.kt
@@ -10,11 +10,9 @@ import net.bytebuddy.description.method.*
import net.bytebuddy.asm.Advice
import java.lang.instrument.*
import java.util.jar.JarFile
-import java.nio.file.Path
import java.io.File
@Suppress("unused") object Agent {
- private const val KNOT = "net.fabricmc.loader.impl.launch.knot"
@JvmStatic fun premain(args: String?, inst: Instrumentation?) = attach(inst!!)
@JvmStatic fun agentmain(args: String?, inst: Instrumentation?) = attach(inst!!)
val injections = listOf(Tick::class, Render::class, Gui::class)
@@ -32,27 +30,11 @@ import java.io.File
}
}
- private fun getKnotDelegate(): Any? {
- val base = Class.forName("$KNOT.KnotClassLoader")
- val delegate = base.getDeclaredField("delegate")
- val threads = Thread.getAllStackTraces().keys
- val loaders = threads.mapNotNull { it.contextClassLoader }
- val knot = loaders.firstOrNull { it.javaClass == base }
- return delegate.apply { isAccessible = true }.get(knot)
- }
-
- private fun exposeToKnot(jar: Path) {
- val delegate = getKnotDelegate()
- val base = Class.forName("$KNOT.KnotClassDelegate")
- val source = base.getMethod("addCodeSource", Path::class.java)
- source.apply { isAccessible = true }.invoke(delegate, jar)
- }
-
private fun attach(inst: Instrumentation) {
val domain = Client::class.java.protectionDomain
val location = File(domain.codeSource.location.toURI())
inst.appendToSystemClassLoaderSearch(JarFile(location))
- runCatching { exposeToKnot(location.toPath()) }
+ Knot(location.toPath()).register(inst)
var builder: AgentBuilder = Default()
builder = builder.disableClassFormatChanges()
diff --git a/src/main/kotlin/dev/zerozipp/loader/Knot.kt b/src/main/kotlin/dev/zerozipp/loader/Knot.kt
new file mode 100644
index 0000000..8a2f2ae
--- /dev/null
+++ b/src/main/kotlin/dev/zerozipp/loader/Knot.kt
@@ -0,0 +1,43 @@
+package dev.zerozipp.loader
+
+import java.lang.instrument.*
+import java.security.ProtectionDomain
+import java.nio.file.Path
+
+class Knot(private val jar: Path) : ClassFileTransformer {
+ private val knot = "net.fabricmc.loader.impl.launch.knot"
+ private var registry = mutableSetOf()
+ override fun transform(classLoader: ClassLoader, className: String,
+ redefined: Class<*>, domain: ProtectionDomain, bytes: ByteArray
+ ): ByteArray? = null.also { load(classLoader) }
+
+ fun register(inst: Instrumentation) {
+ val result = runCatching { expose(jar) }
+ if(result.isSuccess) return
+ inst.addTransformer(this)
+ registry.add(inst)
+ }
+
+ private fun delegate() = run {
+ val base = Class.forName("$knot.KnotClassLoader")
+ val delegate = base.getDeclaredField("delegate")
+ val threads = Thread.getAllStackTraces().keys
+ val loaders = threads.mapNotNull { it.contextClassLoader }
+ val knot = loaders.firstOrNull { it.javaClass == base }
+ delegate.apply { isAccessible = true }.get(knot)
+ }
+
+ private fun expose(jar: Path) = delegate().let {
+ val base = Class.forName("$knot.KnotClassDelegate")
+ val source = base.getMethod("addCodeSource", Path::class.java)
+ source.apply { isAccessible = true }.invoke(it, jar)
+ }
+
+ private fun load(loader: ClassLoader) {
+ val knotLoader = "$knot.KnotClassLoader"
+ if(loader.javaClass.name != knotLoader) return
+ registry.forEach { it.removeTransformer(this) }
+ runCatching { expose(jar) }
+ registry.clear()
+ }
+}
\ No newline at end of file