Skip to content

Commit

Permalink
fix: properly shade lwjgl & forge fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Zxnii committed Oct 19, 2024
1 parent 3a168b8 commit 5c26459
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -55,7 +56,8 @@ fun buildCache(hash: String, `in`: List<ClassNode>): Pair<Map<String, ClassNode>
// todo: abuse coroutines.
val transformers = arrayOf(
LwjglTransformer,
LibraryTransformer
LibraryTransformer,
MemoryStackTransformer
)

val transformable = mutableSetOf<String>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -15,4 +20,7 @@ object OpenAlFixes {

@JvmStatic
fun destroyContext() = AudioHelper.destroyContext()

@JvmStatic
fun mapDevice(device: Any): Long = deviceHandleField.getLong(device)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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",
Expand All @@ -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)
}
}
}
Original file line number Diff line number Diff line change
@@ -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") }
)
}
}
2 changes: 1 addition & 1 deletion modules/lwjgl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ tasks.jar {
}

tasks.shadowJar {
archiveFileName = "lwjgl.jar"
archiveFileName = "lwjgl-bundle"
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> {
return arrayOf(ClassTransformer::class.java.getName())
Expand Down
Original file line number Diff line number Diff line change
@@ -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())
}

0 comments on commit 5c26459

Please sign in to comment.