diff --git a/src/main/java/net/fabricmc/loom/providers/MinecraftMappedProvider.java b/src/main/java/net/fabricmc/loom/providers/MinecraftMappedProvider.java index 2e496a1f..267514c5 100644 --- a/src/main/java/net/fabricmc/loom/providers/MinecraftMappedProvider.java +++ b/src/main/java/net/fabricmc/loom/providers/MinecraftMappedProvider.java @@ -30,12 +30,13 @@ import net.fabricmc.loom.util.DependencyProvider; import net.fabricmc.loom.util.TinyRemapperMappingsHelper; import net.fabricmc.loom.util.srg.AtRemapper; import net.fabricmc.loom.util.srg.CoreModClassRemapper; +import net.fabricmc.loom.util.srg.InnerClassRemapper; import net.fabricmc.mapping.tree.TinyTree; import net.fabricmc.tinyremapper.NonClassCopyMode; import net.fabricmc.tinyremapper.OutputConsumerPath; import net.fabricmc.tinyremapper.TinyRemapper; import org.gradle.api.Project; -import org.zeroturnaround.zip.ZipUtil; +import org.jetbrains.annotations.Nullable; import java.io.*; import java.net.URI; @@ -137,7 +138,7 @@ public class MinecraftMappedProvider extends DependencyProvider { getProject().getLogger().lifecycle(":remapping minecraft (TinyRemapper, " + fromM + " -> " + toM + ")"); - TinyRemapper remapper = getTinyRemapper(fromM, toM); + TinyRemapper remapper = getTinyRemapper(input, fromM, toM); try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(output).build()) { if (getExtension().isForge()) { @@ -201,7 +202,7 @@ public class MinecraftMappedProvider extends DependencyProvider { } } - public TinyRemapper getTinyRemapper(String fromM, String toM) throws IOException { + public TinyRemapper getTinyRemapper(@Nullable Path fromJar, String fromM, String toM) throws IOException { TinyRemapper.Builder builder = TinyRemapper.newRemapper() .withMappings(TinyRemapperMappingsHelper.create(getExtension().isForge() ? getExtension().getMappingsProvider().getMappingsWithSrg() : getExtension().getMappingsProvider().getMappings(), fromM, toM, true)) .renameInvalidLocals(true) @@ -213,6 +214,9 @@ public class MinecraftMappedProvider extends DependencyProvider { * They won't get remapped to their proper packages, so IllegalAccessErrors will happen without ._. */ builder.fixPackageAccess(true); + if (fromJar != null) { + builder.withMappings(InnerClassRemapper.of(fromJar, getExtension().getMappingsProvider().getMappingsWithSrg(), fromM, toM)); + } } else { builder.withMappings(out -> JSR_TO_JETBRAINS.forEach(out::acceptClass)); } diff --git a/src/main/java/net/fabricmc/loom/providers/MinecraftPatchedProvider.java b/src/main/java/net/fabricmc/loom/providers/MinecraftPatchedProvider.java index fb7bdf07..bdaea503 100644 --- a/src/main/java/net/fabricmc/loom/providers/MinecraftPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/providers/MinecraftPatchedProvider.java @@ -25,10 +25,11 @@ package net.fabricmc.loom.providers; import com.google.common.collect.ImmutableMap; -import com.google.common.io.Files; import de.oceanlabs.mcp.mcinjector.adaptors.ParameterAnnotationFixer; import net.fabricmc.loom.util.*; import net.fabricmc.loom.util.function.FsPathConsumer; +import net.fabricmc.loom.util.srg.InnerClassRemapper; +import net.fabricmc.mapping.tree.TinyTree; import net.fabricmc.tinyremapper.OutputConsumerPath; import net.fabricmc.tinyremapper.TinyRemapper; import net.minecraftforge.accesstransformer.TransformerProcessor; @@ -46,33 +47,28 @@ import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.tree.ClassNode; +import org.zeroturnaround.zip.ZipUtil; import java.io.*; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; +import java.nio.file.*; +import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; -import java.util.stream.Stream; public class MinecraftPatchedProvider extends DependencyProvider { private File minecraftClientSrgJar; private File minecraftServerSrgJar; private File minecraftClientPatchedSrgJar; private File minecraftServerPatchedSrgJar; - private File minecraftClientPatchedJar; - private File minecraftServerPatchedJar; + private File minecraftClientPatchedOfficialJar; + private File minecraftServerPatchedOfficialJar; private File minecraftMergedPatchedJar; private File projectAtHash; @Nullable @@ -101,7 +97,7 @@ public class MinecraftPatchedProvider extends DependencyProvider { writeAtHash(); atDirty = projectAt != null; } else { - byte[] expected = Files.asByteSource(projectAtHash).read(); + byte[] expected = com.google.common.io.Files.asByteSource(projectAtHash).read(); byte[] current = projectAt != null ? Checksum.sha256(projectAt) : Checksum.sha256(""); boolean mismatched = !Arrays.equals(current, expected); @@ -120,24 +116,29 @@ public class MinecraftPatchedProvider extends DependencyProvider { File cache = usesProjectCache() ? getExtension().getProjectPersistentCache() : getExtension().getUserCache(); - minecraftClientPatchedJar = new File(cache, "minecraft-" + minecraftVersion + "-client" + jarSuffix + ".jar"); - minecraftServerPatchedJar = new File(cache, "minecraft-" + minecraftVersion + "-server" + jarSuffix + ".jar"); + minecraftClientPatchedOfficialJar = new File(cache, "minecraft-" + minecraftVersion + "-client" + jarSuffix + ".jar"); + minecraftServerPatchedOfficialJar = new File(cache, "minecraft-" + minecraftVersion + "-server" + jarSuffix + ".jar"); minecraftClientSrgJar = new File(cache, "minecraft-" + minecraftVersion + "-client-srg.jar"); minecraftServerSrgJar = new File(cache, "minecraft-" + minecraftVersion + "-server-srg.jar"); minecraftClientPatchedSrgJar = new File(cache, "minecraft-" + minecraftVersion + "-client-srg" + jarSuffix + ".jar"); minecraftServerPatchedSrgJar = new File(cache, "minecraft-" + minecraftVersion + "-server-srg" + jarSuffix + ".jar"); minecraftMergedPatchedJar = new File(cache, "minecraft-" + minecraftVersion + "-merged" + jarSuffix + ".jar"); - + if (isRefreshDeps()) { cleanCache(); } - if (!minecraftClientSrgJar.exists() || !minecraftServerSrgJar.exists()) { - minecraftClientPatchedJar.delete(); - minecraftServerPatchedJar.delete(); + if (!minecraftClientSrgJar.exists() || !minecraftServerSrgJar.exists() + || !minecraftClientPatchedSrgJar.exists() || !minecraftServerPatchedSrgJar.exists() + || !minecraftMergedPatchedJar.exists()) { + minecraftClientSrgJar.delete(); + minecraftServerSrgJar.delete(); + minecraftClientPatchedSrgJar.delete(); + minecraftServerPatchedSrgJar.delete(); + minecraftMergedPatchedJar.delete(); } } - + public void cleanCache() { for (File file : Arrays.asList( projectAtHash, @@ -145,8 +146,8 @@ public class MinecraftPatchedProvider extends DependencyProvider { minecraftServerSrgJar, minecraftClientPatchedSrgJar, minecraftServerPatchedSrgJar, - minecraftClientPatchedJar, - minecraftServerPatchedJar, + minecraftClientPatchedOfficialJar, + minecraftServerPatchedOfficialJar, minecraftMergedPatchedJar )) { file.delete(); @@ -156,13 +157,14 @@ public class MinecraftPatchedProvider extends DependencyProvider { @Override public void provide(DependencyInfo dependency, Consumer postPopulationScheduler) throws Exception { initFiles(); - + if (atDirty) { getProject().getLogger().lifecycle(":found dirty access transformers"); } - if (atDirty || !minecraftClientPatchedJar.exists() || !minecraftServerPatchedJar.exists()) { + if (atDirty || !minecraftClientPatchedOfficialJar.exists() || !minecraftServerPatchedOfficialJar.exists()) { if (!minecraftClientSrgJar.exists() || !minecraftServerSrgJar.exists()) { + // Remap official jars to MCPConfig remapped srg jars createSrgJars(getProject().getLogger()); } @@ -191,12 +193,12 @@ public class MinecraftPatchedProvider extends DependencyProvider { private void createSrgJars(Logger logger) throws Exception { // TODO: FORGE: Get rid of *this* - logger.lifecycle(":remapping minecraft (MCP, official -> srg)"); + logger.lifecycle(":remapping minecraft (MCPRuntime, official -> srg)"); - McpConfigProvider volde = getExtension().getMcpConfigProvider(); + McpConfigProvider mcpProvider = getExtension().getMcpConfigProvider(); File root = new File(getExtension().getUserCache(), "mcp_root"); root.mkdirs(); - MCPWrapper wrapper = new MCPWrapper(volde.getMcp(), root); + MCPWrapper wrapper = new MCPWrapper(mcpProvider.getMcp(), root); // Client { @@ -212,25 +214,25 @@ public class MinecraftPatchedProvider extends DependencyProvider { FileUtils.copyFile(output, minecraftServerSrgJar); } } - + private void fixParameterAnnotation(File jarFile) throws Exception { getProject().getLogger().info(":fixing parameter annotations for " + jarFile.toString()); try (FileSystem fs = FileSystems.newFileSystem(new URI("jar:" + jarFile.toURI()), ImmutableMap.of("create", false))) { for (Path rootDir : fs.getRootDirectories()) { - for (Path file : (Iterable) java.nio.file.Files.walk(rootDir)::iterator) { + for (Path file : (Iterable) Files.walk(rootDir)::iterator) { if (!file.toString().endsWith(".class")) continue; - byte[] bytes = java.nio.file.Files.readAllBytes(file); + byte[] bytes = Files.readAllBytes(file); ClassReader reader = new ClassReader(bytes); - ClassNode cn = new ClassNode(); - ClassVisitor visitor = new ParameterAnnotationFixer(cn, null); + ClassNode node = new ClassNode(); + ClassVisitor visitor = new ParameterAnnotationFixer(node, null); reader.accept(visitor, 0); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); - cn.accept(writer); + node.accept(writer); byte[] out = writer.toByteArray(); if (!Arrays.equals(bytes, out)) { - java.nio.file.Files.delete(file); - java.nio.file.Files.write(file, out); + Files.delete(file); + Files.write(file, out); } } } @@ -256,18 +258,15 @@ public class MinecraftPatchedProvider extends DependencyProvider { walkFileSystems(injection, minecraftServerPatchedSrgJar, it -> !it.getFileName().toString().equals("MANIFEST.MF"), this::copyReplacing); logger.lifecycle(":access transforming minecraft"); - - boolean[] bools = { true, false }; - - for (boolean isClient : bools) { - String side = isClient ? "client" : "server"; - File target = isClient ? minecraftClientPatchedSrgJar : minecraftServerPatchedSrgJar; + for (Environment environment : Environment.values()) { + String side = environment.side(); + File target = environment.patchedSrgJar.apply(this); File atJar = File.createTempFile("at" + side, ".jar"); File at = File.createTempFile("at" + side, ".cfg"); - Files.copy(target, atJar); + FileUtils.copyFile(target, atJar); JarUtil.extractFile(atJar, "META-INF/accesstransformer.cfg", at); - String[] args = new String[] { + String[] args = new String[]{ "--inJar", atJar.getAbsolutePath(), "--outJar", target.getAbsolutePath(), "--atFile", at.getAbsolutePath() @@ -283,8 +282,24 @@ public class MinecraftPatchedProvider extends DependencyProvider { } } + private enum Environment { + CLIENT(provider -> provider.minecraftClientPatchedSrgJar, provider -> provider.minecraftClientPatchedOfficialJar), + SERVER(provider -> provider.minecraftServerPatchedSrgJar, provider -> provider.minecraftServerPatchedOfficialJar); + + Function patchedSrgJar; + Function patchedOfficialJar; + + Environment(Function patchedSrgJar, Function patchedOfficialJar) { + this.patchedSrgJar = patchedSrgJar; + this.patchedOfficialJar = patchedOfficialJar; + } + + public String side() { + return name().toLowerCase(Locale.ROOT); + } + } + private void remapPatchedJars(Logger logger) throws Exception { - boolean[] bools = { true, false }; Path[] libraries = getExtension() .getMinecraftProvider() .getLibraryProvider() @@ -295,21 +310,26 @@ public class MinecraftPatchedProvider extends DependencyProvider { ExecutorService service = Executors.newFixedThreadPool(2); List> futures = new LinkedList<>(); - - for (boolean isClient : bools) { + + for (Environment environment : Environment.values()) { futures.add(service.submit(() -> { try { - logger.lifecycle(":remapping minecraft (TinyRemapper, " + (isClient ? "client" : "server") + ", srg -> official)"); + logger.lifecycle(":remapping minecraft (TinyRemapper, " + environment.side() + ", srg -> official)"); + TinyTree mappingsWithSrg = getExtension().getMappingsProvider().getMappingsWithSrg(); + Path input = environment.patchedSrgJar.apply(this).toPath(); + Path output = environment.patchedOfficialJar.apply(this).toPath(); + + Files.deleteIfExists(output); + TinyRemapper remapper = TinyRemapper.newRemapper() - .withMappings(TinyRemapperMappingsHelper.create(getExtension().getMappingsProvider().getMappingsWithSrg(), "srg", "official", true)) + .withMappings(TinyRemapperMappingsHelper.create(mappingsWithSrg, "srg", "official", true)) + .withMappings(InnerClassRemapper.of(input, mappingsWithSrg, "srg", "official")) .renameInvalidLocals(true) .rebuildSourceFilenames(true) + .fixPackageAccess(true) .build(); - Path input = (isClient ? minecraftClientPatchedSrgJar : minecraftServerPatchedSrgJar).toPath(); - Path output = (isClient ? minecraftClientPatchedJar : minecraftServerPatchedJar).toPath(); - try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(output).build()) { outputConsumer.addNonClassFiles(input); @@ -329,6 +349,10 @@ public class MinecraftPatchedProvider extends DependencyProvider { future.get(); } } + + + + private void patchJars(Logger logger) throws Exception { logger.lifecycle(":patching jars"); @@ -340,7 +364,8 @@ public class MinecraftPatchedProvider extends DependencyProvider { logger.lifecycle(":copying missing classes into patched jars"); copyMissingClasses(minecraftClientSrgJar, minecraftClientPatchedSrgJar); copyMissingClasses(minecraftServerSrgJar, minecraftServerPatchedSrgJar); - + + logger.lifecycle(":fixing parameter annotations for patched jars"); fixParameterAnnotation(minecraftClientPatchedSrgJar); fixParameterAnnotation(minecraftServerPatchedSrgJar); } @@ -364,7 +389,7 @@ public class MinecraftPatchedProvider extends DependencyProvider { private void mergeJars(Logger logger) throws IOException { // FIXME: Hack here: There are no server-only classes so we can just copy the client JAR. - FileUtils.copyFile(minecraftClientPatchedJar, minecraftMergedPatchedJar); + FileUtils.copyFile(minecraftClientPatchedOfficialJar, minecraftMergedPatchedJar); logger.lifecycle(":copying resources"); @@ -374,13 +399,14 @@ public class MinecraftPatchedProvider extends DependencyProvider { copyNonClassFiles(minecraftProvider.minecraftServerJar, minecraftMergedPatchedJar); } - private void walkFileSystems(File source, File target, Predicate filter, Function> toWalk, FsPathConsumer action) throws IOException { + private void walkFileSystems(File source, File target, Predicate filter, Function> toWalk, FsPathConsumer action) + throws IOException { try (FileSystem sourceFs = FileSystems.newFileSystem(new URI("jar:" + source.toURI()), ImmutableMap.of("create", false)); - FileSystem targetFs = FileSystems.newFileSystem(new URI("jar:" + target.toURI()), ImmutableMap.of("create", false))) { + FileSystem targetFs = FileSystems.newFileSystem(new URI("jar:" + target.toURI()), ImmutableMap.of("create", false))) { for (Path sourceDir : toWalk.apply(sourceFs)) { Path dir = sourceDir.toAbsolutePath(); - java.nio.file.Files.walk(dir) - .filter(java.nio.file.Files::isRegularFile) + Files.walk(dir) + .filter(Files::isRegularFile) .filter(filter) .forEach(it -> { boolean root = dir.getParent() == null; @@ -409,14 +435,14 @@ public class MinecraftPatchedProvider extends DependencyProvider { private void copyMissingClasses(File source, File target) throws IOException { walkFileSystems(source, target, it -> it.toString().endsWith(".class"), (sourceFs, targetFs, sourcePath, targetPath) -> { - if (java.nio.file.Files.exists(targetPath)) return; + if (Files.exists(targetPath)) return; Path parent = targetPath.getParent(); if (parent != null) { - java.nio.file.Files.createDirectories(parent); + Files.createDirectories(parent); } - java.nio.file.Files.copy(sourcePath, targetPath); + Files.copy(sourcePath, targetPath); }); } @@ -428,10 +454,10 @@ public class MinecraftPatchedProvider extends DependencyProvider { Path parent = targetPath.getParent(); if (parent != null) { - java.nio.file.Files.createDirectories(parent); + Files.createDirectories(parent); } - java.nio.file.Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING); + Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING); } private void copyUserdevFiles(File source, File target) throws IOException { @@ -439,10 +465,10 @@ public class MinecraftPatchedProvider extends DependencyProvider { Path parent = targetPath.getParent(); if (parent != null) { - java.nio.file.Files.createDirectories(parent); + Files.createDirectories(parent); } - java.nio.file.Files.copy(sourcePath, targetPath); + Files.copy(sourcePath, targetPath); }); } diff --git a/src/main/java/net/fabricmc/loom/util/accesswidener/AccessWidenerJarProcessor.java b/src/main/java/net/fabricmc/loom/util/accesswidener/AccessWidenerJarProcessor.java index aa02c2d1..6258e374 100644 --- a/src/main/java/net/fabricmc/loom/util/accesswidener/AccessWidenerJarProcessor.java +++ b/src/main/java/net/fabricmc/loom/util/accesswidener/AccessWidenerJarProcessor.java @@ -87,7 +87,7 @@ public class AccessWidenerJarProcessor implements JarProcessor { //Remap accessWidener if its not named, allows for AE's to be written in intermediary if (!accessWidener.getNamespace().equals("named")) { try { - TinyRemapper tinyRemapper = loomGradleExtension.getMinecraftMappedProvider().getTinyRemapper("official", "named"); + TinyRemapper tinyRemapper = loomGradleExtension.getMinecraftMappedProvider().getTinyRemapper(null, "official", "named"); tinyRemapper.readClassPath(loomGradleExtension.getMinecraftMappedProvider().getRemapClasspath()); AccessWidenerRemapper remapper = new AccessWidenerRemapper(accessWidener, tinyRemapper.getRemapper(), "named"); diff --git a/src/main/java/net/fabricmc/loom/util/srg/InnerClassRemapper.java b/src/main/java/net/fabricmc/loom/util/srg/InnerClassRemapper.java new file mode 100644 index 00000000..7d57f6fd --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/srg/InnerClassRemapper.java @@ -0,0 +1,56 @@ +package net.fabricmc.loom.util.srg; + +import net.fabricmc.mapping.tree.TinyTree; +import net.fabricmc.tinyremapper.IMappingProvider; +import org.zeroturnaround.zip.ZipUtil; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +public class InnerClassRemapper { + public static IMappingProvider of(Path fromJar, TinyTree mappingsWithSrg, String from, String to) throws IOException { + Map map = buildInnerClassRemap(fromJar, mappingsWithSrg, from, to); + return sink -> { + for (Map.Entry entry : map.entrySet()) { + sink.acceptClass(entry.getKey(), entry.getValue()); + } + }; + } + + private static Map buildInnerClassRemap(Path fromJar, TinyTree mappingsWithSrg, String from, String to) throws IOException { + Map remapInnerClasses = new HashMap<>(); + + try (InputStream inputStream = Files.newInputStream(fromJar)) { + Set availableClasses = mappingsWithSrg.getClasses().stream() + .map(classDef -> classDef.getName(from)) + .collect(Collectors.toSet()); + ZipUtil.iterate(inputStream, (in, zipEntry) -> { + if (!zipEntry.isDirectory() && zipEntry.getName().contains("$") && zipEntry.getName().endsWith(".class")) { + String className = zipEntry.getName().substring(0, zipEntry.getName().length() - 6); + if (!availableClasses.contains(className)) { + String parentName = className.substring(0, className.indexOf('$')); + String childName = className.substring(className.indexOf('$') + 1); + String remappedParentName = mappingsWithSrg.getClasses().stream() + .filter(classDef -> Objects.equals(classDef.getName(from), parentName)) + .findFirst() + .map(classDef -> classDef.getName(to)) + .orElse(parentName); + String remappedName = remappedParentName + "$" + childName; + if (!className.equals(remappedName)) { + remapInnerClasses.put(className, remappedName); + } + } + } + }); + } + + return remapInnerClasses; + } +}