diff --git a/build.gradle b/build.gradle index fa15c858f..1ca2db1ea 100644 --- a/build.gradle +++ b/build.gradle @@ -84,6 +84,8 @@ dependencies { implementation ('org.ow2.asm:asm-tree:9.5') implementation ('org.ow2.asm:asm-util:9.5') implementation ('me.tongfei:progressbar:0.9.0') + implementation 'com.github.jponge:lzma-java:1.3' + runtimeOnly 'dev.architectury.architectury-pack200:dev.architectury.architectury-pack200.gradle.plugin:0.1.3' // game handling utils implementation ('net.fabricmc:stitch:0.6.2') { diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index 74f85e8f9..ac5a204a2 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -97,7 +97,7 @@ default List getMinecraftJars(MappingsNamespace mappingsNamespace) { case INTERMEDIARY -> getIntermediaryMinecraftProvider().getMinecraftJarPaths(); case OFFICIAL -> getMinecraftProvider().getMinecraftJars(); case SRG -> { - ModPlatform.assertPlatform(this, ModPlatform.FORGE, () -> "SRG jars are only available on Forge."); + ModPlatform.assertPlatform(this, () -> "SRG jars are only available on Forge.", ModPlatform.FORGE); yield getSrgMinecraftProvider().getMinecraftJarPaths(); } }; diff --git a/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java index 307fb69ac..1ac007ae4 100644 --- a/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java @@ -26,6 +26,8 @@ import java.util.List; +import net.fabricmc.loom.configuration.providers.forge.fg2.Pack200Provider; + import org.gradle.api.Action; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.provider.Property; @@ -134,6 +136,8 @@ default void mixinConfig(String... mixinConfigs) { @Deprecated(forRemoval = true) void dataGen(Action action); + Property getPack200Provider(); + /** * Data generation config. * @deprecated Removed in favor of configuring the data generator directly. diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index d8fd19a74..1b8b7956d 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -232,10 +232,21 @@ default void splitMinecraftJar() { Provider getPlatform(); + /** + * Most legacy forge functions work with this. + */ default boolean isForge() { + return getPlatform().get() == ModPlatform.FORGE || getPlatform().get() == ModPlatform.LEGACY_FORGE; + } + + default boolean isModernForge() { return getPlatform().get() == ModPlatform.FORGE; } + default boolean isLegacyForge() { + return getPlatform().get() == ModPlatform.LEGACY_FORGE; + } + default boolean isQuilt() { return getPlatform().get() == ModPlatform.QUILT; } diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index d074c698e..05aee1158 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -204,10 +204,12 @@ private synchronized void setupMinecraft(ConfigContext configContext) throws Exc if (extension.isForge()) { ForgeLibrariesProvider.provide(mappingConfiguration, project); + if (extension.isLegacyForge()) ((ForgeMinecraftProvider) minecraftProvider).getPatchedProvider().setMappingConfiguration(mappingConfiguration); ((ForgeMinecraftProvider) minecraftProvider).getPatchedProvider().provide(); } - mappingConfiguration.setupPost(project); + if (!extension.isLegacyForge()) + mappingConfiguration.setupPost(project); mappingConfiguration.applyToProject(getProject(), mappingsDep); if (extension.isForge()) { @@ -363,9 +365,9 @@ public static void setupDependencyProviders(Project project, LoomGradleExtension } if (extension.isForge()) { + dependencyProviders.addProvider(new ForgeUniversalProvider(project)); dependencyProviders.addProvider(new McpConfigProvider(project)); dependencyProviders.addProvider(new PatchProvider(project)); - dependencyProviders.addProvider(new ForgeUniversalProvider(project)); } dependencyProviders.handleDependencies(project); diff --git a/src/main/java/net/fabricmc/loom/configuration/accesstransformer/AccessTransformerJarProcessor.java b/src/main/java/net/fabricmc/loom/configuration/accesstransformer/AccessTransformerJarProcessor.java index 3371820f8..ae04f3fff 100644 --- a/src/main/java/net/fabricmc/loom/configuration/accesstransformer/AccessTransformerJarProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/accesstransformer/AccessTransformerJarProcessor.java @@ -168,6 +168,7 @@ public static void executeAt(Project project, Path input, Path output, AccessTra args.add(output.toAbsolutePath().toString()); configuration.apply(args); + project.getLogger().info(args.toString()); ForgeToolExecutor.exec(project, spec -> { spec.getMainClass().set("net.minecraftforge.accesstransformer.TransformerProcessor"); diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java index 527dea775..4a324e675 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java @@ -310,7 +310,7 @@ public void environmentVariable(String name, Object value) { * Add the {@code -XstartOnFirstThread} JVM argument when on OSX. */ public void startFirstThread() { - if (Platform.CURRENT.getOperatingSystem().isMacOS()) { + if (Platform.CURRENT.getOperatingSystem().isMacOS() && !extension.isLegacyForge()) { vmArg("-XstartOnFirstThread"); } } @@ -355,7 +355,7 @@ public void server() { *

This method can only be used on Forge. */ public void data() { - ModPlatform.assertPlatform(getExtension(), ModPlatform.FORGE, () -> "RunConfigSettings.data() is only usable on Forge."); + ModPlatform.assertPlatform(getExtension(), () -> "RunConfigSettings.data() is only usable on Forge.", ModPlatform.FORGE); environment("data"); forgeTemplate("data"); } @@ -370,7 +370,7 @@ public void data() { * @since 1.0 */ public void forgeTemplate(String templateName) { - ModPlatform.assertPlatform(getExtension(), ModPlatform.FORGE); + ModPlatform.assertPlatform(getExtension(), ModPlatform.FORGE, ModPlatform.LEGACY_FORGE); defaultMainClass(Constants.Forge.UNDETERMINED_MAIN_CLASS); // Evaluate later if Forge hasn't been resolved yet. evaluateNowOrLater(() -> { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingConfiguration.java index 72304ffb2..db33acca7 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingConfiguration.java @@ -113,7 +113,7 @@ protected void manipulateMappings(Project project, Path mappingsJar) throws IOEx if (extension.shouldGenerateSrgTiny()) { if (Files.notExists(rawTinyMappingsWithSrg) || extension.refreshDeps()) { // Merge tiny mappings with srg - SrgMerger.ExtraMappings extraMappings = SrgMerger.ExtraMappings.ofMojmapTsrg(getMojmapSrgFileIfPossible(project)); + SrgMerger.ExtraMappings extraMappings = extension.isLegacyForge() ? null : SrgMerger.ExtraMappings.ofMojmapTsrg(getMojmapSrgFileIfPossible(project)); SrgMerger.mergeSrg(getRawSrgFile(project), rawTinyMappings, rawTinyMappingsWithSrg, extraMappings, true); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeLibrariesProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeLibrariesProvider.java index a80758950..72c0b3b7f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeLibrariesProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeLibrariesProvider.java @@ -63,6 +63,9 @@ public static void provide(MappingConfiguration mappingConfiguration, Project pr // Collect all dependencies with possible relocations, such as Mixin. for (JsonElement lib : extension.getForgeUserdevProvider().getJson().get("libraries").getAsJsonArray()) { + if (extension.isLegacyForge()) { + lib = lib.getAsJsonObject().get("name"); + } String dep = null; if (lib.getAsString().startsWith("org.spongepowered:mixin:")) { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunsProvider.java index b1cb485fb..aab5d57c3 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunsProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunsProvider.java @@ -64,12 +64,28 @@ public ForgeRunsProvider(Project project, JsonObject json) { } private void readTemplates() { - for (Map.Entry entry : json.getAsJsonObject("runs").entrySet()) { + if (!extension.isLegacyForge()) { + configureRuns(json.getAsJsonObject("runs")); + } else { + configureRunsForLegacyForge(); + } + } + + private void configureRuns(JsonObject runs) { + for (Map.Entry entry : runs.entrySet()) { ForgeRunTemplate template = ForgeRunTemplate.fromJson(entry.getValue().getAsJsonObject()); templates.add(template); } } + private void configureRunsForLegacyForge() { + extension.getRunConfigs().configureEach(config -> { + // if (Constants.Forge.LAUNCH_TESTING.equals(config.getDefaultMainClass())) { + config.setDefaultMainClass(Constants.LegacyForge.LAUNCH_WRAPPER); + // } + }); + } + public NamedDomainObjectSet getTemplates() { return templates; } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java index 28ebb56cb..8cb06b148 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java @@ -25,11 +25,13 @@ package net.fabricmc.loom.configuration.providers.forge; import java.io.File; +import java.io.IOException; import java.io.Reader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.List; +import java.util.Map; import com.google.gson.Gson; import com.google.gson.JsonObject; @@ -40,11 +42,14 @@ import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.ZipUtils; +import org.gradle.api.artifacts.repositories.IvyArtifactRepository; + public class ForgeUserdevProvider extends DependencyProvider { private File userdevJar; private JsonObject json; Path joinedPatches; BinaryPatcherConfig binaryPatcherConfig; + private boolean isLegacyForge; public ForgeUserdevProvider(Project project) { super(project); @@ -59,22 +64,80 @@ public void provide(DependencyInfo dependency) throws Exception { if (!userdevJar.exists() || Files.notExists(configJson) || refreshDeps()) { File resolved = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve Forge userdev")); Files.copy(resolved.toPath(), userdevJar.toPath(), StandardCopyOption.REPLACE_EXISTING); - Files.write(configJson, ZipUtils.unpack(resolved.toPath(), "config.json")); + try { + Files.write(configJson, ZipUtils.unpack(resolved.toPath(), "config.json")); + } catch (IOException e) { + Files.write(configJson, ZipUtils.unpack(resolved.toPath(), "dev.json")); + } } try (Reader reader = Files.newBufferedReader(configJson)) { json = new Gson().fromJson(reader, JsonObject.class); } - addDependency(json.get("mcp").getAsString(), Constants.Configurations.MCP_CONFIG); - addDependency(json.get("mcp").getAsString(), Constants.Configurations.SRG); - addDependency(json.get("universal").getAsString(), Constants.Configurations.FORGE_UNIVERSAL); - - if (Files.notExists(joinedPatches)) { - Files.write(joinedPatches, ZipUtils.unpack(userdevJar.toPath(), json.get("binpatches").getAsString())); + isLegacyForge = !json.has("mcp"); + + if (!isLegacyForge) { + addDependency(json.get("mcp").getAsString(), Constants.Configurations.MCP_CONFIG); + addDependency(json.get("mcp").getAsString(), Constants.Configurations.SRG); + addDependency(json.get("universal").getAsString(), Constants.Configurations.FORGE_UNIVERSAL); + + binaryPatcherConfig = BinaryPatcherConfig.fromJson(json.getAsJsonObject("binpatcher")); + + if (Files.notExists(joinedPatches)) { + Files.write(joinedPatches, ZipUtils.unpack(userdevJar.toPath(), json.get("binpatches").getAsString())); + } + } else { + Map mcpDep = Map.of( + "group", "de.oceanlabs.mcp", + "name", "mcp", + "version", json.get("inheritsFrom").getAsString(), + "classifier", "srg", + "ext", "zip" + ); + addDependency(mcpDep, Constants.Configurations.MCP_CONFIG); + addDependency(mcpDep, Constants.Configurations.SRG); + addDependency(dependency.getDepString() + ":universal", Constants.Configurations.FORGE_UNIVERSAL); + addLegacyMCPRepo(); + + binaryPatcherConfig = BinaryPatcherConfig.fromJson(new Gson().fromJson(""" + { + "version": "net.minecraftforge:binarypatcher:1.0.12:fatjar", + "args": [ + "--clean", + "{clean}", + "--output", + "{output}", + "--apply", + "{patch}" + ] + }""", JsonObject.class)); + + if (Files.notExists(joinedPatches)) { + Files.write(joinedPatches, ZipUtils.unpack(userdevJar.toPath(), "devbinpatches.pack.lzma")); + } } + } + + private void addLegacyMCPRepo() { + getProject().getRepositories().ivy(repo -> { + // Old MCP data does not have POMs + repo.setName("LegacyMCP"); + repo.setUrl("https://maven.minecraftforge.net/"); + repo.patternLayout(layout -> { + layout.artifact("[orgPath]/[artifact]/[revision]/[artifact]-[revision](-[classifier])(.[ext])"); + // also check the zip so people do not have to explicitly specify the extension for older versions + layout.artifact("[orgPath]/[artifact]/[revision]/[artifact]-[revision](-[classifier]).zip"); + }); + repo.content(descriptor -> { + descriptor.includeGroup("de.oceanlabs.mcp"); + }); + repo.metadataSources(IvyArtifactRepository.MetadataSources::artifact); + }); + } - binaryPatcherConfig = BinaryPatcherConfig.fromJson(json.getAsJsonObject("binpatcher")); + public boolean isLegacyForge() { + return isLegacyForge; } public File getUserdevJar() { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java index 02cc5803b..bdd636956 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java @@ -24,11 +24,15 @@ package net.fabricmc.loom.configuration.providers.forge; +import java.io.BufferedWriter; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.io.UncheckedIOException; import java.nio.file.FileSystem; import java.nio.file.Files; @@ -57,6 +61,20 @@ import dev.architectury.tinyremapper.NonClassCopyMode; import dev.architectury.tinyremapper.OutputConsumerPath; import dev.architectury.tinyremapper.TinyRemapper; + +import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration; +import net.fabricmc.loom.configuration.providers.mappings.utils.AccessTransformSetMapper; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarMerger; + +import net.fabricmc.loom.util.Pair; +import net.fabricmc.loom.util.legacyforge.CoreModManagerTransformer; +import net.fabricmc.lorenztiny.TinyMappingsReader; +import net.fabricmc.mappingio.MappingReader; +import net.fabricmc.mappingio.tree.MappingTree; + +import org.cadixdev.at.AccessTransformSet; +import org.cadixdev.at.io.AccessTransformFormats; +import org.cadixdev.lorenz.MappingSet; import org.gradle.api.Project; import org.gradle.api.logging.LogLevel; import org.gradle.api.logging.Logger; @@ -110,6 +128,7 @@ public class MinecraftPatchedProvider { private Path minecraftClientExtra; private boolean dirty = false; + private MappingConfiguration mappingConfiguration; public static MinecraftPatchedProvider get(Project project) { MinecraftProvider provider = LoomGradleExtension.get(project).getMinecraftProvider(); @@ -171,19 +190,40 @@ private void checkCache() throws IOException { } } + protected void mergeJars() throws IOException { + project.getLogger().info(":merging jars"); + + File jarToMerge = minecraftProvider.getMinecraftServerJar(); + + if (minecraftProvider.getServerBundleMetadata() != null) { + minecraftProvider.extractBundledServerJar(); + jarToMerge = minecraftProvider.getMinecraftExtractedServerJar(); + } + + Objects.requireNonNull(jarToMerge, "Cannot merge null input jar?"); + + try (var jarMerger = new MinecraftJarMerger(minecraftProvider.getMinecraftClientJar(), jarToMerge, minecraftSrgJar.toFile())) { + jarMerger.enableSyntheticParamsOffset(); + jarMerger.merge(); + } + } + public void provide() throws Exception { initPatchedFiles(); checkCache(); this.dirty = false; - if (Files.notExists(minecraftSrgJar)) { this.dirty = true; try (var tempFiles = new TempFiles()) { - McpExecutor executor = createMcpExecutor(tempFiles.directory("loom-mcp")); - Path output = executor.enqueue("rename").execute(); - Files.copy(output, minecraftSrgJar); + if (getExtension().isLegacyForge()) { + mergeJars(); + } else { + McpExecutor executor = createMcpExecutor(tempFiles.directory("loom-mcp")); + Path output = executor.enqueue("rename").execute(); + Files.copy(output, minecraftSrgJar); + } } } @@ -192,6 +232,10 @@ public void provide() throws Exception { patchJars(); } + if (mappingConfiguration != null) { + mappingConfiguration.setupPost(project); + } + if (dirty || Files.notExists(minecraftPatchedSrgAtJar)) { this.dirty = true; accessTransformForge(); @@ -384,7 +428,13 @@ public static void accessTransform(Project project, Path input, Path target) thr AccessTransformerJarProcessor.executeAt(project, input, target, args -> { for (Path jar : atSources) { byte[] atBytes = ZipUtils.unpackNullable(jar, Constants.Forge.ACCESS_TRANSFORMER_PATH); - + if (atBytes == null) { + atBytes = ZipUtils.unpackNullable(jar, "forge_at.cfg"); + if (atBytes != null) { + atBytes = remapAt(extension, atBytes); + project.getLogger().info("remapped an AT!"); + } + } if (atBytes != null) { Path tmpFile = tempFiles.file("at-conf", ".cfg"); Files.write(tmpFile, atBytes, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); @@ -398,6 +448,68 @@ public static void accessTransform(Project project, Path input, Path target) thr project.getLogger().lifecycle(":access transformed minecraft in " + stopwatch.stop()); } + /** + * so, at this point, minecraft is using official (obfuscated) mappings, but the access transformers are in SRG, we need to fix that. + * @param forgeAt + */ + private static byte[] remapAt(LoomGradleExtension extension, byte[] forgeAt) throws IOException { + AccessTransformSet accessTransformSet = AccessTransformSet.create(); + AccessTransformFormats.FML.read(new InputStreamReader(new ByteArrayInputStream(forgeAt)), accessTransformSet); + MemoryMappingTree mappingTree = new MemoryMappingTree(); + MappingReader.read(extension.getMappingConfiguration().tinyMappingsWithSrg, mappingTree); + MappingSet mappingSet = new TinyMappingsReader(mappingTree, "srg", "official").read(); + accessTransformSet = AccessTransformSetMapper.remap(accessTransformSet, mappingSet); + ByteArrayOutputStream remappedOut = new ByteArrayOutputStream(); + // TODO the extra BufferedWriter wrapper and closing can be removed once https://github.com/CadixDev/at/issues/6 is fixed + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(remappedOut)); + AccessTransformFormats.FML.write(writer, accessTransformSet); + writer.close(); + var bytes = remappedOut.toByteArray(); + var g = Files.createTempFile("remapped-at", ".cfg"); + Files.write(g, bytes); + return bytes; + } + + private void patchForge(Logger logger, File input) throws Exception { + Stopwatch stopwatch = Stopwatch.createStarted(); + logger.lifecycle(":patching forge"); + + Files.copy(input.toPath(), input.toPath(), StandardCopyOption.REPLACE_EXISTING); + + // For the development environment, we need to remove the binpatches, otherwise forge will try to re-apply them + try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(input, false)) { + Files.delete(fs.get().getPath("binpatches.pack.lzma")); + } + + // Older versions of Forge rely on utility classes from log4j-core 2.0-beta9 but we'll upgrade the runtime to a + // release version (so we can use the TerminalConsoleAppender) where some of those classes have been moved from + // a `helpers` to a `utils` package. + // To allow Forge to work regardless, we'll re-package those helper classes into the forge jar. + Path log4jBeta9 = Arrays.stream(TinyRemapperHelper.getMinecraftCompileLibraries(project)) + .filter(it -> it.getFileName().toString().equals("log4j-core-2.0-beta9.jar")) + .findAny() + .orElse(null); + if (log4jBeta9 != null) { + Predicate isHelper = path -> path.startsWith("/org/apache/logging/log4j/core/helpers"); + walkFileSystems(log4jBeta9, input.toPath(), isHelper, this::copyReplacing); + } + + // While Forge will discover mods on the classpath, it won't do the same for ATs, coremods or tweakers. + // ForgeGradle "solves" this problem using a huge, gross hack (GradleForgeHacks and related classes), and only + // for ATs and coremods, not tweakers. + // No clue why FG went the hack route when it's the same project and they could have just added first-party + // support for loading both from the classpath right into Forge (it's even really simply to do). + // We'll have none of those hacks and instead patch first-party support into Forge. + ZipUtils.transform(getForgeJar().toPath(), Stream.of(new Pair<>(CoreModManagerTransformer.FILE, original -> { + ClassReader reader = new ClassReader(original); + ClassWriter writer = new ClassWriter(reader, 0); + reader.accept(new CoreModManagerTransformer(writer), 0); + return writer.toByteArray(); + }))); + + logger.lifecycle(":patched forge in " + stopwatch.stop()); + } + private void remapPatchedJar(SharedServiceManager serviceManager) throws Exception { logger.lifecycle(":remapping minecraft (TinyRemapper, srg -> official)"); Path mcInput = minecraftPatchedSrgAtJar; @@ -423,16 +535,47 @@ private void remapPatchedJar(SharedServiceManager serviceManager) throws Excepti } finally { remapper.finish(); } - copyUserdevFiles(forgeUserdevJar, mcOutput); + if (getExtension().isLegacyForge()) { + patchForge(logger, mcOutput.toFile()); + } applyLoomPatchVersion(mcOutput); } + private void patchLegacyJars() throws Exception { + Stopwatch stopwatch = Stopwatch.createStarted(); + logger.lifecycle(":patching jars"); + var minecraftServerPatchedJar = Files.createTempFile("server", ".jar"); + var minecraftClientPatchedJar = Files.createTempFile("client", ".jar"); + MinecraftProvider minecraftProvider = getExtension().getMinecraftProvider(); + PatchProvider patchProvider = getExtension().getPatchProvider(); + patchJars(minecraftProvider.getMinecraftServerJar().toPath(), minecraftServerPatchedJar, patchProvider.serverPatches); + patchJars(minecraftProvider.getMinecraftClientJar().toPath(), minecraftClientPatchedJar, patchProvider.clientPatches); + + try (var jarMerger = new MinecraftJarMerger(minecraftClientPatchedJar.toFile(), minecraftServerPatchedJar.toFile(), minecraftPatchedSrgJar.toFile())) { + jarMerger.enableSyntheticParamsOffset(); + jarMerger.merge(); + } + + copyMissingClasses(minecraftSrgJar, minecraftPatchedSrgJar); + deleteParameterNames(minecraftPatchedSrgJar); + + if (getExtension().isForgeAndNotOfficial()) { + fixParameterAnnotation(minecraftPatchedSrgJar); + } + + + logger.lifecycle(":patched jars in " + stopwatch.stop()); + } + private void patchJars() throws Exception { + if (getExtension().isLegacyForge()) { + patchLegacyJars(); + return; + } Stopwatch stopwatch = Stopwatch.createStarted(); logger.lifecycle(":patching jars"); patchJars(minecraftSrgJar, minecraftPatchedSrgJar, type.patches.apply(getExtension().getPatchProvider(), getExtension().getForgeUserdevProvider())); - copyMissingClasses(minecraftSrgJar, minecraftPatchedSrgJar); deleteParameterNames(minecraftPatchedSrgJar); @@ -577,6 +720,10 @@ public Path getMinecraftPatchedJar() { return minecraftPatchedJar; } + public void setMappingConfiguration(MappingConfiguration mappingConfiguration) { + this.mappingConfiguration = mappingConfiguration; + } + public enum Type { CLIENT_ONLY("client", "client", (patch, userdev) -> patch.clientPatches), SERVER_ONLY("server", "server", (patch, userdev) -> patch.serverPatches), diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/PatchProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/PatchProvider.java index 1a420d0f3..c0c11e5d2 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/PatchProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/PatchProvider.java @@ -24,21 +24,43 @@ package net.fabricmc.loom.configuration.providers.forge; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; +import java.util.zip.ZipEntry; +import lzma.sdk.lzma.Decoder; +import lzma.sdk.lzma.Encoder; +import lzma.streams.LzmaInputStream; +import lzma.streams.LzmaOutputStream; + +import net.fabricmc.loom.configuration.providers.forge.fg2.Pack200Provider; + +import org.apache.commons.io.IOUtils; import org.gradle.api.Project; import net.fabricmc.loom.configuration.DependencyInfo; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.FileSystemUtil; +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; + public class PatchProvider extends DependencyProvider { public Path clientPatches; public Path serverPatches; + public Path mergedPatches; public PatchProvider(Project project) { super(project); @@ -51,19 +73,28 @@ public void provide(DependencyInfo dependency) throws Exception { if (Files.notExists(clientPatches) || Files.notExists(serverPatches) || refreshDeps()) { getProject().getLogger().info(":extracting forge patches"); - Path installerJar = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve Forge installer")).toPath(); + Path installerJar = getExtension().isModernForge() + ? dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve Forge installer")).toPath() + : getExtension().getForgeUniversalProvider().getForge().toPath(); try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(installerJar, false)) { - Files.copy(fs.getPath("data", "client.lzma"), clientPatches, StandardCopyOption.REPLACE_EXISTING); - Files.copy(fs.getPath("data", "server.lzma"), serverPatches, StandardCopyOption.REPLACE_EXISTING); + if (getExtension().isModernForge()) { + Files.copy(fs.getPath("data", "client.lzma"), clientPatches, StandardCopyOption.REPLACE_EXISTING); + Files.copy(fs.getPath("data", "server.lzma"), serverPatches, StandardCopyOption.REPLACE_EXISTING); + } else { + splitAndConvertLegacyPatches(fs.getPath("binpatches.pack.lzma")); + } } } } - private void init() { + private void init() throws IOException { final Path projectCacheFolder = ForgeProvider.getForgeCache(getProject()); clientPatches = projectCacheFolder.resolve("patches-client.lzma"); serverPatches = projectCacheFolder.resolve("patches-server.lzma"); + if (getExtension().isLegacyForge()) { + mergedPatches = projectCacheFolder.resolve("patches-merged.lzma"); + } try { Files.createDirectories(projectCacheFolder); @@ -76,4 +107,105 @@ private void init() { public String getTargetConfig() { return Constants.Configurations.FORGE_INSTALLER; } + + private void splitAndConvertLegacyPatches(Path joinedLegacyPatches) throws IOException { + try (JarInputStream in = new JarInputStream(new ByteArrayInputStream(unpack200Lzma(joinedLegacyPatches))); + OutputStream clientFileOut = Files.newOutputStream(clientPatches, CREATE, TRUNCATE_EXISTING); + LzmaOutputStream clientLzmaOut = new LzmaOutputStream(clientFileOut, new Encoder()); + JarOutputStream clientJarOut = new JarOutputStream(clientLzmaOut); + OutputStream serverFileOut = Files.newOutputStream(serverPatches, CREATE, TRUNCATE_EXISTING); + LzmaOutputStream serverLzmaOut = new LzmaOutputStream(serverFileOut, new Encoder()); + JarOutputStream serverJarOut = new JarOutputStream(serverLzmaOut); + ) { + for (JarEntry entry; (entry = in.getNextJarEntry()) != null;) { + String name = entry.getName(); + + JarOutputStream out; + + if (name.startsWith("binpatch/client/")) { + out = clientJarOut; + } else if (name.startsWith("binpatch/server/")) { + out = serverJarOut; + } else { + getProject().getLogger().warn("Unexpected file in Forge binpatches archive: " + name); + continue; + } + + out.putNextEntry(new ZipEntry(name)); + + // Converting from legacy format to modern (v1) format + DataInputStream dataIn = new DataInputStream(in); + DataOutputStream dataOut = new DataOutputStream(out); + dataOut.writeByte(1); // version + dataIn.readUTF(); // unused patch name (presumably always the same as the obf class name) + dataOut.writeUTF(dataIn.readUTF().replace('.', '/')); // obf class name + dataOut.writeUTF(dataIn.readUTF().replace('.', '/')); // srg class name + IOUtils.copy(in, out); // remainder is unchanged + + out.closeEntry(); + } + } + + try (JarInputStream in = new JarInputStream(new ByteArrayInputStream(unpack200Lzma(joinedLegacyPatches))); + OutputStream mergedFileOut = Files.newOutputStream(mergedPatches, CREATE, TRUNCATE_EXISTING); + LzmaOutputStream mergedLzmaOut = new LzmaOutputStream(mergedFileOut, new Encoder()); + JarOutputStream mergedJarOut = new JarOutputStream(mergedLzmaOut); + ) { + for (JarEntry entry; (entry = in.getNextJarEntry()) != null;) { + String name = entry.getName(); + + JarOutputStream out; + + if (name.startsWith("binpatch/client/")) { + out = mergedJarOut; + } else if (name.startsWith("binpatch/server/")) { + out = mergedJarOut; + } else { + getProject().getLogger().warn("Unexpected file in Forge binpatches archive: " + name); + continue; + } + + out.putNextEntry(new ZipEntry(name)); + + // Converting from legacy format to modern (v1) format + DataInputStream dataIn = new DataInputStream(in); + DataOutputStream dataOut = new DataOutputStream(out); + dataOut.writeByte(1); // version + dataIn.readUTF(); // unused patch name (presumably always the same as the obf class name) + dataOut.writeUTF(dataIn.readUTF().replace('.', '/')); // obf class name + dataOut.writeUTF(dataIn.readUTF().replace('.', '/')); // srg class name + IOUtils.copy(in, out); // remainder is unchanged + + out.closeEntry(); + } + } + } + + private byte[] unpack200(InputStream in) throws IOException { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + + try (JarOutputStream jarOut = new JarOutputStream(bytes)) { + Pack200Provider provider = getExtension().getForge().getPack200Provider().getOrNull(); + + if (provider == null) { + throw new IllegalStateException("No provider for Pack200 has been found. Did you declare a provider?"); + } + + provider.unpack(in, jarOut); + } + + return bytes.toByteArray(); + } + + private byte[] unpack200Lzma(InputStream in) throws IOException { + try (LzmaInputStream lzmaIn = new LzmaInputStream(in, new Decoder())) { + return unpack200(lzmaIn); + } + } + + private byte[] unpack200Lzma(Path path) throws IOException { + try (InputStream in = Files.newInputStream(path)) { + return unpack200Lzma(in); + } + } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/SrgProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/SrgProvider.java index f5e5dca29..212ff9f2a 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/SrgProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/SrgProvider.java @@ -29,8 +29,10 @@ import java.io.File; import java.io.IOException; import java.io.PrintStream; +import java.io.Reader; import java.io.StringReader; import java.io.UncheckedIOException; +import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -41,6 +43,8 @@ import com.google.common.base.Stopwatch; import org.apache.commons.io.FileUtils; import org.apache.commons.io.output.NullOutputStream; +import org.cadixdev.lorenz.io.srg.SrgReader; +import org.cadixdev.lorenz.io.srg.tsrg.TSrgWriter; import org.gradle.api.Project; import org.gradle.api.logging.LogLevel; @@ -79,7 +83,16 @@ public void provide(DependencyInfo dependency) throws Exception { if (!Files.exists(srg) || refreshDeps()) { Path srgZip = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve srg")).toPath(); - Files.write(srg, ZipUtils.unpack(srgZip, "config/joined.tsrg")); + String srgPath = "joined.srg"; // legacy zip path + if (ZipUtils.contains(srgZip, srgPath)) { + var tempFile = Files.createTempFile(null, ".srg"); + Files.write(tempFile, ZipUtils.unpack(srgZip, srgPath)); + try (Reader reader = Files.newBufferedReader(tempFile); Writer writer = Files.newBufferedWriter(srg)) { + new TSrgWriter(writer).write(new SrgReader(reader).read()); + } + } else { + Files.write(srg, ZipUtils.unpack(srgZip, "config/joined.tsrg")); + } } try (BufferedReader reader = Files.newBufferedReader(srg)) { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/Pack200Provider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/Pack200Provider.java new file mode 100644 index 000000000..3329c82f0 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/Pack200Provider.java @@ -0,0 +1,8 @@ +package net.fabricmc.loom.configuration.providers.forge.fg2; + +import java.io.InputStream; +import java.util.jar.JarOutputStream; + +public interface Pack200Provider { + void unpack(InputStream inputStream, JarOutputStream outputStream); +} \ No newline at end of file diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpConfigProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpConfigProvider.java index 60e528591..009c7fe3d 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpConfigProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpConfigProvider.java @@ -51,6 +51,9 @@ public McpConfigProvider(Project project) { @Override public void provide(DependencyInfo dependency) throws Exception { + if (getExtension().isLegacyForge()) { + return; + } init(dependency.getDependency().getVersion()); Path mcpZip = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve MCPConfig")).toPath(); @@ -96,7 +99,7 @@ public Path getMcp() { } public boolean isOfficial() { - return data.official(); + return getExtension().isLegacyForge() || data.official(); } public String getMappingsPath() { 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 a27165661..e7780585e 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 @@ -201,7 +201,7 @@ public void setupPost(Project project) throws IOException { if (Files.notExists(tinyMappingsWithSrg) || extension.refreshDeps()) { // Merge tiny mappings with srg Stopwatch stopwatch = Stopwatch.createStarted(); - SrgMerger.ExtraMappings extraMappings = SrgMerger.ExtraMappings.ofMojmapTsrg(getMojmapSrgFileIfPossible(project)); + SrgMerger.ExtraMappings extraMappings = extension.isLegacyForge() ? null : SrgMerger.ExtraMappings.ofMojmapTsrg(getMojmapSrgFileIfPossible(project)); SrgMerger.mergeSrg(getRawSrgFile(project), tinyMappings, tinyMappingsWithSrg, extraMappings, true); project.getLogger().info(":merged srg mappings in " + stopwatch.stop()); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/utils/AccessTransformSetMapper.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/utils/AccessTransformSetMapper.java new file mode 100644 index 000000000..bd2675c5e --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/utils/AccessTransformSetMapper.java @@ -0,0 +1,70 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Minecrell (https://github.com/Minecrell) + * + * 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.mappings.utils; + +import java.util.Objects; +import java.util.Optional; + +import org.cadixdev.at.AccessTransformSet; +import org.cadixdev.bombe.type.signature.MethodSignature; +import org.cadixdev.lorenz.MappingSet; +import org.cadixdev.lorenz.model.ClassMapping; +import org.cadixdev.lorenz.model.FieldMapping; +import org.cadixdev.lorenz.model.Mapping; +import org.cadixdev.lorenz.model.MethodMapping; + +// TODO from https://github.com/CadixDev/at/blob/c2b92fc26cf26e64d3ca3b35abf3364d4b95e6c3/src/main/java/org/cadixdev/at/impl/AccessTransformSetMapper.java +// remove once https://github.com/CadixDev/at/issues/7 is fixed +public final class AccessTransformSetMapper { + private AccessTransformSetMapper() { + } + + public static AccessTransformSet remap(AccessTransformSet set, MappingSet mappings) { + Objects.requireNonNull(set, "set"); + Objects.requireNonNull(mappings, "mappings"); + + AccessTransformSet remapped = AccessTransformSet.create(); + set.getClasses().forEach((className, classSet) -> { + Optional> mapping = mappings.getClassMapping(className); + remap(mappings, mapping, classSet, remapped.getOrCreateClass(mapping.map(Mapping::getFullDeobfuscatedName).orElse(className))); + }); + return remapped; + } + + private static void remap(MappingSet mappings, Optional> mapping, AccessTransformSet.Class set, AccessTransformSet.Class remapped) { + remapped.merge(set.get()); + remapped.mergeAllFields(set.allFields()); + remapped.mergeAllMethods(set.allMethods()); + + set.getFields().forEach((name, transform) -> + remapped.mergeField(mapping.flatMap(m -> m.getFieldMapping(name)) + .map(FieldMapping::getDeobfuscatedName).orElse(name), transform)); + + set.getMethods().forEach((signature, transform) -> + remapped.mergeMethod(mapping.flatMap(m -> m.getMethodMapping(signature)) + .map(MethodMapping::getDeobfuscatedSignature) + .orElseGet(() -> new MethodSignature(signature.getName(), mappings.deobfuscate(signature.getDescriptor()))), transform)); + } +} \ No newline at end of file diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java index 8c1dc7005..a67d1cac0 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java @@ -119,7 +119,7 @@ private void provideServerLibraries() { } private List processLibraries(List libraries) { - final LibraryContext libraryContext = new LibraryContext(minecraftProvider.getVersionInfo(), JavaVersion.current()); + final LibraryContext libraryContext = new LibraryContext(minecraftProvider.getVersionInfo(), JavaVersion.current(), LoomGradleExtension.get(project)); return processorManager.processLibraries(libraries, libraryContext); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java index 92207a312..f68daae14 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java @@ -207,7 +207,7 @@ private void downloadJars() throws IOException { } } - protected final void extractBundledServerJar() throws IOException { + public final void extractBundledServerJar() throws IOException { Preconditions.checkArgument(provideServer(), "Not configured to provide server jar"); Objects.requireNonNull(getServerBundleMetadata(), "Cannot bundled mc jar from none bundled server jar"); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/library/LibraryContext.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/library/LibraryContext.java index b344383ec..b123e7807 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/library/LibraryContext.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/library/LibraryContext.java @@ -26,6 +26,8 @@ import java.util.Arrays; +import net.fabricmc.loom.LoomGradleExtension; + import org.gradle.api.JavaVersion; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; @@ -34,10 +36,16 @@ public final class LibraryContext { private final MinecraftVersionMeta versionMeta; private final JavaVersion javaVersion; + private final LoomGradleExtension extension; public LibraryContext(MinecraftVersionMeta versionMeta, JavaVersion javaVersion) { + this(versionMeta, javaVersion, null); + } + + public LibraryContext(MinecraftVersionMeta versionMeta, JavaVersion javaVersion, LoomGradleExtension extension) { this.versionMeta = versionMeta; this.javaVersion = javaVersion; + this.extension = extension; } /** @@ -119,4 +127,8 @@ public boolean hasLibrary(String name) { public boolean isJava19OrLater() { return javaVersion.isCompatibleWith(JavaVersion.VERSION_19); } + + public LoomGradleExtension getExtension() { + return extension; + } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/library/processors/LoomNativeSupportLibraryProcessor.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/library/processors/LoomNativeSupportLibraryProcessor.java index 89f85f82b..49e581a72 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/library/processors/LoomNativeSupportLibraryProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/library/processors/LoomNativeSupportLibraryProcessor.java @@ -40,6 +40,7 @@ public LoomNativeSupportLibraryProcessor(Platform platform, LibraryContext conte @Override public ApplicationResult getApplicationResult() { + if (context.getExtension().isForge()) return ApplicationResult.DONT_APPLY; if (!context.usesLWJGL3()) { // Only supports LWJGL 3 return ApplicationResult.DONT_APPLY; diff --git a/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java b/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java index 16e0799d2..f48aae207 100644 --- a/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java +++ b/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java @@ -88,6 +88,7 @@ public static void addBaseForgeSources(Project project) throws IOException { } public static void addForgeSources(Project project, SharedServiceManager serviceManager, Path sourcesJar) throws IOException { + if (LoomGradleExtension.get(project).isLegacyForge()) return; // don't even try try (FileSystemUtil.Delegate delegate = FileSystemUtil.getJarFileSystem(sourcesJar, true)) { ThreadingUtils.TaskCompleter taskCompleter = ThreadingUtils.taskCompleter(); diff --git a/src/main/java/net/fabricmc/loom/extension/ForgeExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/ForgeExtensionImpl.java index af190850a..07919778a 100644 --- a/src/main/java/net/fabricmc/loom/extension/ForgeExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/ForgeExtensionImpl.java @@ -31,6 +31,8 @@ import javax.inject.Inject; +import net.fabricmc.loom.configuration.providers.forge.fg2.Pack200Provider; + import org.gradle.api.Action; import org.gradle.api.Project; import org.gradle.api.file.ConfigurableFileCollection; @@ -49,6 +51,7 @@ public class ForgeExtensionImpl implements ForgeExtensionAPI { private final SetProperty mixinConfigs; private final Property useCustomMixin; private final Property useForgeLoggerConfig; + private final Property pack200Provider; private final List dataGenMods = new ArrayList<>(); // not a property because it has custom adding logic @Inject @@ -60,6 +63,7 @@ public ForgeExtensionImpl(Project project, LoomGradleExtension extension) { mixinConfigs = project.getObjects().setProperty(String.class).empty(); useCustomMixin = project.getObjects().property(Boolean.class).convention(true); useForgeLoggerConfig = project.getObjects().property(Boolean.class).convention(false); + pack200Provider = project.getObjects().property(Pack200Provider.class); } @Override @@ -122,4 +126,9 @@ public void mod(String... modIds) { } }); } + + @Override + public Property getPack200Provider() { + return pack200Provider; + } } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index 805fb3300..6ba0764e7 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -127,8 +127,6 @@ protected LoomGradleExtensionApiImpl(Project project, LoomFiles directories) { this.modProvidedJavadoc = project.getObjects().property(Boolean.class) .convention(project.provider(() -> !isForge())); this.modProvidedJavadoc.finalizeValueOnRead(); - this.intermediary = project.getObjects().property(String.class) - .convention("https://maven.fabricmc.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar"); this.intermediateMappingsProvider = project.getObjects().property(IntermediateMappingsProvider.class); this.intermediateMappingsProvider.finalizeValueOnRead(); @@ -190,6 +188,8 @@ protected LoomGradleExtensionApiImpl(Project project, LoomFiles directories) { return ModPlatform.FABRIC; })::get); + this.intermediary = project.getObjects().property(String.class) + .convention(isLegacyForge() ? "https://maven.legacyfabric.net/net/legacyfabric/v2/intermediary/%1$s/intermediary-%1$s-v2.jar" : "https://maven.fabricmc.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar"); } @Override diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index 1cedf4d6b..dab86ac02 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -278,7 +278,7 @@ protected String getMinecraftVersion() { @Override public ForgeExtensionAPI getForge() { - ModPlatform.assertPlatform(this, ModPlatform.FORGE); + ModPlatform.assertPlatform(this, ModPlatform.FORGE, ModPlatform.LEGACY_FORGE); return forgeExtension.get(); } @@ -294,13 +294,13 @@ public void setDependencyProviders(DependencyProviders dependencyProviders) { @Override public ForgeRunsProvider getForgeRunsProvider() { - ModPlatform.assertPlatform(this, ModPlatform.FORGE); + ModPlatform.assertPlatform(this, ModPlatform.FORGE, ModPlatform.LEGACY_FORGE); return forgeRunsProvider; } @Override public void setForgeRunsProvider(ForgeRunsProvider forgeRunsProvider) { - ModPlatform.assertPlatform(this, ModPlatform.FORGE); + ModPlatform.assertPlatform(this, ModPlatform.FORGE, ModPlatform.LEGACY_FORGE); this.forgeRunsProvider = forgeRunsProvider; } } diff --git a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java index 756da0c60..6160c5c47 100644 --- a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java @@ -74,7 +74,7 @@ public void run() throws IOException { .property("client", "org.lwjgl.librarypath", nativesPath); } - if (!getExtension().isForge()) { + if (!getExtension().isModernForge()) { launchConfig .argument("client", "--assetIndex") .argument("client", getExtension().getMinecraftProvider().getVersionInfo().assetIndex().fabricId(getExtension().getMinecraftProvider().minecraftVersion())) @@ -97,7 +97,7 @@ public void run() throws IOException { .argument("client", "Architectury Loom"); } - if (getExtension().isForge()) { + if (getExtension().isModernForge()) { // Find the mapping files for Unprotect to use for figuring out // which classes are from Minecraft. String unprotectMappings = getProject().getConfigurations() @@ -154,6 +154,24 @@ public void run() throws IOException { } } + if (getExtension().isLegacyForge()) { + launchConfig + .argument("client", "--tweakClass") + .argument("client", Constants.LegacyForge.FML_TWEAKER) + .argument("server", "--tweakClass") + .argument("server", Constants.LegacyForge.FML_SERVER_TWEAKER) + + .argument("--accessToken") + .argument("undefined") + + .property("net.minecraftforge.gradle.GradleStart.srg.srg-mcp", getExtension().getMappingConfiguration().srgToNamedSrg.toAbsolutePath().toString()) + .property("mixin.env.remapRefMap", "true"); + + for (String config : PropertyUtil.getAndFinalize(getExtension().getForge().getMixinConfigs())) { + launchConfig.argument("--mixin").argument(config); + } + } + final boolean plainConsole = getProject().getGradle().getStartParameter().getConsoleOutput() == ConsoleOutput.Plain; final boolean ansiSupportedIDE = new File(getProject().getRootDir(), ".vscode").exists() || new File(getProject().getRootDir(), ".idea").exists() diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index 8efbd192e..708e12560 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -197,4 +197,10 @@ public static final class Forge { private Forge() { } } + + public static final class LegacyForge { + public static final String LAUNCH_WRAPPER = "net.minecraft.launchwrapper.Launch"; + public static final String FML_TWEAKER = "net.minecraftforge.fml.common.launcher.FMLTweaker"; + public static final String FML_SERVER_TWEAKER = "net.minecraftforge.fml.common.launcher.FMLServerTweaker"; + } } diff --git a/src/main/java/net/fabricmc/loom/util/ModPlatform.java b/src/main/java/net/fabricmc/loom/util/ModPlatform.java index 2b1a196af..f3e5c8dd9 100644 --- a/src/main/java/net/fabricmc/loom/util/ModPlatform.java +++ b/src/main/java/net/fabricmc/loom/util/ModPlatform.java @@ -24,6 +24,8 @@ package net.fabricmc.loom.util; +import java.util.Arrays; +import java.util.List; import java.util.Locale; import java.util.function.Supplier; @@ -36,6 +38,7 @@ public enum ModPlatform { FABRIC(false), FORGE(false), + LEGACY_FORGE(true), QUILT(true); boolean experimental; @@ -48,20 +51,31 @@ public boolean isExperimental() { return experimental; } - public static void assertPlatform(Project project, ModPlatform platform) { + public static void assertPlatform(Project project, ModPlatform... platform) { assertPlatform(LoomGradleExtension.get(project), platform); } - public static void assertPlatform(LoomGradleExtensionAPI extension, ModPlatform platform) { - assertPlatform(extension, platform, () -> { - String msg = "Loom is not running on %s.%nYou can switch to it by adding 'loom.platform = %s' to your gradle.properties"; - String name = platform.name().toLowerCase(Locale.ROOT); - return msg.formatted(name, name); - }); + public static void assertPlatform(LoomGradleExtensionAPI extension, ModPlatform... platform) { + assertPlatform(extension, () -> { + String msg = "Loom is not running on any of %s.%nYou can switch to it by any of the following: Add any of %s to your gradle.properties"; + List names = Arrays.stream(platform).map(Enum::name).toList(); + StringBuilder platformList = new StringBuilder(); + platformList.append("["); + StringBuilder loomPlatform = new StringBuilder(); + for (String name : names) { + String lowercaseName = name.toLowerCase(Locale.ROOT); + platformList.append(lowercaseName).append(", "); + loomPlatform.append("['loom.platform = ").append(lowercaseName).append("'], "); + } + platformList.setLength(platformList.length()-2); + platformList.append("]"); + loomPlatform.setLength(loomPlatform.length()-2); + return msg.formatted(platformList, loomPlatform); + }, platform); } - public static void assertPlatform(LoomGradleExtensionAPI extension, ModPlatform platform, Supplier message) { - if (extension.getPlatform().get() != platform) { + public static void assertPlatform(LoomGradleExtensionAPI extension, Supplier message, ModPlatform... platform) { + if (!Arrays.asList(platform).contains(extension.getPlatform().get())) { throw new GradleException(message.get()); } } diff --git a/src/main/java/net/fabricmc/loom/util/legacyforge/CoreModManagerTransformer.java b/src/main/java/net/fabricmc/loom/util/legacyforge/CoreModManagerTransformer.java new file mode 100644 index 000000000..2693fd1d2 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/legacyforge/CoreModManagerTransformer.java @@ -0,0 +1,238 @@ +package net.fabricmc.loom.util.legacyforge; + +import static org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static org.objectweb.asm.Opcodes.ACC_STATIC; +import static org.objectweb.asm.Opcodes.ALOAD; +import static org.objectweb.asm.Opcodes.ASTORE; +import static org.objectweb.asm.Opcodes.BIPUSH; +import static org.objectweb.asm.Opcodes.CHECKCAST; +import static org.objectweb.asm.Opcodes.DUP; +import static org.objectweb.asm.Opcodes.GOTO; +import static org.objectweb.asm.Opcodes.ICONST_0; +import static org.objectweb.asm.Opcodes.IFEQ; +import static org.objectweb.asm.Opcodes.IFNONNULL; +import static org.objectweb.asm.Opcodes.INVOKEINTERFACE; +import static org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static org.objectweb.asm.Opcodes.INVOKESTATIC; +import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; +import static org.objectweb.asm.Opcodes.NEW; +import static org.objectweb.asm.Opcodes.POP; +import static org.objectweb.asm.Opcodes.RETURN; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * Transforms Forge's CoreModManager class to search the classpath for coremods. + * For motivation, see comments at usage site. + */ +public class CoreModManagerTransformer extends ClassVisitor { + private static final String CLASS = "net/minecraftforge/fml/relauncher/CoreModManager"; + public static final String FILE = CLASS + ".class"; + + private static final String TARGET_METHOD = "discoverCoreMods"; + private static final String OUR_METHOD_NAME = "loom$injectCoremodsFromClasspath"; + private static final String OUR_METHOD_DESCRIPTOR = "(Lnet/minecraft/launchwrapper/LaunchClassLoader;)V"; + + public CoreModManagerTransformer(ClassVisitor classVisitor) { + super(Opcodes.ASM9, classVisitor); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions); + + // We inject a call to our method, which will discover and load coremods from the classpath, at the very start of the + // regular discovery method. + if (name.equals(TARGET_METHOD)) { + methodVisitor = new InjectCallAtHead(methodVisitor); + } + + return methodVisitor; + } + + @Override + public void visitEnd() { + // We add the following method, which will find all coremods on the classpath, and load them. + // + // private static void loom$injectCoremodsFromClasspath(LaunchClassLoader classLoader) throws Exception { + // Enumeration urls = classLoader.getResources("META-INF/MANIFEST.MF"); + // while (urls.hasMoreElements()) { + // URL url = urls.nextElement(); + // InputStream stream = url.openStream(); + // Manifest manifest = new Manifest(stream); + // stream.close(); + // String coreModClass = manifest.getMainAttributes().getValue("FMLCorePlugin"); + // if (coreModClass == null) continue; + // File file; + // if ("jar".equals(url.getProtocol())) { + // file = new File(new URL(url.getPath()).toURI()); + // } else if ("file".equals(url.getProtocol())) { + // file = new File(url.toURI()).getParentFile().getParentFile(); + // } else { + // continue; + // } + // loadCoreMod(classLoader, coreModClass, file); + // } + // } + // + // Converted to ASM via the "ASM Bytecode Viewer" IntelliJ plugin: + { + MethodVisitor methodVisitor = super.visitMethod(ACC_PRIVATE | ACC_STATIC, OUR_METHOD_NAME, OUR_METHOD_DESCRIPTOR, null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(25, label0); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitLdcInsn("META-INF/MANIFEST.MF"); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "net/minecraft/launchwrapper/LaunchClassLoader", "getResources", "(Ljava/lang/String;)Ljava/util/Enumeration;", false); + methodVisitor.visitVarInsn(ASTORE, 1); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(26, label1); + methodVisitor.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"java/util/Enumeration"}, 0, null); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Enumeration", "hasMoreElements", "()Z", true); + Label label2 = new Label(); + methodVisitor.visitJumpInsn(IFEQ, label2); + Label label3 = new Label(); + methodVisitor.visitLabel(label3); + methodVisitor.visitLineNumber(27, label3); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Enumeration", "nextElement", "()Ljava/lang/Object;", true); + methodVisitor.visitTypeInsn(CHECKCAST, "java/net/URL"); + methodVisitor.visitVarInsn(ASTORE, 2); + Label label4 = new Label(); + methodVisitor.visitLabel(label4); + methodVisitor.visitLineNumber(28, label4); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "openStream", "()Ljava/io/InputStream;", false); + methodVisitor.visitVarInsn(ASTORE, 3); + Label label5 = new Label(); + methodVisitor.visitLabel(label5); + methodVisitor.visitLineNumber(29, label5); + methodVisitor.visitTypeInsn(NEW, "java/util/jar/Manifest"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitVarInsn(ALOAD, 3); + methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/util/jar/Manifest", "", "(Ljava/io/InputStream;)V", false); + methodVisitor.visitVarInsn(ASTORE, 4); + Label label6 = new Label(); + methodVisitor.visitLabel(label6); + methodVisitor.visitLineNumber(30, label6); + methodVisitor.visitVarInsn(ALOAD, 3); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/InputStream", "close", "()V", false); + Label label7 = new Label(); + methodVisitor.visitLabel(label7); + methodVisitor.visitLineNumber(31, label7); + methodVisitor.visitVarInsn(ALOAD, 4); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/util/jar/Manifest", "getMainAttributes", "()Ljava/util/jar/Attributes;", false); + methodVisitor.visitLdcInsn("FMLCorePlugin"); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/util/jar/Attributes", "getValue", "(Ljava/lang/String;)Ljava/lang/String;", false); + methodVisitor.visitVarInsn(ASTORE, 5); + Label label8 = new Label(); + methodVisitor.visitLabel(label8); + methodVisitor.visitLineNumber(32, label8); + methodVisitor.visitVarInsn(ALOAD, 5); + Label label9 = new Label(); + methodVisitor.visitJumpInsn(IFNONNULL, label9); + methodVisitor.visitJumpInsn(GOTO, label1); + methodVisitor.visitLabel(label9); + methodVisitor.visitLineNumber(34, label9); + methodVisitor.visitFrame(Opcodes.F_FULL, 6, new Object[]{"net/minecraft/launchwrapper/LaunchClassLoader", "java/util/Enumeration", "java/net/URL", "java/io/InputStream", "java/util/jar/Manifest", "java/lang/String"}, 0, new Object[]{}); + methodVisitor.visitLdcInsn("jar"); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "getProtocol", "()Ljava/lang/String;", false); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false); + Label label10 = new Label(); + methodVisitor.visitJumpInsn(IFEQ, label10); + Label label11 = new Label(); + methodVisitor.visitLabel(label11); + methodVisitor.visitLineNumber(35, label11); + methodVisitor.visitTypeInsn(NEW, "java/io/File"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitTypeInsn(NEW, "java/net/URL"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "getPath", "()Ljava/lang/String;", false); + methodVisitor.visitInsn(ICONST_0); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "getPath", "()Ljava/lang/String;", false); + methodVisitor.visitIntInsn(BIPUSH, 33); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "lastIndexOf", "(I)I", false); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "substring", "(II)Ljava/lang/String;", false); + methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/net/URL", "", "(Ljava/lang/String;)V", false); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "toURI", "()Ljava/net/URI;", false); + methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/io/File", "", "(Ljava/net/URI;)V", false); + methodVisitor.visitVarInsn(ASTORE, 6); + Label label12 = new Label(); + methodVisitor.visitLabel(label12); + Label label13 = new Label(); + methodVisitor.visitJumpInsn(GOTO, label13); + methodVisitor.visitLabel(label10); + methodVisitor.visitLineNumber(36, label10); + methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + methodVisitor.visitLdcInsn("file"); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "getProtocol", "()Ljava/lang/String;", false); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false); + methodVisitor.visitJumpInsn(IFEQ, label1); + Label label14 = new Label(); + methodVisitor.visitLabel(label14); + methodVisitor.visitLineNumber(37, label14); + methodVisitor.visitTypeInsn(NEW, "java/io/File"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "toURI", "()Ljava/net/URI;", false); + methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/io/File", "", "(Ljava/net/URI;)V", false); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/File", "getParentFile", "()Ljava/io/File;", false); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/File", "getParentFile", "()Ljava/io/File;", false); + methodVisitor.visitVarInsn(ASTORE, 6); + methodVisitor.visitLabel(label13); + methodVisitor.visitLineNumber(41, label13); + methodVisitor.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"java/io/File"}, 0, null); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitVarInsn(ALOAD, 5); + methodVisitor.visitVarInsn(ALOAD, 6); + methodVisitor.visitMethodInsn(INVOKESTATIC, "net/minecraftforge/fml/relauncher/CoreModManager", "loadCoreMod", "(Lnet/minecraft/launchwrapper/LaunchClassLoader;Ljava/lang/String;Ljava/io/File;)Lnet/minecraftforge/fml/relauncher/CoreModManager$FMLPluginWrapper;", false); + methodVisitor.visitInsn(POP); + Label label15 = new Label(); + methodVisitor.visitLabel(label15); + methodVisitor.visitLineNumber(42, label15); + methodVisitor.visitJumpInsn(GOTO, label1); + methodVisitor.visitLabel(label2); + methodVisitor.visitLineNumber(43, label2); + methodVisitor.visitFrame(Opcodes.F_FULL, 2, new Object[]{"net/minecraft/launchwrapper/LaunchClassLoader", "java/util/Enumeration"}, 0, new Object[]{}); + methodVisitor.visitInsn(RETURN); + Label label16 = new Label(); + methodVisitor.visitLabel(label16); + methodVisitor.visitLocalVariable("file", "Ljava/io/File;", null, label12, label10, 6); + methodVisitor.visitLocalVariable("url", "Ljava/net/URL;", null, label4, label15, 2); + methodVisitor.visitLocalVariable("stream", "Ljava/io/InputStream;", null, label5, label15, 3); + methodVisitor.visitLocalVariable("manifest", "Ljava/util/jar/Manifest;", null, label6, label15, 4); + methodVisitor.visitLocalVariable("coreModClass", "Ljava/lang/String;", null, label8, label15, 5); + methodVisitor.visitLocalVariable("file", "Ljava/io/File;", null, label13, label15, 6); + methodVisitor.visitLocalVariable("classLoader", "Lnet/minecraft/launchwrapper/LaunchClassLoader;", null, label0, label16, 0); + methodVisitor.visitLocalVariable("urls", "Ljava/util/Enumeration;", "Ljava/util/Enumeration;", label1, label16, 1); + methodVisitor.visitMaxs(8, 7); + methodVisitor.visitEnd(); + } + + super.visitEnd(); + } + + private static class InjectCallAtHead extends MethodVisitor { + private InjectCallAtHead(MethodVisitor methodVisitor) { + super(Opcodes.ASM9, methodVisitor); + } + + @Override + public void visitCode() { + super.visitCode(); + + super.visitVarInsn(Opcodes.ALOAD, 1); + super.visitMethodInsn(Opcodes.INVOKESTATIC, CLASS, OUR_METHOD_NAME, OUR_METHOD_DESCRIPTOR, false); + } + } +} \ No newline at end of file diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeRunConfigTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeRunConfigTest.groovy index 17ed87fbf..910d797dc 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeRunConfigTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeRunConfigTest.groovy @@ -37,9 +37,10 @@ class ForgeRunConfigTest extends Specification implements GradleProjectTestTrait def "verify run configs #mcVersion #forgeVersion"() { setup: def gradle = gradleProject(project: "forge/simple", version: DEFAULT_GRADLE) + gradle.getGradleProperties().text = gradle.getGradleProperties().text.replace("@PLATFORM@", mcVersion == "1.12.2" || mcVersion == "1.8.9" ? "legacy_forge" : "forge") gradle.buildGradle.text = gradle.buildGradle.text.replace('@MCVERSION@', mcVersion) .replace('@FORGEVERSION@', forgeVersion) - .replace('@MAPPINGS@', 'loom.officialMojangMappings()') + .replace('@MAPPINGS@', (mcVersion == "1.12.2" || mcVersion == "1.8.9") ? "loom.layered(){}" : 'loom.officialMojangMappings()') gradle.buildGradle << """ tasks.register('verifyRunConfigs') { doLast { @@ -68,5 +69,7 @@ class ForgeRunConfigTest extends Specification implements GradleProjectTestTrait '1.17.1' | "37.0.67" | 'cpw.mods.bootstraplauncher.BootstrapLauncher' '1.16.5' | "36.2.4" | 'net.minecraftforge.userdev.LaunchTesting' '1.14.4' | "28.2.23" | 'net.minecraftforge.userdev.LaunchTesting' + '1.12.2' |"14.23.0.2486"| 'net.minecraft.launchwrapper.Launch' + '1.8.9' |"11.15.1.2318-1.8.9" /*why*/| 'net.minecraft.launchwrapper.Launch' } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeTest.groovy index 68de1b705..7648fbfaa 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeTest.groovy @@ -37,6 +37,7 @@ class ForgeTest extends Specification implements GradleProjectTestTrait { def "build #mcVersion #forgeVersion #mappings"() { setup: def gradle = gradleProject(project: "forge/simple", version: DEFAULT_GRADLE) + gradle.getGradleProperties().text = gradle.getGradleProperties().text.replace("@PLATFORM@", mcVersion == "1.12.2" || mcVersion == "1.8.9" ? "legacy_forge" : "forge") gradle.buildGradle.text = gradle.buildGradle.text.replace('@MCVERSION@', mcVersion) .replace('@FORGEVERSION@', forgeVersion) .replace('@MAPPINGS@', mappings) @@ -60,5 +61,7 @@ class ForgeTest extends Specification implements GradleProjectTestTrait { '1.16.5' | '36.2.4' | '"de.oceanlabs.mcp:mcp_snapshot:20210309-1.16.5"' '1.14.4' | "28.2.23" | "loom.officialMojangMappings()" '1.14.4' | "28.2.23" | '"net.fabricmc:yarn:1.14.4+build.18:v2"' + '1.12.2' |"14.23.0.2486"| '"de.oceanlabs.mcp:mcp_snapshot:20170615-1.12"' + '1.8.9' |"11.15.1.2318-1.8.9" /*why*/| '"de.oceanlabs.mcp:mcp_stable:22-1.8.9"' } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/forge/PatchedDecompileTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/forge/PatchedDecompileTest.groovy index e9fcb8fdf..922ad0e6d 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/forge/PatchedDecompileTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/forge/PatchedDecompileTest.groovy @@ -37,6 +37,7 @@ class PatchedDecompileTest extends Specification implements GradleProjectTestTra def "decompile #mcVersion #forgeVersion"() { setup: def gradle = gradleProject(project: "forge/simple", version: DEFAULT_GRADLE) + gradle.getGradleProperties().text = gradle.getGradleProperties().text.replace("@PLATFORM@", mcVersion == "1.12.2" || mcVersion == "1.8.9" ? "legacy_forge" : "forge") gradle.buildGradle.text = gradle.buildGradle.text.replace('@MCVERSION@', mcVersion) .replace('@FORGEVERSION@', forgeVersion) .replace('@MAPPINGS@', 'loom.officialMojangMappings()') @@ -52,5 +53,7 @@ class PatchedDecompileTest extends Specification implements GradleProjectTestTra '1.19.2' | "43.1.1" '1.18.1' | "39.0.63" '1.17.1' | "37.0.67" + '1.12.2' | "14.23.0.2486" + '1.8.9' | "11.14.4.1563" } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/forge/SingleJarTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/forge/SingleJarTest.groovy index 7b2a941ed..9a25f6c94 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/forge/SingleJarTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/forge/SingleJarTest.groovy @@ -38,6 +38,7 @@ class SingleJarTest extends Specification implements GradleProjectTestTrait { def "build single jar mc (mc #mc, forge #forge, env #env, gradle #version)"() { setup: def gradle = gradleProject(project: 'forge/singleJar', version: version) + gradle.getGradleProperties().text = gradle.getGradleProperties().text.replace("@PLATFORM@", mc == "1.12.2" || mc == "1.8.9" ? "legacy_forge" : "forge") gradle.buildGradle.text = gradle.buildGradle.text .replace('@MCVERSION@', mc) .replace('@FORGEVERSION@', forge) @@ -54,7 +55,9 @@ class SingleJarTest extends Specification implements GradleProjectTestTrait { [ ['1.19.4', "45.0.43"], ['1.18.1', "39.0.63"], - ['1.17.1', "37.0.67"] + ['1.17.1', "37.0.67"], +// ['1.12.2', "14.23.0.2486"], // MOJMAP +// ['1.8.9', '11.14.4.1563'] ], ['client', 'server'], STANDARD_TEST_VERSIONS diff --git a/src/test/resources/projects/forge/simple/build.gradle b/src/test/resources/projects/forge/simple/build.gradle index 1f540a39b..be516e806 100644 --- a/src/test/resources/projects/forge/simple/build.gradle +++ b/src/test/resources/projects/forge/simple/build.gradle @@ -12,6 +12,12 @@ base { archivesName = project.archives_base_name } +loom { + forge { + pack200Provider = new dev.architectury.pack200.java.Pack200Adapter() + } +} + version = project.mod_version group = project.maven_group diff --git a/src/test/resources/projects/forge/simple/gradle.properties b/src/test/resources/projects/forge/simple/gradle.properties index f3b2ac323..8be089c5d 100644 --- a/src/test/resources/projects/forge/simple/gradle.properties +++ b/src/test/resources/projects/forge/simple/gradle.properties @@ -14,4 +14,4 @@ archives_base_name = fabric-example-mod # currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api fabric_version=0.31.0+1.16 -loom.platform=forge +loom.platform=@PLATFORM@ diff --git a/src/test/resources/projects/forge/singleJar/build.gradle b/src/test/resources/projects/forge/singleJar/build.gradle index 99d3cd63e..0a3f6a64b 100644 --- a/src/test/resources/projects/forge/singleJar/build.gradle +++ b/src/test/resources/projects/forge/singleJar/build.gradle @@ -21,6 +21,9 @@ loom { } else { serverOnlyMinecraftJar() } + forge { + pack200Provider = new dev.architectury.pack200.java.Pack200Adapter() + } } def mcVersion = "@MCVERSION@" diff --git a/src/test/resources/projects/forge/singleJar/gradle.properties b/src/test/resources/projects/forge/singleJar/gradle.properties index 26236f582..a6e4d3ec2 100644 --- a/src/test/resources/projects/forge/singleJar/gradle.properties +++ b/src/test/resources/projects/forge/singleJar/gradle.properties @@ -5,4 +5,4 @@ org.gradle.jvmargs=-Xmx1G mod_version = 1.0.0 maven_group = com.example archives_base_name = fabric-example-mod -loom.platform=forge +loom.platform=@PLATFORM@