diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMappingsMigrator.java similarity index 71% rename from src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingConfiguration.java rename to src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMappingsMigrator.java index 1c12be754..f2afeafa0 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMappingsMigrator.java @@ -33,6 +33,7 @@ import java.nio.file.StandardOpenOption; import java.util.AbstractMap; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -52,38 +53,25 @@ import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; -import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; import net.fabricmc.loom.util.FileSystemUtil; import net.fabricmc.loom.util.ThreadingUtils; -import net.fabricmc.loom.util.service.SharedServiceManager; import net.fabricmc.mappingio.MappingReader; import net.fabricmc.mappingio.format.tiny.Tiny2FileWriter; import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.mappingio.tree.MappingTreeView; import net.fabricmc.mappingio.tree.MemoryMappingTree; -public final class FieldMigratedMappingConfiguration extends MappingConfiguration { +public final class FieldMappingsMigrator implements MappingsMigrator { private List> migratedFields = new ArrayList<>(); public Path migratedFieldsCache; - private Path rawTinyMappings; - private Path rawTinyMappingsWithSrg; - private Path rawTinyMappingsWithMojang; - - public FieldMigratedMappingConfiguration(String mappingsIdentifier, Path mappingsWorkingDir) { - super(mappingsIdentifier, mappingsWorkingDir); - } @Override - protected void setup(Project project, SharedServiceManager serviceManager, MinecraftProvider minecraftProvider, Path inputJar) throws IOException { - final Path forgeCache = ForgeProvider.getForgeCache(project); - Files.createDirectories(forgeCache); - migratedFieldsCache = forgeCache.resolve("migrated-fields.json"); + public long setup(Project project, MinecraftProvider minecraftProvider, Path cache, Path rawMappings, boolean hasSrg, boolean hasMojang) throws IOException { + migratedFieldsCache = cache.resolve("migrated-fields.json"); migratedFields.clear(); - if (minecraftProvider.refreshDeps()) { - Files.deleteIfExists(migratedFieldsCache); - } else if (Files.exists(migratedFieldsCache)) { + if (!minecraftProvider.refreshDeps() && Files.exists(migratedFieldsCache)) { try (BufferedReader reader = Files.newBufferedReader(migratedFieldsCache)) { Map map = new Gson().fromJson(reader, new TypeToken>() { }.getType()); @@ -93,32 +81,34 @@ protected void setup(Project project, SharedServiceManager serviceManager, Minec migratedFields.add(new AbstractMap.SimpleEntry<>(new FieldMember(split[0], split[1]), newDescriptor)); }); } - } + } else { + Files.deleteIfExists(migratedFieldsCache); + migratedFields.clear(); - super.setup(project, serviceManager, minecraftProvider, inputJar); - } + if (hasSrg) { + migratedFields.addAll(generateNewFieldMigration(project, MinecraftPatchedProvider.get(project).getMinecraftPatchedIntermediateJar(), MappingsNamespace.SRG.toString(), rawMappings).entrySet()); + } else if (hasMojang) { + migratedFields.addAll(generateNewFieldMigration(project, MinecraftPatchedProvider.get(project).getMinecraftPatchedIntermediateJar(), MappingsNamespace.MOJANG.toString(), rawMappings).entrySet()); + } - public static String createForgeMappingsIdentifier(LoomGradleExtension extension, String mappingsName, String version, String classifier, String minecraftVersion) { - final String base = FieldMigratedMappingConfiguration.createMappingsIdentifier(mappingsName, version, classifier, minecraftVersion); - final String platform = extension.getPlatform().get().id(); - final String forgeVersion = extension.getForgeProvider().getVersion().getCombined(); - return base + "-" + platform + "-" + forgeVersion; + Map map = new HashMap<>(); + migratedFields.forEach(entry -> { + map.put(entry.getKey().owner + "#" + entry.getKey().field, entry.getValue()); + }); + Files.writeString(migratedFieldsCache, new Gson().toJson(map)); + } + + this.migratedFields.sort(Comparator.comparing(entry -> entry.getKey().owner + "#" + entry.getKey().field)); + return migratedFields.hashCode(); } @Override - protected void manipulateMappings(Project project, Path mappingsJar) throws IOException { + public void migrate(Project project, List entries) { Stopwatch stopwatch = Stopwatch.createStarted(); LoomGradleExtension extension = LoomGradleExtension.get(project); - this.rawTinyMappings = tinyMappings; - this.rawTinyMappingsWithSrg = tinyMappingsWithSrg; - this.rawTinyMappingsWithMojang = tinyMappingsWithMojang; - - tinyMappings = mappingsWorkingDir().resolve("mappings-field-migrated.tiny"); - tinyMappingsWithSrg = mappingsWorkingDir().resolve("mappings-srg-field-migrated.tiny"); - tinyMappingsWithMojang = mappingsWorkingDir().resolve("mappings-mojang-field-migrated.tiny"); try { - updateFieldMigration(project, extension.isNeoForge(), extension.shouldGenerateSrgTiny()); + updateFieldMigration(project, entries); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -126,47 +116,22 @@ protected void manipulateMappings(Project project, Path mappingsJar) throws IOEx project.getLogger().info(":migrated {} fields in " + stopwatch.stop(), extension.getPlatform().get().id()); } - public void updateFieldMigration(Project project, boolean hasMojang, boolean hasSrg) throws IOException { - if (!Files.exists(migratedFieldsCache)) { - migratedFields.clear(); - - if (hasSrg) { - migratedFields.addAll(generateNewFieldMigration(project, MinecraftPatchedProvider.get(project).getMinecraftPatchedIntermediateJar(), MappingsNamespace.SRG.toString(), rawTinyMappingsWithSrg).entrySet()); - } else if (hasMojang) { - migratedFields.addAll(generateNewFieldMigration(project, MinecraftPatchedProvider.get(project).getMinecraftPatchedIntermediateJar(), MappingsNamespace.MOJANG.toString(), rawTinyMappingsWithMojang).entrySet()); - } + public void updateFieldMigration(Project project, List entries) throws IOException { + Table fieldDescriptorMap = HashBasedTable.create(); - Map map = new HashMap<>(); - migratedFields.forEach(entry -> { - map.put(entry.getKey().owner + "#" + entry.getKey().field, entry.getValue()); - }); - Files.writeString(migratedFieldsCache, new Gson().toJson(map)); - Files.deleteIfExists(tinyMappings); - Files.deleteIfExists(tinyMappingsWithSrg); - Files.deleteIfExists(tinyMappingsWithMojang); + for (Map.Entry entry : migratedFields) { + fieldDescriptorMap.put(entry.getKey().owner, entry.getKey().field, entry.getValue()); } - if (Files.notExists(tinyMappings) || (hasSrg && Files.notExists(tinyMappingsWithSrg)) || (hasMojang && Files.notExists(tinyMappingsWithMojang))) { - Table fieldDescriptorMap = HashBasedTable.create(); - - for (Map.Entry entry : migratedFields) { - fieldDescriptorMap.put(entry.getKey().owner, entry.getKey().field, entry.getValue()); - } - - injectMigration(project, fieldDescriptorMap, rawTinyMappings, tinyMappings); - - if (hasSrg) { - injectMigration(project, fieldDescriptorMap, rawTinyMappingsWithSrg, tinyMappingsWithSrg); - } else if (hasMojang) { - injectMigration(project, fieldDescriptorMap, rawTinyMappingsWithMojang, tinyMappingsWithMojang); - } + for (MappingsEntry entry : entries) { + injectMigration(project, fieldDescriptorMap, entry.path()); } } - private static void injectMigration(Project project, Table fieldDescriptorMap, Path source, Path out) throws IOException { + private static void injectMigration(Project project, Table fieldDescriptorMap, Path path) throws IOException { MemoryMappingTree mappings = new MemoryMappingTree(); - try (BufferedReader reader = Files.newBufferedReader(source)) { + try (BufferedReader reader = Files.newBufferedReader(path)) { MappingReader.read(reader, mappings); } @@ -186,7 +151,7 @@ private static void injectMigration(Project project, Table migrators = List.of(new FieldMappingsMigrator(), new MethodInheritanceMappingsMigrator()); + private Path hashPath; + private Path rawTinyMappings; + private Path rawTinyMappingsWithSrg; + private Path rawTinyMappingsWithMojang; + private long hash; + + public ForgeMigratedMappingConfiguration(String mappingsIdentifier, Path mappingsWorkingDir) { + super(mappingsIdentifier, mappingsWorkingDir); + } + + @Override + protected void manipulateMappings(Project project, Path mappingsJar) throws IOException { + LoomGradleExtension extension = LoomGradleExtension.get(project); + final Path forgeCache = ForgeProvider.getForgeCache(project); + Files.createDirectories(forgeCache); + + boolean hasSrg = extension.shouldGenerateSrgTiny(); + boolean hasMojang = extension.isNeoForge(); + + this.hashPath = forgeCache.resolve("mappings-migrated.hash"); + this.hash = 1; + + this.rawTinyMappings = this.tinyMappings; + this.rawTinyMappingsWithSrg = this.tinyMappingsWithSrg; + this.rawTinyMappingsWithMojang = this.tinyMappingsWithMojang; + Path rawTinyMappingsWithNs = hasSrg ? this.rawTinyMappingsWithSrg : hasMojang ? this.rawTinyMappingsWithMojang : this.rawTinyMappings; + + this.tinyMappings = mappingsWorkingDir().resolve("mappings-migrated.tiny"); + this.tinyMappingsWithSrg = mappingsWorkingDir().resolve("mappings-srg-migrated.tiny"); + this.tinyMappingsWithMojang = mappingsWorkingDir().resolve("mappings-mojang-migrated.tiny"); + Path tinyMappingsWithNs = hasSrg ? this.tinyMappingsWithSrg : hasMojang ? this.tinyMappingsWithMojang : this.tinyMappings; + + for (MappingsMigrator migrator : this.migrators) { + hash = hash * 31 + migrator.setup(project, extension.getMinecraftProvider(), forgeCache, rawTinyMappingsWithNs, hasSrg, hasMojang); + } + + if (!isOutdated(extension, hasSrg, hasMojang)) { + project.getLogger().info(":manipulated {} mappings are up to date", extension.getPlatform().get().id()); + return; + } + + Stopwatch stopwatch = Stopwatch.createStarted(); + Files.copy(this.rawTinyMappings, this.tinyMappings, StandardCopyOption.REPLACE_EXISTING); + Files.copy(rawTinyMappingsWithNs, tinyMappingsWithNs, StandardCopyOption.REPLACE_EXISTING); + + Files.writeString(this.hashPath, Long.toString(this.hash), StandardCharsets.UTF_8); + + for (MappingsMigrator migrator : this.migrators) { + Path path = Files.createTempFile("mappings-working", ".tiny"); + Path pathWithNs = Files.createTempFile("mappings-working-ns", ".tiny"); + Files.copy(this.tinyMappings, path, StandardCopyOption.REPLACE_EXISTING); + Files.copy(tinyMappingsWithNs, pathWithNs, StandardCopyOption.REPLACE_EXISTING); + + List entries = List.of(new MappingsMigrator.MappingsEntry(path), new MappingsMigrator.MappingsEntry(pathWithNs)); + migrator.migrate(project, entries); + + Files.copy(path, this.tinyMappings, StandardCopyOption.REPLACE_EXISTING); + Files.copy(pathWithNs, tinyMappingsWithNs, StandardCopyOption.REPLACE_EXISTING); + Files.deleteIfExists(path); + Files.deleteIfExists(pathWithNs); + } + + project.getLogger().info(":manipulated {} mappings in " + stopwatch.stop(), extension.getPlatform().get().id()); + } + + private boolean isOutdated(LoomGradleExtension extension, boolean hasSrg, boolean hasMojang) throws IOException { + if (extension.refreshDeps()) return true; + if (Files.notExists(this.tinyMappings)) return true; + if (hasSrg && Files.notExists(this.tinyMappingsWithSrg)) return true; + if (hasMojang && Files.notExists(this.tinyMappingsWithMojang)) return true; + if (Files.notExists(this.hashPath)) return true; + String hashStr = Files.readString(hashPath, StandardCharsets.UTF_8); + return !Long.toString(this.hash).equals(hashStr); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MappingsMigrator.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MappingsMigrator.java new file mode 100644 index 000000000..e51715411 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MappingsMigrator.java @@ -0,0 +1,42 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2024 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.providers.forge; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +import org.gradle.api.Project; + +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; + +public interface MappingsMigrator { + long setup(Project project, MinecraftProvider minecraftProvider, Path cache, Path rawMappings, boolean hasSrg, boolean hasMojang) throws IOException; + + void migrate(Project project, List entries) throws IOException; + + record MappingsEntry(Path path) { + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MethodInheritanceMappingsMigrator.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MethodInheritanceMappingsMigrator.java new file mode 100644 index 000000000..88b00da7a --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MethodInheritanceMappingsMigrator.java @@ -0,0 +1,241 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2024 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.providers.forge; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; +import org.gradle.api.Project; +import org.gradle.api.logging.Logger; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.util.FileSystemUtil; +import net.fabricmc.loom.util.Pair; +import net.fabricmc.mappingio.MappingReader; +import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; +import net.fabricmc.mappingio.format.tiny.Tiny2FileWriter; +import net.fabricmc.mappingio.tree.MappingTree; +import net.fabricmc.mappingio.tree.MemoryMappingTree; + +/** + * With some forge patches, methods can inherit methods from a class that is not in the mappings. + * This migrator will try to detect all the methods that are inherited from a class that is not in the mappings, + * see if there are different names for the same method in the mappings, and remove them. + */ +public final class MethodInheritanceMappingsMigrator implements MappingsMigrator { + private Set> methodsToRemove; + + @Override + public long setup(Project project, MinecraftProvider minecraftProvider, Path cache, Path rawMappings, boolean hasSrg, boolean hasMojang) throws IOException { + Path cacheFile = cache.resolve("method-inheritance-migrator.json"); + + if (!minecraftProvider.refreshDeps() && Files.exists(cacheFile)) { + try (BufferedReader reader = Files.newBufferedReader(cacheFile)) { + List> list = new Gson().fromJson(reader, new TypeToken>>() { + }.getType()); + methodsToRemove = new HashSet<>(list); + } + } else { + Files.deleteIfExists(cacheFile); + LoomGradleExtension extension = LoomGradleExtension.get(project); + Path patchedIntermediateJar = MinecraftPatchedProvider.get(project).getMinecraftPatchedIntermediateJar(); + List jars = List.of(patchedIntermediateJar, extension.getForgeUniversalProvider().getForge().toPath(), extension.getForgeUserdevProvider().getUserdevJar().toPath()); + methodsToRemove = prepareCache(project.getLogger(), rawMappings, jars, hasSrg, hasMojang); + Files.writeString(cacheFile, new Gson().toJson(methodsToRemove.stream().sorted(Comparator.comparing(p -> p.left() + "|" + p.right())).toList()), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + } + + return methodsToRemove.hashCode(); + } + + @Override + public void migrate(Project project, List entries) throws IOException { + for (MappingsEntry entry : entries) { + MemoryMappingTree mappings = new MemoryMappingTree(); + + try (BufferedReader reader = Files.newBufferedReader(entry.path())) { + MappingReader.read(reader, mappings); + } + + for (MappingTree.ClassMapping classMapping : mappings.getClasses()) { + if (classMapping.getMethods().isEmpty()) continue; + classMapping.getMethods().removeIf(method -> { + return methodsToRemove.contains(new Pair<>(method.getName(MappingsNamespace.INTERMEDIARY.toString()), method.getDesc(MappingsNamespace.INTERMEDIARY.toString()))); + }); + } + + try (Writer writer = Files.newBufferedWriter(entry.path(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + mappings.accept(new Tiny2FileWriter(writer, false)); + } + } + } + + private Set> prepareCache(Logger logger, Path rawMappings, List jars, boolean hasSrg, boolean hasMojang) throws IOException { + MemoryMappingTree mappings = new MemoryMappingTree(); + String patchedNs = hasSrg ? MappingsNamespace.SRG.toString() : MappingsNamespace.MOJANG.toString(); + + try (BufferedReader reader = Files.newBufferedReader(rawMappings)) { + MappingReader.read(reader, new MappingSourceNsSwitch(mappings, patchedNs)); + } + + Pair, Set> collected = collectClassesAndMethods(jars); + Multimap classInheritanceMap = collected.left(); + Set methods = collected.right(); + + Multimap> overriddenIntermediaries = Multimaps.newSetMultimap(new HashMap<>(), LinkedHashSet::new); + + for (MethodKey method : methods) { + // First check if the method is in the mappings, and as a different intermediary name + MappingTree.ClassMapping aClass = mappings.getClass(method.className()); + if (aClass == null) continue; + MappingTree.MethodMapping aMethod = aClass.getMethod(method.name(), method.descriptor()); + if (aMethod == null) continue; + String intermediaryName = aMethod.getName(MappingsNamespace.INTERMEDIARY.toString()); + if (intermediaryName == null || Objects.equals(intermediaryName, method.name())) continue; + + for (String superClass : classInheritanceMap.get(method.className())) { + if (methods.contains(new MethodKey(superClass, method.name(), method.descriptor()))) { + if (mappings.getClass(superClass) == null) { + // We will collect these methods here, and remove them later + // if there are more than intermediary name for the same method + String intermediaryDesc = aMethod.getDesc(MappingsNamespace.INTERMEDIARY.toString()); + overriddenIntermediaries.put(new MethodKey(superClass, method.name(), method.descriptor()), new Pair<>(intermediaryName, intermediaryDesc)); + } + } + } + } + + Set> methodsToRemove = new HashSet<>(); + + for (Map.Entry>> entry : overriddenIntermediaries.asMap().entrySet()) { + if (entry.getValue().size() >= 2) { + // We should remove these names from the mappings + // as the particular method is inherited by multiple different intermediary names + for (Pair pair : entry.getValue()) { + methodsToRemove.add(pair); + logger.info("Removing method {}{} from the mappings", pair.left(), pair.right()); + } + + break; + } + } + + return methodsToRemove; + } + + private static Pair, Set> collectClassesAndMethods(Iterable jars) throws IOException { + Multimap classInheritanceMap = Multimaps.newSetMultimap(new HashMap<>(), LinkedHashSet::new); + Set methods = new HashSet<>(); + Visitor visitor = new Visitor(Opcodes.ASM9, classInheritanceMap, methods); + + for (Path jar : jars) { + try (FileSystemUtil.Delegate system = FileSystemUtil.getJarFileSystem(jar, false)) { + for (Path fsPath : (Iterable) Files.walk(system.get().getPath("/"))::iterator) { + if (Files.isRegularFile(fsPath) && fsPath.toString().endsWith(".class")) { + new ClassReader(Files.readAllBytes(fsPath)).accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + } + } + } + } + + // Populate class inheritance + Multimap classes = Multimaps.newSetMultimap(new HashMap<>(), LinkedHashSet::new); + + for (Map.Entry> entry : classInheritanceMap.asMap().entrySet()) { + Set allSuperClasses = new HashSet<>(); + + for (String superClass : entry.getValue()) { + collectSuperClasses(superClass, new HashSet<>(), allSuperClasses, classInheritanceMap); + } + + classes.putAll(entry.getKey(), allSuperClasses); + } + + return new Pair<>(classes, methods); + } + + private static void collectSuperClasses(String className, Set travelled, Set allSuperClasses, Multimap classInheritanceMap) { + if (className != null && !className.isEmpty()) { + allSuperClasses.add(className); + + for (String superClass : classInheritanceMap.get(className)) { + if (travelled.add(superClass)) { + collectSuperClasses(superClass, travelled, allSuperClasses, classInheritanceMap); + } + } + } + } + + private static class Visitor extends ClassVisitor { + private final Multimap classInheritanceMap; + private final Set methods; + private String lastClass = null; + + Visitor(int api, Multimap classInheritanceMap, Set methods) { + super(api); + this.classInheritanceMap = classInheritanceMap; + this.methods = methods; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + this.lastClass = name; + this.classInheritanceMap.put(name, superName); + this.classInheritanceMap.putAll(name, Arrays.asList(interfaces)); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + this.methods.add(new MethodKey(this.lastClass, name, descriptor)); + return super.visitMethod(access, name, descriptor, signature, exceptions); + } + } + + private record MethodKey(String className, String name, String descriptor) { + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java index a081405de..9f795b3e3 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java @@ -57,7 +57,7 @@ import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.api.mappings.layered.MappingContext; import net.fabricmc.loom.configuration.DependencyInfo; -import net.fabricmc.loom.configuration.providers.forge.FieldMigratedMappingConfiguration; +import net.fabricmc.loom.configuration.providers.forge.ForgeMigratedMappingConfiguration; import net.fabricmc.loom.configuration.providers.forge.SrgProvider; import net.fabricmc.loom.configuration.providers.mappings.tiny.MappingsMerger; import net.fabricmc.loom.configuration.providers.mappings.tiny.TinyJarInfo; @@ -130,7 +130,7 @@ public static MappingConfiguration create(Project project, SharedServiceManager String mappingsIdentifier; if (extension.isForgeLike()) { - mappingsIdentifier = FieldMigratedMappingConfiguration.createForgeMappingsIdentifier(extension, mappingsName, version, getMappingsClassifier(dependency, jarInfo.v2()), minecraftProvider.minecraftVersion()); + mappingsIdentifier = createForgeMappingsIdentifier(extension, mappingsName, version, getMappingsClassifier(dependency, jarInfo.v2()), minecraftProvider.minecraftVersion()); } else { mappingsIdentifier = createMappingsIdentifier(mappingsName, version, getMappingsClassifier(dependency, jarInfo.v2()), minecraftProvider.minecraftVersion()); } @@ -144,7 +144,7 @@ public static MappingConfiguration create(Project project, SharedServiceManager MappingConfiguration mappingConfiguration; if (extension.isForgeLike()) { - mappingConfiguration = new FieldMigratedMappingConfiguration(mappingsIdentifier, workingDir); + mappingConfiguration = new ForgeMigratedMappingConfiguration(mappingsIdentifier, workingDir); } else { mappingConfiguration = new MappingConfiguration(mappingsIdentifier, workingDir); } @@ -515,6 +515,13 @@ protected static String createMappingsIdentifier(String mappingsName, String ver return mappingsName + "." + minecraftVersion.replace(' ', '_').replace('.', '_').replace('-', '_') + "." + version + classifier; } + protected static String createForgeMappingsIdentifier(LoomGradleExtension extension, String mappingsName, String version, String classifier, String minecraftVersion) { + final String base = createMappingsIdentifier(mappingsName, version, classifier, minecraftVersion); + final String platform = extension.getPlatform().get().id(); + final String forgeVersion = extension.getForgeProvider().getVersion().getCombined(); + return base + "-" + platform + "-" + forgeVersion; + } + public String mappingsIdentifier() { return mappingsIdentifier; }