Refactor field migration into a more generalized migrator + add method inheritance migrator

This commit is contained in:
shedaniel
2024-04-28 22:40:33 +09:00
parent ff3546e108
commit 9ce466479a
5 changed files with 434 additions and 74 deletions

View File

@@ -33,6 +33,7 @@ import java.nio.file.Path;
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 org.objectweb.asm.Opcodes;
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<Map.Entry<FieldMember, String>> 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<String, String> map = new Gson().fromJson(reader, new TypeToken<Map<String, String>>() {
}.getType());
@@ -93,47 +81,14 @@ public final class FieldMigratedMappingConfiguration extends MappingConfiguratio
migratedFields.add(new AbstractMap.SimpleEntry<>(new FieldMember(split[0], split[1]), newDescriptor));
});
}
}
super.setup(project, serviceManager, minecraftProvider, inputJar);
}
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;
}
@Override
protected void manipulateMappings(Project project, Path mappingsJar) throws IOException {
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());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
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)) {
} else {
Files.deleteIfExists(migratedFieldsCache);
migratedFields.clear();
if (hasSrg) {
migratedFields.addAll(generateNewFieldMigration(project, MinecraftPatchedProvider.get(project).getMinecraftPatchedIntermediateJar(), MappingsNamespace.SRG.toString(), rawTinyMappingsWithSrg).entrySet());
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(), rawTinyMappingsWithMojang).entrySet());
migratedFields.addAll(generateNewFieldMigration(project, MinecraftPatchedProvider.get(project).getMinecraftPatchedIntermediateJar(), MappingsNamespace.MOJANG.toString(), rawMappings).entrySet());
}
Map<String, String> map = new HashMap<>();
@@ -141,32 +96,42 @@ public final class FieldMigratedMappingConfiguration extends MappingConfiguratio
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);
}
if (Files.notExists(tinyMappings) || (hasSrg && Files.notExists(tinyMappingsWithSrg)) || (hasMojang && Files.notExists(tinyMappingsWithMojang))) {
Table<String, String, String> fieldDescriptorMap = HashBasedTable.create();
this.migratedFields.sort(Comparator.comparing(entry -> entry.getKey().owner + "#" + entry.getKey().field));
return migratedFields.hashCode();
}
for (Map.Entry<FieldMember, String> entry : migratedFields) {
fieldDescriptorMap.put(entry.getKey().owner, entry.getKey().field, entry.getValue());
}
@Override
public void migrate(Project project, List<MappingsEntry> entries) {
Stopwatch stopwatch = Stopwatch.createStarted();
LoomGradleExtension extension = LoomGradleExtension.get(project);
injectMigration(project, fieldDescriptorMap, rawTinyMappings, tinyMappings);
try {
updateFieldMigration(project, entries);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
if (hasSrg) {
injectMigration(project, fieldDescriptorMap, rawTinyMappingsWithSrg, tinyMappingsWithSrg);
} else if (hasMojang) {
injectMigration(project, fieldDescriptorMap, rawTinyMappingsWithMojang, tinyMappingsWithMojang);
}
project.getLogger().info(":migrated {} fields in " + stopwatch.stop(), extension.getPlatform().get().id());
}
public void updateFieldMigration(Project project, List<MappingsEntry> entries) throws IOException {
Table<String, String, String> fieldDescriptorMap = HashBasedTable.create();
for (Map.Entry<FieldMember, String> entry : migratedFields) {
fieldDescriptorMap.put(entry.getKey().owner, entry.getKey().field, entry.getValue());
}
for (MappingsEntry entry : entries) {
injectMigration(project, fieldDescriptorMap, entry.path());
}
}
private static void injectMigration(Project project, Table<String, String, String> fieldDescriptorMap, Path source, Path out) throws IOException {
private static void injectMigration(Project project, Table<String, String, String> 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 @@ public final class FieldMigratedMappingConfiguration extends MappingConfiguratio
}
}
try (Writer writer = Files.newBufferedWriter(out, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
try (Writer writer = Files.newBufferedWriter(path, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
mappings.accept(new Tiny2FileWriter(writer, false));
}
}

View File

@@ -0,0 +1,116 @@
/*
* 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.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.List;
import com.google.common.base.Stopwatch;
import org.gradle.api.Project;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration;
public final class ForgeMigratedMappingConfiguration extends MappingConfiguration {
private final List<MappingsMigrator> 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<MappingsMigrator.MappingsEntry> 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);
}
}

View File

@@ -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<MappingsEntry> entries) throws IOException;
record MappingsEntry(Path path) {
}
}

View File

@@ -0,0 +1,230 @@
/*
* 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<Pair<String, String>> methodsToRemove;
@Override
public long setup(Project project, MinecraftProvider minecraftProvider, Path cache, Path rawMappings, boolean hasSrg, boolean hasMojang) throws IOException {
Path cacheFile = cache.resolve("method-inhertiance-migrator.json");
if (!minecraftProvider.refreshDeps() && Files.exists(cacheFile)) {
try (BufferedReader reader = Files.newBufferedReader(cacheFile)) {
List<Pair<String, String>> list = new Gson().fromJson(reader, new TypeToken<List<Pair<String, String>>>() {
}.getType());
methodsToRemove = new HashSet<>(list);
}
} else {
Files.deleteIfExists(cacheFile);
LoomGradleExtension extension = LoomGradleExtension.get(project);
Path patchedIntermediateJar = MinecraftPatchedProvider.get(project).getMinecraftPatchedIntermediateJar();
List<Path> 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<MappingsEntry> 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<Pair<String, String>> prepareCache(Logger logger, Path rawMappings, List<Path> 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));
}
Multimap<String, String> classInheritanceMap = Multimaps.newSetMultimap(new HashMap<>(), LinkedHashSet::new);
Set<MethodKey> 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<? extends Path>) 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<String, String> newClassInheritanceMap = Multimaps.newSetMultimap(new HashMap<>(), LinkedHashSet::new);
classInheritanceMap.asMap().entrySet().stream().map(entry -> {
// Collect all super classes, and their super classes, and so on
Set<String> allSuperClasses = new HashSet<>();
for (String superClass : entry.getValue()) {
collectSuperClasses(superClass, new HashSet<>(), allSuperClasses, classInheritanceMap);
}
return Map.entry(entry.getKey(), allSuperClasses);
})
.forEach(e -> newClassInheritanceMap.putAll(e.getKey(), e.getValue()));
Set<Pair<String, String>> methodsToRemove = new HashSet<>();
Multimap<MethodKey, Pair<String, String>> overridenIntermedaries = 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 : newClassInheritanceMap.get(method.className())) {
if (methods.contains(new MethodKey(superClass, method.name(), method.descriptor()))) {
if (mappings.getClass(superClass) == null) {
String intermediaryDesc = aMethod.getDesc(MappingsNamespace.INTERMEDIARY.toString());
overridenIntermedaries.put(new MethodKey(superClass, method.name(), method.descriptor()), new Pair<>(intermediaryName, intermediaryDesc));
}
}
}
}
for (Map.Entry<MethodKey, Collection<Pair<String, String>>> entry : overridenIntermedaries.asMap().entrySet()) {
if (entry.getValue().size() >= 2) {
// We should remove these names from the mappings
for (Pair<String, String> pair : entry.getValue()) {
methodsToRemove.add(pair);
logger.info("Removing method {}{} from the mappings", pair.left(), pair.right());
}
break;
}
}
return methodsToRemove;
}
private static void collectSuperClasses(String className, Set<String> travelled, Set<String> allSuperClasses, Multimap<String, String> 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<String, String> classInheritanceMap;
private final Set<MethodKey> methods;
private String lastClass = null;
Visitor(int api, Multimap<String, String> classInheritanceMap, Set<MethodKey> 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) {
}
}

View File

@@ -57,7 +57,7 @@ import net.fabricmc.loom.LoomGradleExtension;
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 class MappingConfiguration {
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 class MappingConfiguration {
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 @@ public class MappingConfiguration {
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;
}