From 1e0839e5f4ce3c887299f06f21996aa9c7ea69e1 Mon Sep 17 00:00:00 2001 From: Cat Core Date: Mon, 31 Mar 2025 16:15:03 +0200 Subject: [PATCH] Implement AccessWidener remapping. Closes #13 --- build.gradle | 4 ++ .../impl/DefaultModCandidate.java | 9 --- .../modremappingapi/impl/ModCandidate.java | 16 +++-- .../impl/context/ModRemapperContext.java | 3 +- .../impl/context/v1/ModRemapperV1Context.java | 10 +-- .../impl/context/v1/V1ModDiscoverer.java | 19 +++-- .../impl/discover/BaseModDiscoverer.java | 29 +++++--- .../impl/remapper/ModTrRemapper.java | 72 ++++++++++++++++++- .../impl/remapper/RemappingFlags.java | 3 +- .../impl/remapper/TrRemapperHelper.java | 8 +++ .../modremappingapi/impl/utils/FileUtils.java | 7 +- 11 files changed, 135 insertions(+), 45 deletions(-) delete mode 100644 src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/DefaultModCandidate.java diff --git a/build.gradle b/build.gradle index 47dce98..3c7ba37 100644 --- a/build.gradle +++ b/build.gradle @@ -48,6 +48,9 @@ dependencies { shadow(runtimeOnly('net.fabricmc:tiny-remapper:0.11.0')) { transitive = false } + shadow(runtimeOnly('net.fabricmc:access-widener:2.1.0')) { + transitive = false + } shadow(runtimeOnly('com.google.code.gson:gson:2.2.4')) { transitive = false @@ -96,6 +99,7 @@ shadowJar { relocate 'net.fabricmc.mappingio', 'fr.catcore.modremapperapi.impl.lib.mappingio' relocate 'net.fabricmc.tinyremapper', 'fr.catcore.modremapperapi.impl.lib.tinyremapper' + relocate 'net.fabricmc.accesswidener', 'fr.catcore.modremapperapi.impl.lib.accesswidener' relocate 'com.google.gson', 'fr.catcore.modremapperapi.impl.lib.gson' } diff --git a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/DefaultModCandidate.java b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/DefaultModCandidate.java deleted file mode 100644 index 5028b49..0000000 --- a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/DefaultModCandidate.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.fabriccompatibiltylayers.modremappingapi.impl; - -import java.nio.file.Path; - -public class DefaultModCandidate extends ModCandidate { - public DefaultModCandidate(String modName, Path file, Path original) { - super(modName, modName, file, original); - } -} diff --git a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/ModCandidate.java b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/ModCandidate.java index 991bd28..3ba9b3f 100644 --- a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/ModCandidate.java +++ b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/ModCandidate.java @@ -1,18 +1,26 @@ package io.github.fabriccompatibiltylayers.modremappingapi.impl; +import org.jetbrains.annotations.Nullable; + import java.nio.file.Path; -public abstract class ModCandidate { +public class ModCandidate { public final String modName; - public final String modId; + public final @Nullable String accessWidenerName; public final Path file; public final Path original; - protected ModCandidate(String modName, String modId, Path file, Path original) { + public byte[] accessWidener; + + public ModCandidate(String modName, @Nullable String accessWidenerName, Path file, Path original) { this.modName = modName; - this.modId = modId; + this.accessWidenerName = accessWidenerName; this.file = file; this.original = original; } + + public ModCandidate(String modName, Path file, Path original) { + this(modName, null, file, original); + } } diff --git a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/context/ModRemapperContext.java b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/context/ModRemapperContext.java index a748708..3b3532e 100644 --- a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/context/ModRemapperContext.java +++ b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/context/ModRemapperContext.java @@ -1,6 +1,7 @@ package io.github.fabriccompatibiltylayers.modremappingapi.impl.context; import io.github.fabriccompatibiltylayers.modremappingapi.impl.LibraryHandler; +import io.github.fabriccompatibiltylayers.modremappingapi.impl.ModCandidate; import io.github.fabriccompatibiltylayers.modremappingapi.impl.mappings.MappingsRegistry; import io.github.fabriccompatibiltylayers.modremappingapi.impl.remapper.RemappingFlags; import net.fabricmc.tinyremapper.TinyRemapper; @@ -12,7 +13,7 @@ public interface ModRemapperContext { void init(); - void remapMods(Map pathMap); + void remapMods(Map pathMap); void afterRemap(); void discoverMods(boolean remapClassEdits); void gatherRemappers(); diff --git a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/context/v1/ModRemapperV1Context.java b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/context/v1/ModRemapperV1Context.java index 73697ec..b965a39 100644 --- a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/context/v1/ModRemapperV1Context.java +++ b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/context/v1/ModRemapperV1Context.java @@ -76,12 +76,12 @@ public void init() { this.mappingsRegistry.generateAdditionalMappings(); } - public void remapMods(Map pathMap) { + public void remapMods(Map pathMap) { Constants.MAIN_LOGGER.debug("Starting jar remapping!"); SoftLockFixer.preloadClasses(); TinyRemapper remapper = ModTrRemapper.makeRemapper(this); Constants.MAIN_LOGGER.debug("Remapper created!"); - ModTrRemapper.remapMods(remapper, pathMap, this.mappingsRegistry); + ModTrRemapper.remapMods(remapper, pathMap, this); Constants.MAIN_LOGGER.debug("Jar remapping done!"); this.mappingsRegistry.writeFullMappings(); @@ -94,10 +94,10 @@ public void afterRemap() { @Override public void discoverMods(boolean remapClassEdits) { - Map modPaths = this.modDiscoverer.init(remappers, remapClassEdits, this); + Map modPaths = this.modDiscoverer.init(remappers, remapClassEdits, this); - for (Path path : modPaths.keySet()) { - mappingsRegistry.addModMappings(path); + for (ModCandidate candidate : modPaths.keySet()) { + mappingsRegistry.addModMappings(candidate.original); } mappingsRegistry.generateModMappings(); diff --git a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/context/v1/V1ModDiscoverer.java b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/context/v1/V1ModDiscoverer.java index b9f24db..03b0a01 100644 --- a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/context/v1/V1ModDiscoverer.java +++ b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/context/v1/V1ModDiscoverer.java @@ -2,7 +2,6 @@ import fr.catcore.modremapperapi.utils.Constants; import io.github.fabriccompatibiltylayers.modremappingapi.api.v1.ModRemapper; -import io.github.fabriccompatibiltylayers.modremappingapi.impl.DefaultModCandidate; import io.github.fabriccompatibiltylayers.modremappingapi.impl.ModCandidate; import io.github.fabriccompatibiltylayers.modremappingapi.impl.compatibility.V0ModRemapper; import io.github.fabriccompatibiltylayers.modremappingapi.impl.discover.BaseModDiscoverer; @@ -24,7 +23,7 @@ public class V1ModDiscoverer extends BaseModDiscoverer { private final Map> excluded = new HashMap<>(); - public Map init(List modRemappers, boolean remapClassEdits, ModRemapperV1Context context) { + public Map init(List modRemappers, boolean remapClassEdits, ModRemapperV1Context context) { Set modFolders = new HashSet<>(); for (ModRemapper remapper : modRemappers) { @@ -66,11 +65,9 @@ public Map init(List modRemappers, boolean remapClassEd throw new RuntimeException(e); } - Map modPaths = mods.stream() + Map modPaths = mods.stream() .filter(entry -> Files.exists(entry.original)) - .collect(Collectors.groupingBy(entry -> entry.modId)) - .entrySet().stream() - .collect(Collectors.toMap(entry -> entry.getValue().get(0).original, entry -> entry.getValue().get(0).file)); + .collect(Collectors.toMap(entry -> entry, entry -> entry.file)); if (!remapClassEdits) { modPaths = excludeClassEdits(modPaths, mainTempDir, context.getMappingsRegistry()); @@ -81,15 +78,15 @@ public Map init(List modRemappers, boolean remapClassEd private void handleV0Excluded(List mods) throws IOException, URISyntaxException { for (ModCandidate modCandidate : mods) { - if (excluded.containsKey(modCandidate.modId)) { + if (excluded.containsKey(modCandidate.modName)) { if (Files.isDirectory(modCandidate.file)) { - for (String excluded : excluded.get(modCandidate.modId)) { + for (String excluded : excluded.get(modCandidate.modName)) { if (Files.deleteIfExists(modCandidate.file.resolve(excluded))) { Constants.MAIN_LOGGER.debug("File deleted: " + modCandidate.file.resolve(excluded)); } } } else { - FileUtils.removeEntriesFromZip(modCandidate.file, excluded.get(modCandidate.modId)); + FileUtils.removeEntriesFromZip(modCandidate.file, excluded.get(modCandidate.modName)); } } } @@ -136,7 +133,7 @@ public Optional discoverFolderMod(Path folder, Path destinationFol if (hasClasses[0]) { return Optional.of( - new DefaultModCandidate( + new ModCandidate( name, destination, folder @@ -166,7 +163,7 @@ public Optional discoverFileMod(Path file, Path destinationFolder) if (found) { return Optional.of( - new DefaultModCandidate( + new ModCandidate( modName, destinationFolder.resolve(fileName), file diff --git a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/discover/BaseModDiscoverer.java b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/discover/BaseModDiscoverer.java index c4621c6..f77c0e6 100644 --- a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/discover/BaseModDiscoverer.java +++ b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/discover/BaseModDiscoverer.java @@ -47,11 +47,12 @@ public List discoverMods(Path folder, Path destination) throws IOE return mods; } - public Map excludeClassEdits(Map modPaths, Path tempFolder, MappingsRegistry mappingsRegistry) { - Map map = new HashMap<>(); - Map convertMap = new HashMap<>(); + public Map excludeClassEdits(Map modPaths, Path tempFolder, MappingsRegistry mappingsRegistry) { + Map map = new HashMap<>(); + Map convertMap = new HashMap<>(); - for (Map.Entry entry : modPaths.entrySet()) { + for (Map.Entry entry : modPaths.entrySet()) { + ModCandidate modCandidate = entry.getKey(); Path tempDir = tempFolder.resolve(entry.getValue().getParent().getFileName().toString()); if (!Files.exists(tempDir)) { @@ -64,24 +65,30 @@ public Map excludeClassEdits(Map modPaths, Path tempFold } Path tempFile = tempDir.resolve(entry.getValue().getFileName().toString()); - map.put(tempFile, entry.getValue()); + map.put(new ModCandidate( + modCandidate.modName, + modCandidate.accessWidenerName, + modCandidate.file, + tempFile + ), entry.getValue()); convertMap.put(entry.getKey(), tempFile); } - List errored = new ArrayList<>(); + List errored = new ArrayList<>(); - for (Map.Entry entry : convertMap.entrySet()) { + for (Map.Entry entry : convertMap.entrySet()) { + ModCandidate modCandidate = entry.getKey(); try { - if (Files.isDirectory(entry.getKey())) { - FileUtils.zipFolder(entry.getKey(), entry.getValue()); + if (Files.isDirectory(modCandidate.original)) { + FileUtils.zipFolder(modCandidate.original, entry.getValue()); } else { - Files.copy(entry.getKey(), entry.getValue(), StandardCopyOption.REPLACE_EXISTING); + Files.copy(modCandidate.original, entry.getValue(), StandardCopyOption.REPLACE_EXISTING); } FileUtils.removeEntriesFromZip(entry.getValue(), mappingsRegistry.getVanillaClassNames()); } catch (IOException | URISyntaxException e) { e.printStackTrace(); - errored.add(entry.getValue()); + errored.add(modCandidate); } } diff --git a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/remapper/ModTrRemapper.java b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/remapper/ModTrRemapper.java index 72b5c1b..4e76089 100644 --- a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/remapper/ModTrRemapper.java +++ b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/remapper/ModTrRemapper.java @@ -1,11 +1,16 @@ package io.github.fabriccompatibiltylayers.modremappingapi.impl.remapper; +import io.github.fabriccompatibiltylayers.modremappingapi.impl.ModCandidate; import io.github.fabriccompatibiltylayers.modremappingapi.impl.context.ModRemapperContext; import io.github.fabriccompatibiltylayers.modremappingapi.impl.mappings.MappingTreeHelper; import io.github.fabriccompatibiltylayers.modremappingapi.impl.mappings.MappingsRegistry; import io.github.fabriccompatibiltylayers.modremappingapi.impl.remapper.minecraft.MinecraftRemapper; import io.github.fabriccompatibiltylayers.modremappingapi.impl.remapper.resource.RefmapRemapper; import io.github.fabriccompatibiltylayers.modremappingapi.impl.remapper.visitor.MixinPostApplyVisitor; +import io.github.fabriccompatibiltylayers.modremappingapi.impl.utils.FileUtils; +import net.fabricmc.accesswidener.AccessWidenerReader; +import net.fabricmc.accesswidener.AccessWidenerRemapper; +import net.fabricmc.accesswidener.AccessWidenerWriter; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.tinyremapper.NonClassCopyMode; @@ -13,10 +18,16 @@ import net.fabricmc.tinyremapper.TinyRemapper; import net.fabricmc.tinyremapper.extension.mixin.MixinExtension; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; +import org.objectweb.asm.commons.Remapper; import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Collectors; @ApiStatus.Internal public class ModTrRemapper { @@ -61,12 +72,69 @@ public static TinyRemapper makeRemapper(ModRemapperContext context) { return remapper; } - public static void remapMods(TinyRemapper remapper, Map paths, MappingsRegistry mappingsRegistry) { + public static void remapMods(TinyRemapper remapper, Map paths, ModRemapperContext context) { List outputConsumerPaths = new ArrayList<>(); List resourceRemappers = new ArrayList<>(NonClassCopyMode.FIX_META_INF.remappers); resourceRemappers.add(new RefmapRemapper()); - TrRemapperHelper.applyRemapper(remapper, paths, outputConsumerPaths, resourceRemappers, true, mappingsRegistry.getSourceNamespace(), mappingsRegistry.getTargetNamespace()); + Consumer consumer = getRemapperConsumer(paths, context); + + TrRemapperHelper.applyRemapper( + remapper, + paths.entrySet().stream().collect(Collectors.toMap(entry -> entry.getKey().original, Map.Entry::getValue)), + outputConsumerPaths, + resourceRemappers, + true, + context.getMappingsRegistry().getSourceNamespace(), + context.getMappingsRegistry().getTargetNamespace(), + consumer + ); + + if (context.getRemappingFlags().contains(RemappingFlags.ACCESS_WIDENER)) { + for (Map.Entry entry : paths.entrySet()) { + ModCandidate candidate = entry.getKey(); + Path jarPath = entry.getValue(); + + if (candidate.accessWidenerName != null && candidate.accessWidener != null) { + try (FileSystem fs = FileUtils.getJarFileSystem(jarPath)) { + Files.delete(fs.getPath(candidate.accessWidenerName)); + Files.write(fs.getPath(candidate.accessWidenerName), candidate.accessWidener); + } catch (Throwable t) { + throw new RuntimeException("Error while writing remapped access widener for '" + candidate.modName + "'", t); + } + } + } + } + } + + private static @Nullable Consumer getRemapperConsumer(Map paths, ModRemapperContext context) { + Consumer consumer = null; + + if (context.getRemappingFlags().contains(RemappingFlags.ACCESS_WIDENER)) { + consumer = (currentRemapper) -> { + for (Map.Entry entry : paths.entrySet()) { + ModCandidate candidate = entry.getKey(); + + if (candidate.accessWidenerName != null) { + try (FileSystem jarFs = FileUtils.getJarFileSystem(candidate.original)) { + candidate.accessWidener = remapAccessWidener(Files.readAllBytes(jarFs.getPath(candidate.accessWidenerName)), currentRemapper.getRemapper(), context.getMappingsRegistry().getTargetNamespace()); + } catch (Throwable t) { + throw new RuntimeException("Error while remapping access widener for '" + candidate.modName + "'", t); + } + } + } + }; + } + + return consumer; + } + + private static byte[] remapAccessWidener(byte[] data, Remapper remapper, String targetNamespace) { + AccessWidenerWriter writer = new AccessWidenerWriter(); + AccessWidenerRemapper remappingDecorator = new AccessWidenerRemapper(writer, remapper, "intermediary", targetNamespace); + AccessWidenerReader accessWidenerReader = new AccessWidenerReader(remappingDecorator); + accessWidenerReader.read(data, "intermediary"); + return writer.write(); } } diff --git a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/remapper/RemappingFlags.java b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/remapper/RemappingFlags.java index 51861d0..06446b6 100644 --- a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/remapper/RemappingFlags.java +++ b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/remapper/RemappingFlags.java @@ -4,5 +4,6 @@ @ApiStatus.Internal public enum RemappingFlags { - MIXIN + MIXIN, + ACCESS_WIDENER } diff --git a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/remapper/TrRemapperHelper.java b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/remapper/TrRemapperHelper.java index af555e8..93c666a 100644 --- a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/remapper/TrRemapperHelper.java +++ b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/remapper/TrRemapperHelper.java @@ -6,16 +6,22 @@ import net.fabricmc.tinyremapper.OutputConsumerPath; import net.fabricmc.tinyremapper.TinyRemapper; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.nio.file.Path; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; @ApiStatus.Internal public class TrRemapperHelper { public static void applyRemapper(TinyRemapper remapper, Map paths, List outputConsumerPaths, List resourceRemappers, boolean analyzeMapping, String srcNamespace, String targetNamespace) { + applyRemapper(remapper, paths, outputConsumerPaths, resourceRemappers, analyzeMapping, srcNamespace, targetNamespace, null); + } + + public static void applyRemapper(TinyRemapper remapper, Map paths, List outputConsumerPaths, List resourceRemappers, boolean analyzeMapping, String srcNamespace, String targetNamespace, @Nullable Consumer action) { try { Map tagMap = new HashMap<>(); @@ -43,6 +49,8 @@ public static void applyRemapper(TinyRemapper remapper, Map paths, L } if (analyzeMapping) ModRemappingAPIImpl.getCurrentContext().getMappingsRegistry().completeMappingsFromTr(remapper.getEnvironment(), srcNamespace); + + if (action != null) action.accept(remapper); } catch (Exception e) { remapper.finish(); outputConsumerPaths.forEach(o -> { diff --git a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/utils/FileUtils.java b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/utils/FileUtils.java index 5eb36eb..7713a63 100644 --- a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/utils/FileUtils.java +++ b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/utils/FileUtils.java @@ -233,9 +233,14 @@ private static void zipFile(File fileToZip, String fileName, ZipOutputStream zip ZIP_PROPERTIES.put("encoding", "UTF-8"); } + @ApiStatus.Internal + public static FileSystem getJarFileSystem(Path path) throws URISyntaxException, IOException { + return FileSystems.newFileSystem(new URI("jar:" + path.toUri()), ZIP_PROPERTIES); + } + @ApiStatus.Internal public static void removeEntriesFromZip(Path zipPath, List entries) throws IOException, URISyntaxException { - try (FileSystem zipfs = FileSystems.newFileSystem(new URI("jar:" + zipPath.toUri()), ZIP_PROPERTIES)) { + try (FileSystem zipfs = getJarFileSystem(zipPath)) { for (String entryName : entries) { Path entryPath = zipfs.getPath(entryName);