mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-28 04:07:01 -05:00
Merge branch 'dev/1.11' into exp/1.12
This commit is contained in:
@@ -35,7 +35,8 @@ import java.util.Set;
|
||||
import dev.architectury.loom.forge.ModDirTransformerDiscovererPatch;
|
||||
import dev.architectury.loom.forge.RemapObjectHolderVisitor;
|
||||
import dev.architectury.loom.mappings.ForgeMappingsMerger;
|
||||
import dev.architectury.loom.neoforge.LaunchHandlerPatcher;
|
||||
import dev.architectury.loom.mappings.MappingOption;
|
||||
import dev.architectury.loom.neoforge.StringConstantPatcher;
|
||||
import dev.architectury.loom.util.ClassVisitorUtil;
|
||||
import dev.architectury.loom.util.PropertyUtil;
|
||||
import org.gradle.api.Project;
|
||||
@@ -52,13 +53,19 @@ import net.fabricmc.loom.configuration.mods.ModConfigurationRemapper;
|
||||
import net.fabricmc.loom.configuration.mods.dependency.LocalMavenHelper;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.GradleMappingContext;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.TinyMappingsService;
|
||||
import net.fabricmc.loom.util.Checksum;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.ExceptionUtil;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
import net.fabricmc.loom.util.service.ScopedServiceFactory;
|
||||
import net.fabricmc.loom.util.service.ServiceFactory;
|
||||
import net.fabricmc.mappingio.tree.MappingTree;
|
||||
import net.fabricmc.mappingio.tree.MemoryMappingTree;
|
||||
|
||||
public class ForgeLibrariesProvider {
|
||||
private static final String FML_PATCH_VERSION = "-loom-patch-v1";
|
||||
|
||||
private static final String FML_LOADER_GROUP = "net.minecraftforge";
|
||||
private static final String FML_LOADER_NAME = "fmlloader";
|
||||
private static final String FANCYML_LOADER_GROUP = "net.neoforged.fancymodloader";
|
||||
@@ -68,6 +75,9 @@ public class ForgeLibrariesProvider {
|
||||
private static final String FORGE_MOD_DIR_TRANSFORMER_DISCOVERER_FILE = "net/minecraftforge/fml/loading/ModDirTransformerDiscoverer.class";
|
||||
private static final String NEOFORGE_OBJECT_HOLDER_FILE = "net/neoforged/fml/common/asm/ObjectHolderDefinalize.class";
|
||||
private static final String NEOFORGE_LAUNCH_HANDLER_FILE = "net/neoforged/fml/loading/targets/CommonUserdevLaunchHandler.class";
|
||||
private static final String NEOFORGE_LOADER_FILE = "net/neoforged/fml/loading/FMLLoader.class";
|
||||
private static final String NEOFORGE_GAME_LOCATOR_FILE = "net/neoforged/fml/loading/moddiscovery/locators/GameLocator.class";
|
||||
private static final String NEOFORGE_REQUIRED_SYSTEM_FILES_FILE = "net/neoforged/fml/loading/moddiscovery/locators/RequiredSystemFiles.class";
|
||||
|
||||
public static void provide(MappingConfiguration mappingConfiguration, Project project) throws Exception {
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
@@ -126,14 +136,14 @@ public class ForgeLibrariesProvider {
|
||||
|
||||
if (isFML || isFancyML) {
|
||||
// If FML, remap it.
|
||||
try {
|
||||
try (var serviceFactory = new ScopedServiceFactory()) {
|
||||
if (isFML) {
|
||||
project.getLogger().info(":remapping FML loader");
|
||||
} else if (isFancyML) {
|
||||
project.getLogger().info(":remapping FancyML loader");
|
||||
}
|
||||
|
||||
dep = remapFmlLoader(project, artifact, mappingConfiguration);
|
||||
dep = remapFmlLoader(project, serviceFactory, artifact, mappingConfiguration);
|
||||
} catch (IOException e) {
|
||||
throw ExceptionUtil.createDescriptiveWrapper(RuntimeException::new, "Could not remap FML", e);
|
||||
}
|
||||
@@ -152,13 +162,13 @@ public class ForgeLibrariesProvider {
|
||||
}
|
||||
|
||||
// Returns a Gradle dependency notation.
|
||||
private static Object remapFmlLoader(Project project, ResolvedArtifact artifact, MappingConfiguration mappingConfiguration) throws IOException {
|
||||
private static Object remapFmlLoader(Project project, ServiceFactory serviceFactory, ResolvedArtifact artifact, MappingConfiguration mappingConfiguration) throws IOException {
|
||||
final LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
|
||||
// A hash of the current mapping configuration. The transformations only need to be done once per mapping set.
|
||||
// While the mappings ID is definitely valid in file names, splitting MC versions parts into nested directories
|
||||
// isn't good.
|
||||
final String mappingHash = Checksum.of(mappingConfiguration.mappingsIdentifier()).sha256().hex();
|
||||
final String mappingHash = Checksum.of(mappingConfiguration.mappingsIdentifier() + FML_PATCH_VERSION).sha256().hex();
|
||||
|
||||
// Resolve the inputs and outputs.
|
||||
final ModuleVersionIdentifier id = artifact.getModuleVersion().getId();
|
||||
@@ -172,6 +182,9 @@ public class ForgeLibrariesProvider {
|
||||
final Path inputJar = artifact.getFile().toPath();
|
||||
final Path outputJar = mavenHelper.getOutputFile(null);
|
||||
|
||||
final TinyMappingsService mappingsService = mappingConfiguration.getMappingsService(project, serviceFactory, MappingOption.DEFAULT);
|
||||
final MappingTree mappings = mappingsService.getMappingTree();
|
||||
|
||||
// Modify jar.
|
||||
if (!Files.exists(outputJar) || extension.refreshDeps()) {
|
||||
mavenHelper.copyToMaven(inputJar, null);
|
||||
@@ -185,7 +198,7 @@ public class ForgeLibrariesProvider {
|
||||
}
|
||||
|
||||
if (Files.exists(fs.getPath(FORGE_MOD_DIR_TRANSFORMER_DISCOVERER_FILE))) {
|
||||
ClassVisitorUtil.rewriteClassFile(fs.getPath(FORGE_MOD_DIR_TRANSFORMER_DISCOVERER_FILE), ModDirTransformerDiscovererPatch::new);
|
||||
ClassVisitorUtil.rewriteClassFile(fs.getPath(FORGE_MOD_DIR_TRANSFORMER_DISCOVERER_FILE), true, ModDirTransformerDiscovererPatch::new);
|
||||
}
|
||||
|
||||
if (Files.exists(fs.getPath(NEOFORGE_OBJECT_HOLDER_FILE))) {
|
||||
@@ -193,7 +206,19 @@ public class ForgeLibrariesProvider {
|
||||
}
|
||||
|
||||
if (Files.exists(fs.getPath(NEOFORGE_LAUNCH_HANDLER_FILE))) {
|
||||
ClassVisitorUtil.rewriteClassFile(fs.getPath(NEOFORGE_LAUNCH_HANDLER_FILE), LaunchHandlerPatcher::new);
|
||||
ClassVisitorUtil.rewriteClassFile(fs.getPath(NEOFORGE_LAUNCH_HANDLER_FILE), StringConstantPatcher::forUserdevLaunchHandler);
|
||||
}
|
||||
|
||||
if (Files.exists(fs.getPath(NEOFORGE_LOADER_FILE))) {
|
||||
ClassVisitorUtil.rewriteClassFile(fs.getPath(NEOFORGE_LOADER_FILE), next -> StringConstantPatcher.forFmlLoader(next, mappings));
|
||||
}
|
||||
|
||||
if (Files.exists(fs.getPath(NEOFORGE_GAME_LOCATOR_FILE))) {
|
||||
ClassVisitorUtil.rewriteClassFile(fs.getPath(NEOFORGE_GAME_LOCATOR_FILE), next -> StringConstantPatcher.forGameLocator(next, mappings));
|
||||
}
|
||||
|
||||
if (Files.exists(fs.getPath(NEOFORGE_REQUIRED_SYSTEM_FILES_FILE))) {
|
||||
ClassVisitorUtil.rewriteClassFile(fs.getPath(NEOFORGE_REQUIRED_SYSTEM_FILES_FILE), next -> StringConstantPatcher.forRequiredSystemFiles(next, mappings));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package dev.architectury.loom.neoforge;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
/*
|
||||
* Patches the Minecraft.class check in FML's CommonUserdevLaunchHandler
|
||||
* to refer to a class that is found in any mapping set (Main.class).
|
||||
*
|
||||
* See https://github.com/architectury/architectury-loom/issues/212
|
||||
*/
|
||||
public final class LaunchHandlerPatcher extends ClassVisitor {
|
||||
private static final String INPUT_CLASS_FILE = "net/minecraft/client/Minecraft.class";
|
||||
private static final String OUTPUT_CLASS_FILE = "net/minecraft/client/main/Main.class";
|
||||
|
||||
public LaunchHandlerPatcher(ClassVisitor next) {
|
||||
super(Constants.ASM_VERSION, next);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||
return new MethodPatcher(super.visitMethod(access, name, descriptor, signature, exceptions));
|
||||
}
|
||||
|
||||
private static final class MethodPatcher extends MethodVisitor {
|
||||
MethodPatcher(MethodVisitor next) {
|
||||
super(Constants.ASM_VERSION, next);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object value) {
|
||||
if (INPUT_CLASS_FILE.equals(value)) {
|
||||
value = OUTPUT_CLASS_FILE;
|
||||
}
|
||||
|
||||
super.visitLdcInsn(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package dev.architectury.loom.neoforge;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.mappingio.tree.MappingTreeView;
|
||||
|
||||
/**
|
||||
* A class visitor that replaces a set of string constants in the processed class file(s).
|
||||
*/
|
||||
public final class StringConstantPatcher extends ClassVisitor {
|
||||
private final Map<String, String> constantChanges;
|
||||
|
||||
private static final String LAUNCH_HANDLER_INPUT_CLASS_FILE = "net/minecraft/client/Minecraft.class";
|
||||
private static final String LAUNCH_HANDLER_OUTPUT_CLASS_FILE = "net/minecraft/client/main/Main.class";
|
||||
|
||||
private static final RemapKey DETECTED_VERSION_KEY = new RemapKey("net/minecraft/DetectedVersion.class", "net/minecraft/class_3797");
|
||||
private static final RemapKey MINECRAFT_KEY = new RemapKey("net/minecraft/client/Minecraft.class", "net/minecraft/class_310");
|
||||
|
||||
private StringConstantPatcher(ClassVisitor next, Map<String, String> constantChanges) {
|
||||
super(Constants.ASM_VERSION, next);
|
||||
this.constantChanges = constantChanges;
|
||||
}
|
||||
|
||||
private StringConstantPatcher(ClassVisitor next, String from, String to) {
|
||||
this(next, Map.of(from, to));
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches the Minecraft.class check in FML's CommonUserdevLaunchHandler
|
||||
* to refer to a class that is found in any mapping set (Main.class).
|
||||
*
|
||||
* <p>See <a href="https://github.com/architectury/architectury-loom/issues/212">issue #212</a>
|
||||
*/
|
||||
public static ClassVisitor forUserdevLaunchHandler(ClassVisitor next) {
|
||||
return new StringConstantPatcher(next, LAUNCH_HANDLER_INPUT_CLASS_FILE, LAUNCH_HANDLER_OUTPUT_CLASS_FILE);
|
||||
}
|
||||
|
||||
private static ClassVisitor forRemapping(ClassVisitor next, MappingTreeView mappings, RemapKey... keys) {
|
||||
final Map<String, String> constantChanges = new HashMap<>();
|
||||
|
||||
for (RemapKey key : keys) {
|
||||
final @Nullable String target = getNamedClassName(mappings, key.intermediary);
|
||||
|
||||
if (target == null || key.constantWithClassSuffix.equals(target + ".class")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
constantChanges.put(key.constantWithClassSuffix, target + ".class");
|
||||
}
|
||||
|
||||
if (!constantChanges.isEmpty()) {
|
||||
return new StringConstantPatcher(next, constantChanges);
|
||||
} else {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable String getNamedClassName(MappingTreeView mappings, String intermediary) {
|
||||
final int intermediaryNsId = mappings.getNamespaceId(MappingsNamespace.INTERMEDIARY.toString());
|
||||
final @Nullable MappingTreeView.ClassMappingView c = mappings.getClass(intermediary, intermediaryNsId);
|
||||
return c != null ? c.getName(MappingsNamespace.NAMED.toString()) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches the DetectedVersion.class check in FML's FMLLoader
|
||||
* to remap that reference to the current deobfuscated ns.
|
||||
*
|
||||
* <p>See <a href="https://github.com/architectury/architectury-loom/issues/299">issue #299</a>
|
||||
*/
|
||||
public static ClassVisitor forFmlLoader(ClassVisitor next, MappingTreeView mappings) {
|
||||
return forRemapping(next, mappings, DETECTED_VERSION_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches the Minecraft.class check in FML's GameLocator
|
||||
* to remap that reference to the current deobfuscated ns.
|
||||
*
|
||||
* <p>See <a href="https://github.com/architectury/architectury-loom/issues/299">issue #299</a>
|
||||
*/
|
||||
public static ClassVisitor forGameLocator(ClassVisitor next, MappingTreeView mappings) {
|
||||
return forRemapping(next, mappings, MINECRAFT_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches the class checks in FML's RequiredSystemFiles
|
||||
* to remap that reference to the current deobfuscated ns.
|
||||
*
|
||||
* <p>See <a href="https://github.com/architectury/architectury-loom/issues/299">issue #299</a>
|
||||
*/
|
||||
public static ClassVisitor forRequiredSystemFiles(ClassVisitor next, MappingTreeView mappings) {
|
||||
return forRemapping(next, mappings, DETECTED_VERSION_KEY, MINECRAFT_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||
return new MethodPatcher(super.visitMethod(access, name, descriptor, signature, exceptions));
|
||||
}
|
||||
|
||||
private final class MethodPatcher extends MethodVisitor {
|
||||
MethodPatcher(MethodVisitor next) {
|
||||
super(Opcodes.ASM9, next);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object value) {
|
||||
if (value instanceof String key) {
|
||||
final @Nullable String target = constantChanges.get(key);
|
||||
|
||||
if (target != null) {
|
||||
value = target;
|
||||
}
|
||||
}
|
||||
|
||||
super.visitLdcInsn(value);
|
||||
}
|
||||
}
|
||||
|
||||
private record RemapKey(String constantWithClassSuffix, String intermediary) {
|
||||
}
|
||||
}
|
||||
@@ -14,11 +14,22 @@ import net.fabricmc.loom.util.ExceptionUtil;
|
||||
|
||||
public final class ClassVisitorUtil {
|
||||
public static void rewriteClassFile(Path path, UnaryOperator<ClassVisitor> visitorFactory) throws IOException {
|
||||
rewriteClassFile(path, false, visitorFactory);
|
||||
}
|
||||
|
||||
public static void rewriteClassFile(Path path, boolean recomputeFrames, UnaryOperator<ClassVisitor> visitorFactory) throws IOException {
|
||||
try {
|
||||
final byte[] inputBytes = Files.readAllBytes(path);
|
||||
final var reader = new ClassReader(inputBytes);
|
||||
final var writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||
reader.accept(visitorFactory.apply(writer), 0);
|
||||
final var writer = new ClassWriter(recomputeFrames ? ClassWriter.COMPUTE_FRAMES : 0);
|
||||
final ClassVisitor visitor = visitorFactory.apply(writer);
|
||||
|
||||
// If we're not doing any changes to the file, no need to process it.
|
||||
if (visitor == writer) {
|
||||
return;
|
||||
}
|
||||
|
||||
reader.accept(visitor, 0);
|
||||
final byte[] outputBytes = writer.toByteArray();
|
||||
|
||||
if (!Arrays.equals(inputBytes, outputBytes)) {
|
||||
|
||||
Reference in New Issue
Block a user