diff --git a/build.gradle b/build.gradle index bdc7326..a951d59 100644 --- a/build.gradle +++ b/build.gradle @@ -38,7 +38,7 @@ dependencies { implementation ('org.zeroturnaround:zt-zip:1.13') implementation ('com.google.code.gson:gson:2.8.5') implementation ('com.google.guava:guava:27.0.1-jre') - implementation ('net.fabricmc:stitch:0.1.0.29') + implementation ('net.fabricmc:stitch:0.1.1.32') implementation ('net.fabricmc:tiny-remapper:0.1.0.22') { transitive = false } diff --git a/src/main/java/net/fabricmc/loom/AbstractPlugin.java b/src/main/java/net/fabricmc/loom/AbstractPlugin.java index 45fc15b..879357d 100644 --- a/src/main/java/net/fabricmc/loom/AbstractPlugin.java +++ b/src/main/java/net/fabricmc/loom/AbstractPlugin.java @@ -29,12 +29,14 @@ import net.fabricmc.loom.providers.MappingsProvider; import net.fabricmc.loom.providers.MinecraftProvider; import net.fabricmc.loom.providers.ModRemapperProvider; import net.fabricmc.loom.task.RemapJar; +import net.fabricmc.loom.task.RemapSourcesJar; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.LoomDependencyManager; import net.fabricmc.loom.util.SetupIntelijRunConfigs; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; +import org.gradle.api.UnknownTaskException; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.repositories.MavenArtifactRepository; import org.gradle.api.plugins.JavaPlugin; @@ -238,6 +240,18 @@ public class AbstractPlugin implements Plugin { remapJarTask.doLast(task -> project1.getArtifacts().add("archives", remapJarTask.jar)); remapJarTask.dependsOn(project1.getTasks().getByName("jar")); project1.getTasks().getByName("build").dependsOn(remapJarTask); + + try { + AbstractArchiveTask sourcesTask = (AbstractArchiveTask) project1.getTasks().getByName("sourcesJar"); + + RemapSourcesJar remapSourcesJarTask = (RemapSourcesJar) project1.getTasks().findByName("remapSourcesJar"); + remapSourcesJarTask.jar = sourcesTask.getArchivePath(); + remapSourcesJarTask.doLast(task -> project1.getArtifacts().add("archives", remapSourcesJarTask.jar)); + remapSourcesJarTask.dependsOn(project1.getTasks().getByName("sourcesJar")); + project1.getTasks().getByName("build").dependsOn(remapSourcesJarTask); + } catch (UnknownTaskException e) { + // pass + } } else { AbstractArchiveTask jarTask = (AbstractArchiveTask) project1.getTasks().getByName("jar"); extension.addUnmappedMod(jarTask.getArchivePath()); diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index 216f543..50abb6d 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -29,6 +29,7 @@ import net.fabricmc.loom.providers.MappingsProvider; import net.fabricmc.loom.providers.MinecraftMappedProvider; import net.fabricmc.loom.providers.MinecraftProvider; import net.fabricmc.loom.util.LoomDependencyManager; +import org.cadixdev.lorenz.MappingSet; import org.gradle.api.Project; import org.gradle.api.artifacts.Dependency; @@ -38,6 +39,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.function.Supplier; public class LoomGradleExtension { public String runDir = "run"; @@ -53,6 +55,11 @@ public class LoomGradleExtension { private LoomDependencyManager dependencyManager; private JsonObject installerJson; private int installerJsonPriority = Integer.MAX_VALUE; // 0+, higher = less prioritized + private MappingSet[] srcMappingCache = new MappingSet[2]; + + public MappingSet getOrCreateSrcMappingCache(int id, Supplier factory) { + return srcMappingCache[id] != null ? srcMappingCache[id] : (srcMappingCache[id] = factory.get()); + } public LoomGradleExtension(Project project) { this.project = project; diff --git a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java index 4c4d6cb..ee3da8a 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java +++ b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java @@ -42,7 +42,7 @@ public class LoomGradlePlugin extends AbstractPlugin { makeTask("vscode", GenVsCodeProjectTask.class).setGroup("ide"); makeTask("genEclipseRuns", GenEclipseRunsTask.class).setGroup("ide"); - makeTask("remapSources", RemapSourcesTask.class); + makeTask("remapSourcesJar", RemapSourcesJar.class); makeTask("runClient", RunClientTask.class).dependsOn("buildNeeded").setGroup("minecraftMapped"); makeTask("runServer", RunServerTask.class).dependsOn("buildNeeded").setGroup("minecraftMapped"); diff --git a/src/main/java/net/fabricmc/loom/providers/MinecraftJarProvider.java b/src/main/java/net/fabricmc/loom/providers/MinecraftJarProvider.java index 9b287ac..6dbcf3e 100644 --- a/src/main/java/net/fabricmc/loom/providers/MinecraftJarProvider.java +++ b/src/main/java/net/fabricmc/loom/providers/MinecraftJarProvider.java @@ -54,19 +54,11 @@ public class MinecraftJarProvider { public void mergeJars(Project project) throws IOException { project.getLogger().lifecycle(":merging jars"); - FileInputStream client = new FileInputStream(minecraftProvider.MINECRAFT_CLIENT_JAR); - FileInputStream server = new FileInputStream(minecraftProvider.MINECRAFT_SERVER_JAR); - FileOutputStream merged = new FileOutputStream(MINECRAFT_MERGED_JAR); - - JarMerger jarMerger = new JarMerger(client, server, merged); + JarMerger jarMerger = new JarMerger(minecraftProvider.MINECRAFT_CLIENT_JAR, minecraftProvider.MINECRAFT_SERVER_JAR, minecraftProvider.MINECRAFT_MERGED_JAR); jarMerger.enableSyntheticParamsOffset(); jarMerger.merge(); jarMerger.close(); - - client.close(); - server.close(); - merged.close(); } private void initFiles(Project project, MinecraftProvider minecraftProvider) { diff --git a/src/main/java/net/fabricmc/loom/task/RemapSourcesTask.java b/src/main/java/net/fabricmc/loom/task/RemapSourcesJar.java similarity index 82% rename from src/main/java/net/fabricmc/loom/task/RemapSourcesTask.java rename to src/main/java/net/fabricmc/loom/task/RemapSourcesJar.java index 2e04080..afcb0b2 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapSourcesJar.java @@ -26,13 +26,22 @@ package net.fabricmc.loom.task; import net.fabricmc.loom.util.SourceRemapper; import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.Input; import org.gradle.api.tasks.TaskAction; -public class RemapSourcesTask extends DefaultTask { +import java.io.File; + +public class RemapSourcesJar extends DefaultTask { + public File jar; + public String direction = "intermediary"; + + @Input + public File getJar() { + return jar; + } @TaskAction public void remap() throws Exception { - SourceRemapper.remapSources(getProject()); + SourceRemapper.remapSources(getProject(), jar, jar, direction.equals("named")); } - } diff --git a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java index 8abed96..3fdb162 100644 --- a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java +++ b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java @@ -24,31 +24,49 @@ package net.fabricmc.loom.util; +import com.google.common.collect.ImmutableMap; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.providers.MappingsProvider; import net.fabricmc.stitch.util.Pair; +import net.fabricmc.stitch.util.StitchUtil; import org.cadixdev.lorenz.MappingSet; +import org.cadixdev.lorenz.io.MappingsReader; import org.cadixdev.lorenz.io.TextMappingsReader; import org.cadixdev.mercury.Mercury; import org.cadixdev.mercury.remapper.MercuryRemapper; import org.gradle.api.Project; +import org.objectweb.asm.commons.Remapper; +import org.zeroturnaround.zip.ZipUtil; -import java.io.File; -import java.io.FileReader; -import java.io.Reader; +import java.io.*; +import java.net.URI; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class SourceRemapper { - - public static void remapSources(Project project) throws Exception { + public static void remapSources(Project project, File source, File destination, boolean toNamed) throws Exception { LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); MappingsProvider mappingsProvider = extension.getMappingsProvider(); - project.getLogger().lifecycle("Setting up source remapper"); + MappingSet mappings = extension.getOrCreateSrcMappingCache(toNamed ? 1 : 0, () -> { + try { + project.getLogger().lifecycle(":loading " + (toNamed ? "intermediary -> named" : "named -> intermediary") + " source mappings"); - FileReader mappingsReader = new FileReader(mappingsProvider.MAPPINGS_TINY); - MappingSet mappings = new TinyReader(mappingsReader, "intermediary", "named").read(); - mappingsReader.close(); + FileReader mappingsReader = new FileReader(mappingsProvider.MAPPINGS_TINY); + MappingSet mappingsOut = new TinyReader(mappingsReader, toNamed ? "intermediary" : "named", toNamed ? "named" : "intermediary").read(); + mappingsReader.close(); + return mappingsOut; + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + project.getLogger().lifecycle(":remapping source jar"); Mercury mercury = new Mercury(); @@ -61,92 +79,170 @@ public class SourceRemapper { } } - mercury.getClassPath().add(extension.getMinecraftMappedProvider().MINECRAFT_INTERMEDIARY_JAR.toPath()); mercury.getClassPath().add(extension.getMinecraftMappedProvider().MINECRAFT_MAPPED_JAR.toPath()); + mercury.getClassPath().add(extension.getMinecraftMappedProvider().MINECRAFT_INTERMEDIARY_JAR.toPath()); mercury.getProcessors().add(MercuryRemapper.create(mappings)); - project.getLogger().lifecycle("Remapping source"); - mercury.rewrite(new File(project.getRootDir(), "src/main/java").toPath(), new File(project.getRootDir(), "src_mapped").toPath()); - } - - //Thanks jamierocks - public static class TinyReader extends TextMappingsReader { - - public TinyReader(final Reader reader, String to, String from) { - super(reader, mappingSet -> new Processor(mappingSet, to, from)); - } - - public static class Processor extends TextMappingsReader.Processor { - - String to; - String from; - - int toOffset = 0; - int fromOffset = 1; - - public Processor(MappingSet mappings, String to, String from) { - super(mappings); - this.to = to; - this.from = from; + if (source.equals(destination)) { + if (source.isDirectory()) { + throw new RuntimeException("Directories must differ!"); } - @Override - public void accept(final String line) { - final String[] params = line.split("\t"); - switch (params[0]) { - case "v1": - Pair offset = getMappingOffset(to, from, line); - System.out.println("To: " + to + " From: " + from + " toOffset:" + offset.getLeft() + " from:" + offset.getRight()); - toOffset = offset.getLeft(); - fromOffset = offset.getRight(); - return; - case "CLASS": { - this.mappings.getOrCreateClassMapping(params[1 + toOffset]) - .setDeobfuscatedName(params[1 + fromOffset]); - System.out.println("Class " + params[1 + toOffset] + " -> " + params[1 + fromOffset]); - return; - } - case "FIELD": { - this.mappings.getOrCreateClassMapping(params[1 + toOffset]) - // params[2] is the descriptor - .getOrCreateFieldMapping(params[3 + toOffset]) - .setDeobfuscatedName(params[3 + fromOffset]); + source = new File(destination.getAbsolutePath().substring(0, destination.getAbsolutePath().lastIndexOf('.')) + "-dev.jar"); + if (!destination.renameTo(source)) { + throw new RuntimeException("Could not rename " + destination.getName() + "!"); + } + } - System.out.println("Field " + params[3 + toOffset] + " -> " + params[3 + fromOffset]); - return; + Path srcPath = source.toPath(); + boolean isSrcTmp = false; + if (!source.isDirectory()) { + // create tmp directory + isSrcTmp = true; + srcPath = Files.createTempDirectory("fabric-loom-src"); + ZipUtil.unpack(source, srcPath.toFile()); + } + + StitchUtil.FileSystemDelegate dstFs = destination.isDirectory() ? null : StitchUtil.getJarFileSystem(destination, true); + Path dstPath = dstFs != null ? dstFs.get().getPath("/") : destination.toPath(); + + mercury.rewrite(srcPath, dstPath); + + if (dstFs != null) { + dstFs.close(); + } + + if (isSrcTmp) { + Files.walkFileTree(srcPath, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException { + Files.delete(path); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path path, IOException e) throws IOException { + Files.delete(path); + return FileVisitResult.CONTINUE; + } + }); + } + } + + static class SimpleClassMapper extends Remapper { + final Map classMap; + + public SimpleClassMapper(Map map) { + this.classMap = map; + } + + public String map(String typeName) { + return this.classMap.getOrDefault(typeName, typeName); + } + } + + // With help from jamierocks; heavily modified + public static class TinyReader extends MappingsReader { + private final Reader reader; + private final String to; + private final String from; + + private int toOffset = 0; + private int fromOffset = 1; + private int lineNumber = 0; + + public TinyReader(Reader reader, String from, String to) { + this.reader = reader; + this.from = from; + this.to = to; + } + + //This looks at the first line of the tiny file and finds the column of the mappings, horrible but works. + public Pair getMappingOffset(String to, String from, String line){ + int toOffset = -1; + int fromOffset = -1; + String[] split = line.split("\t"); + for (int i = 0; i < split.length; i++) { + String mapping = split[i]; + if(mapping.equalsIgnoreCase(to)){ + fromOffset = i -1; + } + if(mapping.equalsIgnoreCase(from)){ + toOffset = i -1; + } + } + return Pair.of(toOffset, fromOffset); + } + + @Override + public MappingSet read(final MappingSet mappings) throws IOException { + // As TinyV1 stores descriptors in obfuscated format always, we need to take a two-step approach. + Map classNames = new HashMap<>(); + List otherNames = new ArrayList<>(); + SimpleClassMapper classMapper = new SimpleClassMapper(classNames); + BufferedReader br = new BufferedReader(reader); + + br.lines().forEach((line) -> { + final String[] params = line.split("\t"); + if ((lineNumber++) == 0) { + if (params.length < 1) { + throw new RuntimeException("Invalid mapping file!"); + } else if (params.length < 3 || !params[0].equals("v1")) { + throw new RuntimeException("Invalid mapping version: '" + params[0] + "'!"); + } + + Pair offset = getMappingOffset(to, from, line); + // System.out.println("To: " + to + " From: " + from + " toOffset:" + offset.getLeft() + " from:" + offset.getRight()); + toOffset = offset.getLeft(); + fromOffset = offset.getRight(); + } else { + switch (params[0]) { + case "CLASS": { + mappings.getOrCreateClassMapping(params[1 + toOffset]) + .setDeobfuscatedName(params[1 + fromOffset]); + classNames.put(params[1], params[1 + toOffset]); + // System.out.println("Class " + params[1 + toOffset] + " -> " + params[1 + fromOffset]); + return; + } + case "FIELD": + case "METHOD": { + otherNames.add(params); + return; + } + } + } + }); + br.close(); + + for (String[] params : otherNames) { + switch (params[0]) { + case "FIELD": { + mappings.getOrCreateClassMapping(classMapper.map(params[1])) + .getOrCreateFieldMapping(params[3 + toOffset], classMapper.mapDesc(params[2])) + .setDeobfuscatedName(params[3 + fromOffset]); + + // System.out.println("Field " + params[3 + toOffset] + classMapper.mapDesc(params[2]) + " -> " + params[3 + fromOffset]); + break; } case "METHOD": { - this.mappings.getOrCreateClassMapping(params[1 + toOffset]) - .getOrCreateMethodMapping(params[3 + toOffset], params[2]) - .setDeobfuscatedName(params[3 + fromOffset]); + mappings.getOrCreateClassMapping(classMapper.map(params[1])) + .getOrCreateMethodMapping(params[3 + toOffset], classMapper.mapMethodDesc(params[2])) + .setDeobfuscatedName(params[3 + fromOffset]); - System.out.println("Method " + params[3 + toOffset] + " -> " + params[3 + fromOffset]); - return; + // System.out.println("Method " + params[3 + toOffset] + classMapper.mapMethodDesc(params[2]) + " -> " + params[3 + fromOffset]); + break; } } } - //This looks at the first line of the tiny file and finds the column of the mappings, horrible but works. - public Pair getMappingOffset(String to, String from, String line){ - int toOffset = -1; - int fromOffset = -1; - String[] split = line.split("\t"); - for (int i = 0; i < split.length; i++) { - String mapping = split[i]; - if(mapping.equalsIgnoreCase(to)){ - fromOffset = i -1; - } - if(mapping.equalsIgnoreCase(from)){ - toOffset = i -1; - } - } - return Pair.of(toOffset, fromOffset); - } - + return mappings; } + @Override + public void close() throws IOException { + + } } - }