From 5c26459431e534e3559a01e572dbacf86fca70d4 Mon Sep 17 00:00:00 2001 From: zani Date: Fri, 18 Oct 2024 23:21:43 -0600 Subject: [PATCH] fix: properly shade lwjgl & forge fixes --- .../org/polyfrost/spice/patcher/Cache.kt | 4 +- .../spice/patcher/fixes/OpenAlFixes.kt | 8 + .../spice/patcher/lwjgl/LwjglProvider.kt | 2 +- .../spice/patcher/lwjgl/LwjglTransformer.kt | 158 +++++++++++------- .../patcher/lwjgl/MemoryStackTransformer.kt | 28 ++++ modules/lwjgl/build.gradle.kts | 2 +- .../impl/forge/asm/ClassTransformer.kt | 12 +- .../impl/forge/asm/TransformerPlugin.kt | 2 + .../platform/impl/forge/util/Relaunch.kt | 28 ++++ 9 files changed, 177 insertions(+), 67 deletions(-) create mode 100644 modules/core/src/main/kotlin/org/polyfrost/spice/patcher/lwjgl/MemoryStackTransformer.kt create mode 100644 versions/src/main/kotlin/org/polyfrost/spice/platform/impl/forge/util/Relaunch.kt diff --git a/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/Cache.kt b/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/Cache.kt index a2fb91b..31bb5c9 100644 --- a/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/Cache.kt +++ b/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/Cache.kt @@ -9,6 +9,7 @@ import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.MethodNode import org.polyfrost.spice.patcher.lwjgl.LibraryTransformer import org.polyfrost.spice.patcher.lwjgl.LwjglTransformer +import org.polyfrost.spice.patcher.lwjgl.MemoryStackTransformer import org.polyfrost.spice.spiceDirectory import org.polyfrost.spice.util.SpiceClassWriter import java.util.jar.JarFile @@ -55,7 +56,8 @@ fun buildCache(hash: String, `in`: List): Pair // todo: abuse coroutines. val transformers = arrayOf( LwjglTransformer, - LibraryTransformer + LibraryTransformer, + MemoryStackTransformer ) val transformable = mutableSetOf() diff --git a/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/fixes/OpenAlFixes.kt b/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/fixes/OpenAlFixes.kt index 7024d1d..e83f175 100644 --- a/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/fixes/OpenAlFixes.kt +++ b/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/fixes/OpenAlFixes.kt @@ -4,6 +4,11 @@ import org.polyfrost.spice.patcher.util.AudioHelper @Suppress("unused") object OpenAlFixes { + private val deviceHandleField = + Class + .forName("org.lwjgl.openal.ALCdevice") + .getDeclaredField("device") + @JvmStatic fun create() { try { @@ -15,4 +20,7 @@ object OpenAlFixes { @JvmStatic fun destroyContext() = AudioHelper.destroyContext() + + @JvmStatic + fun mapDevice(device: Any): Long = deviceHandleField.getLong(device) } diff --git a/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/lwjgl/LwjglProvider.kt b/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/lwjgl/LwjglProvider.kt index 631a92b..19947c7 100644 --- a/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/lwjgl/LwjglProvider.kt +++ b/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/lwjgl/LwjglProvider.kt @@ -64,7 +64,7 @@ class LwjglProvider { private fun openStream(): InputStream? = LwjglProvider::class.java .classLoader - .getResource("lwjgl.jar") + .getResource("lwjgl-bundle") ?.openStream() private fun readEntryUntil(path: String?): ByteArray? { diff --git a/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/lwjgl/LwjglTransformer.kt b/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/lwjgl/LwjglTransformer.kt index 2ed1134..dcae47e 100644 --- a/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/lwjgl/LwjglTransformer.kt +++ b/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/lwjgl/LwjglTransformer.kt @@ -5,11 +5,92 @@ import org.apache.logging.log4j.LogManager import org.objectweb.asm.Opcodes import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.FieldNode +import org.objectweb.asm.tree.InsnList import org.objectweb.asm.tree.MethodNode import org.polyfrost.spice.platform.api.IClassTransformer +private data class InjectedMethod( + val name: String, + val desc: String, + val access: Int, + val instructions: InsnList +) + object LwjglTransformer : IClassTransformer { private val logger = LogManager.getLogger("Spice/Transformer")!! + private val injectableMethods = mapOf( + "org/lwjgl/openal/AL" to listOf( + InjectedMethod( + "create", + "()V", + Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_SYNTHETIC, + asm { + invokestatic( + "org/polyfrost/spice/patcher/fixes/OpenAlFixes", + "create", + "()V" + ) + _return + } + ), + InjectedMethod( + "destroy", + "()V", + Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_SYNTHETIC, + asm { + invokestatic( + "org/polyfrost/spice/patcher/fixes/OpenAlFixes", + "destroyContext", + "()V" + ) + _return + } + ) + ), + "org/lwjgl/openal/ALC10" to listOf( + InjectedMethod( + "alcGetString", + "(Lorg/lwjgl/openal/ALCdevice;I)Ljava/lang/String;", + Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_SYNTHETIC, + asm { + aload(0) + invokestatic( + "org/polyfrost/spice/patcher/fixes/OpenAlFixes", + "mapDevice", + "(Ljava/lang/Object;)J", + ) + iload(1) + invokestatic( + "org/lwjgl/openal/ALC10", + "alGetString", + "(JI)Ljava/lang/String;" + ) + + areturn + } + ) + ), + "org/lwjgl/opengl/GL20" to listOf( + InjectedMethod( + "glShaderSource", + "(ILjava/nio/ByteBuffer;)V", + Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_SYNTHETIC, + asm { + iload(0) + aload(1) + + invokestatic( + "org/polyfrost/spice/patcher/fixes/OpenGlFixes", + "glShaderSource", + "(ILjava/nio/ByteBuffer;)V" + ) + + _return + } + ) + ) + ) + override val targets = null val provider = LwjglProvider() @@ -88,44 +169,6 @@ object LwjglTransformer : IClassTransformer { ) when (node.name) { - "org/lwjgl/openal/AL" -> { - var foundDestroy = true - - val createMethod = MethodNode() - val destroyMethod = node - .methods - .find { - (it as MethodNode).name == "destroy" && it.desc == "()V" - } as? MethodNode ?: run { - foundDestroy = false - - MethodNode() - } - - createMethod.name = "create" - createMethod.desc = "()V" - createMethod.access = Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_SYNTHETIC - - destroyMethod.name = "destroy" - destroyMethod.desc = "()V" - destroyMethod.access = Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_SYNTHETIC - - createMethod.instructions = asm { - invokestatic("org/polyfrost/spice/patcher/fixes/OpenAlFixes", "create", "()V") - - _return - } - - destroyMethod.instructions = asm { - invokestatic("org/polyfrost/spice/patcher/fixes/OpenAlFixes", "destroyContext", "()V") - - _return - } - - node.methods.add(createMethod) - if (!foundDestroy) node.methods.add(destroyMethod) - } - "org/lwjgl/openal/AL10", "org/lwjgl/opengl/GL11", "org/lwjgl/opengl/GL20", @@ -137,30 +180,23 @@ object LwjglTransformer : IClassTransformer { if (second == method.desc) method.name = first } } - - if (node.name == "org/lwjgl/opengl/GL20") { - val method = MethodNode() - - method.name = "glShaderSource" - method.desc = "(ILjava/nio/ByteBuffer;)V" - method.access = Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_SYNTHETIC - - method.instructions = asm { - iload(0) - aload(1) - - invokestatic( - "org/polyfrost/spice/patcher/fixes/OpenGlFixes", - method.name, - method.desc - ) - - _return - } - - node.methods.add(method) - } } } + + injectableMethods[node.name]?.forEach { injection -> + node.methods.removeIf { method -> + method as MethodNode + method.name == injection.name && method.desc == injection.desc + } + + val method = MethodNode() + + method.name = injection.name + method.desc = injection.desc + method.access = injection.access + method.instructions = injection.instructions + + node.methods.add(method) + } } } diff --git a/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/lwjgl/MemoryStackTransformer.kt b/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/lwjgl/MemoryStackTransformer.kt new file mode 100644 index 0000000..24c0c50 --- /dev/null +++ b/modules/core/src/main/kotlin/org/polyfrost/spice/patcher/lwjgl/MemoryStackTransformer.kt @@ -0,0 +1,28 @@ +package org.polyfrost.spice.patcher.lwjgl + +import net.weavemc.loader.api.util.asm +import org.objectweb.asm.Opcodes.ARETURN +import org.objectweb.asm.tree.ClassNode +import org.objectweb.asm.tree.MethodNode +import org.polyfrost.spice.platform.api.IClassTransformer + +object MemoryStackTransformer : IClassTransformer { + override val targets = arrayOf("org.lwjgl.system.MemoryStack") + + override fun transform(node: ClassNode) { + val method = node.methods.find { method -> + method as MethodNode + method.name == "create" && method.desc == "(Ljava/nio/ByteBuffer;)Lorg/lwjgl/system/MemoryStack;" + }!! as MethodNode + + method + .instructions + .insertBefore( + method + .instructions + .toArray() + .find { insn -> insn.opcode == ARETURN }, + asm { checkcast("org/lwjgl/system/MemoryStack") } + ) + } +} \ No newline at end of file diff --git a/modules/lwjgl/build.gradle.kts b/modules/lwjgl/build.gradle.kts index d5206c0..020ec58 100644 --- a/modules/lwjgl/build.gradle.kts +++ b/modules/lwjgl/build.gradle.kts @@ -31,5 +31,5 @@ tasks.jar { } tasks.shadowJar { - archiveFileName = "lwjgl.jar" + archiveFileName = "lwjgl-bundle" } diff --git a/versions/src/main/kotlin/org/polyfrost/spice/platform/impl/forge/asm/ClassTransformer.kt b/versions/src/main/kotlin/org/polyfrost/spice/platform/impl/forge/asm/ClassTransformer.kt index 4edb857..21632e8 100644 --- a/versions/src/main/kotlin/org/polyfrost/spice/platform/impl/forge/asm/ClassTransformer.kt +++ b/versions/src/main/kotlin/org/polyfrost/spice/platform/impl/forge/asm/ClassTransformer.kt @@ -16,8 +16,9 @@ import org.polyfrost.spice.platform.api.IClassTransformer import org.polyfrost.spice.platform.api.Transformer import org.polyfrost.spice.platform.bootstrapTransformer import org.polyfrost.spice.platform.impl.forge.util.LaunchWrapperLogger -import org.polyfrost.spice.util.collectResources +import org.polyfrost.spice.platform.impl.forge.util.relaunch import org.polyfrost.spice.util.SpiceClassWriter +import org.polyfrost.spice.util.collectResources import java.net.URL import java.util.concurrent.TimeUnit import net.minecraft.launchwrapper.IClassTransformer as LaunchTransformer @@ -64,7 +65,7 @@ class ClassTransformer : LaunchTransformer, Transformer { override fun transform(name: String, transformedName: String, bytes: ByteArray?): ByteArray? { if (bytes == null) return null - + @Suppress("NAME_SHADOWING") val bytes = transformerCache[name.replace(".", "/")] ?: bytes val validTransformers = transformers.filter { @@ -135,7 +136,12 @@ class ClassTransformer : LaunchTransformer, Transformer { logger.info("Transformed ${transformed.first.size}/${classes.size} classes") logger.info("Built cache in ${stopwatch.elapsed(TimeUnit.MILLISECONDS)}ms") - transformed.second + logger.info("Relaunching..") + + // todo: figure out an actual elegant solution to classes not being loaded properly lol + relaunch() + + return transformed.second } else { logger.info("Loading classes from cache $hash") stopwatch.start() diff --git a/versions/src/main/kotlin/org/polyfrost/spice/platform/impl/forge/asm/TransformerPlugin.kt b/versions/src/main/kotlin/org/polyfrost/spice/platform/impl/forge/asm/TransformerPlugin.kt index 05c8ddb..39d7eed 100644 --- a/versions/src/main/kotlin/org/polyfrost/spice/platform/impl/forge/asm/TransformerPlugin.kt +++ b/versions/src/main/kotlin/org/polyfrost/spice/platform/impl/forge/asm/TransformerPlugin.kt @@ -3,6 +3,8 @@ package org.polyfrost.spice.platform.impl.forge.asm import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin +@IFMLLoadingPlugin.Name("Spice") +@IFMLLoadingPlugin.MCVersion("1.8.9") class TransformerPlugin : IFMLLoadingPlugin { override fun getASMTransformerClass(): Array { return arrayOf(ClassTransformer::class.java.getName()) diff --git a/versions/src/main/kotlin/org/polyfrost/spice/platform/impl/forge/util/Relaunch.kt b/versions/src/main/kotlin/org/polyfrost/spice/platform/impl/forge/util/Relaunch.kt new file mode 100644 index 0000000..909c3a5 --- /dev/null +++ b/versions/src/main/kotlin/org/polyfrost/spice/platform/impl/forge/util/Relaunch.kt @@ -0,0 +1,28 @@ +package org.polyfrost.spice.platform.impl.forge.util + +import java.lang.management.ManagementFactory +import kotlin.io.path.Path + +fun relaunch() { + val runtimeBean = ManagementFactory.getRuntimeMXBean() + val binary = Path(System.getProperty("sun.boot.library.path")).resolve("java.exe") + + val builder = ProcessBuilder() + .command( + binary.toAbsolutePath().toString(), + *runtimeBean.inputArguments.toTypedArray(), + "-cp", + runtimeBean.classPath, + *System.getProperty("sun.java.command").split(" ").toTypedArray() + ) + + println("Starting process: ${builder.command().joinToString(" ")}") + + val process = + builder + .inheritIO() + .start() + + Runtime.getRuntime().addShutdownHook(Thread(process::destroy)) + Runtime.getRuntime().halt(process.waitFor()) +} \ No newline at end of file