diff --git a/build.gradle b/build.gradle index 3566492..94e84a7 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ targetCompatibility = 1.8 group = 'com.openmodloader' archivesBaseName = project.name -version = '0.0.10-SNAPSHOT' +version = '0.0.11-SNAPSHOT' repositories { mavenCentral() @@ -56,7 +56,7 @@ dependencies { shade ('enigma-asm:enigma:0.12.0.33:lib'){ exclude group: 'org.ow2.asm' } - shade 'OpenModLoader.tiny-remapper:tiny-remapper:+' + shade 'OpenModLoader.tiny-remapper:tiny-remapper:0.1.0.7' shade 'net.sf.jopt-simple:jopt-simple:5.0.4' shade 'org.apache.logging.log4j:log4j-api:2.11.0' shade 'org.apache.logging.log4j:log4j-core:2.11.0' diff --git a/src/main/java/net/fabricmc/loom/task/RunClientTask.java b/src/main/java/net/fabricmc/loom/task/RunClientTask.java index 1e3e9e4..31b4bf5 100644 --- a/src/main/java/net/fabricmc/loom/task/RunClientTask.java +++ b/src/main/java/net/fabricmc/loom/task/RunClientTask.java @@ -66,6 +66,11 @@ public class RunClientTask extends JavaExec { } } libs.add(Constants.MINECRAFT_CLIENT_JAR.get(extension).getAbsolutePath()); + + //Removes the deobf jars + libs.removeIf(s -> s.contains(Constants.MINECRAFT_FINAL_JAR.get(extension).getName())); + libs.removeIf(s -> s.contains(getProject().getName() + "-" + getProject().getVersion() + "-deobf.jar")); + classpath(libs); args("--launchTarget", "oml", "--accessToken", "NOT_A_TOKEN", "--version", extension.version, "--assetIndex", version.assetIndex.id, "--assetsDir", new File(extension.getUserCache(), "assets-" + extension.version).getAbsolutePath()); @@ -85,7 +90,7 @@ public class RunClientTask extends JavaExec { LoomGradleExtension extension = this.getProject().getExtensions().getByType(LoomGradleExtension.class); List args = new ArrayList<>(); args.add("-Djava.library.path=" + Constants.MINECRAFT_NATIVES.get(extension).getAbsolutePath()); - args.add("-XstartOnFirstThread"); //Fixes lwjgl starting on an incorrect thread + //args.add("-XstartOnFirstThread"); //Fixes lwjgl starting on an incorrect thread return args; } diff --git a/src/main/java/net/fabricmc/loom/util/ModRemapper.java b/src/main/java/net/fabricmc/loom/util/ModRemapper.java index 6784712..80858bf 100644 --- a/src/main/java/net/fabricmc/loom/util/ModRemapper.java +++ b/src/main/java/net/fabricmc/loom/util/ModRemapper.java @@ -25,17 +25,21 @@ package net.fabricmc.loom.util; import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.tinyremapper.OutputConsumerPath; -import net.fabricmc.tinyremapper.TinyRemapper; -import net.fabricmc.tinyremapper.TinyUtils; +import net.fabricmc.tinyremapper.*; import org.apache.commons.io.FileUtils; import org.gradle.api.Project; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; import java.io.File; import java.io.IOException; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; +import java.util.*; public class ModRemapper { @@ -77,6 +81,7 @@ public class ModRemapper { TinyRemapper remapper = TinyRemapper.newRemapper() .withMappings(TinyUtils.createTinyMappingProvider(mappings, fromM, toM)) + .withRemapperExtension(new MixinRemapper()) .build(); try { @@ -100,4 +105,131 @@ public class ModRemapper { //Add the deobf jar to be uploaded to maven project.getArtifacts().add("archives", deobfJar); } + + public static class MixinRemapper implements IRemapperExtension{ + + @Override + public byte[] handleUnmappedClass(byte[] inBytes, TinyRemapper remapper) { + //I know this isnt the fastest, but its the easiest + ClassNode classNode = readClassFromBytes(inBytes); + if(isMixin(classNode)){ + String target = getMixinTarget(classNode); + System.out.println("Remapping mixin (" + classNode.name + ") targeting: " + target); + for(MethodNode methodNode : classNode.methods){ + if(needsTargetMap(methodNode.visibleAnnotations)){ + methodNode.visibleAnnotations.add(createTargetMap(methodNode, target, remapper)); + } + } + for(FieldNode fieldNode : classNode.fields){ + if(needsTargetMap(fieldNode.visibleAnnotations)){ + //fieldNode.visibleAnnotations.add(createTargetMap(fieldNode)); + } + } + return writeClassToBytes(classNode); + } + return inBytes; + } + + private AnnotationNode createTargetMap(MethodNode methodNode, String targetClass, TinyRemapper remapper){ + AnnotationNode targetMapNode = new AnnotationNode("Lme/modmuss50/fusion/api/TargetMap;"); + String deobfTarget = methodNode.name + methodNode.desc; + if(getRewriteTarget(methodNode).isPresent()){ + deobfTarget = getRewriteTarget(methodNode).get(); + } + + if(deobfTarget.equals("")){ + //No need to handle constructors, may need to do something about the desc but we will see + return targetMapNode; + } + String oldName = deobfTarget.substring(0, deobfTarget.indexOf("(")); + String oldDesc = deobfTarget.substring(deobfTarget.lastIndexOf("(")); + + String newName = remapper.remapper.mapMethodName(targetClass.replaceAll("\\.", "/"), oldName, oldDesc); + String newDesc = remapper.remapper.mapDesc(oldDesc); + + System.out.println(oldName + oldDesc + " -> " + newName + newDesc); + targetMapNode.visit("value", newName + newDesc); + return targetMapNode; + } + + private boolean isMixin(ClassNode classNode){ + if(classNode.visibleAnnotations == null){ + return false; + } + for(AnnotationNode annotation : classNode.visibleAnnotations){ + if(annotation.desc.equals("Lme/modmuss50/fusion/api/Mixin;")){ + return true; + } + } + return false; + } + + private String getMixinTarget(ClassNode classNode){ + if(classNode.visibleAnnotations == null){ + throw new RuntimeException(classNode.name + " is not a mixin!"); + } + for(AnnotationNode annotation : classNode.visibleAnnotations){ + if(annotation.desc.equals("Lme/modmuss50/fusion/api/Mixin;")){ + for (int i = 0; i < annotation.values.size(); i++) { + Object value = annotation.values.get(i); + if(value instanceof String && value.toString().equals("value")){ + Type target = (Type) annotation.values.get(i + 1); + return target.getClassName(); + } + } + + } + } + throw new RuntimeException(classNode.name + " is not a valid mixin!"); + } + + private Optional getRewriteTarget(MethodNode methodNode){ + if(methodNode.visibleAnnotations == null){ + return Optional.empty(); + } + for(AnnotationNode annotation : methodNode.visibleAnnotations){ + if(annotation.desc.equals("Lme/modmuss50/fusion/api/Rewrite;")){ + for (int i = 0; i < annotation.values.size(); i++) { + Object value = annotation.values.get(i); + if(value instanceof String && value.toString().equals("target")){ + return Optional.of((String) annotation.values.get(i + 1)); + } + } + } + } + return Optional.empty(); + } + + private boolean needsTargetMap(List annotationNodes){ + if(annotationNodes == null){ + return false; + } + for(AnnotationNode annotation : annotationNodes){ + if(annotation.desc.equals("Lme/modmuss50/fusion/api/Rewrite;")){ + return true; + } + if(annotation.desc.equals("Lme/modmuss50/fusion/api/Inject;")){ + return true; + } + } + return false; + } + + + private static ClassNode readClassFromBytes(byte[] bytes) { + ClassNode classNode = new org.objectweb.asm.tree.ClassNode(); + ClassReader classReader = new ClassReader(bytes); + classReader.accept(classNode, 0); + return classNode; + } + + private static byte[] writeClassToBytes(ClassNode classNode) { + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + classNode.accept(writer); + return writer.toByteArray(); + } + + } + + }