diff --git a/src/main/java/fr/catcore/modremapperapi/remapping/RemapUtil.java b/src/main/java/fr/catcore/modremapperapi/remapping/RemapUtil.java index 2eaed84..235651c 100644 --- a/src/main/java/fr/catcore/modremapperapi/remapping/RemapUtil.java +++ b/src/main/java/fr/catcore/modremapperapi/remapping/RemapUtil.java @@ -27,6 +27,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -57,12 +58,31 @@ public static void init(List sourceNamespace = remapper.getSourceNamespace(); + + if (sourceNamespace.isPresent()) { + MappingsUtilsImpl.setSourceNamespace(sourceNamespace.get()); + break; + } + } + + for (ModRemapper remapper : remappers) { + Optional> mappings = remapper.getExtraMapping(); + + if (mappings.isPresent()) { + MappingsUtilsImpl.loadExtraMappings(mappings.get().get()); + break; + } + } + MINECRAFT_TREE = MappingsUtilsImpl.getMinecraftMappings(); + LOADER_TREE = generateMappings(); MappingsUtilsImpl.addMappingsToContext(LOADER_TREE); for (MappingTree.ClassMapping classView : MINECRAFT_TREE.getClasses()) { - String className = classView.getName("official"); + String className = classView.getName(MappingsUtilsImpl.getSourceNamespace()); if (className != null) { MC_CLASS_NAMES.add(className); @@ -504,7 +524,7 @@ private static TinyRemapper makeRemapper(MappingTree... trees) { } for (MappingTree tree : trees) { - builder.withMappings(MappingsUtilsImpl.createProvider(tree, "official", MappingsUtils.getTargetNamespace())); + builder.withMappings(MappingsUtilsImpl.createProvider(tree, MappingsUtilsImpl.getSourceNamespace(), MappingsUtils.getTargetNamespace())); } MRAApplyVisitor preApplyVisitor = new MRAApplyVisitor(); @@ -530,7 +550,11 @@ private static TinyRemapper makeRemapper(MappingTree... trees) { TinyRemapper remapper = builder.build(); - MappingsUtils.addMinecraftJar(remapper); + try { + MappingsUtils.addMinecraftJar(remapper); + } catch (IOException e) { + throw new RuntimeException(e); + } for (ModRemapper modRemapper : remappers) { List libraries = new ArrayList<>(); @@ -562,11 +586,11 @@ private static void remapFiles(TinyRemapper remapper, Map paths) { List resourceRemappers = new ArrayList<>(NonClassCopyMode.FIX_META_INF.remappers); resourceRemappers.add(new RefmapRemapper()); - applyRemapper(remapper, paths, outputConsumerPaths, resourceRemappers); + applyRemapper(remapper, paths, outputConsumerPaths, resourceRemappers, true); } @ApiStatus.Internal - public static void applyRemapper(TinyRemapper remapper, Map paths, List outputConsumerPaths, List resourceRemappers) { + public static void applyRemapper(TinyRemapper remapper, Map paths, List outputConsumerPaths, List resourceRemappers, boolean analyzeMapping) { try { Map tagMap = new HashMap<>(); @@ -593,7 +617,7 @@ public static void applyRemapper(TinyRemapper remapper, Map paths, L Constants.MAIN_LOGGER.debug("Done 1!"); } - MappingsUtilsImpl.completeMappingsFromTr(remapper.getEnvironment()); + if (analyzeMapping) MappingsUtilsImpl.completeMappingsFromTr(remapper.getEnvironment()); } catch (Exception e) { remapper.finish(); outputConsumerPaths.forEach(o -> { diff --git a/src/main/java/fr/catcore/modremapperapi/utils/MappingsUtils.java b/src/main/java/fr/catcore/modremapperapi/utils/MappingsUtils.java index d48bdb6..28fac2c 100644 --- a/src/main/java/fr/catcore/modremapperapi/utils/MappingsUtils.java +++ b/src/main/java/fr/catcore/modremapperapi/utils/MappingsUtils.java @@ -15,14 +15,12 @@ import java.io.*; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import static fr.catcore.modremapperapi.remapping.RemapUtil.getRemapClasspath; public class MappingsUtils { + @Deprecated public static String getNativeNamespace() { if (ModRemappingAPI.BABRIC) { return FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT ? "client" : "server"; @@ -66,15 +64,18 @@ private static IMappingProvider createBackwardProvider(MappingTree mappings) { return MappingsUtilsImpl.createProvider(mappings, getTargetNamespace(), "official"); } - private static Path[] getMinecraftJar() throws IOException { - Path[] originalClassPath = getRemapClasspath().toArray(new Path[0]); + private static Path[] getMinecraftJar(List sourcePaths, String src, String target) throws IOException { + Path[] originalClassPath = sourcePaths.toArray(new Path[0]); Map paths = new HashMap<>(); for (Path path : originalClassPath) { Constants.MAIN_LOGGER.info(path.toString()); - paths.put(path, new File(Constants.LIB_FOLDER, path.toFile().getName()).toPath()); + paths.put(path, new File( + new File(Constants.LIB_FOLDER, target), + path.toFile().getName()).toPath() + ); paths.get(path).toFile().delete(); } @@ -85,26 +86,28 @@ private static Path[] getMinecraftJar() throws IOException { .propagatePrivate(true) .ignoreConflicts(true) .fixPackageAccess(true) - .withMappings(createBackwardProvider(getMinecraftMappings())); + .withMappings( + MappingsUtilsImpl.createProvider(MappingsUtilsImpl.getMinecraftMappings(), src, target) + ); TinyRemapper remapper = builder.build(); - Constants.MAIN_LOGGER.info("Remapping minecraft jar back to obfuscated!"); + Constants.MAIN_LOGGER.info("Remapping minecraft jar from " + src + " to " + target + "!"); List outputConsumerPaths = new ArrayList<>(); List resourceRemappers = new ArrayList<>(NonClassCopyMode.FIX_META_INF.remappers); - RemapUtil.applyRemapper(remapper, paths, outputConsumerPaths, resourceRemappers); + RemapUtil.applyRemapper(remapper, paths, outputConsumerPaths, resourceRemappers, false); return paths.values().toArray(new Path[0]); } @ApiStatus.Internal - public static void addMinecraftJar(TinyRemapper remapper) { + public static void addMinecraftJar(TinyRemapper remapper) throws IOException { if (FabricLoader.getInstance().isDevelopmentEnvironment()) { try { - remapper.readClassPathAsync(getMinecraftJar()); + remapper.readClassPathAsync(getMinecraftJar(getRemapClasspath(), getTargetNamespace(), "official")); } catch (IOException e) { throw new RuntimeException("Failed to populate default remap classpath", e); } @@ -139,7 +142,10 @@ public static void addMinecraftJar(TinyRemapper remapper) { if (realmsJar instanceof Path) list.add((Path) realmsJar); - for (Path path : list) { + for (Path path : + !Objects.equals(MappingsUtilsImpl.getSourceNamespace(), "official") ? + getMinecraftJar(list, "official", MappingsUtilsImpl.getSourceNamespace()) + : list.toArray(new Path[0])) { Constants.MAIN_LOGGER.debug("Appending '%s' to remapper classpath", path); remapper.readClassPathAsync(path); } diff --git a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/api/v1/ModRemapper.java b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/api/v1/ModRemapper.java index 863c494..93e11a2 100644 --- a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/api/v1/ModRemapper.java +++ b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/api/v1/ModRemapper.java @@ -2,8 +2,10 @@ import net.fabricmc.api.EnvType; +import java.io.InputStream; import java.util.List; import java.util.Optional; +import java.util.function.Supplier; public interface ModRemapper { String[] getJarFolders(); @@ -20,4 +22,12 @@ default Optional getDefaultPackage() { } default void afterRemap() {} + + default Optional getSourceNamespace() { + return Optional.empty(); + } + + default Optional> getExtraMapping() { + return Optional.empty(); + } } diff --git a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/MappingsUtilsImpl.java b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/MappingsUtilsImpl.java index 56185d3..b536506 100644 --- a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/MappingsUtilsImpl.java +++ b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/MappingsUtilsImpl.java @@ -2,6 +2,7 @@ import fr.catcore.wfvaio.WhichFabricVariantAmIOn; import io.github.fabriccompatibiltylayers.modremappingapi.api.MappingUtils; +import io.github.fabriccompatibiltylayers.modremappingapi.impl.utils.MappingTreeHelper; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.impl.launch.MappingConfiguration; import net.fabricmc.loader.impl.util.log.Log; @@ -28,6 +29,7 @@ import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; @@ -41,9 +43,13 @@ public class MappingsUtilsImpl { private static boolean initialized = false; private static MappingTree VANILLA_MAPPINGS; - private static MappingTree MINECRAFT_MAPPINGS; + private static VisitableMappingTree MINECRAFT_MAPPINGS; private static VisitableMappingTree FULL_MAPPINGS = new MemoryMappingTree(); + private static String sourceNamespace = "official"; + + private static MappingTree EXTRA_MAPPINGS; + @ApiStatus.Internal public static MappingTree getVanillaMappings() { loadMappings(); @@ -58,6 +64,54 @@ public static MappingTree getMinecraftMappings() { return MINECRAFT_MAPPINGS; } + @ApiStatus.Internal + public static String getSourceNamespace() { + return sourceNamespace; + } + + @ApiStatus.Internal + public static void setSourceNamespace(String sourceNamespace) { + MappingsUtilsImpl.sourceNamespace = sourceNamespace; + } + + @ApiStatus.Internal + public static void loadExtraMappings(InputStream stream) { + try { + EXTRA_MAPPINGS = loadMappings(stream); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @ApiStatus.Internal + public static MemoryMappingTree loadMappings(InputStream stream) throws IOException { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { + long time = System.currentTimeMillis(); + MemoryMappingTree mappingTree = new MemoryMappingTree(); + + // We will only ever need to read tiny here + // so to strip the other formats from the included copy of mapping IO, don't use MappingReader.read() + reader.mark(4096); + final MappingFormat format = MappingReader.detectFormat(reader); + reader.reset(); + + switch (format) { + case TINY_FILE: + Tiny1FileReader.read(reader, mappingTree); + break; + case TINY_2_FILE: + Tiny2FileReader.read(reader, mappingTree); + break; + default: + throw new UnsupportedOperationException("Unsupported mapping format: " + format); + } + + Log.debug(LogCategory.MAPPINGS, "Loading mappings took %d ms", System.currentTimeMillis() - time); + + return mappingTree; + } + } + private static void loadMappings() { if (initialized) return; @@ -67,31 +121,7 @@ private static void loadMappings() { try { URLConnection connection = url.openConnection(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { - long time = System.currentTimeMillis(); - MemoryMappingTree mappingTree = new MemoryMappingTree(); - - // We will only ever need to read tiny here - // so to strip the other formats from the included copy of mapping IO, don't use MappingReader.read() - reader.mark(4096); - final MappingFormat format = MappingReader.detectFormat(reader); - reader.reset(); - - switch (format) { - case TINY_FILE: - Tiny1FileReader.read(reader, mappingTree); - break; - case TINY_2_FILE: - Tiny2FileReader.read(reader, mappingTree); - break; - default: - throw new UnsupportedOperationException("Unsupported mapping format: " + format); - } - - Log.debug(LogCategory.MAPPINGS, "Loading mappings took %d ms", System.currentTimeMillis() - time); - - VANILLA_MAPPINGS = mappingTree; - } + VANILLA_MAPPINGS = loadMappings(connection.getInputStream()); } catch (IOException | ZipError e) { throw new RuntimeException("Error reading "+url, e); } @@ -143,22 +173,29 @@ private static void adaptVanillaMappings() { break; } - MappingVisitor visitor = getMappingVisitor(switchNamespace, renames); + MemoryMappingTree tempTree = new MemoryMappingTree(); + MappingVisitor visitor = getMappingVisitor(tempTree, switchNamespace, renames); try { VANILLA_MAPPINGS.accept(visitor); + + if (EXTRA_MAPPINGS == null) { + MINECRAFT_MAPPINGS.accept(tempTree); + } else { + MappingTreeHelper.mergeIntoNew(MINECRAFT_MAPPINGS, tempTree, EXTRA_MAPPINGS); + } } catch (IOException e) { e.printStackTrace(); } } - private static @NotNull MappingVisitor getMappingVisitor(boolean switchNamespace, Map renames) { + private static @NotNull MappingVisitor getMappingVisitor(MemoryMappingTree tempTree, boolean switchNamespace, Map renames) { List targetNamespace = new ArrayList<>(); targetNamespace.add("intermediary"); if (VANILLA_MAPPINGS.getDstNamespaces().contains("named")) targetNamespace.add("named"); - MappingVisitor visitor = (MappingVisitor) MINECRAFT_MAPPINGS; + MappingVisitor visitor = tempTree; if (switchNamespace) { visitor = new MappingSourceNsSwitch( @@ -181,7 +218,7 @@ public static IMappingProvider createProvider(MappingTree mappings, String from, @ApiStatus.Internal public static void initializeMappingTree(MappingVisitor mappingVisitor) throws IOException { - initializeMappingTree(mappingVisitor, "official", "intermediary"); + initializeMappingTree(mappingVisitor, getSourceNamespace(), "intermediary"); } @ApiStatus.Internal @@ -191,24 +228,20 @@ public static void initializeMappingTree(MappingVisitor mappingVisitor, String s List namespaces = new ArrayList<>(); namespaces.add(target); - if (getMinecraftMappings().getDstNamespaces().contains("named")) { - namespaces.add("named"); - } - mappingVisitor.visitNamespaces(src, namespaces); } @ApiStatus.Internal - public static void addMappingsToContext(MappingTreeView mappingTreeView) { + public static void addMappingsToContext(MappingTree mappingTreeView) { try { - mappingTreeView.accept(FULL_MAPPINGS); + MappingTreeHelper.merge(FULL_MAPPINGS, mappingTreeView); } catch (IOException e) { e.printStackTrace(); } } public static void completeMappingsFromTr(TrEnvironment trEnvironment) { - int srcNamespace = FULL_MAPPINGS.getNamespaceId("official"); + int srcNamespace = FULL_MAPPINGS.getNamespaceId(getSourceNamespace()); int targetNamespace = FULL_MAPPINGS.getNamespaceId(getTargetNamespace()); Map> classMembers = new HashMap<>(); @@ -307,7 +340,7 @@ public ExtendedClassMember(String name, @Nullable String desc, String owner) { } public static String mapClass(String className) { - int srcNamespace = FULL_MAPPINGS.getNamespaceId("official"); + int srcNamespace = FULL_MAPPINGS.getNamespaceId(getSourceNamespace()); int targetNamespace = FULL_MAPPINGS.getNamespaceId(getTargetNamespace()); return FULL_MAPPINGS.mapClassName(className, srcNamespace, targetNamespace); @@ -315,13 +348,13 @@ public static String mapClass(String className) { public static String unmapClass(String className) { int srcNamespace = FULL_MAPPINGS.getNamespaceId(getTargetNamespace()); - int targetNamespace = FULL_MAPPINGS.getNamespaceId("official"); + int targetNamespace = FULL_MAPPINGS.getNamespaceId(getSourceNamespace()); return FULL_MAPPINGS.mapClassName(className, srcNamespace, targetNamespace); } public static MappingUtils.ClassMember mapField(String className, String fieldName, @Nullable String fieldDesc) { - int srcNamespace = FULL_MAPPINGS.getNamespaceId("official"); + int srcNamespace = FULL_MAPPINGS.getNamespaceId(getSourceNamespace()); int targetNamespace = FULL_MAPPINGS.getNamespaceId(getTargetNamespace()); MappingTree.FieldMapping fieldMapping = FULL_MAPPINGS.getField(className, fieldName, fieldDesc, srcNamespace); @@ -330,7 +363,7 @@ public static MappingUtils.ClassMember mapField(String className, String fieldNa } public static MappingUtils.ClassMember mapFieldFromRemappedClass(String className, String fieldName, @Nullable String fieldDesc) { - int srcNamespace = FULL_MAPPINGS.getNamespaceId("official"); + int srcNamespace = FULL_MAPPINGS.getNamespaceId(getSourceNamespace()); int targetNamespace = FULL_MAPPINGS.getNamespaceId(getTargetNamespace()); MappingTree.ClassMapping classMapping = FULL_MAPPINGS.getClass(className, targetNamespace); @@ -341,7 +374,7 @@ public static MappingUtils.ClassMember mapFieldFromRemappedClass(String classNam } public static MappingUtils.ClassMember mapMethod(String className, String methodName, String methodDesc) { - int srcNamespace = FULL_MAPPINGS.getNamespaceId("official"); + int srcNamespace = FULL_MAPPINGS.getNamespaceId(getSourceNamespace()); int targetNamespace = FULL_MAPPINGS.getNamespaceId(getTargetNamespace()); MappingTree.MethodMapping methodMapping = FULL_MAPPINGS.getMethod(className, methodName, methodDesc, srcNamespace); @@ -355,7 +388,7 @@ public static MappingUtils.ClassMember mapMethod(String className, String method } public static MappingUtils.ClassMember mapMethodFromRemappedClass(String className, String methodName, String methodDesc) { - int srcNamespace = FULL_MAPPINGS.getNamespaceId("official"); + int srcNamespace = FULL_MAPPINGS.getNamespaceId(getSourceNamespace()); int targetNamespace = FULL_MAPPINGS.getNamespaceId(getTargetNamespace()); MappingTree.ClassMapping classMapping = FULL_MAPPINGS.getClass(className, targetNamespace); @@ -395,7 +428,7 @@ private static MappingUtils.ClassMember mapMember(String memberName, @Nullable S } public static MappingUtils.ClassMember mapField(Class owner, String fieldName) { - int srcNamespace = FULL_MAPPINGS.getNamespaceId("official"); + int srcNamespace = FULL_MAPPINGS.getNamespaceId(getSourceNamespace()); int targetNamespace = FULL_MAPPINGS.getNamespaceId(getTargetNamespace()); MappingTree.ClassMapping classMapping = FULL_MAPPINGS.getClass(owner.getName().replace(".", "/"), targetNamespace); @@ -417,7 +450,7 @@ public static MappingUtils.ClassMember mapField(Class owner, String fieldName public static MappingUtils.ClassMember mapMethod(Class owner, String methodName, Class[] parameterTypes) { String argDesc = classTypeToDescriptor(parameterTypes); - int srcNamespace = FULL_MAPPINGS.getNamespaceId("official"); + int srcNamespace = FULL_MAPPINGS.getNamespaceId(getSourceNamespace()); int targetNamespace = FULL_MAPPINGS.getNamespaceId(getTargetNamespace()); MappingTree.ClassMapping classMapping = FULL_MAPPINGS.getClass(owner.getName().replace(".", "/"), targetNamespace); @@ -456,7 +489,7 @@ private static String classTypeToDescriptor(Class[] classTypes) { } public static String mapDescriptor(String desc) { - int srcNamespace = FULL_MAPPINGS.getNamespaceId("official"); + int srcNamespace = FULL_MAPPINGS.getNamespaceId(getSourceNamespace()); int targetNamespace = FULL_MAPPINGS.getNamespaceId(getTargetNamespace()); return FULL_MAPPINGS.mapDesc(desc, srcNamespace, targetNamespace); diff --git a/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/utils/MappingTreeHelper.java b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/utils/MappingTreeHelper.java new file mode 100644 index 0000000..c0bd9ad --- /dev/null +++ b/src/main/java/io/github/fabriccompatibiltylayers/modremappingapi/impl/utils/MappingTreeHelper.java @@ -0,0 +1,66 @@ +package io.github.fabriccompatibiltylayers.modremappingapi.impl.utils; + +import net.fabricmc.mappingio.MappingVisitor; +import net.fabricmc.mappingio.adapter.MappingDstNsReorder; +import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; +import net.fabricmc.mappingio.tree.MappingTree; +import net.fabricmc.mappingio.tree.MemoryMappingTree; +import net.fabricmc.mappingio.tree.VisitOrder; +import net.fabricmc.mappingio.tree.VisitableMappingTree; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class MappingTreeHelper { + public static void mergeIntoNew(VisitableMappingTree result, MappingTree left, MappingTree right) throws IOException { + assert Objects.equals(left.getSrcNamespace(), right.getSrcNamespace()); + + result.visitHeader(); + + List dstNamespaces = new ArrayList<>(left.getDstNamespaces()); + + for (String dstNamespace : right.getDstNamespaces()) { + if (!dstNamespaces.contains(dstNamespace)) { + dstNamespaces.add(dstNamespace); + } + } + + result.visitNamespaces(left.getSrcNamespace(), dstNamespaces); + result.visitEnd(); + + MemoryMappingTree reorderedLeft = new MemoryMappingTree(); + left.accept(new MappingDstNsReorder(reorderedLeft, dstNamespaces)); + MemoryMappingTree reorderedRight = new MemoryMappingTree(); + right.accept(new MappingDstNsReorder(reorderedRight, dstNamespaces)); + + result.accept(reorderedLeft, VisitOrder.createByName()); + result.accept(reorderedRight, VisitOrder.createByName()); + } + + public static void merge(VisitableMappingTree main, MappingTree additional) throws IOException { + if (!Objects.equals(additional.getSrcNamespace(), main.getSrcNamespace())) { + MemoryMappingTree reorder = new MemoryMappingTree(); + + MappingVisitor visitor = new MappingSourceNsSwitch(reorder, main.getSrcNamespace()); + + if (!additional.getDstNamespaces().contains(main.getSrcNamespace())) { + List dstNamespaces = new ArrayList<>(additional.getDstNamespaces()); + dstNamespaces.add(main.getSrcNamespace()); + + visitor = new MappingDstNsReorder(reorder, dstNamespaces); + } + + additional.accept(visitor); + + additional = reorder; + } + + MemoryMappingTree reordered = new MemoryMappingTree(); + + additional.accept(new MappingDstNsReorder(reordered, main.getDstNamespaces())); + + reordered.accept(main, VisitOrder.createByInputOrder()); + } +}