From 03b23957827c42d5df5a11f3d07f807c5343e87e Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Thu, 6 Jul 2023 18:42:50 -0400 Subject: [PATCH] Add custom agent to work around https://github.com/FabricMC/fabric-loader/issues/817 --- .gitignore | 1 + build.gradle | 2 +- fabric/build.gradle | 16 ++++ settings.gradle | 1 + test_agent/build.gradle | 86 +++++++++++++++++++ .../embeddedt/modernfix/testing/Agent.java | 61 +++++++++++++ .../modernfix/testing/AgentHooks.java | 17 ++++ 7 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 test_agent/build.gradle create mode 100644 test_agent/src/main/java/org/embeddedt/modernfix/testing/Agent.java create mode 100644 test_agent/src/main/java/org/embeddedt/modernfix/testing/AgentHooks.java diff --git a/.gitignore b/.gitignore index 3241d732..f427fdd2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ media classes/ .architectury-transformer/ fabric/fabricloader.log +fabric/test_run # Changelog CHANGELOG.md diff --git a/build.gradle b/build.gradle index d9368c1b..d383f731 100644 --- a/build.gradle +++ b/build.gradle @@ -93,7 +93,7 @@ allprojects { } } -subprojects { +configure(subprojects.findAll {it.name == "common" || it.name == "forge" || it.name == "fabric"}) { apply plugin: "dev.architectury.loom" loom { diff --git a/fabric/build.gradle b/fabric/build.gradle index 02c657f9..1b813a9e 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -22,6 +22,10 @@ configurations { include.extendsFrom modIncludeImplementation modImplementation.extendsFrom modIncludeImplementation + + testAgent { + canBeConsumed = false + } } dependencies { @@ -51,10 +55,22 @@ dependencies { testImplementation("org.assertj:assertj-core:3.19.0") testImplementation("com.google.guava:guava-testlib:21.0") testImplementation("org.mockito:mockito-junit-jupiter:5.3.1") + + testAgent(project("path": ":test_agent", "configuration": "agentJar")) } test { useJUnitPlatform() + def runDir = file('test_run') + doFirst() { + runDir.mkdir() + } + workingDir = runDir + + // inject our custom agent to fix #817 + FileCollection agentFile = configurations.getByName("testAgent") + jvmArgs "-javaagent:${agentFile.singleFile.absolutePath}" + dependsOn(agentFile) } processResources { diff --git a/settings.gradle b/settings.gradle index 8a77caf9..4afff011 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,6 +7,7 @@ pluginManagement { } } +include("test_agent") include("common") include("fabric") include("forge") diff --git a/test_agent/build.gradle b/test_agent/build.gradle new file mode 100644 index 00000000..2141b87d --- /dev/null +++ b/test_agent/build.gradle @@ -0,0 +1,86 @@ +plugins { + //id 'com.github.johnrengelman.shadow' version '7.1.2' + id 'java' +} + +group 'org.embeddedt' +archivesBaseName = 'modernfix-test-agent' +version '1.0' + +sourceCompatibility = '1.8' +targetCompatibility = '1.8' + +repositories { + mavenCentral() + mavenLocal() + maven { + name = 'forge' + url = 'https://maven.minecraftforge.net/' + } +} + +/* + +shadowJar { + relocate 'net.bytebuddy.agent', 'org.embeddedt.modernfix.testing.shadow.bytebuddyagent' + relocate 'org.objectweb.asm', 'org.embeddedt.modernfix.testing.shadow.asm' +} + + + +shadowJar { + project.configurations.implementation.canBeResolved = true + configurations = [project.configurations.implementation] +} + */ +dependencies { + compileOnly "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" + implementation "org.ow2.asm:asm-tree:9.1" + implementation "org.ow2.asm:asm-commons:9.1" + implementation "org.ow2.asm:asm-util:9.1" + + //implementation('net.bytebuddy:byte-buddy-agent:1.12.22') +} + +tasks.withType(JavaCompile) { + // ensure that the encoding is set to UTF-8, no matter what the system default is + // this fixes some edge cases with special characters not displaying correctly + // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html + // If Javadoc is generated, this must be specified in that task too. + options.encoding = "UTF-8" +} + +jar { + manifest { + attributes( + "Premain-Class": "org.embeddedt.modernfix.testing.Agent", + "Can-Redefine-Classes": false, + "Can-Set-Native-Method-Prefix": false + ) + } +} +/* + +shadowJar { + archiveBaseName.set('modernfix-test-agent') + archiveClassifier.set('') + archiveVersion.set('v1') +} + + */ + +configurations { + agentJar { + canBeConsumed = true + canBeResolved = false + } +} + +artifacts { + agentJar(jar) +} +/* +project.tasks.shadowJar.dependsOn build +defaultTasks 'shadowJar' + + */ \ No newline at end of file diff --git a/test_agent/src/main/java/org/embeddedt/modernfix/testing/Agent.java b/test_agent/src/main/java/org/embeddedt/modernfix/testing/Agent.java new file mode 100644 index 00000000..b7b69479 --- /dev/null +++ b/test_agent/src/main/java/org/embeddedt/modernfix/testing/Agent.java @@ -0,0 +1,61 @@ +package org.embeddedt.modernfix.testing; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.security.ProtectionDomain; +import java.util.ListIterator; + +public class Agent { + /** + * Simple agent that transforms Fabric Loader to never mark game JARs as system libraries. + * + * Ugly, but usable workaround for issue #817 + * on the Loader bug tracker. + */ + public static void premain(String args, Instrumentation instrumentation) { + instrumentation.addTransformer(new ClassFileTransformer() { + @Override + public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { + if(className.equals("net/fabricmc/loader/impl/game/LibClassifier")) { + ClassNode node = new ClassNode(); + ClassReader reader = new ClassReader(classfileBuffer); + reader.accept(node, 0); + for(MethodNode m : node.methods) { + if(m.name.equals("")) { + ListIterator iter = m.instructions.iterator(); + int addMatches = 0; + while(iter.hasNext()) { + AbstractInsnNode n = iter.next(); + if(n instanceof MethodInsnNode) { + MethodInsnNode invokeNode = (MethodInsnNode)n; + if(invokeNode.name.equals("add") && invokeNode.owner.equals("java/util/Set") && invokeNode.desc.equals("(Ljava/lang/Object;)Z")) { + addMatches++; + if(addMatches == 2) { + iter.set(new MethodInsnNode(Opcodes.INVOKESTATIC, "org/embeddedt/modernfix/testing/AgentHooks", "addLibraryWithCheck", "(Ljava/util/Set;Ljava/lang/Object;)Z", false)); + break; + } + } + } + } + } + } + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); + node.accept(writer); + byte[] finalArray = writer.toByteArray(); + //dumpDebugClass(className, finalArray); + return finalArray; + } + return classfileBuffer; + } + }); + } +} diff --git a/test_agent/src/main/java/org/embeddedt/modernfix/testing/AgentHooks.java b/test_agent/src/main/java/org/embeddedt/modernfix/testing/AgentHooks.java new file mode 100644 index 00000000..05b8de12 --- /dev/null +++ b/test_agent/src/main/java/org/embeddedt/modernfix/testing/AgentHooks.java @@ -0,0 +1,17 @@ +package org.embeddedt.modernfix.testing; + +import java.nio.file.Path; +import java.util.Set; + +@SuppressWarnings("unused") +public class AgentHooks { + @SuppressWarnings({"unchecked", "rawtypes" }) + public static boolean addLibraryWithCheck(Set pathSet, Object path) { + boolean shouldAdd; + if(path instanceof Path) { + shouldAdd = !((Path)path).toString().contains("minecraft-merged"); + } else + shouldAdd = true; + return shouldAdd && pathSet.add(path); + } +}