diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index eb8ebb5b8..258c37bce 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -30,4 +30,5 @@ jobs: - name: Upload to Maven run: ./gradlew publish --stacktrace env: - MAVEN_PASS: ${{ secrets.MAVEN_PASS }} + NEXUS_USER: ${{ secrets.NEXUS_USER }} + NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} diff --git a/README.md b/README.md index 0c32bc99f..87f33d732 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ +# Essential Loom + +A fork of [Architectury Loom](https://github.com/architectury/architectury-loom/), primarily to support legacy forge versions +but with some other changes for our purposes. Used by [Essential Gradle Toolkit](https://github.com/EssentialGG/essential-gradle-toolkit/). + +Talk to us on [Discord](https://discord.gg/essential). Use the `#programmer-chat` channel for Essential Loom discussions. +Issues can be reported on Discord or using GitHub issues. + +# Original Readme + +--- + # Architectury Loom Talk to us on [Discord](https://discord.gg/C2RdJDpRBP)! diff --git a/bootstrap/test-project/build.gradle b/bootstrap/test-project/build.gradle index 6be442a1d..4093a6bf5 100644 --- a/bootstrap/test-project/build.gradle +++ b/bootstrap/test-project/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' version '0.13.local' + id 'gg.essential.loom' version '0.13.local' } dependencies { diff --git a/build.gradle b/build.gradle index be71038b8..8e50c9913 100644 --- a/build.gradle +++ b/build.gradle @@ -49,7 +49,7 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { } } -group = "dev.architectury" +group = "gg.essential" def baseVersion = '1.4' def ENV = System.getenv() @@ -192,6 +192,9 @@ dependencies { // Forge mods.toml parsing implementation libs.night.config.toml + // Legacy Forge patches + implementation libs.lzma + // Testing testImplementation(gradleTestKit()) testImplementation(testLibs.spock) { @@ -205,6 +208,8 @@ dependencies { testImplementation testLibs.mockito testImplementation testLibs.java.debug + runtimeOnly testLibs.pack200 + compileOnly runtimeLibs.jetbrains.annotations testCompileOnly runtimeLibs.jetbrains.annotations @@ -241,7 +246,7 @@ java { spotless { java { - licenseHeaderFile(rootProject.file("HEADER")).yearSeparator("-") + // licenseHeaderFile(rootProject.file("HEADER")).yearSeparator("-") targetExclude("**/loom/util/DownloadUtil.java", "**/loom/util/FileSystemUtil.java") targetExclude("**/dev/architectury/**") } @@ -284,7 +289,7 @@ codenarc { gradlePlugin { plugins { fabricLoom { - id = 'dev.architectury.loom' + id = 'gg.essential.loom' implementationClass = 'net.fabricmc.loom.bootstrap.LoomGradlePluginBootstrap' } } @@ -326,52 +331,16 @@ test { import org.gradle.api.internal.artifacts.configurations.ConfigurationRoles import org.gradle.launcher.cli.KotlinDslVersion import org.gradle.util.GradleVersion -import org.w3c.dom.Document -import org.w3c.dom.Element -import org.w3c.dom.Node publishing { - publications { - if (isSnapshot) return - - // Also publish a snapshot so people can use the latest version if they wish - snapshot(MavenPublication) { publication -> - groupId project.group - artifactId project.base.archivesName.get() - version baseVersion + '-SNAPSHOT' - - from components.java - } - - // Manually crate the plugin marker for snapshot versions - snapshotPlugin(MavenPublication) { - groupId 'dev.architectury.loom' - artifactId 'dev.architectury.loom.gradle.plugin' - version baseVersion + '-SNAPSHOT' - - pom.withXml { - // Based off org.gradle.plugin.devel.plugins.MavenPluginPublishPlugin - Element root = asElement() - Document document = root.getOwnerDocument() - Node dependencies = root.appendChild(document.createElement('dependencies')) - Node dependency = dependencies.appendChild(document.createElement('dependency')) - Node groupId = dependency.appendChild(document.createElement('groupId')) - groupId.setTextContent(project.group) - Node artifactId = dependency.appendChild(document.createElement('artifactId')) - artifactId.setTextContent(project.archivesBaseName) - Node version = dependency.appendChild(document.createElement('version')) - version.setTextContent(baseVersion + '-SNAPSHOT') - } - } - } - repositories { - if (ENV.MAVEN_PASS != null) { + if (System.getenv("NEXUS_USER") != null) { maven { - url = "https://deploy.shedaniel.me/" + url = "https://repo.sk1er.club/repository/maven-releases/" + name = "nexus-public" credentials { - username = "shedaniel" - password = ENV.MAVEN_PASS + username = System.getenv("NEXUS_USER") + password = System.getenv("NEXUS_PASSWORD") } } } diff --git a/checkstyle.xml b/checkstyle.xml index 279b740b2..796f70869 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -61,6 +61,8 @@ + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 139a2e261..e8eb9dfc1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,6 +30,7 @@ forge-diffpatch = "2.0.7" night-config = "3.6.6" datafixerupper = "6.0.8" at = "1.0.1" +lzma = "1.3" [libraries] # Loom compile libraries @@ -64,6 +65,7 @@ forge-diffpatch = { module = "net.minecraftforge:DiffPatch", version.ref = "forg night-config-toml = { module = "com.electronwill.night-config:toml", version.ref = "night-config" } datafixerupper = { module = "com.mojang:datafixerupper", version.ref = "datafixerupper" } at = { module = "dev.architectury:at", version.ref = "at" } +lzma = { module = "com.github.jponge:lzma-java", version.ref = "lzma" } [plugins] kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } diff --git a/gradle/test.libs.versions.toml b/gradle/test.libs.versions.toml index 1c637f779..aea7cc6d9 100644 --- a/gradle/test.libs.versions.toml +++ b/gradle/test.libs.versions.toml @@ -5,6 +5,7 @@ javalin = "5.6.2" mockito = "5.4.0" java-debug = "0.48.0" mixin = "0.11.4+mixin.0.8.5" +pack200 = "0.1.3" gradle-nightly = "8.5-20230908221250+0000" fabric-loader = "0.14.22" @@ -18,6 +19,7 @@ javalin = { module = "io.javalin:javalin", version.ref = "javalin" } mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" } java-debug = { module = "com.microsoft.java:com.microsoft.java.debug.core", version.ref = "java-debug" } mixin = { module = "net.fabricmc:sponge-mixin", version.ref = "mixin" } +pack200 = { module = "dev.architectury.architectury-pack200:dev.architectury.architectury-pack200.gradle.plugin", version.ref = "pack200" } gradle-nightly = { module = "org.gradle:dummy", version.ref = "gradle-nightly" } fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" } fabric-installer = { module = "net.fabricmc:fabric-installer", version.ref = "fabric-installer" } \ No newline at end of file diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index 12775e34c..7c6996c8a 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -166,6 +166,14 @@ default boolean isForgeLikeAndNotOfficial() { return isForgeLike() && !getMcpConfigProvider().isOfficial(); } + default boolean isLegacyForge() { + return isForge() && getForgeUserdevProvider().isLegacyForge(); + } + + default boolean isModernForge() { + return isForge() && !isLegacyForge(); + } + DependencyProviders getDependencyProviders(); void setDependencyProviders(DependencyProviders dependencyProviders); diff --git a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java index 9ae6b3a5d..d1e6ba1b4 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java +++ b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java @@ -88,12 +88,12 @@ public void apply(Project project) { if (!loggedVersions.contains(LOOM_VERSION)) { loggedVersions.add(LOOM_VERSION); System.setProperty("loom.printed.logged", String.join(",", loggedVersions)); - project.getLogger().lifecycle("Architectury Loom: " + LOOM_VERSION); + project.getLogger().lifecycle("Essential Loom: " + LOOM_VERSION); if (Constants.PLUGIN_BETA) { - project.getLogger().lifecycle("This version of Architectury Loom is in beta! Please report any issues you encounter: https://github.com/architectury/architectury-loom/issues"); + project.getLogger().lifecycle("This version of Essential Loom is in beta!"); } else if (Constants.PLUGIN_DEPRECATED) { - project.getLogger().lifecycle("You are using an outdated version of Architectury Loom! This version will not receive any support, please consider updating!"); + project.getLogger().lifecycle("You are using an outdated version of Essential Loom! This version will not receive any support, please consider updating!"); } } diff --git a/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java index 17f5b3a54..a5f033ebd 100644 --- a/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java @@ -32,6 +32,8 @@ import org.gradle.api.provider.SetProperty; import org.jetbrains.annotations.ApiStatus; +import net.fabricmc.loom.configuration.providers.forge.fg2.Pack200Provider; + /** * This is the Forge extension API available to build scripts. */ @@ -112,6 +114,8 @@ default void mixinConfig(String... mixinConfigs) { */ Property getUseForgeLoggerConfig(); + Property getPack200Provider(); + /** * A list of mod IDs for mods applied for data generation. * The returned list is unmodifiable but not immutable - it will reflect changes done with diff --git a/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java b/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java index 3466459c1..efb6c6868 100644 --- a/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java +++ b/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java @@ -64,10 +64,12 @@ import net.fabricmc.loom.util.fmj.FabricModJsonFactory; public final class IncludedJarFactory { + private final Task task; private final Project project; - public IncludedJarFactory(Project project) { - this.project = project; + public IncludedJarFactory(Task task) { + this.task = task; + this.project = task.getProject(); } public Provider getNestedJars(final Configuration configuration) { diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index afecb1610..148e85e3d 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -67,6 +67,7 @@ import net.fabricmc.loom.configuration.providers.forge.SrgProvider; import net.fabricmc.loom.configuration.providers.forge.mcpconfig.McpConfigProvider; import net.fabricmc.loom.configuration.providers.forge.minecraft.ForgeMinecraftProvider; +import net.fabricmc.loom.configuration.providers.mappings.GeneratedIntermediateMappingsProvider; import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; @@ -79,7 +80,9 @@ import net.fabricmc.loom.configuration.sources.ForgeSourcesRemapper; import net.fabricmc.loom.extension.MixinExtension; import net.fabricmc.loom.util.Checksum; +import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.ExceptionUtil; +import net.fabricmc.loom.util.LoomVersions; import net.fabricmc.loom.util.gradle.GradleUtils; import net.fabricmc.loom.util.gradle.SourceSetHelper; import net.fabricmc.loom.util.service.ScopedSharedServiceManager; @@ -201,6 +204,18 @@ private synchronized void setupMinecraft(ConfigContext configContext) throws Exc // but before MinecraftPatchedProvider.provide. setupDependencyProviders(project, extension); + if (extension.isLegacyForge()) { + extension.setIntermediateMappingsProvider(GeneratedIntermediateMappingsProvider.class, provider -> { + provider.minecraftProvider = minecraftProvider; + }); + } + + if (extension.isForgeLike() && !extension.isLegacyForge()) { + // Excluded on legacy forge because it pulls in a log4j-api version newer than what forge wants and we don't + // need it anyway + project.getDependencies().add(Constants.Configurations.FORGE_EXTRA, LoomVersions.UNPROTECT.mavenNotation()); + } + final DependencyInfo mappingsDep = DependencyInfo.create(getProject(), Configurations.MAPPINGS); final MappingConfiguration mappingConfiguration = MappingConfiguration.create(getProject(), configContext.serviceManager(), mappingsDep, minecraftProvider); extension.setMappingConfiguration(mappingConfiguration); @@ -380,9 +395,9 @@ public static void setupDependencyProviders(Project project, LoomGradleExtension } if (extension.isForgeLike()) { + 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/LoomConfigurations.java b/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java index bb2b17354..c2f213b3d 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java @@ -158,7 +158,7 @@ public void run() { extendsFrom(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.FORGE_EXTRA); // Add Forge/NeoForge shared dev-time dependencies - getDependencies().add(Constants.Configurations.FORGE_EXTRA, LoomVersions.UNPROTECT.mavenNotation()); + // Note: Unprotect is set up later in CompileConfiguration because we need to skip it on legacy forge getDependencies().add(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME, LoomVersions.JAVAX_ANNOTATIONS.mavenNotation()); // Add Forge-only dev-time dependencies diff --git a/src/main/java/net/fabricmc/loom/configuration/decompile/SingleJarDecompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/decompile/SingleJarDecompileConfiguration.java index d7ee6df6e..6f623aa86 100644 --- a/src/main/java/net/fabricmc/loom/configuration/decompile/SingleJarDecompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/decompile/SingleJarDecompileConfiguration.java @@ -75,7 +75,7 @@ public final void afterEvaluation() { }); // TODO: Support for env-only jars? - if (extension.isForge() && extension.getMinecraftJarConfiguration().get() == MinecraftJarConfiguration.MERGED) { + if (extension.isForge() && !extension.isLegacyForge() && extension.getMinecraftJarConfiguration().get() == MinecraftJarConfiguration.MERGED) { project.getTasks().register("genForgePatchedSources", GenerateForgePatchedSourcesTask.class, task -> { task.setDescription("Decompile Minecraft using Forge's toolchain."); task.setGroup(Constants.TaskGroup.FABRIC); diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/dependency/LocalMavenHelper.java b/src/main/java/net/fabricmc/loom/configuration/mods/dependency/LocalMavenHelper.java index 33af9b963..61c6d780e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/dependency/LocalMavenHelper.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/dependency/LocalMavenHelper.java @@ -51,7 +51,7 @@ public LocalMavenHelper(String group, String name, String version, @Nullable Str } public Path copyToMaven(Path artifact, @Nullable String classifier) throws IOException { - if (!artifact.getFileName().toString().endsWith(".jar")) { + if (!artifact.getFileName().toString().endsWith(".jar") && !artifact.getFileName().toString().endsWith(".zip")) { throw new UnsupportedOperationException(); } 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 c9ef74022..646ad9c46 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 @@ -109,6 +109,13 @@ public static String createForgeMappingsIdentifier(LoomGradleExtension extension protected void manipulateMappings(Project project, Path mappingsJar) throws IOException { Stopwatch stopwatch = Stopwatch.createStarted(); LoomGradleExtension extension = LoomGradleExtension.get(project); + + if (extension.isLegacyForge()) { + // Legacy forge patches are in official namespace, so if the type of a field is changed by them, then that + // is effectively a new field and not traceable to any mapping. Therefore this does not apply to it. + return; + } + this.rawTinyMappings = tinyMappings; this.rawTinyMappingsWithSrg = tinyMappingsWithSrg; this.rawTinyMappingsWithMojang = tinyMappingsWithMojang; 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 c75f0408b..cefad0de2 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,19 +25,27 @@ 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.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.util.List; import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.mojang.serialization.JsonOps; import dev.architectury.loom.forge.UserdevConfig; import org.gradle.api.Project; +import org.gradle.api.artifacts.repositories.IvyArtifactRepository; import net.fabricmc.loom.configuration.DependencyInfo; +import net.fabricmc.loom.configuration.mods.dependency.LocalMavenHelper; import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.FileSystemUtil; import net.fabricmc.loom.util.ZipUtils; public class ForgeUserdevProvider extends DependencyProvider { @@ -45,6 +53,7 @@ public class ForgeUserdevProvider extends DependencyProvider { private JsonObject json; private UserdevConfig config; Path joinedPatches; + private Boolean isLegacyForge; public ForgeUserdevProvider(Project project) { super(project); @@ -59,11 +68,32 @@ 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")); + + byte[] bytes; + + try { + bytes = ZipUtils.unpack(resolved.toPath(), "config.json"); + } catch (NoSuchFileException e) { + // If we cannot find a modern config json, try the legacy/FG2-era one + try { + bytes = ZipUtils.unpack(resolved.toPath(), "dev.json"); + } catch (NoSuchFileException e1) { + e.addSuppressed(e1); + throw e; + } + } + + Files.write(configJson, bytes); } try (Reader reader = Files.newBufferedReader(configJson)) { json = new Gson().fromJson(reader, JsonObject.class); + isLegacyForge = !json.has("mcp"); + + if (isLegacyForge) { + json = createManifestFromForgeGradle2(dependency, json); + } + config = UserdevConfig.CODEC.parse(JsonOps.INSTANCE, json) .getOrThrow(false, msg -> getProject().getLogger().error("Couldn't read userdev config, {}", msg)); } @@ -76,11 +106,118 @@ public void provide(DependencyInfo dependency) throws Exception { addDependency(config.universal(), Constants.Configurations.FORGE_UNIVERSAL); - if (Files.notExists(joinedPatches)) { + if (!isLegacyForge && Files.notExists(joinedPatches)) { Files.write(joinedPatches, ZipUtils.unpack(userdevJar.toPath(), config.binpatches())); } } + private JsonObject createManifestFromForgeGradle2(DependencyInfo dependency, JsonObject fg2Json) throws IOException { + JsonObject json = new JsonObject(); + + addLegacyMCPRepo(); + String mcVersion = fg2Json.get("inheritsFrom").getAsString(); + json.addProperty("mcp", "de.oceanlabs.mcp:mcp:" + mcVersion + ":srg@zip"); + + json.addProperty("universal", dependency.getDepString() + ":universal"); + json.addProperty("sources", createLegacySources(dependency)); + json.addProperty("patches", ""); + json.addProperty("binpatches", ""); + json.add("binpatcher", createLegacyBinpatcher()); + json.add("libraries", createLegacyLibs(fg2Json)); + json.add("runs", createLegacyRuns()); + + return json; + } + + private static JsonObject createLegacyBinpatcher() { + JsonObject binpatcher = new JsonObject(); + binpatcher.addProperty("version", "net.minecraftforge:binarypatcher:1.1.1:fatjar"); + JsonArray args = new JsonArray(); + List.of("--clean", "{clean}", "--output", "{output}", "--apply", "{patch}").forEach(args::add); + binpatcher.add("args", args); + return binpatcher; + } + + private static JsonArray createLegacyLibs(JsonObject json) { + JsonArray array = new JsonArray(); + + for (JsonElement lib : json.getAsJsonArray("libraries")) { + array.add(lib.getAsJsonObject().get("name")); + } + + return array; + } + + private static JsonObject createLegacyRuns() { + JsonObject clientRun = new JsonObject(); + JsonObject serverRun = new JsonObject(); + clientRun.addProperty("name", "client"); + serverRun.addProperty("name", "server"); + clientRun.addProperty("main", Constants.LegacyForge.LAUNCH_WRAPPER); + serverRun.addProperty("main", Constants.LegacyForge.LAUNCH_WRAPPER); + JsonArray clientArgs = new JsonArray(); + JsonArray serverArgs = new JsonArray(); + clientArgs.add("--tweakClass"); + serverArgs.add("--tweakClass"); + clientArgs.add(Constants.LegacyForge.FML_TWEAKER); + serverArgs.add(Constants.LegacyForge.FML_SERVER_TWEAKER); + clientArgs.add("--accessToken"); + serverArgs.add("--accessToken"); + clientArgs.add("undefined"); + serverArgs.add("undefined"); + clientRun.add("args", clientArgs); + serverRun.add("args", serverArgs); + JsonObject runs = new JsonObject(); + runs.add("client", clientRun); + runs.add("server", serverRun); + return runs; + } + + private String createLegacySources(DependencyInfo dependency) throws IOException { + Path sourceRepo = getExtension().getForgeProvider().getGlobalCache().toPath().resolve("source-repo"); + String group = dependency.getDependency().getGroup(); + String name = dependency.getDependency().getName() + "_sources"; + String version = dependency.getResolvedVersion(); + LocalMavenHelper sourcesMaven = new LocalMavenHelper(group, name, version, "sources", sourceRepo); + getProject().getRepositories().maven(repo -> { + repo.setName("LoomFG2Source"); + repo.setUrl(sourceRepo); + }); + + if (!sourcesMaven.exists(null)) { + try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(userdevJar.toPath(), false)) { + sourcesMaven.copyToMaven(fs.getPath("sources.zip"), null); + } + } + + return sourcesMaven.getNotation(); + } + + 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); + }); + } + + public boolean isLegacyForge() { + if (isLegacyForge == null) { + throw new IllegalArgumentException("Not yet resolved."); + } + + return isLegacyForge; + } + public File getUserdevJar() { return userdevJar; } 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 4c7b3463c..1ed6dc864 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 @@ -96,13 +96,13 @@ public class MinecraftPatchedProvider { private static final String LOOM_PATCH_VERSION_KEY = "Loom-Patch-Version"; - private static final String CURRENT_LOOM_PATCH_VERSION = "9"; + private static final String CURRENT_LOOM_PATCH_VERSION = "9+essential.1"; private static final String NAME_MAPPING_SERVICE_PATH = "/inject/META-INF/services/cpw.mods.modlauncher.api.INameMappingService"; - private final Project project; - private final Logger logger; - private final MinecraftProvider minecraftProvider; - private final Type type; + protected final Project project; + protected final Logger logger; + protected final MinecraftProvider minecraftProvider; + protected final Type type; // Step 1: Remap Minecraft to intermediate mappings, merge if needed private Path minecraftIntermediateJar; @@ -346,7 +346,7 @@ private File getForgeUserdevJar() { return getExtension().getForgeUserdevProvider().getUserdevJar(); } - private boolean isPatchedJarUpToDate(Path jar) throws IOException { + protected boolean isPatchedJarUpToDate(Path jar) throws IOException { if (Files.notExists(jar)) return false; byte[] manifestBytes = ZipUtils.unpackNullable(jar, "META-INF/MANIFEST.MF"); @@ -460,7 +460,7 @@ private void patchJars() throws Exception { logger.lifecycle(":patched jars in " + stopwatch.stop()); } - private void patchJars(Path clean, Path output, Path patches) { + protected void patchJars(Path clean, Path output, Path patches) throws Exception { ForgeToolExecutor.exec(project, spec -> { UserdevConfig.BinaryPatcherConfig config = getExtension().getForgeUserdevProvider().getConfig().binpatcher(); spec.classpath(DependencyDownloader.download(project, config.dependency())); @@ -503,11 +503,11 @@ private void walkFileSystems(Path source, Path target, Predicate filter, F } } - private void walkFileSystems(Path source, Path target, Predicate filter, FsPathConsumer action) throws IOException { + protected void walkFileSystems(Path source, Path target, Predicate filter, FsPathConsumer action) throws IOException { walkFileSystems(source, target, filter, FileSystem::getRootDirectories, action); } - private void copyMissingClasses(Path source, Path target) throws IOException { + protected void copyMissingClasses(Path source, Path target) throws IOException { walkFileSystems(source, target, it -> it.toString().endsWith(".class"), (sourceFs, targetFs, sourcePath, targetPath) -> { if (Files.exists(targetPath)) return; Path parent = targetPath.getParent(); @@ -529,7 +529,7 @@ private void copyNonClassFiles(Path source, Path target) throws IOException { walkFileSystems(source, target, filter, this::copyReplacing); } - private void copyReplacing(FileSystem sourceFs, FileSystem targetFs, Path sourcePath, Path targetPath) throws IOException { + protected void copyReplacing(FileSystem sourceFs, FileSystem targetFs, Path sourcePath, Path targetPath) throws IOException { Path parent = targetPath.getParent(); if (parent != null) { @@ -606,7 +606,7 @@ public enum Type { SERVER_ONLY("server", "server", (patch, userdev) -> patch.serverPatches), MERGED("merged", "joined", (patch, userdev) -> userdev.joinedPatches); - private final String id; + public final String id; private final String mcpId; private final BiFunction patches; 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..fd50e1601 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,15 +24,34 @@ package net.fabricmc.loom.configuration.providers.forge; +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; + +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 org.apache.commons.io.IOUtils; import org.gradle.api.Project; import net.fabricmc.loom.configuration.DependencyInfo; +import net.fabricmc.loom.configuration.providers.forge.fg2.Pack200Provider; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.FileSystemUtil; @@ -51,11 +70,17 @@ 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")); + } } } } @@ -72,6 +97,73 @@ private void init() { } } + 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(); + } + } + } + + 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); + } + } + @Override public String getTargetConfig() { return Constants.Configurations.FORGE_INSTALLER; 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 a6a25738d..c27c55874 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 @@ -26,13 +26,18 @@ import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStreamReader; 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.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.HashMap; @@ -41,6 +46,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; @@ -80,7 +87,22 @@ 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")); + + try { + Files.write(srg, ZipUtils.unpack(srgZip, "config/joined.tsrg")); + } catch (NoSuchFileException e) { + try { + // FG2-era MCP uses the older SRG format, convert it on the fly + byte[] srgBytes = ZipUtils.unpack(srgZip, "joined.srg"); + + try (Reader reader = new InputStreamReader(new ByteArrayInputStream(srgBytes)); Writer writer = Files.newBufferedWriter(srg)) { + new TSrgWriter(writer).write(new SrgReader(reader).read()); + } + } catch (NoSuchFileException e1) { + e.addSuppressed(e1); + throw e; + } + } } try (BufferedReader reader = Files.newBufferedReader(srg)) { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java new file mode 100644 index 000000000..c67882f4d --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java @@ -0,0 +1,502 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.providers.forge.fg2; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StringReader; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Arrays; +import java.util.Iterator; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import java.util.stream.Stream; + +import com.google.common.base.Stopwatch; +import dev.architectury.loom.util.MappingOption; +import org.cadixdev.at.AccessTransformSet; +import org.cadixdev.at.io.AccessTransformFormats; +import org.cadixdev.lorenz.MappingSet; +import org.gradle.api.Project; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.providers.forge.ForgeProvider; +import net.fabricmc.loom.configuration.providers.forge.MinecraftPatchedProvider; +import net.fabricmc.loom.configuration.providers.forge.PatchProvider; +import net.fabricmc.loom.configuration.providers.mappings.TinyMappingsService; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.FileSystemUtil; +import net.fabricmc.loom.util.Pair; +import net.fabricmc.loom.util.ThreadingUtils; +import net.fabricmc.loom.util.TinyRemapperHelper; +import net.fabricmc.loom.util.ZipUtils; +import net.fabricmc.loom.util.legacyforge.CoreModManagerTransformer; +import net.fabricmc.loom.util.service.ScopedSharedServiceManager; +import net.fabricmc.loom.util.service.SharedServiceManager; +import net.fabricmc.loom.util.srg.AccessTransformSetMapper; +import net.fabricmc.lorenztiny.TinyMappingsReader; +import net.fabricmc.mappingio.tree.MappingTree; +import net.fabricmc.stitch.merge.JarMerger; + +public class MinecraftLegacyPatchedProvider extends MinecraftPatchedProvider { + // Step 1: Binary Patch + private Path minecraftClientPatchedJar; + private Path minecraftServerPatchedJar; + // Step 2: Merge (global) + private Path minecraftMergedPatchedJar; + // Step 4: Access Transform (global or project) + private Path minecraftPatchedAtJar; + + private Path forgeJar; + + private boolean dirty; + + public MinecraftLegacyPatchedProvider(Project project, MinecraftProvider minecraftProvider, Type type) { + super(project, minecraftProvider, type); + } + + private LoomGradleExtension getExtension() { + return LoomGradleExtension.get(project); + } + + @Override + public void provide() throws Exception { + String forgeVersion = getExtension().getForgeProvider().getVersion().getCombined(); + Path forgeWorkingDir = ForgeProvider.getForgeCache(project); + String patchId = "forge-" + forgeVersion + "-"; + + minecraftProvider.setJarPrefix(patchId); + + minecraftClientPatchedJar = forgeWorkingDir.resolve("client-patched.jar"); + minecraftServerPatchedJar = forgeWorkingDir.resolve("server-patched.jar"); + minecraftMergedPatchedJar = forgeWorkingDir.resolve("merged-patched.jar"); + minecraftPatchedAtJar = forgeWorkingDir.resolve(type.id + "-at-patched.jar"); + forgeJar = forgeWorkingDir.resolve("forge.jar"); + + checkCache(); + + dirty = false; + } + + private void cleanAllCache() throws IOException { + for (Path path : getGlobalCaches()) { + Files.deleteIfExists(path); + } + } + + private Path[] getGlobalCaches() { + return new Path[] { + minecraftClientPatchedJar, + minecraftServerPatchedJar, + minecraftMergedPatchedJar, + minecraftPatchedAtJar, + forgeJar, + }; + } + + protected void checkCache() throws IOException { + if (getExtension().refreshDeps() || Stream.of(getGlobalCaches()).anyMatch(Files::notExists) + || !isPatchedJarUpToDate(minecraftPatchedAtJar) || !isPatchedJarUpToDate(forgeJar)) { + cleanAllCache(); + } + } + + @Override + public void remapJar() throws Exception { + if (Files.notExists(forgeJar)) { + dirty = true; + + try (var serviceManager = new ScopedSharedServiceManager()) { + patchForge(serviceManager); + } + + applyLoomPatchVersion(forgeJar); + } + + if (Files.notExists(minecraftClientPatchedJar) || Files.notExists(minecraftServerPatchedJar)) { + dirty = true; + patchJars(); + } + + if (type == Type.MERGED && (dirty || Files.notExists(minecraftMergedPatchedJar))) { + dirty = true; + mergeJars(); + } + + if (dirty || Files.notExists(minecraftPatchedAtJar)) { + dirty = true; + Path minecraftPatchedJar = switch (type) { + case CLIENT_ONLY -> minecraftClientPatchedJar; + case SERVER_ONLY -> minecraftServerPatchedJar; + case MERGED -> minecraftMergedPatchedJar; + }; + accessTransform(project, minecraftPatchedJar, minecraftPatchedAtJar); + walkFileSystems(forgeJar, minecraftPatchedAtJar, (path) -> true, this::copyReplacing); + applyLoomPatchVersion(minecraftPatchedAtJar); + } + } + + private void patchForge(SharedServiceManager serviceManager) throws Exception { + Stopwatch stopwatch = Stopwatch.createStarted(); + logger.lifecycle(":patching forge"); + + Files.copy(getExtension().getForgeUniversalProvider().getForge().toPath(), forgeJar, StandardCopyOption.REPLACE_EXISTING); + + try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(forgeJar, false)) { + // For the development environment, we need to remove the binpatches, otherwise forge will try to re-apply them + Files.delete(fs.get().getPath("binpatches.pack.lzma")); + + // We also need to remove Forge's jar signature data + // Based off https://github.com/FabricMC/tiny-remapper/blob/7f341dcbf1bae754ba992f0b0f127566f347f37f/src/main/java/net/fabricmc/tinyremapper/MetaInfFixer.java + Files.walkFileTree(fs.get().getPath("META-INF"), new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(Path path, BasicFileAttributes attributes) throws IOException { + String fileName = path.getFileName().toString(); + + if (fileName.endsWith(".SF") || fileName.endsWith(".DSA") || fileName.endsWith(".RSA") || fileName.endsWith(".EC") || fileName.startsWith("SIG-")) { + Files.delete(path); + } else if (path.getNameCount() == 2 && fileName.equals("MANIFEST.MF")) { + Manifest manifest = new Manifest(); + + try (InputStream is = Files.newInputStream(path)) { + manifest.read(is); + } + + for (Iterator it = manifest.getEntries().values().iterator(); it.hasNext(); ) { + Attributes attrs = it.next(); + + for (Iterator it2 = attrs.keySet().iterator(); it2.hasNext(); ) { + Attributes.Name attrName = (Attributes.Name) it2.next(); + String name = attrName.toString(); + + if (name.endsWith("-Digest") || name.contains("-Digest-") || name.equals("Magic")) { + it2.remove(); + } + } + + if (attrs.isEmpty()) it.remove(); + } + + try (OutputStream os = Files.newOutputStream(path)) { + manifest.write(os); + } + } + + return FileVisitResult.CONTINUE; + } + }); + } + + // 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, forgeJar, 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(forgeJar, 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(); + }))); + + // Remap the legacy srg access transformers into modern style official ones so `accessTransform` picks them up + String ats = new String(ZipUtils.unpack(forgeJar, "forge_at.cfg"), StandardCharsets.UTF_8); + ats = remapAts(serviceManager, ats); + ZipUtils.add(forgeJar, Constants.Forge.ACCESS_TRANSFORMER_PATH, ats); + + logger.lifecycle(":patched forge in " + stopwatch.stop()); + } + + private void patchJars() throws Exception { + Stopwatch stopwatch = Stopwatch.createStarted(); + logger.lifecycle(":patching jars"); + + MinecraftProvider minecraftProvider = getExtension().getMinecraftProvider(); + PatchProvider patchProvider = getExtension().getPatchProvider(); + patchJars(minecraftProvider.getMinecraftServerJar().toPath(), minecraftServerPatchedJar, patchProvider.serverPatches); + patchJars(minecraftProvider.getMinecraftClientJar().toPath(), minecraftClientPatchedJar, patchProvider.clientPatches); + + logger.lifecycle(":patched jars in " + stopwatch.stop()); + } + + @Override + protected void patchJars(Path clean, Path output, Path patches) throws Exception { + super.patchJars(clean, output, patches); + + // Patching only preserves affected classes, everything else we need to copy manually + copyMissingClasses(clean, output); + walkFileSystems(clean, output, file -> !file.toString().endsWith(".class"), this::copyReplacing); + + // Workaround Forge patches apparently violating the JVM spec (see ParameterAnnotationsFixer for details) + modifyClasses(output, ParameterAnnotationsFixer::new); + } + + private void mergeJars() throws Exception { + logger.info(":merging jars"); + Stopwatch stopwatch = Stopwatch.createStarted(); + + try (JarMerger jarMerger = new JarMerger(minecraftClientPatchedJar.toFile(), minecraftServerPatchedJar.toFile(), minecraftMergedPatchedJar.toFile())) { + jarMerger.enableSyntheticParamsOffset(); + jarMerger.merge(); + } + + // The JarMerger adds Sided annotations but so do the Forge patches. The latter doesn't require extra + // dependencies beyond Forge, so we'll keep those and convert any non-redundant Fabric ones. + modifyClasses(minecraftMergedPatchedJar, SideAnnotationMerger::new); + + logger.info(":merged jars in " + stopwatch); + } + + private String remapAts(SharedServiceManager serviceManager, String ats) throws Exception { + AccessTransformSet accessTransformSet = AccessTransformSet.create(); + AccessTransformFormats.FML.read(new StringReader(ats), accessTransformSet); + + TinyMappingsService mappingsService = getExtension().getMappingConfiguration().getMappingsService(serviceManager, MappingOption.WITH_SRG); + MappingTree mappingTree = mappingsService.getMappingTree(); + MappingSet mappingSet = new TinyMappingsReader(mappingTree, "srg", "official").read(); + accessTransformSet = AccessTransformSetMapper.remap(accessTransformSet, mappingSet); + + StringWriter remappedOut = new StringWriter(); + // TODO the extra BufferedWriter wrapper and closing can be removed once https://github.com/CadixDev/at/issues/6 is fixed + BufferedWriter writer = new BufferedWriter(remappedOut); + AccessTransformFormats.FML.write(writer, accessTransformSet); + writer.close(); + return remappedOut.toString(); + } + + private void modifyClasses(Path jarFile, Function func) throws Exception { + try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(jarFile, false)) { + ThreadingUtils.TaskCompleter completer = ThreadingUtils.taskCompleter(); + + for (Path file : (Iterable) Files.walk(fs.getPath("/"))::iterator) { + if (!file.toString().endsWith(".class")) continue; + + completer.add(() -> { + byte[] original = Files.readAllBytes(file); + + ClassReader reader = new ClassReader(original); + ClassWriter writer = new ClassWriter(reader, 0); + reader.accept(func.apply(writer), 0); + + byte[] modified = writer.toByteArray(); + + if (!Arrays.equals(original, modified)) { + Files.write(file, modified, StandardOpenOption.TRUNCATE_EXISTING); + } + }); + } + + completer.complete(); + } + } + + @Override + public Path getMinecraftIntermediateJar() { + throw new UnsupportedOperationException(); + } + + @Override + public Path getMinecraftPatchedIntermediateJar() { + return forgeJar; // TODO current used only for ATs + } + + @Override + public Path getMinecraftPatchedJar() { + return minecraftPatchedAtJar; + } + + /** + * It seems that Forge patches produce class files which are in violation of the JVM spec. Specifically, the value + * of Runtime[In]VisibleParameterAnnotation.num_parameters is by SE8 spec required to match the number of formal + * parameters of the method (and may be ignored in favor of directly looking at the method arguments, which is + * indeed what the OpenJDK 8 compiler does). Using a smaller value (possible if e.g. the last parameter has no + * annotations) will cause the compiler to read past the end of the table, throwing an exception and therefore being + * unable to read the class file. + *
+ * This class visitor fixes that by ignoring the original num_parameters value, letting the MethodVisitor compute a + * new value based on its signature. This will at first produce an invalid count when there are synthetic parameters + * but later, during mergeJars, those will be properly offset (enableSyntheticParamsOffset). + *
+ * SE8 JVM spec: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.18 + * Example method affected: RenderGlobal.ContainerLocalRenderInformation(RenderChunk, EnumFacing, int) + */ + private static class ParameterAnnotationsFixer extends ClassVisitor { + private ParameterAnnotationsFixer(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); + + // This issue has so far only been observed with constructors, so we can skip everything else + if (name.equals("")) { + methodVisitor = new MethodFixer(methodVisitor); + } + + return methodVisitor; + } + + private static class MethodFixer extends MethodVisitor { + private MethodFixer(MethodVisitor methodVisitor) { + super(Opcodes.ASM9, methodVisitor); + } + + @Override + public void visitAnnotableParameterCount(int parameterCount, boolean visible) { + // Not calling visitAnnotableParameterCount will cause it to compute its value from the method signature + } + } + } + + private static class SideAnnotationMerger extends ClassVisitor { + private static final String FABRIC_ANNOTATION_DESCRIPTOR = "Lnet/fabricmc/api/Environment;"; + private static final String FORGE_ANNOTATION_DESCRIPTOR = "Lnet/minecraftforge/fml/relauncher/SideOnly;"; + private static final String FORGE_SIDE_DESCRIPTOR = "Lnet/minecraftforge/fml/relauncher/Side;"; + + private static boolean isSideAnnotation(String descriptor) { + return FABRIC_ANNOTATION_DESCRIPTOR.equals(descriptor) || FORGE_ANNOTATION_DESCRIPTOR.equals(descriptor); + } + + private boolean visitedAnnotation; + + private SideAnnotationMerger(ClassVisitor classVisitor) { + super(Opcodes.ASM9, classVisitor); + } + + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + if (isSideAnnotation(descriptor)) { + if (visitedAnnotation) { + return null; + } + + visitedAnnotation = true; + return new FabricToForgeConverter(super.visitAnnotation(FORGE_ANNOTATION_DESCRIPTOR, true)); + } + + return super.visitAnnotation(descriptor, visible); + } + + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + return new FieldSideAnnotationMerger(super.visitField(access, name, descriptor, signature, value)); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + return new MethodSideAnnotationMerger(super.visitMethod(access, name, descriptor, signature, exceptions)); + } + + private static class FieldSideAnnotationMerger extends FieldVisitor { + private boolean visitedAnnotation; + + private FieldSideAnnotationMerger(FieldVisitor fieldVisitor) { + super(Opcodes.ASM9, fieldVisitor); + } + + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + if (isSideAnnotation(descriptor)) { + if (visitedAnnotation) { + return null; + } + + visitedAnnotation = true; + return new FabricToForgeConverter(super.visitAnnotation(FORGE_ANNOTATION_DESCRIPTOR, true)); + } + + return super.visitAnnotation(descriptor, visible); + } + } + + private static class MethodSideAnnotationMerger extends MethodVisitor { + private boolean visitedAnnotation; + + private MethodSideAnnotationMerger(MethodVisitor methodVisitor) { + super(Opcodes.ASM9, methodVisitor); + } + + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + if (isSideAnnotation(descriptor)) { + if (visitedAnnotation) { + return null; + } + + visitedAnnotation = true; + return new FabricToForgeConverter(super.visitAnnotation(FORGE_ANNOTATION_DESCRIPTOR, true)); + } + + return super.visitAnnotation(descriptor, visible); + } + } + + private static class FabricToForgeConverter extends AnnotationVisitor { + private FabricToForgeConverter(AnnotationVisitor annotationVisitor) { + super(Opcodes.ASM9, annotationVisitor); + } + + @Override + public void visitEnum(String name, String descriptor, String value) { + super.visitEnum(name, FORGE_SIDE_DESCRIPTOR, value); + } + } + } +} 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..8912c42cf --- /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); +} 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 9bcaaf1c1..71a1e8f0f 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 @@ -28,6 +28,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.util.Objects; import com.google.gson.Gson; import com.google.gson.JsonObject; @@ -53,6 +54,37 @@ public McpConfigProvider(Project project) { public void provide(DependencyInfo dependency) throws Exception { init(dependency.getDependency().getVersion()); + if (getExtension().isLegacyForge()) { + //CHECKSTYLE:OFF + String json = """ +{ + "version": "$version", + "data": { + "mappings": "TODO mappings" + }, + "steps": { + "joined": [ + { + "type": "downloadClient" + }, + { + "type": "downloadServer" + }, + { + "name": "rename", + "type": "downloadManifest" + } + ] + }, + "functions": {} +} + """.replace("$version", Objects.requireNonNull(dependency.getDependency().getVersion())); + //CHECKSTYLE:ON + + data = McpConfigData.fromJson(new Gson().fromJson(json, JsonObject.class)); + return; + } + Path mcpZip = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve MCPConfig")).toPath(); if (!Files.exists(mcp) || !Files.exists(unpacked) || refreshDeps()) { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/minecraft/MergedForgeMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/minecraft/MergedForgeMinecraftProvider.java index 04ca74c4a..0d06dfcc4 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/minecraft/MergedForgeMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/minecraft/MergedForgeMinecraftProvider.java @@ -28,16 +28,17 @@ import java.nio.file.Path; import java.util.List; +import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.ConfigContext; import net.fabricmc.loom.configuration.providers.forge.MinecraftPatchedProvider; +import net.fabricmc.loom.configuration.providers.forge.fg2.MinecraftLegacyPatchedProvider; import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider; public final class MergedForgeMinecraftProvider extends MergedMinecraftProvider implements ForgeMinecraftProvider { - private final MinecraftPatchedProvider patchedProvider; + private MinecraftPatchedProvider patchedProvider; public MergedForgeMinecraftProvider(ConfigContext configContext) { super(configContext); - this.patchedProvider = new MinecraftPatchedProvider(configContext.project(), this, MinecraftPatchedProvider.Type.MERGED); } @Override @@ -57,6 +58,14 @@ public List getMinecraftJars() { @Override public MinecraftPatchedProvider getPatchedProvider() { + if (this.patchedProvider == null) { + if (LoomGradleExtension.get(getProject()).isModernForge()) { + this.patchedProvider = new MinecraftPatchedProvider(getProject(), this, MinecraftPatchedProvider.Type.MERGED); + } else { + this.patchedProvider = new MinecraftLegacyPatchedProvider(getProject(), this, MinecraftPatchedProvider.Type.MERGED); + } + } + return patchedProvider; } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GeneratedIntermediateMappingsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GeneratedIntermediateMappingsProvider.java new file mode 100644 index 000000000..21f9a3c5a --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GeneratedIntermediateMappingsProvider.java @@ -0,0 +1,78 @@ +package net.fabricmc.loom.configuration.providers.mappings; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import com.google.common.base.Stopwatch; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarMerger; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.util.LoggerFilter; +import net.fabricmc.mappingio.MappingReader; +import net.fabricmc.mappingio.MappingWriter; +import net.fabricmc.mappingio.format.MappingFormat; +import net.fabricmc.stitch.commands.CommandGenerateIntermediary; + +public abstract class GeneratedIntermediateMappingsProvider extends IntermediateMappingsProvider { + private static final Logger LOGGER = LoggerFactory.getLogger(GeneratedIntermediateMappingsProvider.class); + + public MinecraftProvider minecraftProvider; + + @Override + public void provide(Path tinyMappings) throws IOException { + if (Files.exists(tinyMappings)) { + return; + } + + Stopwatch stopwatch = Stopwatch.createStarted(); + LOGGER.info(":generating dummy intermediary"); + + // create a temporary folder into which stitch will output the v1 file + // we cannot just create a temporary file directly, cause stitch will try to read it if it exists + Path tmpFolder = Files.createTempDirectory("dummy-intermediary"); + Path tinyV1 = tmpFolder.resolve("intermediary-v1.tiny"); + Path mergedJar = tmpFolder.resolve("merged.jar"); + + try { + File clientJar = minecraftProvider.getMinecraftClientJar(); + File serverJar = minecraftProvider.getMinecraftServerJar(); + + try (var jarMerger = new MinecraftJarMerger(clientJar, serverJar, mergedJar.toFile())) { + jarMerger.enableSyntheticParamsOffset(); + jarMerger.merge(); + } + + CommandGenerateIntermediary command = new CommandGenerateIntermediary(); + LoggerFilter.withSystemOutAndErrSuppressed(() -> { + try { + command.run(new String[]{ mergedJar.toAbsolutePath().toString(), tinyV1.toAbsolutePath().toString() }); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException("Failed to generate intermediary", e); + } + }); + + try (MappingWriter writer = MappingWriter.create(tinyMappings, MappingFormat.TINY_2)) { + MappingReader.read(tinyV1, writer); + } + } finally { + Files.deleteIfExists(mergedJar); + Files.deleteIfExists(tinyV1); + Files.delete(tmpFolder); + } + + LOGGER.info(":generated dummy intermediary in " + stopwatch.stop()); + } + + @Override + public @NotNull String getName() { + return "generated-intermediate"; + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java index d605ea27c..1f03c3902 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java @@ -37,6 +37,8 @@ import com.google.common.base.Suppliers; import org.gradle.api.Project; import org.jetbrains.annotations.VisibleForTesting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; @@ -49,6 +51,7 @@ import net.fabricmc.mappingio.tree.MemoryMappingTree; public final class IntermediateMappingsService implements SharedService { + private static final Logger LOGGER = LoggerFactory.getLogger(IntermediateMappingsService.class); private final Path intermediaryTiny; private final Supplier memoryMappingTree = Suppliers.memoize(this::createMemoryMappingTree); 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 2e58c8391..f79d5c4b1 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 @@ -226,7 +226,10 @@ public void setupPost(Project project) throws IOException { if (Files.notExists(tinyMappingsWithSrg) || extension.refreshDeps()) { // Merge tiny mappings with srg Stopwatch stopwatch = Stopwatch.createStarted(); - ForgeMappingsMerger.ExtraMappings extraMappings = ForgeMappingsMerger.ExtraMappings.ofMojmapTsrg(getMojmapSrgFileIfPossible(project)); + // FIXME why is this special case necessary? + ForgeMappingsMerger.ExtraMappings extraMappings = extension.isLegacyForge() + ? null + : ForgeMappingsMerger.ExtraMappings.ofMojmapTsrg(getMojmapSrgFileIfPossible(project)); try (Tiny2Writer writer = new Tiny2Writer(Files.newBufferedWriter(tinyMappingsWithSrg), false)) { ForgeMappingsMerger.mergeSrg(getRawSrgFile(project), tinyMappings, extraMappings, true).accept(writer); @@ -261,7 +264,7 @@ public void applyToProject(Project project, DependencyInfo dependency) throws IO if (Files.notExists(srgToNamedSrg) || extension.refreshDeps()) { try (var serviceManager = new ScopedSharedServiceManager()) { TinyMappingsService mappingsService = getMappingsService(serviceManager, MappingOption.WITH_SRG); - SrgNamedWriter.writeTo(project.getLogger(), srgToNamedSrg, mappingsService.getMappingTree(), "srg", "named"); + SrgNamedWriter.writeTo(srgToNamedSrg, mappingsService.getMappingTree(), "srg", "named", extension.isLegacyForge()); } } } @@ -369,6 +372,7 @@ private void readAndMergeMCP(Project project, SharedServiceManager serviceManage project.getDependencies().add(provider.getTargetConfig(), "de.oceanlabs.mcp:mcp_config:" + extension.getMinecraftProvider().minecraftVersion()); Configuration configuration = project.getConfigurations().getByName(provider.getTargetConfig()); provider.provide(DependencyInfo.create(project, configuration.getDependencies().iterator().next(), configuration)); + extension.getDependencyProviders().addProvider(provider); } Path srgPath = getRawSrgFile(project); 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 2715a635a..f67f1fe4e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java +++ b/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java @@ -28,9 +28,12 @@ import java.io.IOException; import java.io.PrintStream; import java.io.UncheckedIOException; +import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -39,6 +42,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.stream.Collectors; +import java.util.stream.IntStream; import dev.architectury.loom.util.MappingOption; import org.apache.commons.io.output.NullOutputStream; @@ -246,6 +250,7 @@ private static void remapForgeSourcesInner(Project project, SharedServiceManager try (FileSystemUtil.Delegate outputFs = FileSystemUtil.getJarFileSystem(tmpOutput, true)) { Path outputFsRoot = outputFs.get().getPath("/"); mercury.rewrite(tmpInput, outputFsRoot); + fixupLineNumbers(tmpInput, outputFsRoot); } catch (Exception e) { project.getLogger().warn("Could not remap " + tmpInput + " fully!", e); } @@ -273,4 +278,41 @@ private static Map extractSources(List forgeInstallerSourc taskCompleter.complete(); return sources; } + + /** + * Mercury re-organizes imports during remapping, which can result in mismatching line information when debugging. + * This method works around the issue by forcefully re-aligning the output files with the input files by inserting + * empty lines or joining multiple lines into one. + */ + private static void fixupLineNumbers(Path srcRoot, Path outRoot) throws IOException { + Files.walkFileTree(srcRoot, new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(Path srcPath, BasicFileAttributes attrs) throws IOException { + Path outPath = outRoot.resolve(srcRoot.relativize(srcPath).toString()); + List src = Files.readAllLines(srcPath); + List out = Files.readAllLines(outPath); + int lastSrc = IntStream.range(0, src.size()).filter(i -> src.get(i).startsWith("import")).max().orElse(0); + int lastOut = IntStream.range(0, out.size()).filter(i -> out.get(i).startsWith("import")).max().orElse(0); + + if (lastSrc == lastOut) { + return FileVisitResult.CONTINUE; + } + + while (lastOut < lastSrc) { + out.add(lastOut + 1, ""); + lastOut++; + } + + while (lastSrc < lastOut && lastOut > 0) { + out.set(lastOut - 1, out.get(lastOut - 1) + out.get(lastOut)); + out.remove(lastOut); + lastOut--; + } + + Files.write(outPath, out); + + return FileVisitResult.CONTINUE; + } + }); + } } diff --git a/src/main/java/net/fabricmc/loom/extension/ForgeExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/ForgeExtensionImpl.java index af190850a..6d7040255 100644 --- a/src/main/java/net/fabricmc/loom/extension/ForgeExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/ForgeExtensionImpl.java @@ -40,6 +40,7 @@ import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.ForgeExtensionAPI; import net.fabricmc.loom.configuration.ide.RunConfigSettings; +import net.fabricmc.loom.configuration.providers.forge.fg2.Pack200Provider; public class ForgeExtensionImpl implements ForgeExtensionAPI { private final LoomGradleExtension extension; @@ -50,6 +51,7 @@ public class ForgeExtensionImpl implements ForgeExtensionAPI { private final Property useCustomMixin; private final Property useForgeLoggerConfig; private final List dataGenMods = new ArrayList<>(); // not a property because it has custom adding logic + private final Property pack200Provider; @Inject public ForgeExtensionImpl(Project project, LoomGradleExtension extension) { @@ -60,6 +62,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 +125,9 @@ public void mod(String... modIds) { } }); } + + @Override + public Property getPack200Provider() { + return pack200Provider; + } } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java index 241419087..bf90da164 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java @@ -50,7 +50,7 @@ private static File createFile(File parent, String child) { @Override public File getUserCache() { - return createFile(getGradleUserHomeDir(), "caches" + File.separator + "fabric-loom"); + return createFile(getGradleUserHomeDir(), "caches" + File.separator + "essential-loom"); } @Override diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index ff2d328dd..63b22de6a 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -145,7 +145,7 @@ public RemapJarTask() { getInjectAccessWidener().convention(false); Configuration includeConfiguration = getProject().getConfigurations().getByName(Constants.Configurations.INCLUDE); - IncludedJarFactory factory = new IncludedJarFactory(getProject()); + IncludedJarFactory factory = new IncludedJarFactory(this); if (!LoomGradleExtension.get(getProject()).isForgeLike()) { getNestedJars().from(factory.getNestedJars(includeConfiguration)); 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 5076220ed..ebcb128f1 100644 --- a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java @@ -29,7 +29,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -76,13 +76,15 @@ public void run() throws IOException { .property("client", "org.lwjgl.librarypath", nativesPath); } - if (!getExtension().isForgeLike()) { + if (!getExtension().isForgeLike() || getExtension().isLegacyForge()) { launchConfig .argument("client", "--assetIndex") .argument("client", getExtension().getMinecraftProvider().getVersionInfo().assetIndex().fabricId(getExtension().getMinecraftProvider().minecraftVersion())) .argument("client", "--assetsDir") .argument("client", assetsDirectory.getAbsolutePath()); + } + if (!getExtension().isForgeLike()) { if (getExtension().areEnvironmentSourceSetsSplit()) { launchConfig.property("client", !quilt ? "fabric.gameJarPath.client" : "loader.gameJarPath.client", getGameJarPath("client")); launchConfig.property(!quilt ? "fabric.gameJarPath" : "loader.gameJarPath", getGameJarPath("common")); @@ -133,7 +135,7 @@ public void run() throws IOException { launchConfig.property("mixin.env.remapRefMap", "true"); - if (PropertyUtil.getAndFinalize(getExtension().getForge().getUseCustomMixin())) { + if (getExtension().isModernForge() && PropertyUtil.getAndFinalize(getExtension().getForge().getUseCustomMixin())) { // See mixin remapper service in forge-runtime launchConfig .property("architectury.mixinRemapper.sourceNamespace", intermediateNs) @@ -146,7 +148,7 @@ public void run() throws IOException { if (!mixinConfigs.isEmpty()) { for (String config : mixinConfigs) { - launchConfig.argument("-mixin.config"); + launchConfig.argument(getExtension().isLegacyForge() ? "--mixin" : "-mixin.config"); launchConfig.argument(config); } } @@ -210,7 +212,7 @@ private String getClassPathGroups() { } public static class LaunchConfig { - private final Map> values = new HashMap<>(); + private final Map> values = new LinkedHashMap<>(); public LaunchConfig property(String key, String value) { return property("common", key, value); diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index dd7ccb5f5..da9d07538 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -27,7 +27,7 @@ import org.objectweb.asm.Opcodes; public class Constants { - public static final String PLUGIN_ID = "dev.architectury.loom"; + public static final String PLUGIN_ID = "gg.essential.loom"; public static final boolean PLUGIN_BETA = false; public static final boolean PLUGIN_DEPRECATED = false; public static final String LIBRARIES_BASE = "https://libraries.minecraft.net/"; @@ -168,4 +168,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/LoggerFilter.java b/src/main/java/net/fabricmc/loom/util/LoggerFilter.java index 1f9f9b96a..712172dc2 100644 --- a/src/main/java/net/fabricmc/loom/util/LoggerFilter.java +++ b/src/main/java/net/fabricmc/loom/util/LoggerFilter.java @@ -26,6 +26,7 @@ import java.io.PrintStream; +import org.apache.commons.io.output.NullOutputStream; import org.jetbrains.annotations.NotNull; public class LoggerFilter { @@ -46,4 +47,31 @@ public PrintStream printf(@NotNull String format, Object... args) { // Failed to replace logger filter, just ignore } } + + public static void withSystemOutAndErrSuppressed(CheckedRunnable block) throws T { + PrintStream previousOut = System.out; + PrintStream previousErr = System.err; + + try { + System.setOut(new PrintStream(NullOutputStream.NULL_OUTPUT_STREAM)); + System.setErr(new PrintStream(NullOutputStream.NULL_OUTPUT_STREAM)); + } catch (SecurityException ignored) { + // Failed to replace logger, just ignore + } + + try { + block.run(); + } finally { + try { + System.setOut(previousOut); + System.setErr(previousErr); + } catch (SecurityException ignored) { + // Failed to replace logger, just ignore + } + } + } + + public interface CheckedRunnable { + void run() throws T; + } } diff --git a/src/main/java/net/fabricmc/loom/util/fmj/FabricModJsonFactory.java b/src/main/java/net/fabricmc/loom/util/fmj/FabricModJsonFactory.java index bf1013706..ea6b4f46d 100644 --- a/src/main/java/net/fabricmc/loom/util/fmj/FabricModJsonFactory.java +++ b/src/main/java/net/fabricmc/loom/util/fmj/FabricModJsonFactory.java @@ -167,7 +167,7 @@ public static boolean isModJar(File file, ModPlatform platform) { public static boolean isModJar(Path input, ModPlatform platform) { if (platform.isForgeLike()) { - return ZipUtils.contains(input, "META-INF/mods.toml"); + return ZipUtils.contains(input, "META-INF/mods.toml") || ZipUtils.contains(input, "mcmod.info"); } else if (platform == ModPlatform.QUILT) { return ZipUtils.contains(input, "quilt.mod.json") || isModJar(input, ModPlatform.FABRIC); } @@ -191,7 +191,7 @@ public static boolean containsMod(FileSystemUtil.Delegate fs, ModPlatform platfo } if (platform.isForgeLike()) { - return Files.exists(fs.getPath("META-INF/mods.toml")); + return Files.exists(fs.getPath("META-INF/mods.toml")) || Files.exists(fs.getPath("mcmod.info")); } else if (platform == ModPlatform.QUILT) { return Files.exists(fs.getPath("quilt.mod.json")) || containsMod(fs, ModPlatform.FABRIC); } 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..e3ee932a4 --- /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); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/util/service/BuildSharedServiceManager.java b/src/main/java/net/fabricmc/loom/util/service/BuildSharedServiceManager.java index 6575611de..972ac61c7 100644 --- a/src/main/java/net/fabricmc/loom/util/service/BuildSharedServiceManager.java +++ b/src/main/java/net/fabricmc/loom/util/service/BuildSharedServiceManager.java @@ -27,6 +27,7 @@ import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; +import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.provider.Provider; import org.gradle.api.services.BuildService; @@ -37,6 +38,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.fabricmc.loom.LoomGradleExtension; + public abstract class BuildSharedServiceManager implements BuildService { private static final Logger LOGGER = LoggerFactory.getLogger(BuildSharedServiceManager.class); private static final String NAME = "loom:sharedServiceManager"; @@ -45,7 +48,14 @@ public abstract class BuildSharedServiceManager implements BuildService createForTask(Task task, BuildEventsListenerRegistry buildEventsListenerRegistry) { - Provider provider = task.getProject().getGradle().getSharedServices().registerIfAbsent(NAME, BuildSharedServiceManager.class, spec -> { + Project project = task.getProject(); + String name = NAME; + + if (!LoomGradleExtension.get(project).multiProjectOptimisation()) { + name += "-" + project.getPath(); + } + + Provider provider = project.getGradle().getSharedServices().registerIfAbsent(name, BuildSharedServiceManager.class, spec -> { }); task.usesService(provider); diff --git a/src/main/java/net/fabricmc/loom/util/srg/AccessTransformSetMapper.java b/src/main/java/net/fabricmc/loom/util/srg/AccessTransformSetMapper.java new file mode 100644 index 000000000..d17eabfb1 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/srg/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.util.srg; + +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)); + } +} diff --git a/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java b/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java index 456b8b1bb..cf795448c 100644 --- a/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java +++ b/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java @@ -25,23 +25,49 @@ package net.fabricmc.loom.util.srg; import java.io.IOException; +import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; +import org.cadixdev.lorenz.MappingSet; import org.cadixdev.lorenz.io.srg.SrgWriter; -import org.gradle.api.logging.Logger; import net.fabricmc.lorenztiny.TinyMappingsReader; import net.fabricmc.mappingio.tree.MappingTree; public class SrgNamedWriter { - public static void writeTo(Logger logger, Path srgFile, MappingTree mappings, String from, String to) throws IOException { + public static void writeTo(Path srgFile, MappingTree mappings, String from, String to, boolean includeIdentityMappings) throws IOException { Files.deleteIfExists(srgFile); - try (SrgWriter writer = new SrgWriter(Files.newBufferedWriter(srgFile))) { + try (SrgWriter writer = newSrgWriter(Files.newBufferedWriter(srgFile), includeIdentityMappings)) { try (TinyMappingsReader reader = new TinyMappingsReader(mappings, from, to)) { writer.write(reader.read()); } } } + + private static SrgWriter newSrgWriter(Writer writer, boolean includeIdentityMappings) { + return includeIdentityMappings ? new SrgWithIdentitiesWriter(writer) : new SrgWriter(writer); + } + + /** + * Legacy Forge's FMLDeobfuscatingRemapper requires class mappings, even if they are identity maps, but such + * mappings are filtered out by the SrgWriter. To get around that, this SrgWriter manually emits identity mappings + * before emitting all regular mappings. + */ + private static class SrgWithIdentitiesWriter extends SrgWriter { + private SrgWithIdentitiesWriter(Writer writer) { + super(writer); + } + + @Override + public void write(MappingSet mappings) { + mappings.getTopLevelClassMappings().stream() + .filter(cls -> !cls.hasDeobfuscatedName()) + .sorted(getConfig().getClassMappingComparator()) + .forEach(cls -> writer.format("CL: %s %s%n", cls.getFullObfuscatedName(), cls.getFullDeobfuscatedName())); + + super.write(mappings); + } + } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy index 7a4d82986..54f723ff7 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy @@ -54,8 +54,8 @@ class FabricAPITest extends Specification implements GradleProjectTestTrait { // Set the version to something constant gradle.buildGradle.text = gradle.buildGradle.text.replace('project.version + "+" + (ENV.GITHUB_RUN_NUMBER ? "" : "local-") + getBranch()', "\"$API_VERSION\"") - .replace('id "fabric-loom" version "0.9.50"', 'id "dev.architectury.loom"') - .replace('"fabric-loom"', '"dev.architectury.loom"') + .replace('id "fabric-loom" version "0.9.50"', 'id "gg.essential.loom"') + .replace('"fabric-loom"', '"gg.essential.loom"') def server = ServerRunner.create(gradle.projectDir, "23w33a") .withMod(gradle.getOutputFile("fabric-api-${API_VERSION}.jar")) 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 c4c0edebf..170c93d23 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 @@ -65,5 +65,7 @@ class ForgeTest extends Specification implements GradleProjectTestTrait { '1.16.5' | '36.2.4' | '8' | '"de.oceanlabs.mcp:mcp_snapshot:20210309-1.16.5"' '1.14.4' | "28.2.23" | '8' | "loom.officialMojangMappings()" '1.14.4' | "28.2.23" | '8' | '"net.fabricmc:yarn:1.14.4+build.18:v2"' + '1.12.2' | "14.23.0.2486" | '8' | '"de.oceanlabs.mcp:mcp_snapshot:20170615-1.12"' + '1.8.9' | "11.15.1.2318-1.8.9" | '8' | '"de.oceanlabs.mcp:mcp_stable:22-1.8.9"' } } diff --git a/src/test/resources/projects/accesswidener/build.gradle b/src/test/resources/projects/accesswidener/build.gradle index db62caeee..264b3c718 100644 --- a/src/test/resources/projects/accesswidener/build.gradle +++ b/src/test/resources/projects/accesswidener/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/decompile/build.gradle b/src/test/resources/projects/decompile/build.gradle index 103c11373..497fe0db4 100644 --- a/src/test/resources/projects/decompile/build.gradle +++ b/src/test/resources/projects/decompile/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } dependencies { diff --git a/src/test/resources/projects/dependencyResolutionManagement/basic/build.gradle b/src/test/resources/projects/dependencyResolutionManagement/basic/build.gradle index 9d80b1bad..8964b2de9 100644 --- a/src/test/resources/projects/dependencyResolutionManagement/basic/build.gradle +++ b/src/test/resources/projects/dependencyResolutionManagement/basic/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/dependencyResolutionManagement/projmap/build.gradle b/src/test/resources/projects/dependencyResolutionManagement/projmap/build.gradle index 7b772547f..e15cf07f4 100644 --- a/src/test/resources/projects/dependencyResolutionManagement/projmap/build.gradle +++ b/src/test/resources/projects/dependencyResolutionManagement/projmap/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/dependencyResolutionManagement/settings.gradle b/src/test/resources/projects/dependencyResolutionManagement/settings.gradle index fda5e86c6..862ae6b0b 100644 --- a/src/test/resources/projects/dependencyResolutionManagement/settings.gradle +++ b/src/test/resources/projects/dependencyResolutionManagement/settings.gradle @@ -1,11 +1,11 @@ pluginManagement { plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } } plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } dependencyResolutionManagement { diff --git a/src/test/resources/projects/forge/accessTransformer/build.gradle b/src/test/resources/projects/forge/accessTransformer/build.gradle index cecc62d24..dee899067 100644 --- a/src/test/resources/projects/forge/accessTransformer/build.gradle +++ b/src/test/resources/projects/forge/accessTransformer/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/forge/aw2At/build.gradle b/src/test/resources/projects/forge/aw2At/build.gradle index 7ed9dca74..5e14fde58 100644 --- a/src/test/resources/projects/forge/aw2At/build.gradle +++ b/src/test/resources/projects/forge/aw2At/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/forge/externalModDependency/build.gradle b/src/test/resources/projects/forge/externalModDependency/build.gradle index fede83e97..e2734f1b0 100644 --- a/src/test/resources/projects/forge/externalModDependency/build.gradle +++ b/src/test/resources/projects/forge/externalModDependency/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/forge/legacyAw2AtMojmap/build.gradle b/src/test/resources/projects/forge/legacyAw2AtMojmap/build.gradle index f0326ce3f..125becb42 100644 --- a/src/test/resources/projects/forge/legacyAw2AtMojmap/build.gradle +++ b/src/test/resources/projects/forge/legacyAw2AtMojmap/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/forge/legacyAw2AtYarn/build.gradle b/src/test/resources/projects/forge/legacyAw2AtYarn/build.gradle index 967f25906..927a591ea 100644 --- a/src/test/resources/projects/forge/legacyAw2AtYarn/build.gradle +++ b/src/test/resources/projects/forge/legacyAw2AtYarn/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/forge/loggerConfig/build.gradle b/src/test/resources/projects/forge/loggerConfig/build.gradle index d6865c5e5..c0de33d10 100644 --- a/src/test/resources/projects/forge/loggerConfig/build.gradle +++ b/src/test/resources/projects/forge/loggerConfig/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } base { diff --git a/src/test/resources/projects/forge/simple/build.gradle b/src/test/resources/projects/forge/simple/build.gradle index 844f05b6b..cc5279ab9 100644 --- a/src/test/resources/projects/forge/simple/build.gradle +++ b/src/test/resources/projects/forge/simple/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } @@ -63,6 +63,13 @@ jar { } } +loom { + forge { + // FIXME should eventually be handled by architectury-pack200 gradle plugin? + pack200Provider = new dev.architectury.pack200.java.Pack200Adapter() + } +} + // configure the maven publication publishing { publications { diff --git a/src/test/resources/projects/forge/simpleMixinAp/build.gradle b/src/test/resources/projects/forge/simpleMixinAp/build.gradle index a8f550144..586fd69d0 100644 --- a/src/test/resources/projects/forge/simpleMixinAp/build.gradle +++ b/src/test/resources/projects/forge/simpleMixinAp/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/forge/singleJar/build.gradle b/src/test/resources/projects/forge/singleJar/build.gradle index 99d3cd63e..a6477a96a 100644 --- a/src/test/resources/projects/forge/singleJar/build.gradle +++ b/src/test/resources/projects/forge/singleJar/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/interfaceInjection/build.gradle b/src/test/resources/projects/interfaceInjection/build.gradle index 7d0a7c17e..27fe6ef50 100644 --- a/src/test/resources/projects/interfaceInjection/build.gradle +++ b/src/test/resources/projects/interfaceInjection/build.gradle @@ -1,7 +1,7 @@ // This is used by a range of tests that append to this file before running the gradle tasks. // Can be used for tests that require minimal custom setup plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/java16/build.gradle b/src/test/resources/projects/java16/build.gradle index 707cdc682..566bf2295 100644 --- a/src/test/resources/projects/java16/build.gradle +++ b/src/test/resources/projects/java16/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/kotlin/build.gradle.kts b/src/test/resources/projects/kotlin/build.gradle.kts index f1b6bb71a..cec271b59 100644 --- a/src/test/resources/projects/kotlin/build.gradle.kts +++ b/src/test/resources/projects/kotlin/build.gradle.kts @@ -3,7 +3,7 @@ import java.util.Properties plugins { kotlin("jvm") version "1.7.22" kotlin("plugin.serialization") version "1.7.22" - id("dev.architectury.loom") + id("gg.essential.loom") `maven-publish` } diff --git a/src/test/resources/projects/legacy/build.gradle b/src/test/resources/projects/legacy/build.gradle index 481c139af..4e11cdebb 100644 --- a/src/test/resources/projects/legacy/build.gradle +++ b/src/test/resources/projects/legacy/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } version = project.mod_version diff --git a/src/test/resources/projects/localFileDependency/build.gradle b/src/test/resources/projects/localFileDependency/build.gradle index 67e94cf41..8a07c78ef 100644 --- a/src/test/resources/projects/localFileDependency/build.gradle +++ b/src/test/resources/projects/localFileDependency/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/localRuntime/build.gradle b/src/test/resources/projects/localRuntime/build.gradle index bf5519d0b..a2a13648f 100644 --- a/src/test/resources/projects/localRuntime/build.gradle +++ b/src/test/resources/projects/localRuntime/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/maven/build.gradle b/src/test/resources/projects/maven/build.gradle index b79c63d5b..c0e0208e2 100644 --- a/src/test/resources/projects/maven/build.gradle +++ b/src/test/resources/projects/maven/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/mavenLibrary/build.gradle b/src/test/resources/projects/mavenLibrary/build.gradle index 5f6642f60..b266a4e3a 100644 --- a/src/test/resources/projects/mavenLibrary/build.gradle +++ b/src/test/resources/projects/mavenLibrary/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/minimalBase/build.gradle b/src/test/resources/projects/minimalBase/build.gradle index dbe70455d..42c1bfc46 100644 --- a/src/test/resources/projects/minimalBase/build.gradle +++ b/src/test/resources/projects/minimalBase/build.gradle @@ -1,7 +1,7 @@ // This is used by a range of tests that append to this file before running the gradle tasks. // Can be used for tests that require minimal custom setup plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/mixinApAutoRefmap/build.gradle b/src/test/resources/projects/mixinApAutoRefmap/build.gradle index ae698037c..fb2933bd8 100644 --- a/src/test/resources/projects/mixinApAutoRefmap/build.gradle +++ b/src/test/resources/projects/mixinApAutoRefmap/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'com.github.johnrengelman.shadow' version '7.0.0' id 'maven-publish' } diff --git a/src/test/resources/projects/mixinApSimple/build.gradle b/src/test/resources/projects/mixinApSimple/build.gradle index d368d500e..14742c4ce 100644 --- a/src/test/resources/projects/mixinApSimple/build.gradle +++ b/src/test/resources/projects/mixinApSimple/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/modJavadoc/build.gradle b/src/test/resources/projects/modJavadoc/build.gradle index 986377fdc..44aaa29a0 100644 --- a/src/test/resources/projects/modJavadoc/build.gradle +++ b/src/test/resources/projects/modJavadoc/build.gradle @@ -1,7 +1,7 @@ // This is used by a range of tests that append to this file before running the gradle tasks. // Can be used for tests that require minimal custom setup plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/mojangMappings/build.gradle b/src/test/resources/projects/mojangMappings/build.gradle index e8a66fa15..3b2446178 100644 --- a/src/test/resources/projects/mojangMappings/build.gradle +++ b/src/test/resources/projects/mojangMappings/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/multi-mc-versions/build.gradle b/src/test/resources/projects/multi-mc-versions/build.gradle index ef8182d72..1ed63ae46 100644 --- a/src/test/resources/projects/multi-mc-versions/build.gradle +++ b/src/test/resources/projects/multi-mc-versions/build.gradle @@ -2,7 +2,7 @@ import groovy.json.JsonSlurper plugins { id "java" - id 'dev.architectury.loom' apply false + id 'gg.essential.loom' apply false } allprojects { @@ -19,7 +19,7 @@ def getMappingVersion(String mcVersion) { } subprojects { - apply plugin: "dev.architectury.loom" + apply plugin: "gg.essential.loom" base { archivesName = rootProject.name + "-" + project.name diff --git a/src/test/resources/projects/multiproject/build.gradle b/src/test/resources/projects/multiproject/build.gradle index 978685622..122a612a0 100644 --- a/src/test/resources/projects/multiproject/build.gradle +++ b/src/test/resources/projects/multiproject/build.gradle @@ -1,11 +1,11 @@ plugins { id "java" id "java-library" - id 'dev.architectury.loom' apply false + id 'gg.essential.loom' apply false } allprojects { - apply plugin: "dev.architectury.loom" + apply plugin: "gg.essential.loom" version = "1.0.0" diff --git a/src/test/resources/projects/parchment/build.gradle b/src/test/resources/projects/parchment/build.gradle index 9be2eddf4..ae2510a55 100644 --- a/src/test/resources/projects/parchment/build.gradle +++ b/src/test/resources/projects/parchment/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } repositories { diff --git a/src/test/resources/projects/reproducible/build.gradle b/src/test/resources/projects/reproducible/build.gradle index c866b2655..09eefebf1 100644 --- a/src/test/resources/projects/reproducible/build.gradle +++ b/src/test/resources/projects/reproducible/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/runconfigs/build.gradle b/src/test/resources/projects/runconfigs/build.gradle index 2b146cee0..76019579c 100644 --- a/src/test/resources/projects/runconfigs/build.gradle +++ b/src/test/resources/projects/runconfigs/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } loom { diff --git a/src/test/resources/projects/signed/build.gradle b/src/test/resources/projects/signed/build.gradle index c5ff324eb..703a89593 100644 --- a/src/test/resources/projects/signed/build.gradle +++ b/src/test/resources/projects/signed/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' id 'signing' } diff --git a/src/test/resources/projects/simple/build.gradle b/src/test/resources/projects/simple/build.gradle index d17c31340..93d033cf3 100644 --- a/src/test/resources/projects/simple/build.gradle +++ b/src/test/resources/projects/simple/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/splitSources/build.gradle b/src/test/resources/projects/splitSources/build.gradle index a074a191d..be76fd008 100644 --- a/src/test/resources/projects/splitSources/build.gradle +++ b/src/test/resources/projects/splitSources/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' version '0.12.local' + id 'gg.essential.loom' version '0.12.local' id 'maven-publish' } diff --git a/src/test/resources/projects/transitiveAccesswidener/build.gradle b/src/test/resources/projects/transitiveAccesswidener/build.gradle index 986377fdc..44aaa29a0 100644 --- a/src/test/resources/projects/transitiveAccesswidener/build.gradle +++ b/src/test/resources/projects/transitiveAccesswidener/build.gradle @@ -1,7 +1,7 @@ // This is used by a range of tests that append to this file before running the gradle tasks. // Can be used for tests that require minimal custom setup plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/unpick/build.gradle b/src/test/resources/projects/unpick/build.gradle index a0d8b2234..21b8da1e9 100644 --- a/src/test/resources/projects/unpick/build.gradle +++ b/src/test/resources/projects/unpick/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } dependencies {