diff --git a/build.gradle b/build.gradle index a73752b72..287b0ce3d 100644 --- a/build.gradle +++ b/build.gradle @@ -186,6 +186,7 @@ dependencies { implementation libs.mcinjector implementation libs.opencsv implementation libs.forge.diffpatch + implementation libs.datafixerupper // Forge mods.toml parsing implementation libs.night.config.toml @@ -254,8 +255,11 @@ spotless { target 'src/**/*.gradle', '*.gradle' greclipse() - // This file uses a @MAPPINGS@ token which is not valid Groovy - targetExclude('**/projects/forge/simple/build.gradle') + targetExclude( + // These files use a @MAPPINGS@ token which is not valid Groovy + '**/projects/forge/simple/build.gradle', + '**/projects/neoforge/simple/build.gradle' + ) } kotlin { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 40cda0113..a35845722 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ jackson = "2.15.2" guava = "32.1.2-jre" stitch = "0.6.2" -tiny-remapper = "1.9.22" +tiny-remapper = "1.10.23" access-widener = "2.1.0" mapping-io = "0.4.2" lorenz-tiny = "4.0.2" @@ -28,6 +28,7 @@ mcinjector = "3.8.0" opencsv = "5.4" forge-diffpatch = "2.0.7" night-config = "3.6.6" +datafixerupper = "6.0.8" [libraries] # Loom compile libraries @@ -60,6 +61,7 @@ mcinjector = { module = "de.oceanlabs.mcp:mcinjector", version.ref = "mcinjector opencsv = { module = "com.opencsv:opencsv", version.ref = "opencsv" } forge-diffpatch = { module = "net.minecraftforge:DiffPatch", version.ref = "forge-diffpatch" } night-config-toml = { module = "com.electronwill.night-config:toml", version.ref = "night-config" } +datafixerupper = { module = "com.mojang:datafixerupper", version.ref = "datafixerupper" } [plugins] kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } diff --git a/gradle/runtime.libs.versions.toml b/gradle/runtime.libs.versions.toml index 6074369ac..fe93550da 100644 --- a/gradle/runtime.libs.versions.toml +++ b/gradle/runtime.libs.versions.toml @@ -13,7 +13,7 @@ native-support = "1.0.1" # Forge Runtime depedencies javax-annotations = "3.0.2" -forge-runtime = "1.1.8" +forge-runtime = "2.0.9" access-transformers = "3.0.1" access-transformers-new = "8.0.5" unprotect = "1.2.0" @@ -34,7 +34,9 @@ native-support = { module = "net.fabricmc:fabric-loom-native-support", version.r # Forge Runtime depedencies javax-annotations = { module = "com.google.code.findbugs:jsr305", version.ref = "javax-annotations" } -forge-runtime = { module = "dev.architectury:architectury-loom-runtime", version.ref = "forge-runtime" } +mixin-remapper-service = { module = "dev.architectury:architectury-mixin-remapper-service", version.ref = "forge-runtime" } +naming-service = { module = "dev.architectury:architectury-naming-service", version.ref = "forge-runtime" } +mcp-annotations = { module = "dev.architectury:mcp-annotations", version.ref = "forge-runtime" } access-transformers = { module = "net.minecraftforge:accesstransformers", version.ref = "access-transformers" } access-transformers-new = { module = "net.minecraftforge:accesstransformers", version.ref = "access-transformers-new" } unprotect = { module = "io.github.juuxel:unprotect", version.ref = "unprotect" } diff --git a/src/main/java/dev/architectury/loom/forge/UserdevConfig.java b/src/main/java/dev/architectury/loom/forge/UserdevConfig.java new file mode 100644 index 000000000..a22aecb55 --- /dev/null +++ b/src/main/java/dev/architectury/loom/forge/UserdevConfig.java @@ -0,0 +1,45 @@ +package dev.architectury.loom.forge; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.fabricmc.loom.configuration.providers.forge.ForgeRunTemplate; + +public record UserdevConfig( + String mcp, + String universal, + String sources, + String patches, + Optional patchesOriginalPrefix, + Optional patchesModifiedPrefix, + String binpatches, + BinaryPatcherConfig binpatcher, + List libraries, + Map runs, + List sass +) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.STRING.fieldOf("mcp").forGetter(UserdevConfig::mcp), + Codec.STRING.fieldOf("universal").forGetter(UserdevConfig::universal), + Codec.STRING.fieldOf("sources").forGetter(UserdevConfig::sources), + Codec.STRING.fieldOf("patches").forGetter(UserdevConfig::patches), + Codec.STRING.optionalFieldOf("patchesOriginalPrefix").forGetter(UserdevConfig::patchesOriginalPrefix), + Codec.STRING.optionalFieldOf("patchesModifiedPrefix").forGetter(UserdevConfig::patchesModifiedPrefix), + Codec.STRING.fieldOf("binpatches").forGetter(UserdevConfig::binpatches), + BinaryPatcherConfig.CODEC.fieldOf("binpatcher").forGetter(UserdevConfig::binpatcher), + Codec.STRING.listOf().fieldOf("libraries").forGetter(UserdevConfig::libraries), + ForgeRunTemplate.MAP_CODEC.fieldOf("runs").forGetter(UserdevConfig::runs), + Codec.STRING.listOf().optionalFieldOf("sass", List.of()).forGetter(UserdevConfig::sass) + ).apply(instance, UserdevConfig::new)); + + public record BinaryPatcherConfig(String dependency, List args) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.STRING.fieldOf("version").forGetter(BinaryPatcherConfig::dependency), + Codec.STRING.listOf().fieldOf("args").forGetter(BinaryPatcherConfig::args) + ).apply(instance, BinaryPatcherConfig::new)); + } +} diff --git a/src/main/java/dev/architectury/loom/util/ForgeLoggerConfig.java b/src/main/java/dev/architectury/loom/util/ForgeLoggerConfig.java index 6d7093010..f3667cce3 100644 --- a/src/main/java/dev/architectury/loom/util/ForgeLoggerConfig.java +++ b/src/main/java/dev/architectury/loom/util/ForgeLoggerConfig.java @@ -8,8 +8,6 @@ import java.util.List; import java.util.StringJoiner; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; import org.gradle.api.Project; import org.jetbrains.annotations.Nullable; @@ -31,18 +29,16 @@ public static void copyToPath(Project project, Path outputFile) { throw new UncheckedIOException(e); } - final JsonArray libraries = LoomGradleExtension.get(project) + final List libraries = LoomGradleExtension.get(project) .getForgeUserdevProvider() - .getJson() - .getAsJsonArray("libraries"); + .getConfig() + .libraries(); boolean found = false; - for (JsonElement library : libraries) { - final String notation = library.getAsString(); - - if (LOGGER_CONFIG_ARTIFACTS.stream().anyMatch(artifact -> artifact.matches(notation))) { + for (String library : libraries) { + if (LOGGER_CONFIG_ARTIFACTS.stream().anyMatch(artifact -> artifact.matches(library))) { final File libraryFile = project.getConfigurations() - .detachedConfiguration(project.getDependencies().create(notation)) + .detachedConfiguration(project.getDependencies().create(library)) .setTransitive(false) .getSingleFile(); diff --git a/src/main/java/dev/architectury/loom/util/MappingOption.java b/src/main/java/dev/architectury/loom/util/MappingOption.java new file mode 100644 index 000000000..47ac2565b --- /dev/null +++ b/src/main/java/dev/architectury/loom/util/MappingOption.java @@ -0,0 +1,38 @@ +package dev.architectury.loom.util; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.loom.api.LoomGradleExtensionAPI; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; + +public enum MappingOption { + DEFAULT(null), + WITH_SRG(MappingsNamespace.SRG.toString()), + WITH_MOJANG(MappingsNamespace.MOJANG.toString()); + + private final String extraNamespace; + + MappingOption(@Nullable String extraNamespace) { + this.extraNamespace = extraNamespace; + } + + public MappingOption forNamespaces(String... namespaces) { + if (extraNamespace == null) return this; + + for (String namespace : namespaces) { + if (extraNamespace.equals(namespace)) { + return this; + } + } + + return DEFAULT; + } + + public static MappingOption forPlatform(LoomGradleExtensionAPI extension) { + return switch (extension.getPlatform().get()) { + case FORGE -> WITH_SRG; + case NEOFORGE -> WITH_MOJANG; + default -> DEFAULT; + }; + } +} diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index cd32359f5..12775e34c 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -50,6 +50,7 @@ import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.library.LibraryProcessorManager; import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.MojangMappedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.SrgMinecraftProvider; import net.fabricmc.loom.extension.LoomFiles; @@ -95,6 +96,10 @@ static LoomGradleExtension get(Project project) { void setSrgMinecraftProvider(SrgMinecraftProvider srgMinecraftProvider); + MojangMappedMinecraftProvider getMojangMappedMinecraftProvider(); + + void setMojangMappedMinecraftProvider(MojangMappedMinecraftProvider srgMinecraftProvider); + default List getMinecraftJars(MappingsNamespace mappingsNamespace) { return switch (mappingsNamespace) { case NAMED -> getNamedMinecraftProvider().getMinecraftJarPaths(); @@ -104,6 +109,10 @@ default List getMinecraftJars(MappingsNamespace mappingsNamespace) { ModPlatform.assertPlatform(this, ModPlatform.FORGE, () -> "SRG jars are only available on Forge."); yield getSrgMinecraftProvider().getMinecraftJarPaths(); } + case MOJANG -> { + ModPlatform.assertPlatform(this, ModPlatform.NEOFORGE, () -> "Mojang-mapped jars are only available on NeoForge."); + yield getMojangMappedMinecraftProvider().getMinecraftJarPaths(); + } }; } @@ -149,12 +158,12 @@ default boolean isDataGenEnabled() { return isForge() && !getForge().getDataGenMods().isEmpty(); } - default boolean isForgeAndOfficial() { - return isForge() && getMcpConfigProvider().isOfficial(); + default boolean isForgeLikeAndOfficial() { + return isForgeLike() && getMcpConfigProvider().isOfficial(); } - default boolean isForgeAndNotOfficial() { - return isForge() && !getMcpConfigProvider().isOfficial(); + default boolean isForgeLikeAndNotOfficial() { + return isForgeLike() && !getMcpConfigProvider().isOfficial(); } DependencyProviders getDependencyProviders(); @@ -179,4 +188,14 @@ default ForgeProvider getForgeProvider() { ForgeRunsProvider getForgeRunsProvider(); void setForgeRunsProvider(ForgeRunsProvider forgeRunsProvider); + + /** + * The mapping file that is specific to the platform settings. + * It contains SRG (Forge/common) or Mojang mappings (NeoForge) as needed. + * + * @return the platform mapping file path + */ + default Path getPlatformMappingFile() { + return getMappingConfiguration().getPlatformMappingFile(this); + } } diff --git a/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java index 307fb69ac..17f5b3a54 100644 --- a/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java @@ -33,7 +33,7 @@ import org.jetbrains.annotations.ApiStatus; /** - * This is the forge extension api available exposed to build scripts. + * This is the Forge extension API available to build scripts. */ @ApiStatus.NonExtendable public interface ForgeExtensionAPI { diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index 7c62569ec..bdd72f9d3 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -235,10 +235,18 @@ default void splitMinecraftJar() { Provider getPlatform(); + default boolean isForgeLike() { + return getPlatform().get().isForgeLike(); + } + default boolean isForge() { return getPlatform().get() == ModPlatform.FORGE; } + default boolean isNeoForge() { + return getPlatform().get() == ModPlatform.NEOFORGE; + } + default boolean isQuilt() { return getPlatform().get() == ModPlatform.QUILT; } @@ -265,4 +273,15 @@ default void addTaskBeforeRun(String task) { ForgeExtensionAPI getForge(); void forge(Action action); + + /** + * Gets the NeoForge extension used to configure NeoForge details. + * + * @return the NeoForge extension + * @throws UnsupportedOperationException if running on another platform + * @see #isNeoForge() + */ + NeoForgeExtensionAPI getNeoForge(); + + void neoForge(Action action); } diff --git a/src/main/java/net/fabricmc/loom/api/NeoForgeExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/NeoForgeExtensionAPI.java new file mode 100644 index 000000000..9d5474b3d --- /dev/null +++ b/src/main/java/net/fabricmc/loom/api/NeoForgeExtensionAPI.java @@ -0,0 +1,50 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2023 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.api; + +import org.gradle.api.file.ConfigurableFileCollection; + +/** + * This is the NeoForge extension API available to build scripts. + */ +public interface NeoForgeExtensionAPI { + /** + * A collection of all project access transformers. + * The collection should only contain AT files, and not directories or other files. + * + *

If this collection is empty, Loom tries to resolve the AT from the default path + * ({@code META-INF/accesstransformer.cfg} in the {@code main} source set). + * + * @return the collection of AT files + */ + ConfigurableFileCollection getAccessTransformers(); + + /** + * Adds a {@linkplain #getAccessTransformers() project access transformer}. + * + * @param file the file, evaluated as per {@link org.gradle.api.Project#file(Object)} + */ + void accessTransformer(Object file); +} diff --git a/src/main/java/net/fabricmc/loom/api/mappings/layered/MappingsNamespace.java b/src/main/java/net/fabricmc/loom/api/mappings/layered/MappingsNamespace.java index 67a35168b..2d8316c7a 100644 --- a/src/main/java/net/fabricmc/loom/api/mappings/layered/MappingsNamespace.java +++ b/src/main/java/net/fabricmc/loom/api/mappings/layered/MappingsNamespace.java @@ -54,6 +54,14 @@ public enum MappingsNamespace { */ SRG, + /** + * Mojang's official names from their deobfuscation maps. + * + *

They are used as the mapping set in a NeoForge production environment akin to Fabric's + * {@linkplain #INTERMEDIARY intermediary mappings}. + */ + MOJANG, + /** * Named mappings are the developer friendly names used to develop mods against. */ @@ -70,6 +78,7 @@ public enum MappingsNamespace { case "official" -> OFFICIAL; case "intermediary" -> INTERMEDIARY; case "srg" -> SRG; + case "mojang" -> MOJANG; case "named" -> NAMED; default -> null; }; diff --git a/src/main/java/net/fabricmc/loom/build/IntermediaryNamespaces.java b/src/main/java/net/fabricmc/loom/build/IntermediaryNamespaces.java index 36f5d9583..361c5ded6 100644 --- a/src/main/java/net/fabricmc/loom/build/IntermediaryNamespaces.java +++ b/src/main/java/net/fabricmc/loom/build/IntermediaryNamespaces.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2022 FabricMC + * Copyright (c) 2022-2023 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 @@ -34,8 +34,19 @@ public final class IntermediaryNamespaces { * Returns the intermediary namespace of the project. */ public static String intermediary(Project project) { + return intermediaryNamespace(project).toString(); + } + + /** + * Returns the intermediary namespace of the project. + */ + public static MappingsNamespace intermediaryNamespace(Project project) { LoomGradleExtension extension = LoomGradleExtension.get(project); - return extension.isForge() ? "srg" : "intermediary"; + return switch (extension.getPlatform().get()) { + case FABRIC, QUILT -> MappingsNamespace.INTERMEDIARY; + case FORGE -> MappingsNamespace.SRG; + case NEOFORGE -> MappingsNamespace.MOJANG; + }; } /** diff --git a/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java b/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java index a90d5de01..9bb9912da 100644 --- a/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java +++ b/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java @@ -64,7 +64,7 @@ public static void nestJars(Collection jars, List forgeJars, F } }).collect(Collectors.toList())); - if (platform == ModPlatform.FORGE) { + if (platform.isForgeLike()) { handleForgeJarJar(forgeJars, modJar, logger); return; } diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index eea545d42..afecb1610 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -36,6 +36,7 @@ import javax.inject.Inject; import org.gradle.api.Project; +import org.gradle.api.file.FileCollection; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.tasks.AbstractCopyTask; @@ -72,6 +73,7 @@ import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.configuration.providers.minecraft.mapped.AbstractMappedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.MojangMappedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.SrgMinecraftProvider; import net.fabricmc.loom.configuration.sources.ForgeSourcesRemapper; @@ -132,7 +134,7 @@ public void run() { configureDecompileTasks(configContext); - if (extension.isForge()) { + if (extension.isForgeLike()) { if (extension.isDataGenEnabled()) { getProject().getExtensions().getByType(JavaPluginExtension.class).getSourceSets().getByName("main").resources(files -> { files.srcDir(getProject().file("src/generated/resources")); @@ -165,7 +167,7 @@ public void run() { getTasks().withType(AbstractCopyTask.class).configureEach(abstractCopyTask -> abstractCopyTask.setFilteringCharset(StandardCharsets.UTF_8.name())); getTasks().withType(JavaCompile.class).configureEach(javaCompile -> javaCompile.getOptions().setEncoding(StandardCharsets.UTF_8.name())); - if (extension.isForge()) { + if (extension.isForgeLike()) { // Create default mod from main source set extension.mods(mods -> { final SourceSet main = getProject().getExtensions().getByType(JavaPluginExtension.class).getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME); @@ -188,7 +190,7 @@ private synchronized void setupMinecraft(ConfigContext configContext) throws Exc // Provide the vanilla mc jars -- TODO share across getProject()s. final MinecraftProvider minecraftProvider = jarConfiguration.getMinecraftProviderFunction().apply(configContext); - if (extension.isForge() && !(minecraftProvider instanceof ForgeMinecraftProvider)) { + if (extension.isForgeLike() && !(minecraftProvider instanceof ForgeMinecraftProvider)) { throw new UnsupportedOperationException("Using Forge with split jars is not supported!"); } @@ -203,7 +205,7 @@ private synchronized void setupMinecraft(ConfigContext configContext) throws Exc final MappingConfiguration mappingConfiguration = MappingConfiguration.create(getProject(), configContext.serviceManager(), mappingsDep, minecraftProvider); extension.setMappingConfiguration(mappingConfiguration); - if (extension.isForge()) { + if (extension.isForgeLike()) { ForgeLibrariesProvider.provide(mappingConfiguration, project); ((ForgeMinecraftProvider) minecraftProvider).getPatchedProvider().provide(); } @@ -211,7 +213,7 @@ private synchronized void setupMinecraft(ConfigContext configContext) throws Exc mappingConfiguration.setupPost(project); mappingConfiguration.applyToProject(getProject(), mappingsDep); - if (extension.isForge()) { + if (extension.isForgeLike()) { extension.setForgeRunsProvider(ForgeRunsProvider.create(project)); } @@ -243,6 +245,10 @@ private synchronized void setupMinecraft(ConfigContext configContext) throws Exc final SrgMinecraftProvider srgMinecraftProvider = jarConfiguration.getSrgMinecraftProviderBiFunction().apply(project, minecraftProvider); extension.setSrgMinecraftProvider(srgMinecraftProvider); srgMinecraftProvider.provide(provideContext); + } else if (extension.isNeoForge()) { + final MojangMappedMinecraftProvider mojangMappedMinecraftProvider = jarConfiguration.getMojangMappedMinecraftProviderBiFunction().apply(project, minecraftProvider); + extension.setMojangMappedMinecraftProvider(mojangMappedMinecraftProvider); + mojangMappedMinecraftProvider.provide(provideContext); } } @@ -262,8 +268,16 @@ private void registerGameProcessors(ConfigContext configContext) { extension.addMinecraftJarProcessor(InterfaceInjectionProcessor.class, "fabric-loom:interface-inject", interfaceInjection.getEnableDependencyInterfaceInjection().get()); } - if (extension.isForge()) { - extension.addMinecraftJarProcessor(AccessTransformerJarProcessor.class, "loom:access-transformer", configContext.project(), extension.getForge().getAccessTransformers()); + if (extension.isForgeLike()) { + FileCollection accessTransformers; + + if (extension.isNeoForge()) { + accessTransformers = extension.getNeoForge().getAccessTransformers(); + } else { + accessTransformers = extension.getForge().getAccessTransformers(); + } + + extension.addMinecraftJarProcessor(AccessTransformerJarProcessor.class, "loom:access-transformer", configContext.project(), accessTransformers); } } @@ -356,7 +370,7 @@ public static void setupDependencyProviders(Project project, LoomGradleExtension DependencyProviders dependencyProviders = new DependencyProviders(); extension.setDependencyProviders(dependencyProviders); - if (extension.isForge()) { + if (extension.isForgeLike()) { dependencyProviders.addProvider(new ForgeProvider(project)); dependencyProviders.addProvider(new ForgeUserdevProvider(project)); } @@ -365,7 +379,7 @@ public static void setupDependencyProviders(Project project, LoomGradleExtension dependencyProviders.addProvider(new SrgProvider(project)); } - if (extension.isForge()) { + if (extension.isForgeLike()) { dependencyProviders.addProvider(new McpConfigProvider(project)); dependencyProviders.addProvider(new PatchProvider(project)); dependencyProviders.addProvider(new ForgeUniversalProvider(project)); diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java b/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java index 0aff96170..bb2b17354 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java @@ -118,9 +118,16 @@ public void run() { } }); - if (extension.isForge()) { - // Set up Forge configurations - registerNonTransitive(Constants.Configurations.FORGE, Role.RESOLVABLE); + if (extension.isForgeLike()) { + // Set up Forge and NeoForge configurations + if (extension.isForge()) { + // Forge-specific configurations + registerNonTransitive(Constants.Configurations.FORGE, Role.RESOLVABLE); + } else if (extension.isNeoForge()) { + // NeoForge-specific configurations + registerNonTransitive(Constants.Configurations.NEOFORGE, Role.RESOLVABLE); + } + registerNonTransitive(Constants.Configurations.FORGE_USERDEV, Role.RESOLVABLE); registerNonTransitive(Constants.Configurations.FORGE_INSTALLER, Role.RESOLVABLE); registerNonTransitive(Constants.Configurations.FORGE_UNIVERSAL, Role.RESOLVABLE); @@ -150,10 +157,16 @@ public void run() { extendsFrom(JavaPlugin.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.FORGE_EXTRA); extendsFrom(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.FORGE_EXTRA); - // Add Forge dev-time dependencies - getDependencies().add(Constants.Configurations.FORGE_EXTRA, LoomVersions.FORGE_RUNTIME.mavenNotation()); + // Add Forge/NeoForge shared dev-time dependencies getDependencies().add(Constants.Configurations.FORGE_EXTRA, LoomVersions.UNPROTECT.mavenNotation()); getDependencies().add(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME, LoomVersions.JAVAX_ANNOTATIONS.mavenNotation()); + + // Add Forge-only dev-time dependencies + if (extension.isForge()) { + getDependencies().add(Constants.Configurations.FORGE_EXTRA, LoomVersions.NAMING_SERVICE.mavenNotation()); + getDependencies().add(Constants.Configurations.FORGE_EXTRA, LoomVersions.MIXIN_REMAPPER_SERVICE.mavenNotation()); + getDependencies().add(Constants.Configurations.FORGE_EXTRA, LoomVersions.MCP_ANNOTATIONS.mavenNotation()); + } } } diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java index 67afe66db..7372bb11f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java @@ -37,14 +37,14 @@ public void handleDependencies(Project project, SharedServiceManager serviceMana LoomGradleExtension extension = LoomGradleExtension.get(project); SourceRemapper sourceRemapper = new SourceRemapper(project, serviceManager, true); - String platformSuffix = extension.isForge() ? "_forge" : extension.isQuilt() ? "_arch_quilt" : ""; + String platformSuffix = extension.isForgeLike() ? "_forge" : extension.isQuilt() ? "_arch_quilt" : ""; String mappingsIdentifier = extension.getMappingConfiguration().mappingsIdentifier() + platformSuffix; ModConfigurationRemapper.supplyModConfigurations(project, serviceManager, mappingsIdentifier, extension, sourceRemapper); sourceRemapper.remapAll(); - if (extension.getInstallerData() == null && !extension.isForge()) { + if (extension.getInstallerData() == null && !extension.isForgeLike()) { if (extension.isQuilt()) { project.getLogger().warn("quilt_installer.json not found in dependencies!"); } else { diff --git a/src/main/java/net/fabricmc/loom/configuration/MavenPublication.java b/src/main/java/net/fabricmc/loom/configuration/MavenPublication.java index 7fbe9702c..18991bbaa 100644 --- a/src/main/java/net/fabricmc/loom/configuration/MavenPublication.java +++ b/src/main/java/net/fabricmc/loom/configuration/MavenPublication.java @@ -100,7 +100,7 @@ private void processEntry(String scope, Configuration config, PublishingExtensio if (hasSoftwareComponent(publication) || EXCLUDED_PUBLICATIONS.contains(publication)) { continue; - } else if (!reportedDeprecation.get() && !LoomGradleExtension.get(getProject()).isForge()) { + } else if (!reportedDeprecation.get() && !LoomGradleExtension.get(getProject()).isForgeLike()) { DeprecationHelper deprecationHelper = LoomGradleExtension.get(getProject()).getDeprecationHelper(); deprecationHelper.warn("Loom is applying dependency data manually to publications instead of using a software component (from(components[\"java\"])). This is deprecated."); reportedDeprecation.set(true); diff --git a/src/main/java/net/fabricmc/loom/configuration/accesstransformer/AccessTransformerJarProcessor.java b/src/main/java/net/fabricmc/loom/configuration/accesstransformer/AccessTransformerJarProcessor.java index 5afba5234..39c0b86f7 100644 --- a/src/main/java/net/fabricmc/loom/configuration/accesstransformer/AccessTransformerJarProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/accesstransformer/AccessTransformerJarProcessor.java @@ -54,6 +54,7 @@ import net.fabricmc.loom.api.processor.MinecraftJarProcessor; import net.fabricmc.loom.api.processor.ProcessorContext; import net.fabricmc.loom.api.processor.SpecContext; +import net.fabricmc.loom.build.IntermediaryNamespaces; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.DependencyDownloader; import net.fabricmc.loom.util.ExceptionUtil; @@ -138,7 +139,7 @@ private Path mergeAndRemapAccessTransformers(ProcessorContext context, ListThis method can only be used on Forge. */ public void data() { - ModPlatform.assertPlatform(getExtension(), ModPlatform.FORGE, () -> "RunConfigSettings.data() is only usable on Forge."); + ModPlatform.assertForgeLike(getExtension(), () -> "RunConfigSettings.data() is only usable on Forge."); environment("data"); forgeTemplate("data"); } @@ -384,7 +384,7 @@ public void data() { * @since 1.0 */ public void forgeTemplate(String templateName) { - ModPlatform.assertPlatform(getExtension(), ModPlatform.FORGE); + ModPlatform.assertForgeLike(getExtension()); defaultMainClass(Constants.Forge.UNDETERMINED_MAIN_CLASS); // Evaluate later if Forge hasn't been resolved yet. evaluateNowOrLater(() -> { @@ -437,11 +437,11 @@ public void setIdeConfigGenerated(boolean ideConfigGenerated) { * {@linkplain net.fabricmc.loom.api.LoomGradleExtensionAPI#getMods global container} * declared in the {@code loom} extension. * - *

This method is currently only available on Forge. + *

This method is currently only available on Forge and NeoForge. */ @ApiStatus.Experimental public NamedDomainObjectContainer getMods() { - ModPlatform.assertPlatform(project, ModPlatform.FORGE); + ModPlatform.assertForgeLike(extension); return mods; } diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ArtifactMetadata.java b/src/main/java/net/fabricmc/loom/configuration/mods/ArtifactMetadata.java index 17bf81844..32a2d2902 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ArtifactMetadata.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ArtifactMetadata.java @@ -60,8 +60,8 @@ public static ArtifactMetadata create(ArtifactRef artifact, ModPlatform platform RemapRequirements remapRequirements = RemapRequirements.DEFAULT; InstallerData installerData = null; - // Force-remap all mods on Forge. - if (platform == ModPlatform.FORGE) { + // Force-remap all mods on Forge and NeoForge. + if (platform.isForgeLike()) { remapRequirements = RemapRequirements.OPT_IN; } diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java index e4d92b020..5958e8d7b 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java @@ -41,10 +41,12 @@ import com.google.common.base.Stopwatch; import com.google.gson.JsonObject; +import dev.architectury.loom.util.MappingOption; import dev.architectury.tinyremapper.InputTag; import dev.architectury.tinyremapper.NonClassCopyMode; import dev.architectury.tinyremapper.OutputConsumerPath; import dev.architectury.tinyremapper.TinyRemapper; +import dev.architectury.tinyremapper.extension.mixin.MixinExtension; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.attributes.Usage; @@ -166,8 +168,15 @@ private void remapJars(List remapList) throws IOException { Stopwatch stopwatch = Stopwatch.createStarted(); - boolean srg = (fromM.equals("srg") || toM.equals("srg")) && extension.isForge(); - MemoryMappingTree mappings = mappingConfiguration.getMappingsService(serviceManager, srg).getMappingTree(); + MappingOption mappingOption = MappingOption.DEFAULT; + + if ((fromM.equals(MappingsNamespace.SRG.toString()) || toM.equals(MappingsNamespace.SRG.toString())) && extension.isForge()) { + mappingOption = MappingOption.WITH_SRG; + } else if ((fromM.equals(MappingsNamespace.MOJANG.toString()) || toM.equals(MappingsNamespace.MOJANG.toString())) && extension.isNeoForge()) { + mappingOption = MappingOption.WITH_MOJANG; + } + + MemoryMappingTree mappings = mappingConfiguration.getMappingsService(serviceManager, mappingOption).getMappingTree(); LoggerFilter.replaceSystemOut(); TinyRemapper.Builder builder = TinyRemapper.newRemapper() .withKnownIndyBsm(extension.getKnownIndyBsms().get()) @@ -185,9 +194,13 @@ private void remapJars(List remapList) throws IOException { builder.extension(kotlinRemapperClassloader.getTinyRemapperExtension()); } + if (extension.isNeoForge()) { + builder.extension(new MixinExtension()); + } + final TinyRemapper remapper = builder.build(); - for (Path minecraftJar : extension.getMinecraftJars(extension.isForge() ? MappingsNamespace.SRG : MappingsNamespace.INTERMEDIARY)) { + for (Path minecraftJar : extension.getMinecraftJars(IntermediaryNamespaces.intermediaryNamespace(project))) { remapper.readClassPathAsync(minecraftJar); } @@ -264,9 +277,9 @@ private void remapJars(List remapList) throws IOException { stripNestedJars(output); remapJarManifestEntries(output); - if (extension.isForge()) { - AtRemapper.remap(project.getLogger(), output, mappings); - CoreModClassRemapper.remapJar(output, mappings, project.getLogger()); + if (extension.isForgeLike()) { + AtRemapper.remap(project, output, mappings); + CoreModClassRemapper.remapJar(project, extension.getPlatform().get(), output, mappings); } dependency.copyToCache(project, output, null); diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/ProcessorContextImpl.java b/src/main/java/net/fabricmc/loom/configuration/processors/ProcessorContextImpl.java index 2fa5cf226..20f0d16e1 100644 --- a/src/main/java/net/fabricmc/loom/configuration/processors/ProcessorContextImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/processors/ProcessorContextImpl.java @@ -24,6 +24,7 @@ package net.fabricmc.loom.configuration.processors; +import dev.architectury.loom.util.MappingOption; import dev.architectury.tinyremapper.TinyRemapper; import net.fabricmc.loom.LoomGradleExtension; @@ -64,6 +65,7 @@ public LazyCloseable createRemapper(MappingsNamespace from, Mappin @Override public MemoryMappingTree getMappings() { LoomGradleExtension extension = LoomGradleExtension.get(configContext().project()); - return extension.getMappingConfiguration().getMappingsService(configContext().serviceManager(), extension.isForge()).getMappingTree(); + final MappingOption mappingOption = MappingOption.forPlatform(extension); + return extension.getMappingConfiguration().getMappingsService(configContext().serviceManager(), mappingOption).getMappingTree(); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ConfigValue.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ConfigValue.java index ca7b36fd6..d288910fd 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ConfigValue.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ConfigValue.java @@ -24,6 +24,8 @@ package net.fabricmc.loom.configuration.providers.forge; +import com.mojang.serialization.Codec; + /** * A string or a variable in a Forge configuration file, or an MCPConfig step or function. */ @@ -41,6 +43,16 @@ public sealed interface ConfigValue { */ String LOG = "log"; + Codec CODEC = Codec.STRING.xmap(ConfigValue::of, configValue -> { + if (configValue instanceof Constant constant) { + return constant.value(); + } else if (configValue instanceof Variable variable) { + return "{" + variable.name() + "}"; + } + + throw new IllegalArgumentException("Unmatched config value"); + }); + String resolve(Resolver variableResolver); static ConfigValue of(String str) { 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 7a06ed06b..c9ef74022 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 @@ -66,8 +66,9 @@ public final class FieldMigratedMappingConfiguration extends MappingConfiguration { private List> migratedFields = new ArrayList<>(); public Path migratedFieldsCache; - public Path rawTinyMappings; - public Path rawTinyMappingsWithSrg; + private Path rawTinyMappings; + private Path rawTinyMappingsWithSrg; + private Path rawTinyMappingsWithMojang; public FieldMigratedMappingConfiguration(String mappingsIdentifier, Path mappingsWorkingDir) { super(mappingsIdentifier, mappingsWorkingDir); @@ -98,7 +99,10 @@ protected void setup(Project project, SharedServiceManager serviceManager, Minec } public static String createForgeMappingsIdentifier(LoomGradleExtension extension, String mappingsName, String version, String classifier, String minecraftVersion) { - return FieldMigratedMappingConfiguration.createMappingsIdentifier(mappingsName, version, classifier, minecraftVersion) + "-forge-" + extension.getForgeProvider().getVersion().getCombined(); + final String base = FieldMigratedMappingConfiguration.createMappingsIdentifier(mappingsName, version, classifier, minecraftVersion); + final String platform = extension.getPlatform().get().id(); + final String forgeVersion = extension.getForgeProvider().getVersion().getCombined(); + return base + "-" + platform + "-" + forgeVersion; } @Override @@ -107,23 +111,31 @@ protected void manipulateMappings(Project project, Path mappingsJar) throws IOEx LoomGradleExtension extension = LoomGradleExtension.get(project); this.rawTinyMappings = tinyMappings; this.rawTinyMappingsWithSrg = tinyMappingsWithSrg; + this.rawTinyMappingsWithMojang = tinyMappingsWithMojang; tinyMappings = mappingsWorkingDir().resolve("mappings-field-migrated.tiny"); tinyMappingsWithSrg = mappingsWorkingDir().resolve("mappings-srg-field-migrated.tiny"); + tinyMappingsWithMojang = mappingsWorkingDir().resolve("mappings-mojang-field-migrated.tiny"); try { - updateFieldMigration(project); + updateFieldMigration(project, extension.isNeoForge(), extension.shouldGenerateSrgTiny()); } catch (IOException e) { throw new UncheckedIOException(e); } - project.getLogger().info(":migrated srg fields in " + stopwatch.stop()); + project.getLogger().info(":migrated {} fields in " + stopwatch.stop(), extension.getPlatform().get().id()); } - public void updateFieldMigration(Project project) throws IOException { + public void updateFieldMigration(Project project, boolean hasMojang, boolean hasSrg) throws IOException { if (!Files.exists(migratedFieldsCache)) { migratedFields.clear(); - migratedFields.addAll(generateNewFieldMigration(project, MinecraftPatchedProvider.get(project).getMinecraftPatchedSrgJar(), MappingsNamespace.SRG.toString(), rawTinyMappingsWithSrg).entrySet()); + + if (hasSrg) { + migratedFields.addAll(generateNewFieldMigration(project, MinecraftPatchedProvider.get(project).getMinecraftPatchedIntermediateJar(), MappingsNamespace.SRG.toString(), rawTinyMappingsWithSrg).entrySet()); + } else if (hasMojang) { + migratedFields.addAll(generateNewFieldMigration(project, MinecraftPatchedProvider.get(project).getMinecraftPatchedIntermediateJar(), MappingsNamespace.MOJANG.toString(), rawTinyMappingsWithMojang).entrySet()); + } + Map map = new HashMap<>(); migratedFields.forEach(entry -> { map.put(entry.getKey().owner + "#" + entry.getKey().field, entry.getValue()); @@ -131,9 +143,10 @@ public void updateFieldMigration(Project project) throws IOException { Files.writeString(migratedFieldsCache, new Gson().toJson(map)); Files.deleteIfExists(tinyMappings); Files.deleteIfExists(tinyMappingsWithSrg); + Files.deleteIfExists(tinyMappingsWithMojang); } - if (Files.notExists(tinyMappings) || Files.notExists(tinyMappingsWithSrg)) { + if (Files.notExists(tinyMappings) || (hasSrg && Files.notExists(tinyMappingsWithSrg)) || (hasMojang && Files.notExists(tinyMappingsWithMojang))) { Table fieldDescriptorMap = HashBasedTable.create(); for (Map.Entry entry : migratedFields) { @@ -141,7 +154,12 @@ public void updateFieldMigration(Project project) throws IOException { } injectMigration(project, fieldDescriptorMap, rawTinyMappings, tinyMappings); - injectMigration(project, fieldDescriptorMap, rawTinyMappingsWithSrg, tinyMappingsWithSrg); + + if (hasSrg) { + injectMigration(project, fieldDescriptorMap, rawTinyMappingsWithSrg, tinyMappingsWithSrg); + } else if (hasMojang) { + injectMigration(project, fieldDescriptorMap, rawTinyMappingsWithMojang, tinyMappingsWithMojang); + } } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeLibrariesProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeLibrariesProvider.java index debb7df11..6b56d81e7 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeLibrariesProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeLibrariesProvider.java @@ -32,7 +32,6 @@ import java.util.List; import com.google.common.hash.Hashing; -import com.google.gson.JsonElement; import org.gradle.api.Project; import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.ModuleDependency; @@ -41,16 +40,18 @@ import org.gradle.api.artifacts.ResolvedConfiguration; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.api.mappings.layered.MappingContext; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.mods.ModConfigurationRemapper; import net.fabricmc.loom.configuration.mods.dependency.LocalMavenHelper; +import net.fabricmc.loom.configuration.providers.mappings.GradleMappingContext; import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.ExceptionUtil; import net.fabricmc.loom.util.FileSystemUtil; import net.fabricmc.loom.util.PropertyUtil; import net.fabricmc.loom.util.srg.RemapObjectHolderVisitor; -import net.fabricmc.loom.util.srg.SrgMerger; +import net.fabricmc.loom.util.srg.ForgeMappingsMerger; import net.fabricmc.mappingio.tree.MemoryMappingTree; public class ForgeLibrariesProvider { @@ -64,21 +65,31 @@ public static void provide(MappingConfiguration mappingConfiguration, Project pr final List dependencies = new ArrayList<>(); // Collect all dependencies with possible relocations, such as Mixin. - for (JsonElement lib : extension.getForgeUserdevProvider().getJson().get("libraries").getAsJsonArray()) { + for (String lib : extension.getForgeUserdevProvider().getConfig().libraries()) { String dep = null; - if (lib.getAsString().startsWith("org.spongepowered:mixin:")) { - if (PropertyUtil.getAndFinalize(extension.getForge().getUseCustomMixin())) { - if (lib.getAsString().contains("0.8.2")) { + if (lib.startsWith("org.spongepowered:mixin:")) { + // Don't apply custom mixin on NeoForge. + if (extension.isForge() && PropertyUtil.getAndFinalize(extension.getForge().getUseCustomMixin())) { + if (lib.contains("0.8.2")) { dep = "net.fabricmc:sponge-mixin:0.8.2+build.24"; } else { - dep = "dev.architectury:mixin-patched" + lib.getAsString().substring(lib.getAsString().lastIndexOf(":")) + ".+"; + String version = lib.substring(lib.lastIndexOf(":")); + // Used for the file extension, for example @jar + int atIndex = version.indexOf('@'); + + if (atIndex >= 0) { + // Strip the file extension away + version = version.substring(0, atIndex); + } + + dep = "dev.architectury:mixin-patched" + version + ".+"; } } } if (dep == null) { - dep = lib.getAsString(); + dep = lib; } dependencies.add(project.getDependencies().create(dep)); @@ -158,6 +169,10 @@ private static Object remapFmlLoader(Project project, ResolvedArtifact artifact, if (Files.exists(fs.get().getPath("net/minecraftforge/fml/common/asm/ObjectHolderDefinalize.class"))) { remapObjectHolder(project, outputJar, mappingConfiguration); } + + if (Files.exists(fs.getPath("net/neoforged/fml/common/asm/ObjectHolderDefinalize.class"))) { + remapNeoForgeObjectHolder(project, outputJar, mappingConfiguration); + } } // Copy sources when not running under CI. @@ -178,8 +193,8 @@ private static void remapObjectHolder(Project project, Path outputJar, MappingCo // Merge SRG mappings. The real SRG mapping file hasn't been created yet since the usual SRG merging // process occurs after all Forge libraries have been provided. // Forge libs are needed for MC, which is needed for the mappings. - final SrgMerger.ExtraMappings extraMappings = SrgMerger.ExtraMappings.ofMojmapTsrg(MappingConfiguration.getMojmapSrgFileIfPossible(project)); - final MemoryMappingTree mappings = SrgMerger.mergeSrg(MappingConfiguration.getRawSrgFile(project), mappingConfiguration.tinyMappings, extraMappings, true); + final ForgeMappingsMerger.ExtraMappings extraMappings = ForgeMappingsMerger.ExtraMappings.ofMojmapTsrg(MappingConfiguration.getMojmapSrgFileIfPossible(project)); + final MemoryMappingTree mappings = ForgeMappingsMerger.mergeSrg(MappingConfiguration.getRawSrgFile(project), mappingConfiguration.tinyMappings, extraMappings, true); // Remap the object holders. RemapObjectHolderVisitor.remapObjectHolder( @@ -191,6 +206,24 @@ private static void remapObjectHolder(Project project, Path outputJar, MappingCo } } + private static void remapNeoForgeObjectHolder(Project project, Path outputJar, MappingConfiguration mappingConfiguration) throws IOException { + try { + // Merge Mojang mappings. The real Mojang mapping file hasn't been created yet since the usual Mojang merging + // process occurs after all Forge libraries have been provided. + // Forge libs are needed for MC, which is needed for the mappings. + final MappingContext context = new GradleMappingContext(project, "tmp-neoforge-libs"); + final MemoryMappingTree mappings = ForgeMappingsMerger.mergeMojang(context, mappingConfiguration.tinyMappings, null, true); + + // Remap the object holders. + RemapObjectHolderVisitor.remapObjectHolder( + outputJar, "net.neoforged.fml.common.asm.ObjectHolderDefinalize", mappings, + MappingsNamespace.MOJANG.toString(), MappingsNamespace.NAMED.toString() + ); + } catch (IOException e) { + throw new IOException("Could not remap object holders in " + outputJar, e); + } + } + /** * Reconstructs the dependency notation of a resolved artifact. * @param artifact the artifact diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeProvider.java index b4af73f54..a35458136 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeProvider.java @@ -32,14 +32,16 @@ import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.DependencyInfo; import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.ModPlatform; public class ForgeProvider extends DependencyProvider { + private final ModPlatform platform; private ForgeVersion version = new ForgeVersion(null); private File globalCache; - private File projectCache; public ForgeProvider(Project project) { super(project); + platform = getExtension().getPlatform().get(); } @Override @@ -55,25 +57,16 @@ public ForgeVersion getVersion() { public File getGlobalCache() { if (globalCache == null) { - globalCache = getMinecraftProvider().dir("forge/" + version.getCombined()); + globalCache = getMinecraftProvider().dir(platform.id() + "/" + version.getCombined()); globalCache.mkdirs(); } return globalCache; } - public File getProjectCache() { - if (projectCache == null) { - projectCache = new File(getDirectories().getRootProjectPersistentCache(), getMinecraftProvider().minecraftVersion() + "/forge/" + getExtension().getForgeProvider().getVersion().getCombined() + "/project-" + getProject().getPath().replace(':', '@')); - projectCache.mkdirs(); - } - - return projectCache; - } - @Override public String getTargetConfig() { - return Constants.Configurations.FORGE; + return platform == ModPlatform.NEOFORGE ? Constants.Configurations.NEOFORGE : Constants.Configurations.FORGE; } /** @@ -83,9 +76,10 @@ public String getTargetConfig() { */ public static Path getForgeCache(Project project) { final LoomGradleExtension extension = LoomGradleExtension.get(project); + final ModPlatform platform = extension.getPlatform().get(); final String version = extension.getForgeProvider().getVersion().getCombined(); return LoomGradleExtension.get(project).getMinecraftProvider() - .dir("forge/" + version).toPath(); + .dir(platform.id() + "/" + version).toPath(); } public static final class ForgeVersion { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunTemplate.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunTemplate.java index cf068cbfa..ceb0dac20 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunTemplate.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunTemplate.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2022 FabricMC + * Copyright (c) 2022-2023 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 @@ -24,18 +24,17 @@ package net.fabricmc.loom.configuration.providers.forge; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; -import java.util.stream.Collectors; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import org.gradle.api.Named; import net.fabricmc.loom.configuration.ide.RunConfigSettings; import net.fabricmc.loom.util.Constants; -import net.fabricmc.loom.util.Pair; import net.fabricmc.loom.util.function.CollectionUtil; public record ForgeRunTemplate( @@ -46,6 +45,51 @@ public record ForgeRunTemplate( Map env, Map props ) implements Named { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.STRING.optionalFieldOf("name", "") // note: empty is used since DFU crashes with null + .forGetter(ForgeRunTemplate::name), + Codec.STRING.fieldOf("main") + .forGetter(ForgeRunTemplate::main), + ConfigValue.CODEC.listOf().optionalFieldOf("args", List.of()) + .forGetter(ForgeRunTemplate::args), + ConfigValue.CODEC.listOf().optionalFieldOf("jvmArgs", List.of()) + .forGetter(ForgeRunTemplate::jvmArgs), + Codec.unboundedMap(Codec.STRING, ConfigValue.CODEC).optionalFieldOf("env", Map.of()) + .forGetter(ForgeRunTemplate::env), + Codec.unboundedMap(Codec.STRING, ConfigValue.CODEC).optionalFieldOf("props", Map.of()) + .forGetter(ForgeRunTemplate::props) + ).apply(instance, ForgeRunTemplate::new)); + + public static final Codec> MAP_CODEC = Codec.unboundedMap(Codec.STRING, CODEC) + .xmap( + map -> { + final Map newMap = new HashMap<>(map); + + // Iterate through all templates and fill in empty names. + // The NeoForge format doesn't include the name property, so we'll use the map keys + // as a replacement. + for (Map.Entry entry : newMap.entrySet()) { + final ForgeRunTemplate template = entry.getValue(); + + if (template.name.isEmpty()) { + final ForgeRunTemplate completed = new ForgeRunTemplate( + entry.getKey(), + template.main, + template.args, + template.jvmArgs, + template.env, + template.props + ); + + entry.setValue(completed); + } + } + + return newMap; + }, + Function.identity() + ); + @Override public String getName() { return name; @@ -66,29 +110,4 @@ public void applyTo(RunConfigSettings settings, ConfigValue.Resolver configValue // Add MOD_CLASSES, this is something that ForgeGradle does settings.getEnvironmentVariables().computeIfAbsent("MOD_CLASSES", $ -> ConfigValue.of("{source_roots}").resolve(configValueResolver)); } - - public static ForgeRunTemplate fromJson(JsonObject json) { - if (json.has("parents") && !json.getAsJsonArray("parents").isEmpty()) { - throw new IllegalArgumentException("Non-empty parents for run config template not supported!"); - } - - String name = json.getAsJsonPrimitive("name").getAsString(); - String main = json.getAsJsonPrimitive("main").getAsString(); - List args = json.has("args") ? fromJson(json.getAsJsonArray("args")) : List.of(); - List jvmArgs = json.has("jvmArgs") ? fromJson(json.getAsJsonArray("jvmArgs")) : List.of(); - Map env = json.has("env") ? fromJson(json.getAsJsonObject("env"), ConfigValue::of) : Map.of(); - Map props = json.has("props") ? fromJson(json.getAsJsonObject("props"), ConfigValue::of) : Map.of(); - return new ForgeRunTemplate(name, main, args, jvmArgs, env, props); - } - - private static List fromJson(JsonArray json) { - return CollectionUtil.map(json, child -> ConfigValue.of(child.getAsJsonPrimitive().getAsString())); - } - - private static Map fromJson(JsonObject json, Function converter) { - return json.entrySet().stream().map(entry -> { - String value = entry.getValue().getAsJsonPrimitive().getAsString(); - return new Pair<>(entry.getKey(), converter.apply(value)); - }).collect(Collectors.toMap(Pair::left, Pair::right)); - } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunsProvider.java index d08cf12fb..0fe3953ce 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunsProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunsProvider.java @@ -29,7 +29,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -39,6 +38,7 @@ import com.google.common.collect.MultimapBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import dev.architectury.loom.forge.UserdevConfig; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.NamedDomainObjectSet; import org.gradle.api.Project; @@ -58,19 +58,12 @@ public class ForgeRunsProvider { private final JsonObject json; private final NamedDomainObjectSet templates; - public ForgeRunsProvider(Project project, JsonObject json) { + public ForgeRunsProvider(Project project, JsonObject json, UserdevConfig userdevConfig) { this.project = project; this.extension = LoomGradleExtension.get(project); this.json = json; this.templates = project.getObjects().namedDomainObjectSet(ForgeRunTemplate.class); - readTemplates(); - } - - private void readTemplates() { - for (Map.Entry entry : json.getAsJsonObject("runs").entrySet()) { - ForgeRunTemplate template = ForgeRunTemplate.fromJson(entry.getValue().getAsJsonObject()); - templates.add(template); - } + this.templates.addAll(userdevConfig.runs().values()); } public NamedDomainObjectSet getTemplates() { @@ -78,8 +71,8 @@ public NamedDomainObjectSet getTemplates() { } public static ForgeRunsProvider create(Project project) { - JsonObject json = LoomGradleExtension.get(project).getForgeUserdevProvider().getJson(); - return new ForgeRunsProvider(project, json); + final ForgeUserdevProvider userdevProvider = LoomGradleExtension.get(project).getForgeUserdevProvider(); + return new ForgeRunsProvider(project, userdevProvider.getJson(), userdevProvider.getConfig()); } public ConfigValue.Resolver getResolver(@Nullable RunConfigSettings runConfig) { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java index 28ebb56cb..c75f0408b 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 @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2020-2022 FabricMC + * Copyright (c) 2020-2023 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 @@ -29,13 +29,13 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.util.List; import com.google.gson.Gson; import com.google.gson.JsonObject; +import com.mojang.serialization.JsonOps; +import dev.architectury.loom.forge.UserdevConfig; import org.gradle.api.Project; -import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.configuration.DependencyInfo; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.ZipUtils; @@ -43,8 +43,8 @@ public class ForgeUserdevProvider extends DependencyProvider { private File userdevJar; private JsonObject json; + private UserdevConfig config; Path joinedPatches; - BinaryPatcherConfig binaryPatcherConfig; public ForgeUserdevProvider(Project project) { super(project); @@ -64,17 +64,21 @@ public void provide(DependencyInfo dependency) throws Exception { try (Reader reader = Files.newBufferedReader(configJson)) { json = new Gson().fromJson(reader, JsonObject.class); + config = UserdevConfig.CODEC.parse(JsonOps.INSTANCE, json) + .getOrThrow(false, msg -> getProject().getLogger().error("Couldn't read userdev config, {}", msg)); } - addDependency(json.get("mcp").getAsString(), Constants.Configurations.MCP_CONFIG); - addDependency(json.get("mcp").getAsString(), Constants.Configurations.SRG); - addDependency(json.get("universal").getAsString(), Constants.Configurations.FORGE_UNIVERSAL); + addDependency(config.mcp(), Constants.Configurations.MCP_CONFIG); - if (Files.notExists(joinedPatches)) { - Files.write(joinedPatches, ZipUtils.unpack(userdevJar.toPath(), json.get("binpatches").getAsString())); + if (!getExtension().isNeoForge()) { + addDependency(config.mcp(), Constants.Configurations.SRG); } - binaryPatcherConfig = BinaryPatcherConfig.fromJson(json.getAsJsonObject("binpatcher")); + addDependency(config.universal(), Constants.Configurations.FORGE_UNIVERSAL); + + if (Files.notExists(joinedPatches)) { + Files.write(joinedPatches, ZipUtils.unpack(userdevJar.toPath(), config.binpatches())); + } } public File getUserdevJar() { @@ -90,11 +94,7 @@ public JsonObject getJson() { return json; } - public record BinaryPatcherConfig(String dependency, List args) { - public static BinaryPatcherConfig fromJson(JsonObject json) { - String dependency = json.get("version").getAsString(); - List args = List.of(LoomGradlePlugin.GSON.fromJson(json.get("args"), String[].class)); - return new BinaryPatcherConfig(dependency, args); - } + public UserdevConfig getConfig() { + return config; } } 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 98d3fa652..4c7b3463c 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 @@ -52,6 +52,8 @@ import com.google.common.base.Preconditions; import com.google.common.base.Stopwatch; import de.oceanlabs.mcp.mcinjector.adaptors.ParameterAnnotationFixer; +import dev.architectury.loom.forge.UserdevConfig; +import dev.architectury.loom.util.MappingOption; import dev.architectury.loom.util.TempFiles; import dev.architectury.tinyremapper.InputTag; import dev.architectury.tinyremapper.NonClassCopyMode; @@ -69,6 +71,7 @@ import org.objectweb.asm.tree.ClassNode; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.build.IntermediaryNamespaces; import net.fabricmc.loom.configuration.accesstransformer.AccessTransformerJarProcessor; import net.fabricmc.loom.configuration.providers.forge.mcpconfig.McpConfigProvider; import net.fabricmc.loom.configuration.providers.forge.mcpconfig.McpExecutor; @@ -86,12 +89,14 @@ import net.fabricmc.loom.util.function.FsPathConsumer; import net.fabricmc.loom.util.service.ScopedSharedServiceManager; import net.fabricmc.loom.util.service.SharedServiceManager; +import net.fabricmc.loom.util.srg.CoreModClassRemapper; import net.fabricmc.loom.util.srg.InnerClassRemapper; +import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.mappingio.tree.MemoryMappingTree; public class MinecraftPatchedProvider { private static final String LOOM_PATCH_VERSION_KEY = "Loom-Patch-Version"; - private static final String CURRENT_LOOM_PATCH_VERSION = "8"; + private static final String CURRENT_LOOM_PATCH_VERSION = "9"; private static final String NAME_MAPPING_SERVICE_PATH = "/inject/META-INF/services/cpw.mods.modlauncher.api.INameMappingService"; private final Project project; @@ -99,12 +104,12 @@ public class MinecraftPatchedProvider { private final MinecraftProvider minecraftProvider; private final Type type; - // Step 1: Remap Minecraft to SRG, merge if needed - private Path minecraftSrgJar; + // Step 1: Remap Minecraft to intermediate mappings, merge if needed + private Path minecraftIntermediateJar; // Step 2: Binary Patch - private Path minecraftPatchedSrgJar; + private Path minecraftPatchedIntermediateJar; // Step 3: Access Transform - private Path minecraftPatchedSrgAtJar; + private Path minecraftPatchedIntermediateAtJar; // Step 4: Remap Patched AT & Forge to official private Path minecraftPatchedJar; private Path minecraftClientExtra; @@ -135,13 +140,16 @@ private LoomGradleExtension getExtension() { private void initPatchedFiles() { String forgeVersion = getExtension().getForgeProvider().getVersion().getCombined(); Path forgeWorkingDir = ForgeProvider.getForgeCache(project); - String patchId = "forge-" + forgeVersion + "-"; + // Note: strings used instead of platform id since FML requires one of these exact strings + // depending on the loader to recognise Minecraft. + String patchId = (getExtension().isNeoForge() ? "neoforge" : "forge") + "-" + forgeVersion + "-"; minecraftProvider.setJarPrefix(patchId); - minecraftSrgJar = forgeWorkingDir.resolve("minecraft-" + type.id + "-srg.jar"); - minecraftPatchedSrgJar = forgeWorkingDir.resolve("minecraft-" + type.id + "-srg-patched.jar"); - minecraftPatchedSrgAtJar = forgeWorkingDir.resolve("minecraft-" + type.id + "-srg-at-patched.jar"); + final String intermediateId = getExtension().isNeoForge() ? "mojang" : "srg"; + minecraftIntermediateJar = forgeWorkingDir.resolve("minecraft-" + type.id + "-" + intermediateId + ".jar"); + minecraftPatchedIntermediateJar = forgeWorkingDir.resolve("minecraft-" + type.id + "-" + intermediateId + "-patched.jar"); + minecraftPatchedIntermediateAtJar = forgeWorkingDir.resolve("minecraft-" + type.id + "-" + intermediateId + "-at-patched.jar"); minecraftPatchedJar = forgeWorkingDir.resolve("minecraft-" + type.id + "-patched.jar"); minecraftClientExtra = forgeWorkingDir.resolve("client-extra.jar"); } @@ -154,9 +162,9 @@ private void cleanAllCache() throws IOException { private Path[] getGlobalCaches() { Path[] files = { - minecraftSrgJar, - minecraftPatchedSrgJar, - minecraftPatchedSrgAtJar, + minecraftIntermediateJar, + minecraftPatchedIntermediateJar, + minecraftPatchedIntermediateAtJar, minecraftPatchedJar, minecraftClientExtra, }; @@ -177,22 +185,22 @@ public void provide() throws Exception { this.dirty = false; - if (Files.notExists(minecraftSrgJar)) { + if (Files.notExists(minecraftIntermediateJar)) { this.dirty = true; try (var tempFiles = new TempFiles()) { McpExecutor executor = createMcpExecutor(tempFiles.directory("loom-mcp")); Path output = executor.enqueue("rename").execute(); - Files.copy(output, minecraftSrgJar); + Files.copy(output, minecraftIntermediateJar); } } - if (dirty || Files.notExists(minecraftPatchedSrgJar)) { + if (dirty || Files.notExists(minecraftPatchedIntermediateJar)) { this.dirty = true; patchJars(); } - if (dirty || Files.notExists(minecraftPatchedSrgAtJar)) { + if (dirty || Files.notExists(minecraftPatchedIntermediateAtJar)) { this.dirty = true; accessTransformForge(); } @@ -207,7 +215,6 @@ public void remapJar() throws Exception { fillClientExtraJar(); } - this.dirty = false; DependencyProvider.addDependency(project, minecraftClientExtra, Constants.Configurations.FORGE_EXTRA); } @@ -220,14 +227,16 @@ private void fillClientExtraJar() throws IOException { private TinyRemapper buildRemapper(SharedServiceManager serviceManager, Path input) throws IOException { Path[] libraries = TinyRemapperHelper.getMinecraftCompileLibraries(project); - TinyMappingsService mappingsService = getExtension().getMappingConfiguration().getMappingsService(serviceManager, true); - MemoryMappingTree mappingsWithSrg = mappingsService.getMappingTree(); + final MappingOption mappingOption = MappingOption.forPlatform(getExtension()); + TinyMappingsService mappingsService = getExtension().getMappingConfiguration().getMappingsService(serviceManager, mappingOption); + final String sourceNamespace = IntermediaryNamespaces.intermediary(project); + MemoryMappingTree mappings = mappingsService.getMappingTree(); TinyRemapper remapper = TinyRemapper.newRemapper() .logger(logger::lifecycle) .logUnknownInvokeDynamic(false) - .withMappings(TinyRemapperHelper.create(mappingsWithSrg, "srg", "official", true)) - .withMappings(InnerClassRemapper.of(InnerClassRemapper.readClassNames(input), mappingsWithSrg, "srg", "official")) + .withMappings(TinyRemapperHelper.create(mappings, sourceNamespace, "official", true)) + .withMappings(InnerClassRemapper.of(InnerClassRemapper.readClassNames(input), mappings, sourceNamespace, "official")) .renameInvalidLocals(true) .rebuildSourceFilenames(true) .build(); @@ -359,8 +368,8 @@ private boolean isPatchedJarUpToDate(Path jar) throws IOException { } private void accessTransformForge() throws IOException { - Path input = minecraftPatchedSrgJar; - Path target = minecraftPatchedSrgAtJar; + Path input = minecraftPatchedIntermediateJar; + Path target = minecraftPatchedIntermediateAtJar; accessTransform(project, input, target); } @@ -375,7 +384,7 @@ public static void accessTransform(Project project, Path input, Path target) thr extension.getForgeUserdevProvider().getUserdevJar().toPath(), ((ForgeMinecraftProvider) extension.getMinecraftProvider()) .getPatchedProvider() - .getMinecraftPatchedSrgJar() + .getMinecraftPatchedIntermediateJar() ); Files.deleteIfExists(target); @@ -400,7 +409,7 @@ public static void accessTransform(Project project, Path input, Path target) thr private void remapPatchedJar(SharedServiceManager serviceManager) throws Exception { logger.lifecycle(":remapping minecraft (TinyRemapper, srg -> official)"); - Path mcInput = minecraftPatchedSrgAtJar; + Path mcInput = minecraftPatchedIntermediateAtJar; Path mcOutput = minecraftPatchedJar; Path forgeJar = getForgeJar().toPath(); Path forgeUserdevJar = getForgeUserdevJar().toPath(); @@ -425,19 +434,27 @@ private void remapPatchedJar(SharedServiceManager serviceManager) throws Excepti } copyUserdevFiles(forgeUserdevJar, mcOutput); + remapCoreMods(mcOutput, serviceManager); applyLoomPatchVersion(mcOutput); } + private void remapCoreMods(Path patchedJar, SharedServiceManager serviceManager) throws Exception { + final MappingOption mappingOption = MappingOption.forPlatform(getExtension()); + final TinyMappingsService mappingsService = getExtension().getMappingConfiguration().getMappingsService(serviceManager, mappingOption); + final MappingTree mappings = mappingsService.getMappingTree(); + CoreModClassRemapper.remapJar(project, getExtension().getPlatform().get(), patchedJar, mappings); + } + private void patchJars() throws Exception { Stopwatch stopwatch = Stopwatch.createStarted(); logger.lifecycle(":patching jars"); - patchJars(minecraftSrgJar, minecraftPatchedSrgJar, type.patches.apply(getExtension().getPatchProvider(), getExtension().getForgeUserdevProvider())); + patchJars(minecraftIntermediateJar, minecraftPatchedIntermediateJar, type.patches.apply(getExtension().getPatchProvider(), getExtension().getForgeUserdevProvider())); - copyMissingClasses(minecraftSrgJar, minecraftPatchedSrgJar); - deleteParameterNames(minecraftPatchedSrgJar); + copyMissingClasses(minecraftIntermediateJar, minecraftPatchedIntermediateJar); + deleteParameterNames(minecraftPatchedIntermediateJar); - if (getExtension().isForgeAndNotOfficial()) { - fixParameterAnnotation(minecraftPatchedSrgJar); + if (getExtension().isForgeLikeAndNotOfficial()) { + fixParameterAnnotation(minecraftPatchedIntermediateJar); } logger.lifecycle(":patched jars in " + stopwatch.stop()); @@ -445,7 +462,7 @@ private void patchJars() throws Exception { private void patchJars(Path clean, Path output, Path patches) { ForgeToolExecutor.exec(project, spec -> { - ForgeUserdevProvider.BinaryPatcherConfig config = getExtension().getForgeUserdevProvider().binaryPatcherConfig; + UserdevConfig.BinaryPatcherConfig config = getExtension().getForgeUserdevProvider().getConfig().binpatcher(); spec.classpath(DependencyDownloader.download(project, config.dependency())); spec.getMainClass().set("net.minecraftforge.binarypatcher.ConsoleTool"); @@ -565,18 +582,25 @@ public McpExecutor createMcpExecutor(Path cache) { return new McpExecutor(project, minecraftProvider, cache, provider, type.mcpId); } - public Path getMinecraftSrgJar() { - return minecraftSrgJar; + public Path getMinecraftIntermediateJar() { + return minecraftIntermediateJar; } - public Path getMinecraftPatchedSrgJar() { - return minecraftPatchedSrgJar; + public Path getMinecraftPatchedIntermediateJar() { + return minecraftPatchedIntermediateJar; } public Path getMinecraftPatchedJar() { return minecraftPatchedJar; } + /** + * Checks whether the provider's state is dirty (regenerating jars). + */ + public boolean isDirty() { + return dirty; + } + public enum Type { CLIENT_ONLY("client", "client", (patch, userdev) -> patch.clientPatches), SERVER_ONLY("server", "server", (patch, userdev) -> patch.serverPatches), diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/SrgProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/SrgProvider.java index f5e5dca29..a6a25738d 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 @@ -45,6 +45,7 @@ import org.gradle.api.logging.LogLevel; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.api.mappings.layered.MappingContext; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.DependencyInfo; import net.fabricmc.loom.configuration.providers.mappings.GradleMappingContext; @@ -216,7 +217,8 @@ public static Path getMojmapTsrg(Project project, LoomGradleExtension extension) if (Files.notExists(mojmapTsrg) || extension.refreshDeps()) { try (BufferedWriter writer = Files.newBufferedWriter(mojmapTsrg, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { - Tsrg2Utils.writeTsrg(visitor -> visitMojmap(visitor, project), + GradleMappingContext context = new GradleMappingContext(project, "tmp-mojmap"); + Tsrg2Utils.writeTsrg(visitor -> visitMojangMappings(visitor, context), MappingsNamespace.NAMED.toString(), false, writer); } } @@ -233,8 +235,9 @@ public static Path getMojmapTsrg2(Project project, LoomGradleExtension extension if (Files.notExists(mojmapTsrg2) || extension.refreshDeps()) { try (BufferedWriter writer = Files.newBufferedWriter(mojmapTsrg2, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + GradleMappingContext context = new GradleMappingContext(project, "tmp-mojmap"); MemoryMappingTree tree = new MemoryMappingTree(); - visitMojmap(tree, project); + visitMojangMappings(tree, context); writer.write(Tsrg2Writer.serialize(tree)); } } @@ -243,21 +246,12 @@ public static Path getMojmapTsrg2(Project project, LoomGradleExtension extension return mojmapTsrg2; } - private static void visitMojmap(MappingVisitor visitor, Project project) { - GradleMappingContext context = new GradleMappingContext(project, "tmp-mojmap"); - + public static void visitMojangMappings(MappingVisitor visitor, MappingContext context) { try { - FileUtils.deleteDirectory(context.workingDirectory("/").toFile()); MojangMappingLayer layer = new MojangMappingsSpec(() -> true, true).createLayer(context); layer.visit(visitor); } catch (IOException e) { throw new UncheckedIOException(e); - } finally { - try { - FileUtils.deleteDirectory(context.workingDirectory("/").toFile()); - } catch (IOException e) { - e.printStackTrace(); - } } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpConfigFunction.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpConfigFunction.java index 8ae7e2c14..fd26e8671 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpConfigFunction.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpConfigFunction.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2022 FabricMC + * Copyright (c) 2022-2023 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 @@ -24,12 +24,17 @@ package net.fabricmc.loom.configuration.providers.forge.mcpconfig; +import java.io.IOException; +import java.nio.file.Path; import java.util.List; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import org.jetbrains.annotations.Nullable; import net.fabricmc.loom.configuration.providers.forge.ConfigValue; +import net.fabricmc.loom.configuration.providers.forge.mcpconfig.steplogic.StepLogic; import net.fabricmc.loom.util.function.CollectionUtil; /** @@ -38,15 +43,23 @@ * @param version the Gradle-style dependency string of the program * @param args the command-line arguments * @param jvmArgs the JVM arguments - * @param repo the Maven repository to download the dependency from + * @param repo the Maven repository to download the dependency from, or {@code null} if not specified */ -public record McpConfigFunction(String version, List args, List jvmArgs, String repo) { +public record McpConfigFunction(String version, List args, List jvmArgs, @Nullable String repo) { private static final String VERSION_KEY = "version"; private static final String ARGS_KEY = "args"; private static final String JVM_ARGS_KEY = "jvmargs"; private static final String REPO_KEY = "repo"; - public String getDownloadUrl() { + public Path download(StepLogic.ExecutionContext executionContext) throws IOException { + if (repo != null) { + return executionContext.downloadFile(getDownloadUrl()); + } else { + return executionContext.downloadDependency(version); + } + } + + private String getDownloadUrl() { String[] parts = version.split(":"); StringBuilder builder = new StringBuilder(); builder.append(repo); @@ -72,7 +85,8 @@ public static McpConfigFunction fromJson(JsonObject json) { String version = json.get(VERSION_KEY).getAsString(); List args = json.has(ARGS_KEY) ? configValuesFromJson(json.getAsJsonArray(ARGS_KEY)) : List.of(); List jvmArgs = json.has(JVM_ARGS_KEY) ? configValuesFromJson(json.getAsJsonArray(JVM_ARGS_KEY)) : List.of(); - String repo = json.get(REPO_KEY).getAsString(); + JsonElement repoJson = json.get(REPO_KEY); + @Nullable String repo = repoJson.isJsonPrimitive() ? repoJson.getAsString() : null; return new McpConfigFunction(version, args, jvmArgs, repo); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpConfigProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpConfigProvider.java index 60e528591..9bcaaf1c1 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 @@ -77,7 +77,8 @@ public void provide(DependencyInfo dependency) throws Exception { } private void init(String version) { - Path dir = getMinecraftProvider().dir("mcp/" + version).toPath(); + String mcpName = getExtension().isNeoForge() ? "neoform" : "mcp"; + Path dir = getMinecraftProvider().dir(mcpName + "/" + version).toPath(); mcp = dir.resolve("mcp.zip"); unpacked = dir.resolve("unpacked"); configJson = unpacked.resolve("config.json"); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpExecutor.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpExecutor.java index 9575df487..e5b93b442 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpExecutor.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpExecutor.java @@ -46,6 +46,8 @@ import com.google.gson.JsonObject; import org.gradle.api.Action; import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; import org.gradle.api.logging.LogLevel; import org.gradle.api.logging.Logger; import org.gradle.process.JavaExecSpec; @@ -298,12 +300,20 @@ public String resolve(ConfigValue value) { } @Override - public Path download(String url) throws IOException { + public Path downloadFile(String url) throws IOException { Path path = getDownloadCache().resolve(Hashing.sha256().hashString(url, StandardCharsets.UTF_8).toString().substring(0, 24)); redirectAwareDownload(url, path); return path; } + @Override + public Path downloadDependency(String notation) { + final Dependency dependency = project.getDependencies().create(notation); + final Configuration configuration = project.getConfigurations().detachedConfiguration(dependency); + configuration.setTransitive(false); + return configuration.getSingleFile().toPath(); + } + @Override public DownloadBuilder downloadBuilder(String url) { return LoomGradleExtension.get(project).download(url); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/steplogic/FunctionLogic.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/steplogic/FunctionLogic.java index 4fad74cba..fe67eb657 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/steplogic/FunctionLogic.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/steplogic/FunctionLogic.java @@ -47,7 +47,7 @@ public void execute(ExecutionContext context) throws IOException { // The other tools seem to work with the name containing .jar anyway. // Technically, FG supports an "outputExtension" config value for steps, but it's not used in practice. context.setOutput("output.jar"); - Path jar = context.download(function.getDownloadUrl()); + Path jar = function.download(context); String mainClass; try (JarFile jarFile = new JarFile(jar.toFile())) { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/steplogic/StepLogic.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/steplogic/StepLogic.java index 373211c83..590b81cc0 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/steplogic/StepLogic.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/steplogic/StepLogic.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2022 FabricMC + * Copyright (c) 2022-2023 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 @@ -61,7 +61,8 @@ interface ExecutionContext { /** Mappings extracted from {@code data.mappings} in the MCPConfig JSON. */ Path mappings(); String resolve(ConfigValue value); - Path download(String url) throws IOException; + Path downloadFile(String url) throws IOException; + Path downloadDependency(String notation); DownloadBuilder downloadBuilder(String url); void javaexec(Action configurator); Set getMinecraftLibraries(); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/minecraft/ForgeMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/minecraft/ForgeMinecraftProvider.java index f9d851b7c..9ad86a575 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/minecraft/ForgeMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/minecraft/ForgeMinecraftProvider.java @@ -38,14 +38,14 @@ public interface ForgeMinecraftProvider { MinecraftPatchedProvider getPatchedProvider(); static MergedMinecraftProvider createMerged(ConfigContext context) { - return LoomGradleExtension.get(context.project()).isForge() ? new MergedForgeMinecraftProvider(context) : new MergedMinecraftProvider(context); + return LoomGradleExtension.get(context.project()).isForgeLike() ? new MergedForgeMinecraftProvider(context) : new MergedMinecraftProvider(context); } static SingleJarMinecraftProvider createServerOnly(ConfigContext context) { - return LoomGradleExtension.get(context.project()).isForge() ? SingleJarForgeMinecraftProvider.server(context) : SingleJarMinecraftProvider.server(context); + return LoomGradleExtension.get(context.project()).isForgeLike() ? SingleJarForgeMinecraftProvider.server(context) : SingleJarMinecraftProvider.server(context); } static SingleJarMinecraftProvider createClientOnly(ConfigContext context) { - return LoomGradleExtension.get(context.project()).isForge() ? SingleJarForgeMinecraftProvider.client(context) : SingleJarMinecraftProvider.client(context); + return LoomGradleExtension.get(context.project()).isForgeLike() ? SingleJarForgeMinecraftProvider.client(context) : SingleJarMinecraftProvider.client(context); } } 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 bef4fdbd7..2e58c8391 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 @@ -44,6 +44,7 @@ import com.google.common.base.Stopwatch; import com.google.gson.JsonObject; +import dev.architectury.loom.util.MappingOption; import org.apache.tools.ant.util.StringUtils; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; @@ -54,6 +55,7 @@ import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.api.mappings.layered.MappingContext; import net.fabricmc.loom.configuration.DependencyInfo; import net.fabricmc.loom.configuration.providers.forge.FieldMigratedMappingConfiguration; import net.fabricmc.loom.configuration.providers.forge.SrgProvider; @@ -67,10 +69,11 @@ import net.fabricmc.loom.util.service.ScopedSharedServiceManager; import net.fabricmc.loom.util.service.SharedServiceManager; import net.fabricmc.loom.util.srg.MCPReader; -import net.fabricmc.loom.util.srg.SrgMerger; +import net.fabricmc.loom.util.srg.ForgeMappingsMerger; import net.fabricmc.loom.util.srg.SrgNamedWriter; import net.fabricmc.mappingio.MappingReader; import net.fabricmc.mappingio.format.MappingFormat; +import net.fabricmc.mappingio.format.Tiny2Writer; import net.fabricmc.stitch.Command; import net.fabricmc.stitch.commands.CommandProposeFieldNames; import net.fabricmc.stitch.commands.tinyv2.TinyFile; @@ -87,6 +90,7 @@ public class MappingConfiguration { // The mappings we use in practice public Path tinyMappings; public final Path tinyMappingsJar; + public Path tinyMappingsWithMojang; public Path tinyMappingsWithSrg; public final Map mixinTinyMappings; // The mixin mappings have other names in intermediary. public final Path srgToNamedSrg; // FORGE: srg to named in srg file format @@ -105,6 +109,7 @@ protected MappingConfiguration(String mappingsIdentifier, Path mappingsWorkingDi this.tinyMappingsJar = mappingsWorkingDir.resolve("mappings.jar"); this.unpickDefinitions = mappingsWorkingDir.resolve("mappings.unpick"); this.tinyMappingsWithSrg = mappingsWorkingDir.resolve("mappings-srg.tiny"); + this.tinyMappingsWithMojang = mappingsWorkingDir.resolve("mappings-mojang.tiny"); this.mixinTinyMappings = new HashMap<>(); this.srgToNamedSrg = mappingsWorkingDir.resolve("mappings-srg-named.srg"); } @@ -124,7 +129,7 @@ public static MappingConfiguration create(Project project, SharedServiceManager final LoomGradleExtension extension = LoomGradleExtension.get(project); String mappingsIdentifier; - if (extension.isForge()) { + if (extension.isForgeLike()) { mappingsIdentifier = FieldMigratedMappingConfiguration.createForgeMappingsIdentifier(extension, mappingsName, version, getMappingsClassifier(dependency, jarInfo.v2()), minecraftProvider.minecraftVersion()); } else { mappingsIdentifier = createMappingsIdentifier(mappingsName, version, getMappingsClassifier(dependency, jarInfo.v2()), minecraftProvider.minecraftVersion()); @@ -138,7 +143,7 @@ public static MappingConfiguration create(Project project, SharedServiceManager MappingConfiguration mappingConfiguration; - if (extension.isForge()) { + if (extension.isForgeLike()) { mappingConfiguration = new FieldMigratedMappingConfiguration(mappingsIdentifier, workingDir); } else { mappingConfiguration = new MappingConfiguration(mappingsIdentifier, workingDir); @@ -155,21 +160,27 @@ public static MappingConfiguration create(Project project, SharedServiceManager } public TinyMappingsService getMappingsService(SharedServiceManager serviceManager) { - return getMappingsService(serviceManager, false); + return getMappingsService(serviceManager, MappingOption.DEFAULT); } - public TinyMappingsService getMappingsService(SharedServiceManager serviceManager, boolean withSrg) { - final Path tinyMappings; - - if (withSrg) { + public TinyMappingsService getMappingsService(SharedServiceManager serviceManager, MappingOption mappingOption) { + final Path tinyMappings = switch (mappingOption) { + case WITH_SRG -> { if (Files.notExists(this.tinyMappingsWithSrg)) { throw new UnsupportedOperationException("Cannot get mappings service with SRG mappings without SRG enabled!"); } - tinyMappings = this.tinyMappingsWithSrg; - } else { - tinyMappings = this.tinyMappings; + yield this.tinyMappingsWithSrg; } + case WITH_MOJANG -> { + if (Files.notExists(this.tinyMappingsWithMojang)) { + throw new UnsupportedOperationException("Cannot get mappings service with Mojang mappings without Mojang merging enabled!"); + } + + yield this.tinyMappingsWithMojang; + } + default -> this.tinyMappings; + }; return TinyMappingsService.create(serviceManager, Objects.requireNonNull(tinyMappings)); } @@ -196,12 +207,31 @@ protected void setup(Project project, SharedServiceManager serviceManager, Minec public void setupPost(Project project) throws IOException { LoomGradleExtension extension = LoomGradleExtension.get(project); + if (extension.isNeoForge()) { + // Generate the Mojmap-merged mappings if needed. + // Note that this needs to happen before manipulateMappings for FieldMigratedMappingConfiguration. + if (Files.notExists(tinyMappingsWithMojang) || extension.refreshDeps()) { + final Stopwatch stopwatch = Stopwatch.createStarted(); + final MappingContext context = new GradleMappingContext(project, "tmp-neoforge"); + + try (Tiny2Writer writer = new Tiny2Writer(Files.newBufferedWriter(tinyMappingsWithMojang), false)) { + ForgeMappingsMerger.mergeMojang(context, tinyMappings, null, true).accept(writer); + } + + project.getLogger().info(":merged mojang mappings in {}", stopwatch.stop()); + } + } + if (extension.shouldGenerateSrgTiny()) { if (Files.notExists(tinyMappingsWithSrg) || extension.refreshDeps()) { // Merge tiny mappings with srg Stopwatch stopwatch = Stopwatch.createStarted(); - SrgMerger.ExtraMappings extraMappings = SrgMerger.ExtraMappings.ofMojmapTsrg(getMojmapSrgFileIfPossible(project)); - SrgMerger.mergeSrg(getRawSrgFile(project), tinyMappings, tinyMappingsWithSrg, extraMappings, true); + ForgeMappingsMerger.ExtraMappings extraMappings = ForgeMappingsMerger.ExtraMappings.ofMojmapTsrg(getMojmapSrgFileIfPossible(project)); + + try (Tiny2Writer writer = new Tiny2Writer(Files.newBufferedWriter(tinyMappingsWithSrg), false)) { + ForgeMappingsMerger.mergeSrg(getRawSrgFile(project), tinyMappings, extraMappings, true).accept(writer); + } + project.getLogger().info(":merged srg mappings in " + stopwatch.stop()); } } @@ -230,7 +260,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, true); + TinyMappingsService mappingsService = getMappingsService(serviceManager, MappingOption.WITH_SRG); SrgNamedWriter.writeTo(project.getLogger(), srgToNamedSrg, mappingsService.getMappingTree(), "srg", "named"); } } @@ -300,7 +330,7 @@ private void storeMappings(Project project, SharedServiceManager serviceManager, MappingsMerger.mergeAndSaveMappings(baseTinyMappings, tinyMappings, intermediateMappingsService); } else { - if (LoomGradleExtension.get(project).isForge()) { + if (LoomGradleExtension.get(project).isForgeLike()) { // (2022-09-11) This is due to ordering issues. // To complete V1 mappings, we need the full MC jar. // On Forge, producing the full MC jar needs the list of all Forge dependencies @@ -507,14 +537,14 @@ public String getBuildServiceName(String name, String from, String to) { } public Path getReplacedTarget(LoomGradleExtension loom, String namespace) { - if (namespace.equals("intermediary")) return loom.shouldGenerateSrgTiny() ? tinyMappingsWithSrg : tinyMappings; + if (namespace.equals("intermediary")) return getPlatformMappingFile(loom); return mixinTinyMappings.computeIfAbsent(namespace, k -> { Path path = mappingsWorkingDir.resolve("mappings-mixin-" + namespace + ".tiny"); try { if (Files.notExists(path) || loom.refreshDeps()) { - List lines = new ArrayList<>(Files.readAllLines(loom.shouldGenerateSrgTiny() ? tinyMappingsWithSrg : tinyMappings)); + List lines = new ArrayList<>(Files.readAllLines(getPlatformMappingFile(loom))); lines.set(0, lines.get(0).replace("intermediary", "yraidemretni").replace(namespace, "intermediary")); Files.deleteIfExists(path); Files.write(path, lines); @@ -527,6 +557,22 @@ public Path getReplacedTarget(LoomGradleExtension loom, String namespace) { }); } + /** + * The mapping file that is specific to the platform settings. + * It contains SRG (Forge/common) or Mojang mappings (NeoForge) as needed. + * + * @return the platform mapping file path + */ + public Path getPlatformMappingFile(LoomGradleExtension extension) { + if (extension.shouldGenerateSrgTiny()) { + return tinyMappingsWithSrg; + } else if (extension.isNeoForge()) { + return tinyMappingsWithMojang; + } else { + return tinyMappings; + } + } + public record UnpickMetadata(String unpickGroup, String unpickVersion) { } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJarConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJarConfiguration.java index 47370df71..a265889cf 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJarConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJarConfiguration.java @@ -38,6 +38,7 @@ import net.fabricmc.loom.configuration.providers.forge.minecraft.ForgeMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.MojangMappedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.ProcessedNamedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.SrgMinecraftProvider; @@ -48,6 +49,7 @@ public enum MinecraftJarConfiguration { IntermediaryMinecraftProvider.MergedImpl::new, NamedMinecraftProvider.MergedImpl::new, SrgMinecraftProvider.MergedImpl::new, + MojangMappedMinecraftProvider.MergedImpl::new, ProcessedNamedMinecraftProvider.MergedImpl::new, SingleJarDecompileConfiguration::new, List.of("client", "server") @@ -57,6 +59,7 @@ public enum MinecraftJarConfiguration { IntermediaryMinecraftProvider.SingleJarImpl::server, NamedMinecraftProvider.SingleJarImpl::server, SrgMinecraftProvider.SingleJarImpl::server, + MojangMappedMinecraftProvider.SingleJarImpl::server, ProcessedNamedMinecraftProvider.SingleJarImpl::server, SingleJarDecompileConfiguration::new, List.of("server") @@ -66,6 +69,7 @@ public enum MinecraftJarConfiguration { IntermediaryMinecraftProvider.SingleJarImpl::client, NamedMinecraftProvider.SingleJarImpl::client, SrgMinecraftProvider.SingleJarImpl::client, + MojangMappedMinecraftProvider.SingleJarImpl::client, ProcessedNamedMinecraftProvider.SingleJarImpl::client, SingleJarDecompileConfiguration::new, List.of("client") @@ -75,6 +79,7 @@ public enum MinecraftJarConfiguration { IntermediaryMinecraftProvider.SplitImpl::new, NamedMinecraftProvider.SplitImpl::new, SrgMinecraftProvider.SplitImpl::new, + MojangMappedMinecraftProvider.SplitImpl::new, ProcessedNamedMinecraftProvider.SplitImpl::new, SplitDecompileConfiguration::new, List.of("client", "server") @@ -84,6 +89,7 @@ public enum MinecraftJarConfiguration { private final BiFunction> intermediaryMinecraftProviderBiFunction; private final BiFunction> namedMinecraftProviderBiFunction; private final BiFunction> srgMinecraftProviderBiFunction; + private final BiFunction> mojangMappedMinecraftProviderBiFunction; private final BiFunction, MinecraftJarProcessorManager, ProcessedNamedMinecraftProvider> processedNamedMinecraftProviderBiFunction; private final BiFunction> decompileConfigurationBiFunction; private final List supportedEnvironments; @@ -94,6 +100,7 @@ , Q extends Map BiFunction> intermediaryMinecraftProviderBiFunction, BiFunction namedMinecraftProviderBiFunction, BiFunction> srgMinecraftProviderBiFunction, + BiFunction> mojangMappedMinecraftProviderBiFunction, BiFunction> processedNamedMinecraftProviderBiFunction, BiFunction> decompileConfigurationBiFunction, List supportedEnvironments @@ -102,6 +109,7 @@ , Q extends Map this.intermediaryMinecraftProviderBiFunction = (BiFunction>) (Object) intermediaryMinecraftProviderBiFunction; this.namedMinecraftProviderBiFunction = (BiFunction>) namedMinecraftProviderBiFunction; this.srgMinecraftProviderBiFunction = (BiFunction>) (Object) srgMinecraftProviderBiFunction; + this.mojangMappedMinecraftProviderBiFunction = (BiFunction>) (Object) mojangMappedMinecraftProviderBiFunction; this.processedNamedMinecraftProviderBiFunction = (BiFunction, MinecraftJarProcessorManager, ProcessedNamedMinecraftProvider>) (Object) processedNamedMinecraftProviderBiFunction; this.decompileConfigurationBiFunction = (BiFunction>) decompileConfigurationBiFunction; this.supportedEnvironments = supportedEnvironments; @@ -131,6 +139,10 @@ public BiFunction> getSrgMin return srgMinecraftProviderBiFunction; } + public BiFunction> getMojangMappedMinecraftProviderBiFunction() { + return mojangMappedMinecraftProviderBiFunction; + } + public List getSupportedEnvironments() { return supportedEnvironments; } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java index 681d65d4e..d7b53451f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java @@ -76,7 +76,7 @@ public void provide() throws Exception { final DependencyInfo dependency = DependencyInfo.create(getProject(), Constants.Configurations.MINECRAFT); minecraftVersion = dependency.getDependency().getVersion(); - if (getExtension().shouldGenerateSrgTiny() && !getExtension().isForge()) { + if (getExtension().shouldGenerateSrgTiny() && !getExtension().isForgeLike()) { getProject().getDependencies().add(Constants.Configurations.SRG, "de.oceanlabs.mcp:mcp_config:" + minecraftVersion); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java index 9ed5a50d4..a975f30eb 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java @@ -70,7 +70,7 @@ protected void createConfigurations(Project project) { configuration.extendsFrom(configurations.getByName(Constants.Configurations.LOADER_DEPENDENCIES)); configuration.extendsFrom(configurations.getByName(Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES)); - if (LoomGradleExtension.get(project).isForge()) { + if (LoomGradleExtension.get(project).isForgeLike()) { configurations.getByName(Constants.Configurations.FORGE_RUNTIME_LIBRARY).extendsFrom(configuration); } }); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java index 239de6c70..ef5a53d90 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java @@ -36,14 +36,17 @@ import java.util.StringJoiner; import java.util.function.Function; +import dev.architectury.loom.util.MappingOption; import dev.architectury.tinyremapper.OutputConsumerPath; import dev.architectury.tinyremapper.TinyRemapper; import org.gradle.api.Project; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.build.IntermediaryNamespaces; import net.fabricmc.loom.configuration.ConfigContext; import net.fabricmc.loom.configuration.mods.dependency.LocalMavenHelper; +import net.fabricmc.loom.configuration.providers.forge.minecraft.ForgeMinecraftProvider; import net.fabricmc.loom.configuration.providers.mappings.IntermediaryMappingsProvider; import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration; import net.fabricmc.loom.configuration.providers.mappings.TinyMappingsService; @@ -176,6 +179,11 @@ private boolean areOutputsValid(List remappedJars) { } } + // Architectury: regenerate jars if patches have changed. + if (minecraftProvider instanceof ForgeMinecraftProvider withForge && withForge.getPatchedProvider().isDirty()) { + return false; + } + return true; } @@ -194,7 +202,7 @@ private void remapJar(RemappedJars remappedJars, ConfigContext configContext) th Files.deleteIfExists(remappedJars.outputJarPath()); - final Set classNames = extension.isForge() ? InnerClassRemapper.readClassNames(remappedJars.inputJar()) : Set.of(); + final Set classNames = extension.isForgeLike() ? InnerClassRemapper.readClassNames(remappedJars.inputJar()) : Set.of(); final Map remappedSignatures = SignatureFixerApplyVisitor.getRemappedSignatures(getTargetNamespace() == MappingsNamespace.INTERMEDIARY, mappingConfiguration, getProject(), configContext.serviceManager(), toM); TinyRemapper remapper = TinyRemapperHelper.getTinyRemapper(getProject(), configContext.serviceManager(), fromM, toM, true, (builder) -> { builder.extraPostApplyVisitor(new SignatureFixerApplyVisitor(remappedSignatures)); @@ -219,11 +227,21 @@ private void remapJar(RemappedJars remappedJars, ConfigContext configContext) th getMavenHelper(remappedJars.name()).savePom(); - if (extension.isForgeAndOfficial()) { + if (extension.isForgeLikeAndOfficial()) { try (var serviceManager = new ScopedSharedServiceManager()) { - TinyMappingsService mappingsService = extension.getMappingConfiguration().getMappingsService(serviceManager, true); - MemoryMappingTree mappingsWithSrg = mappingsService.getMappingTree(); - RemapObjectHolderVisitor.remapObjectHolder(remappedJars.outputJar().getPath(), "net.minecraftforge.registries.ObjectHolderRegistry", mappingsWithSrg, "srg", "named"); + final MappingOption mappingOption = MappingOption.forPlatform(extension); + final TinyMappingsService mappingsService = extension.getMappingConfiguration().getMappingsService(serviceManager, mappingOption); + final String className; + + if (extension.isNeoForge()) { + className = "net.neoforged.neoforge.registries.ObjectHolderRegistry"; + } else { + className = "net.minecraftforge.registries.ObjectHolderRegistry"; + } + + final String sourceNamespace = IntermediaryNamespaces.intermediary(project); + final MemoryMappingTree mappings = mappingsService.getMappingTree(); + RemapObjectHolderVisitor.remapObjectHolder(remappedJars.outputJar().getPath(), className, mappings, sourceNamespace, "named"); } } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/MojangMappedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/MojangMappedMinecraftProvider.java new file mode 100644 index 000000000..8e9e3bd22 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/MojangMappedMinecraftProvider.java @@ -0,0 +1,117 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2023 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.minecraft.mapped; + +import java.util.List; + +import dev.architectury.tinyremapper.TinyRemapper; +import org.gradle.api.Project; + +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.SingleJarEnvType; +import net.fabricmc.loom.configuration.providers.minecraft.SingleJarMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider; +import net.fabricmc.loom.util.SidedClassVisitor; + +public abstract sealed class MojangMappedMinecraftProvider extends AbstractMappedMinecraftProvider permits MojangMappedMinecraftProvider.MergedImpl, MojangMappedMinecraftProvider.SingleJarImpl, MojangMappedMinecraftProvider.SplitImpl { + public MojangMappedMinecraftProvider(Project project, M minecraftProvider) { + super(project, minecraftProvider); + } + + @Override + public MavenScope getMavenScope() { + return MavenScope.GLOBAL; + } + + @Override + public final MappingsNamespace getTargetNamespace() { + return MappingsNamespace.MOJANG; + } + + public static final class MergedImpl extends MojangMappedMinecraftProvider implements Merged { + public MergedImpl(Project project, MergedMinecraftProvider minecraftProvider) { + super(project, minecraftProvider); + } + + @Override + public List getRemappedJars() { + return List.of( + new RemappedJars(minecraftProvider.getMergedJar(), getMergedJar(), MappingsNamespace.OFFICIAL) + ); + } + } + + public static final class SplitImpl extends MojangMappedMinecraftProvider implements Split { + public SplitImpl(Project project, SplitMinecraftProvider minecraftProvider) { + super(project, minecraftProvider); + } + + @Override + public List getRemappedJars() { + return List.of( + new RemappedJars(minecraftProvider.getMinecraftCommonJar(), getCommonJar(), MappingsNamespace.OFFICIAL), + new RemappedJars(minecraftProvider.getMinecraftClientOnlyJar(), getClientOnlyJar(), MappingsNamespace.OFFICIAL, minecraftProvider.getMinecraftCommonJar()) + ); + } + + @Override + protected void configureRemapper(RemappedJars remappedJars, TinyRemapper.Builder tinyRemapperBuilder) { + if (remappedJars.outputJar().equals(getClientOnlyJar())) { + tinyRemapperBuilder.extraPostApplyVisitor(SidedClassVisitor.CLIENT); + } + } + } + + public static final class SingleJarImpl extends MojangMappedMinecraftProvider implements SingleJar { + private final SingleJarEnvType env; + + private SingleJarImpl(Project project, SingleJarMinecraftProvider minecraftProvider, SingleJarEnvType env) { + super(project, minecraftProvider); + this.env = env; + } + + public static SingleJarImpl server(Project project, SingleJarMinecraftProvider minecraftProvider) { + return new SingleJarImpl(project, minecraftProvider, SingleJarEnvType.SERVER); + } + + public static SingleJarImpl client(Project project, SingleJarMinecraftProvider minecraftProvider) { + return new SingleJarImpl(project, minecraftProvider, SingleJarEnvType.CLIENT); + } + + @Override + public List getRemappedJars() { + return List.of( + new RemappedJars(minecraftProvider.getMinecraftEnvOnlyJar(), getEnvOnlyJar(), MappingsNamespace.OFFICIAL) + ); + } + + @Override + public SingleJarEnvType env() { + return env; + } + } +} 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 2e41e25e2..2715a635a 100644 --- a/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java +++ b/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021-2022 FabricMC + * Copyright (c) 2021-2023 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 @@ -40,6 +40,7 @@ import java.util.function.BiConsumer; import java.util.stream.Collectors; +import dev.architectury.loom.util.MappingOption; import org.apache.commons.io.output.NullOutputStream; import org.cadixdev.lorenz.MappingSet; import org.cadixdev.mercury.Mercury; @@ -48,6 +49,7 @@ import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.build.IntermediaryNamespaces; import net.fabricmc.loom.configuration.providers.mappings.TinyMappingsService; import net.fabricmc.loom.task.GenerateSourcesTask; import net.fabricmc.loom.util.DeletingFileVisitor; @@ -113,7 +115,7 @@ public static void addForgeSources(Project project, SharedServiceManager service public static void provideForgeSources(Project project, SharedServiceManager serviceManager, BiConsumer consumer) throws IOException { LoomGradleExtension extension = LoomGradleExtension.get(project); - String sourceDependency = extension.getForgeUserdevProvider().getJson().getAsJsonPrimitive("sources").getAsString(); + String sourceDependency = extension.getForgeUserdevProvider().getConfig().sources(); List forgeInstallerSources = new ArrayList<>(); for (File file : DependencyDownloader.download(project, sourceDependency)) { @@ -202,8 +204,10 @@ private static void remapForgeSourcesInner(Project project, SharedServiceManager LoomGradleExtension extension = LoomGradleExtension.get(project); Mercury mercury = SourceRemapper.createMercuryWithClassPath(project, false); - TinyMappingsService mappingsService = extension.getMappingConfiguration().getMappingsService(serviceManager, true); - MappingSet mappings = new TinyMappingsReader(mappingsService.getMappingTree(), "srg", "named").read(); + final MappingOption mappingOption = MappingOption.forPlatform(extension); + final String sourceNamespace = IntermediaryNamespaces.intermediary(project); + TinyMappingsService mappingsService = extension.getMappingConfiguration().getMappingsService(serviceManager, mappingOption); + MappingSet mappings = new TinyMappingsReader(mappingsService.getMappingTree(), sourceNamespace, "named").read(); for (Map.Entry entry : TinyRemapperHelper.JSR_TO_JETBRAINS.entrySet()) { mappings.getOrCreateClassMapping(entry.getKey()).setDeobfuscatedName(entry.getValue()); @@ -217,8 +221,9 @@ private static void remapForgeSourcesInner(Project project, SharedServiceManager mercury.getClassPath().add(file.toPath()); } - // Distinct and add the srg jar at the top, so it gets prioritized - mercury.getClassPath().addAll(0, extension.getMinecraftJars(MappingsNamespace.SRG)); + // Distinct and add the srg/mojang jar at the top, so it gets prioritized + MappingsNamespace sourceNs = extension.isNeoForge() ? MappingsNamespace.MOJANG : MappingsNamespace.SRG; + mercury.getClassPath().addAll(0, extension.getMinecraftJars(sourceNs)); List newClassPath = mercury.getClassPath().stream() .distinct() diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index bd8ca8625..43959a217 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -54,6 +54,7 @@ import net.fabricmc.loom.api.LoomGradleExtensionAPI; import net.fabricmc.loom.api.MixinExtensionAPI; import net.fabricmc.loom.api.ModSettings; +import net.fabricmc.loom.api.NeoForgeExtensionAPI; import net.fabricmc.loom.api.RemapConfigurationSettings; import net.fabricmc.loom.api.decompilers.DecompilerOptions; import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; @@ -134,7 +135,7 @@ protected LoomGradleExtensionApiImpl(Project project, LoomFiles directories) { .convention(true); this.transitiveAccessWideners.finalizeValueOnRead(); this.modProvidedJavadoc = project.getObjects().property(Boolean.class) - .convention(project.provider(() -> !isForge())); + .convention(project.provider(() -> !isForgeLike())); this.modProvidedJavadoc.finalizeValueOnRead(); this.intermediary = project.getObjects().property(String.class) .convention("https://maven.fabricmc.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar"); @@ -446,6 +447,14 @@ public Provider getPlatform() { @Override public void setGenerateSrgTiny(Boolean generateSrgTiny) { + if (isNeoForge()) { + // This is unsupported because supporting the full 2x2 combination of + // [no extra NS] [SRG] + // [mojang] [SRG+mojang] + // is a bit verbose to support. + throw new UnsupportedOperationException("SRG is not supported on NeoForge."); + } + this.generateSrgTiny = generateSrgTiny; } @@ -473,6 +482,11 @@ public void forge(Action action) { action.execute(getForge()); } + @Override + public void neoForge(Action action) { + action.execute(getNeoForge()); + } + // This is here to ensure that LoomGradleExtensionApiImpl compiles without any unimplemented methods private final class EnsureCompile extends LoomGradleExtensionApiImpl { private EnsureCompile() { @@ -514,5 +528,10 @@ protected String getMinecraftVersion() { public ForgeExtensionAPI getForge() { throw new RuntimeException("Yeah... something is really wrong"); } + + @Override + public NeoForgeExtensionAPI getNeoForge() { + throw new RuntimeException("Yeah... something is really wrong"); + } } } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index d135f4b68..fc6e9e421 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -40,6 +40,7 @@ import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.ForgeExtensionAPI; +import net.fabricmc.loom.api.NeoForgeExtensionAPI; import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.InstallerData; @@ -52,6 +53,7 @@ import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.library.LibraryProcessorManager; import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.MojangMappedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.SrgMinecraftProvider; import net.fabricmc.loom.util.Constants; @@ -65,7 +67,6 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen private final MixinExtension mixinApExtension; private final LoomFiles loomFiles; private final ConfigurableFileCollection unmappedMods; - private final Supplier forgeExtension; private final List transitiveAccessWideners = new ArrayList<>(); @@ -75,6 +76,7 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen private NamedMinecraftProvider namedMinecraftProvider; private IntermediaryMinecraftProvider intermediaryMinecraftProvider; private SrgMinecraftProvider srgMinecraftProvider; + private MojangMappedMinecraftProvider mojangMappedMinecraftProvider; private InstallerData installerData; private boolean refreshDeps; private Provider multiProjectOptimisation; @@ -85,6 +87,8 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen // +-------------------+ private DependencyProviders dependencyProviders; private ForgeRunsProvider forgeRunsProvider; + private final Supplier forgeExtension; + private final Supplier neoForgeExtension; public LoomGradleExtensionImpl(Project project, LoomFiles files) { super(project, files); @@ -94,6 +98,7 @@ public LoomGradleExtensionImpl(Project project, LoomFiles files) { this.loomFiles = files; this.unmappedMods = project.files(); this.forgeExtension = Suppliers.memoize(() -> isForge() ? project.getObjects().newInstance(ForgeExtensionImpl.class, project, this) : null); + this.neoForgeExtension = Suppliers.memoize(() -> isNeoForge() ? project.getObjects().newInstance(NeoForgeExtensionImpl.class, project) : null); // Setup the default intermediate mappings provider. setIntermediateMappingsProvider(IntermediaryMappingsProvider.class, provider -> { @@ -185,6 +190,16 @@ public void setSrgMinecraftProvider(SrgMinecraftProvider srgMinecraftProvider this.srgMinecraftProvider = srgMinecraftProvider; } + @Override + public MojangMappedMinecraftProvider getMojangMappedMinecraftProvider() { + return Objects.requireNonNull(mojangMappedMinecraftProvider, "Cannot get MojangMappedMinecraftProvider before it has been setup"); + } + + @Override + public void setMojangMappedMinecraftProvider(MojangMappedMinecraftProvider mojangMappedMinecraftProvider) { + this.mojangMappedMinecraftProvider = mojangMappedMinecraftProvider; + } + @Override public FileCollection getMinecraftJarsCollection(MappingsNamespace mappingsNamespace) { return getProject().files( @@ -293,6 +308,12 @@ public ForgeExtensionAPI getForge() { return forgeExtension.get(); } + @Override + public NeoForgeExtensionAPI getNeoForge() { + ModPlatform.assertPlatform(this, ModPlatform.NEOFORGE); + return neoForgeExtension.get(); + } + @Override public DependencyProviders getDependencyProviders() { return dependencyProviders; @@ -305,13 +326,13 @@ public void setDependencyProviders(DependencyProviders dependencyProviders) { @Override public ForgeRunsProvider getForgeRunsProvider() { - ModPlatform.assertPlatform(this, ModPlatform.FORGE); + ModPlatform.assertForgeLike(this); return forgeRunsProvider; } @Override public void setForgeRunsProvider(ForgeRunsProvider forgeRunsProvider) { - ModPlatform.assertPlatform(this, ModPlatform.FORGE); + ModPlatform.assertForgeLike(this); this.forgeRunsProvider = forgeRunsProvider; } } diff --git a/src/main/java/net/fabricmc/loom/extension/MixinExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/MixinExtensionApiImpl.java index 970daa7a4..2559613fe 100644 --- a/src/main/java/net/fabricmc/loom/extension/MixinExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/MixinExtensionApiImpl.java @@ -36,6 +36,7 @@ import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.util.PatternSet; +import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.MixinExtensionAPI; import net.fabricmc.loom.build.IntermediaryNamespaces; @@ -49,8 +50,7 @@ public abstract class MixinExtensionApiImpl implements MixinExtensionAPI { public MixinExtensionApiImpl(Project project) { this.project = Objects.requireNonNull(project); this.useMixinAp = project.getObjects().property(Boolean.class) - // .convention(project.provider(() -> LoomGradleExtension.get(project).isForge())); - .convention(true); + .convention(project.provider(() -> !LoomGradleExtension.get(project).isNeoForge())); this.refmapTargetNamespace = project.getObjects().property(String.class) .convention(project.provider(() -> IntermediaryNamespaces.intermediary(project))); diff --git a/src/main/java/net/fabricmc/loom/extension/NeoForgeExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/NeoForgeExtensionImpl.java new file mode 100644 index 000000000..7795978c9 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/extension/NeoForgeExtensionImpl.java @@ -0,0 +1,51 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2023 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.extension; + +import javax.inject.Inject; + +import org.gradle.api.Project; +import org.gradle.api.file.ConfigurableFileCollection; + +import net.fabricmc.loom.api.NeoForgeExtensionAPI; + +public class NeoForgeExtensionImpl implements NeoForgeExtensionAPI { + private final ConfigurableFileCollection accessTransformers; + + @Inject + public NeoForgeExtensionImpl(Project project) { + accessTransformers = project.getObjects().fileCollection(); + } + + @Override + public ConfigurableFileCollection getAccessTransformers() { + return accessTransformers; + } + + @Override + public void accessTransformer(Object file) { + accessTransformers.from(file); + } +} diff --git a/src/main/java/net/fabricmc/loom/task/GenerateForgePatchedSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateForgePatchedSourcesTask.java index d1d61951d..a7008464e 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateForgePatchedSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateForgePatchedSourcesTask.java @@ -37,8 +37,6 @@ import codechicken.diffpatch.util.LoggingOutputStream; import codechicken.diffpatch.util.PatchMode; import com.google.common.base.Stopwatch; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; import dev.architectury.loom.forge.ForgeTools; import dev.architectury.loom.util.TempFiles; import org.gradle.api.file.FileCollection; @@ -62,6 +60,7 @@ import net.fabricmc.loom.util.service.ScopedSharedServiceManager; import net.fabricmc.loom.util.service.SharedServiceManager; +// TODO: NeoForge support public abstract class GenerateForgePatchedSourcesTask extends AbstractLoomTask { /** * The SRG Minecraft file produced by the MCP executor. @@ -136,7 +135,7 @@ private Path decompileAndPatch(Path cache, Path gameJar) throws IOException { private Path sourcePatch(Path cache, Path rawDecompiled) throws IOException { ForgeUserdevProvider userdev = getExtension().getForgeUserdevProvider(); - String patchPathInZip = userdev.getJson().getAsJsonPrimitive("patches").getAsString(); + String patchPathInZip = userdev.getConfig().patches(); Path output = cache.resolve("patched.jar"); Path rejects = cache.resolve("rejects"); @@ -148,8 +147,8 @@ private Path sourcePatch(Path cache, Path rawDecompiled) throws IOException { .outputPath(output) .mode(PatchMode.ACCESS) .rejectsPath(rejects) - .aPrefix(userdev.getJson().getAsJsonPrimitive("patchesOriginalPrefix").getAsString()) - .bPrefix(userdev.getJson().getAsJsonPrimitive("patchesModifiedPrefix").getAsString()) + .aPrefix(userdev.getConfig().patchesOriginalPrefix().orElseThrow()) + .bPrefix(userdev.getConfig().patchesModifiedPrefix().orElseThrow()) .build() .operate(); @@ -173,18 +172,18 @@ private void stripSideAnnotations(Path input, Path output) throws IOException { try (var tempFiles = new TempFiles()) { final ForgeUserdevProvider userdevProvider = getExtension().getForgeUserdevProvider(); - final JsonArray sass = userdevProvider.getJson().getAsJsonArray("sass"); + final List sass = userdevProvider.getConfig().sass(); final List sasPaths = new ArrayList<>(); try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(userdevProvider.getUserdevJar(), false)) { - for (JsonElement sasPath : sass) { + for (String sasPath : sass) { try { - final Path from = fs.getPath(sasPath.getAsString()); + final Path from = fs.getPath(sasPath); final Path to = tempFiles.file(null, ".sas"); Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING); sasPaths.add(to); } catch (IOException e) { - throw new IOException("Could not extract SAS " + sasPath.getAsString()); + throw new IOException("Could not extract SAS " + sasPath); } } } diff --git a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java index b40fa6b8b..c4cb22e59 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java @@ -192,7 +192,7 @@ public void run() throws IOException { } // Inject Forge's own sources - if (getExtension().isForge()) { + if (getExtension().isForgeLike()) { try (var serviceManager = new ScopedSharedServiceManager()) { ForgeSourcesRemapper.addForgeSources(getProject(), serviceManager, getOutputJar().get().getAsFile().toPath()); } @@ -296,7 +296,7 @@ private void doWork(@Nullable IPCServer ipcServer, Path inputJar, Path runtimeJa params.getClassPath().setFrom(getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_COMPILE_LIBRARIES)); // Architectury - params.getForge().set(getExtension().isForge()); + params.getForge().set(getExtension().isForgeLike()); }); try { @@ -416,9 +416,12 @@ private void doDecompile(IOStringConsumer logger) { if (Files.exists(linemap)) { if (getParameters().getForge().get()) { try { - // Remove Forge classes from linemap + // Remove Forge and NeoForge classes from linemap // TODO: We should instead not decompile Forge's classes at all - LineMapVisitor.process(linemap, next -> new LineMapClassFilter(next, name -> !name.startsWith("net/minecraftforge/"))); + LineMapVisitor.process(linemap, next -> new LineMapClassFilter(next, name -> { + // Skip both Forge and NeoForge classes. + return !name.startsWith("net/minecraftforge/") && !name.startsWith("net/neoforged/"); + })); } catch (IOException e) { throw new UncheckedIOException("Failed to process linemap", e); } @@ -470,7 +473,7 @@ public static File getMappedJarFileWithSuffix(String suffix, Path runtimeJar) { } private Path getMappings() { - Path inputMappings = getExtension().isForge() ? getExtension().getMappingConfiguration().tinyMappingsWithSrg : getExtension().getMappingConfiguration().tinyMappings; + Path inputMappings = getExtension().getPlatformMappingFile(); MemoryMappingTree mappingTree = new MemoryMappingTree(); diff --git a/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java b/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java index 0ace2d061..488b6d73b 100644 --- a/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java +++ b/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java @@ -196,6 +196,10 @@ private static void migrateMappings(Project project, LoomGradleExtension extensi for (Path srgJar : extension.getMinecraftJars(MappingsNamespace.SRG)) { mercury.getClassPath().add(srgJar); } + } else if (extension.isNeoForge()) { + for (Path mojangJar : extension.getMinecraftJars(MappingsNamespace.MOJANG)) { + mercury.getClassPath().add(mojangJar); + } } mercury.getProcessors().add(MercuryRemapper.create(mappingSet)); diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index 44259867e..ff2d328dd 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -141,13 +141,13 @@ public RemapJarTask() { getClasspath().from(getProject().getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)); getAddNestedDependencies().convention(true).finalizeValueOnRead(); - getReadMixinConfigsFromManifest().convention(LoomGradleExtension.get(getProject()).isForge()).finalizeValueOnRead(); + getReadMixinConfigsFromManifest().convention(LoomGradleExtension.get(getProject()).isForgeLike()).finalizeValueOnRead(); getInjectAccessWidener().convention(false); Configuration includeConfiguration = getProject().getConfigurations().getByName(Constants.Configurations.INCLUDE); IncludedJarFactory factory = new IncludedJarFactory(getProject()); - if (!LoomGradleExtension.get(getProject()).isForge()) { + if (!LoomGradleExtension.get(getProject()).isForgeLike()) { getNestedJars().from(factory.getNestedJars(includeConfiguration)); } else { Provider, TaskDependency>> forgeNestedJars = factory.getForgeNestedJars(includeConfiguration); @@ -196,7 +196,7 @@ public void run() { if (getAddNestedDependencies().get()) { params.getNestedJars().from(getNestedJars()); - if (extension.isForge()) { + if (extension.isForgeLike()) { params.getForgeNestedJars().set(getForgeNestedJars()); } } @@ -326,7 +326,7 @@ public void execute() { addNestedJars(); ModBuildExtensions.convertAwToAt(getParameters().getAtAccessWideners(), outputFile, getParameters().getMappingBuildServiceUuid()); - if (getParameters().getPlatform().get() != ModPlatform.FORGE) { + if (!getParameters().getPlatform().get().isForgeLike()) { modifyJarManifest(); } 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 341849427..5076220ed 100644 --- a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java @@ -40,6 +40,7 @@ import org.gradle.api.logging.configuration.ConsoleOutput; import org.gradle.api.tasks.TaskAction; +import net.fabricmc.loom.build.IntermediaryNamespaces; import net.fabricmc.loom.configuration.providers.forge.ConfigValue; import net.fabricmc.loom.configuration.providers.forge.ForgeRunTemplate; import net.fabricmc.loom.configuration.providers.forge.ForgeRunsProvider; @@ -75,7 +76,7 @@ public void run() throws IOException { .property("client", "org.lwjgl.librarypath", nativesPath); } - if (!getExtension().isForge()) { + if (!getExtension().isForgeLike()) { launchConfig .argument("client", "--assetIndex") .argument("client", getExtension().getMinecraftProvider().getVersionInfo().assetIndex().fabricId(getExtension().getMinecraftProvider().minecraftVersion())) @@ -98,7 +99,7 @@ public void run() throws IOException { .argument("client", "Architectury Loom"); } - if (getExtension().isForge()) { + if (getExtension().isForgeLike()) { // Find the mapping files for Unprotect to use for figuring out // which classes are from Minecraft. String unprotectMappings = getProject().getConfigurations() @@ -108,37 +109,46 @@ public void run() throws IOException { .map(File::getAbsolutePath) .collect(Collectors.joining(File.pathSeparator)); + final String intermediateNs = IntermediaryNamespaces.intermediary(getProject()); + final String mappingsPath = getExtension().getPlatformMappingFile().toAbsolutePath().toString(); + launchConfig - // Should match YarnNamingService.PATH_TO_MAPPINGS in forge-runtime - .property("fabric.yarnWithSrg.path", getExtension().getMappingConfiguration().tinyMappingsWithSrg.toAbsolutePath().toString()) .property("unprotect.mappings", unprotectMappings) + // See ArchitecturyNamingService in forge-runtime + .property("architectury.naming.sourceNamespace", intermediateNs) + .property("architectury.naming.mappingsPath", mappingsPath); + + if (getExtension().isForge()) { + final List dataGenMods = getExtension().getForge().getDataGenMods(); + + // Only apply the hardcoded data arguments if the deprecated data generator API is being used. + if (!dataGenMods.isEmpty()) { + launchConfig + .argument("data", "--all") + .argument("data", "--mod") + .argument("data", String.join(",", getExtension().getForge().getDataGenMods())) + .argument("data", "--output") + .argument("data", getProject().file("src/generated/resources").getAbsolutePath()); + } - .property("mixin.env.remapRefMap", "true"); - - final List dataGenMods = getExtension().getForge().getDataGenMods(); + launchConfig.property("mixin.env.remapRefMap", "true"); - // Only apply the hardcoded data arguments if the deprecated data generator API is being used. - if (!dataGenMods.isEmpty()) { - launchConfig - .argument("data", "--all") - .argument("data", "--mod") - .argument("data", String.join(",", getExtension().getForge().getDataGenMods())) - .argument("data", "--output") - .argument("data", getProject().file("src/generated/resources").getAbsolutePath()); - } - - if (PropertyUtil.getAndFinalize(getExtension().getForge().getUseCustomMixin())) { - launchConfig.property("mixin.forgeloom.inject.mappings.srg-named", getExtension().getMappingConfiguration().getReplacedTarget(getExtension(), "srg").toAbsolutePath().toString()); - } else { - launchConfig.property("net.minecraftforge.gradle.GradleStart.srg.srg-mcp", getExtension().getMappingConfiguration().srgToNamedSrg.toAbsolutePath().toString()); - } + if (PropertyUtil.getAndFinalize(getExtension().getForge().getUseCustomMixin())) { + // See mixin remapper service in forge-runtime + launchConfig + .property("architectury.mixinRemapper.sourceNamespace", intermediateNs) + .property("architectury.mixinRemapper.mappingsPath", mappingsPath); + } else { + launchConfig.property("net.minecraftforge.gradle.GradleStart.srg.srg-mcp", getExtension().getMappingConfiguration().srgToNamedSrg.toAbsolutePath().toString()); + } - Set mixinConfigs = PropertyUtil.getAndFinalize(getExtension().getForge().getMixinConfigs()); + Set mixinConfigs = PropertyUtil.getAndFinalize(getExtension().getForge().getMixinConfigs()); - if (!mixinConfigs.isEmpty()) { - for (String config : mixinConfigs) { - launchConfig.argument("-mixin.config"); - launchConfig.argument(config); + if (!mixinConfigs.isEmpty()) { + for (String config : mixinConfigs) { + launchConfig.argument("-mixin.config"); + launchConfig.argument(config); + } } } diff --git a/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java b/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java index ab6ad0131..01eff08ca 100644 --- a/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java +++ b/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java @@ -108,7 +108,7 @@ private record MixinVersion(String group, String version) implements Serializabl private static Provider getMixinVersion(Project project) { return project.getConfigurations().named(Constants.Configurations.LOADER_DEPENDENCIES).map(configuration -> { - if (LoomGradleExtension.get(project).isForge()) return new MixinVersion("unknown", "unknown"); + if (LoomGradleExtension.get(project).isForgeLike()) return new MixinVersion("unknown", "unknown"); // Not super ideal that this uses the mod compile classpath, should prob look into making this not a thing at somepoint Optional dependency = configuration diff --git a/src/main/java/net/fabricmc/loom/task/service/LorenzMappingService.java b/src/main/java/net/fabricmc/loom/task/service/LorenzMappingService.java index c6ead9ff5..5f272560a 100644 --- a/src/main/java/net/fabricmc/loom/task/service/LorenzMappingService.java +++ b/src/main/java/net/fabricmc/loom/task/service/LorenzMappingService.java @@ -28,6 +28,7 @@ import java.io.UncheckedIOException; import java.util.Objects; +import dev.architectury.loom.util.MappingOption; import org.cadixdev.lorenz.MappingSet; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; @@ -46,8 +47,15 @@ public LorenzMappingService(MappingSet mappings) { public static synchronized LorenzMappingService create(SharedServiceManager sharedServiceManager, MappingConfiguration mappingConfiguration, MappingsNamespace from, MappingsNamespace to) { return sharedServiceManager.getOrCreateService(mappingConfiguration.getBuildServiceName("LorenzMappingService", from.toString(), to.toString()), () -> { - boolean srg = (from == MappingsNamespace.SRG || to == MappingsNamespace.SRG); - MemoryMappingTree m = mappingConfiguration.getMappingsService(sharedServiceManager, srg).getMappingTree(); + MappingOption mappingOption = MappingOption.DEFAULT; + + if (from == MappingsNamespace.SRG || to == MappingsNamespace.SRG) { + mappingOption = MappingOption.WITH_SRG; + } else if (from == MappingsNamespace.MOJANG || to == MappingsNamespace.MOJANG) { + mappingOption = MappingOption.WITH_MOJANG; + } + + MemoryMappingTree m = mappingConfiguration.getMappingsService(sharedServiceManager, mappingOption).getMappingTree(); try { try (var reader = new TinyMappingsReader(m, from.toString(), to.toString())) { diff --git a/src/main/java/net/fabricmc/loom/task/service/MappingsService.java b/src/main/java/net/fabricmc/loom/task/service/MappingsService.java index fccd798bb..7f8e61238 100644 --- a/src/main/java/net/fabricmc/loom/task/service/MappingsService.java +++ b/src/main/java/net/fabricmc/loom/task/service/MappingsService.java @@ -52,7 +52,7 @@ public static MappingsService createDefault(Project project, SharedServiceManage final MappingConfiguration mappingConfiguration = LoomGradleExtension.get(project).getMappingConfiguration(); final String name = mappingConfiguration.getBuildServiceName("mappingsProvider", from, to); - return MappingsService.create(serviceManager, name, (from.equals("srg") || to.equals("srg")) && LoomGradleExtension.get(project).shouldGenerateSrgTiny() ? mappingConfiguration.tinyMappingsWithSrg : mappingConfiguration.tinyMappings, from, to, false); + return MappingsService.create(serviceManager, name, LoomGradleExtension.get(project).getPlatformMappingFile(), from, to, false); } private final Options options; diff --git a/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java b/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java index a6a533d33..05e3b40a2 100644 --- a/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java +++ b/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java @@ -81,8 +81,8 @@ public static synchronized TinyRemapperService getOrCreate(SharedServiceManager extension.getKnownIndyBsms().get().stream().sorted().forEach(joiner::add); - if (extension.isForge()) { - joiner.add("forge"); + if (extension.isForgeLike()) { + joiner.add(extension.getPlatform().get().id()); } final String id = joiner.toString(); diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index 9c98d2ce8..37b1ffafe 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -74,6 +74,7 @@ public static final class Configurations { public static final String SRG = "srg"; public static final String MCP_CONFIG = "mcp"; public static final String FORGE = "forge"; + public static final String NEOFORGE = "neoForge"; public static final String FORGE_USERDEV = "forgeUserdev"; public static final String FORGE_INSTALLER = "forgeInstaller"; public static final String FORGE_UNIVERSAL = "forgeUniversal"; diff --git a/src/main/java/net/fabricmc/loom/util/ModPlatform.java b/src/main/java/net/fabricmc/loom/util/ModPlatform.java index 2b1a196af..11641c031 100644 --- a/src/main/java/net/fabricmc/loom/util/ModPlatform.java +++ b/src/main/java/net/fabricmc/loom/util/ModPlatform.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021-2022 FabricMC + * Copyright (c) 2021-2023 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 @@ -36,7 +36,8 @@ public enum ModPlatform { FABRIC(false), FORGE(false), - QUILT(true); + QUILT(true), + NEOFORGE(true); boolean experimental; @@ -44,10 +45,21 @@ public enum ModPlatform { this.experimental = experimental; } + /** + * Returns the lowercase ID of this mod platform. + */ + public String id() { + return name().toLowerCase(Locale.ROOT); + } + public boolean isExperimental() { return experimental; } + public boolean isForgeLike() { + return this == FORGE || this == NEOFORGE; + } + public static void assertPlatform(Project project, ModPlatform platform) { assertPlatform(LoomGradleExtension.get(project), platform); } @@ -55,8 +67,8 @@ public static void assertPlatform(Project project, ModPlatform platform) { public static void assertPlatform(LoomGradleExtensionAPI extension, ModPlatform platform) { assertPlatform(extension, platform, () -> { String msg = "Loom is not running on %s.%nYou can switch to it by adding 'loom.platform = %s' to your gradle.properties"; - String name = platform.name().toLowerCase(Locale.ROOT); - return msg.formatted(name, name); + String id = platform.id(); + return msg.formatted(id, id); }); } @@ -65,4 +77,14 @@ public static void assertPlatform(LoomGradleExtensionAPI extension, ModPlatform throw new GradleException(message.get()); } } + + public static void assertForgeLike(LoomGradleExtensionAPI extension) { + assertForgeLike(extension, () -> "Loom is not running on a Forge-like platform (Forge or NeoForge)."); + } + + public static void assertForgeLike(LoomGradleExtensionAPI extension, Supplier message) { + if (!extension.getPlatform().get().isForgeLike()) { + throw new GradleException(message.get()); + } + } } diff --git a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java index 39944c333..53ad034ec 100644 --- a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java +++ b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java @@ -203,6 +203,10 @@ private Mercury getMercuryInstance() { for (Path srgJar : extension.getMinecraftJars(MappingsNamespace.SRG)) { mercury.getClassPath().add(srgJar); } + } else if (extension.isNeoForge()) { + for (Path mojangJar : extension.getMinecraftJars(MappingsNamespace.MOJANG)) { + mercury.getClassPath().add(mojangJar); + } } Set files = project.getConfigurations() diff --git a/src/main/java/net/fabricmc/loom/util/TinyRemapperHelper.java b/src/main/java/net/fabricmc/loom/util/TinyRemapperHelper.java index cca3e8c0d..3ed4b81c3 100644 --- a/src/main/java/net/fabricmc/loom/util/TinyRemapperHelper.java +++ b/src/main/java/net/fabricmc/loom/util/TinyRemapperHelper.java @@ -33,6 +33,7 @@ import java.util.regex.Pattern; import com.google.common.collect.ImmutableMap; +import dev.architectury.loom.util.MappingOption; import dev.architectury.tinyremapper.IMappingProvider; import dev.architectury.tinyremapper.TinyRemapper; import org.gradle.api.Project; @@ -70,8 +71,8 @@ public static TinyRemapper getTinyRemapper(Project project, SharedServiceManager public static TinyRemapper getTinyRemapper(Project project, SharedServiceManager serviceManager, String fromM, String toM, boolean fixRecords, Consumer builderConsumer, Set fromClassNames) throws IOException { LoomGradleExtension extension = LoomGradleExtension.get(project); - boolean srg = (fromM.equals(MappingsNamespace.SRG.toString()) || toM.equals(MappingsNamespace.SRG.toString())) && extension.isForge(); - MemoryMappingTree mappingTree = extension.getMappingConfiguration().getMappingsService(serviceManager, srg).getMappingTree(); + final MappingOption mappingOption = MappingOption.forPlatform(extension).forNamespaces(fromM, toM); + MemoryMappingTree mappingTree = extension.getMappingConfiguration().getMappingsService(serviceManager, mappingOption).getMappingTree(); if (fixRecords && !mappingTree.getSrcNamespace().equals(fromM)) { throw new IllegalStateException("Mappings src namespace must match remap src namespace"); @@ -81,7 +82,7 @@ public static TinyRemapper getTinyRemapper(Project project, SharedServiceManager TinyRemapper.Builder builder = TinyRemapper.newRemapper() .logUnknownInvokeDynamic(false) - .ignoreConflicts(extension.isForge()) + .ignoreConflicts(extension.isForgeLike()) .cacheMappings(true) .threads(Runtime.getRuntime().availableProcessors()) .logger(project.getLogger()::lifecycle) @@ -99,7 +100,7 @@ public static TinyRemapper getTinyRemapper(Project project, SharedServiceManager return next; }); - if (extension.isForge()) { + if (extension.isForgeLike()) { if (!fromClassNames.isEmpty()) { builder.withMappings(InnerClassRemapper.of(fromClassNames, mappingTree, fromM, toM)); } 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 d5b806def..bf1013706 100644 --- a/src/main/java/net/fabricmc/loom/util/fmj/FabricModJsonFactory.java +++ b/src/main/java/net/fabricmc/loom/util/fmj/FabricModJsonFactory.java @@ -166,7 +166,7 @@ public static boolean isModJar(File file, ModPlatform platform) { } public static boolean isModJar(Path input, ModPlatform platform) { - if (platform == ModPlatform.FORGE) { + if (platform.isForgeLike()) { return ZipUtils.contains(input, "META-INF/mods.toml"); } else if (platform == ModPlatform.QUILT) { return ZipUtils.contains(input, "quilt.mod.json") || isModJar(input, ModPlatform.FABRIC); @@ -180,8 +180,8 @@ public static boolean isNestableModJar(File file, ModPlatform platform) { } public static boolean isNestableModJar(Path input, ModPlatform platform) { - // Forge don't care if the main jar is mod jar. - if (platform == ModPlatform.FORGE) return true; + // Forge and NeoForge don't care if the main jar is mod jar. + if (platform.isForgeLike()) return true; return isModJar(input, platform); } @@ -190,7 +190,7 @@ public static boolean containsMod(FileSystemUtil.Delegate fs, ModPlatform platfo return true; } - if (platform == ModPlatform.FORGE) { + if (platform.isForgeLike()) { return Files.exists(fs.getPath("META-INF/mods.toml")); } 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/srg/AtRemapper.java b/src/main/java/net/fabricmc/loom/util/srg/AtRemapper.java index 7ad7f55a3..cd3f56f07 100644 --- a/src/main/java/net/fabricmc/loom/util/srg/AtRemapper.java +++ b/src/main/java/net/fabricmc/loom/util/srg/AtRemapper.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2020-2021 FabricMC + * Copyright (c) 2020-2023 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 @@ -34,8 +34,10 @@ import java.util.List; import java.util.function.UnaryOperator; +import org.gradle.api.Project; import org.gradle.api.logging.Logger; +import net.fabricmc.loom.build.IntermediaryNamespaces; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.FileSystemUtil; import net.fabricmc.loom.util.function.CollectionUtil; @@ -47,7 +49,10 @@ * @author Juuz */ public final class AtRemapper { - public static void remap(Logger logger, Path jar, MappingTree mappings) throws IOException { + public static void remap(Project project, Path jar, MappingTree mappings) throws IOException { + final Logger logger = project.getLogger(); + final String sourceNamespace = IntermediaryNamespaces.intermediary(project); + try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(jar, false)) { Path atPath = fs.getPath(Constants.Forge.ACCESS_TRANSFORMER_PATH); @@ -76,7 +81,7 @@ public static void remap(Logger logger, Path jar, MappingTree mappings) throws I String name = parts[1].replace('.', '/'); parts[1] = CollectionUtil.find( mappings.getClasses(), - def -> def.getName("srg").equals(name) + def -> def.getName(sourceNamespace).equals(name) ).map(def -> def.getName("named")).orElse(name).replace('/', '.'); if (parts.length >= 3) { @@ -84,7 +89,7 @@ public static void remap(Logger logger, Path jar, MappingTree mappings) throws I parts[2] = parts[2].substring(0, parts[2].indexOf('(')) + remapDescriptor(parts[2].substring(parts[2].indexOf('(')), s -> { return CollectionUtil.find( mappings.getClasses(), - def -> def.getName("srg").equals(s) + def -> def.getName(sourceNamespace).equals(s) ).map(def -> def.getName("named")).orElse(s); }); } diff --git a/src/main/java/net/fabricmc/loom/util/srg/CoreModClassRemapper.java b/src/main/java/net/fabricmc/loom/util/srg/CoreModClassRemapper.java index 8d47f5929..bb8f26a26 100644 --- a/src/main/java/net/fabricmc/loom/util/srg/CoreModClassRemapper.java +++ b/src/main/java/net/fabricmc/loom/util/srg/CoreModClassRemapper.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2020-2021 FabricMC + * Copyright (c) 2020-2023 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 @@ -34,15 +34,19 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import org.gradle.api.Project; import org.gradle.api.logging.Logger; +import net.fabricmc.loom.build.IntermediaryNamespaces; import net.fabricmc.loom.util.FileSystemUtil; +import net.fabricmc.loom.util.ModPlatform; import net.fabricmc.loom.util.function.CollectionUtil; import net.fabricmc.mappingio.tree.MappingTree; @@ -53,8 +57,12 @@ */ public final class CoreModClassRemapper { private static final Pattern CLASS_NAME_PATTERN = Pattern.compile("^(.*')((?:com\\.mojang\\.|net\\.minecraft\\.)[A-Za-z0-9.-_$]+)('.*)$"); + private static final Pattern REDIRECT_FIELD_TO_METHOD_PATTERN = Pattern.compile("^(.*\\w+\\s*\\.\\s*redirectFieldToMethod\\s*\\(\\s*\\w+\\s*,\\s*')(\\w*)('\\s*,(?:\\s*'(\\w+)'\\s*|.*)\\).*)$"); + + public static void remapJar(Project project, ModPlatform platform, Path jar, MappingTree mappings) throws IOException { + final Logger logger = project.getLogger(); + final String sourceNamespace = IntermediaryNamespaces.intermediary(project); - public static void remapJar(Path jar, MappingTree mappings, Logger logger) throws IOException { try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(jar, false)) { Path coremodsJsonPath = fs.getPath("META-INF", "coremods.json"); @@ -75,7 +83,7 @@ public static void remapJar(Path jar, MappingTree mappings, Logger logger) throw if (Files.exists(js)) { logger.info(":remapping coremod '" + file + "'"); - remap(js, mappings); + remap(js, platform, mappings, sourceNamespace); } else { logger.warn("Coremod '" + file + "' listed in coremods.json but not found"); } @@ -83,9 +91,10 @@ public static void remapJar(Path jar, MappingTree mappings, Logger logger) throw } } - public static void remap(Path js, MappingTree mappings) throws IOException { + public static void remap(Path js, ModPlatform platform, MappingTree mappings, String sourceNamespace) throws IOException { List lines = Files.readAllLines(js); List output = new ArrayList<>(lines); + String lastClassName = null; for (int i = 0; i < lines.size(); i++) { String line = lines.get(i); @@ -93,13 +102,39 @@ public static void remap(Path js, MappingTree mappings) throws IOException { if (matcher.matches()) { String className = matcher.group(2).replace('.', '/'); - String remapped = CollectionUtil.find(mappings.getClasses(), def -> def.getName("srg").equals(className)) + String remapped = CollectionUtil.find(mappings.getClasses(), def -> def.getName(sourceNamespace).equals(className)) .map(def -> def.getName("named")) .orElse(className); + lastClassName = remapped; if (!className.equals(remapped)) { output.set(i, matcher.group(1) + remapped.replace('/', '.') + matcher.group(3)); } + } else if (platform == ModPlatform.NEOFORGE && lastClassName != null) { + matcher = REDIRECT_FIELD_TO_METHOD_PATTERN.matcher(line); + + if (matcher.matches()) { + String fieldName = matcher.group(2); + String remapped = Optional.ofNullable(mappings.getClass(lastClassName, mappings.getNamespaceId("named"))) + .flatMap(clazz -> Optional.ofNullable(clazz.getField(fieldName, null, mappings.getNamespaceId(sourceNamespace)))) + .map(field -> field.getName("named")) + .orElse(fieldName); + + if (!fieldName.equals(remapped)) { + String optionalMethod = matcher.group(4); + String remappedMethod = optionalMethod == null ? null + : Optional.ofNullable(mappings.getClass(lastClassName, mappings.getNamespaceId("named"))) + .flatMap(clazz -> Optional.ofNullable(clazz.getMethod(optionalMethod, null, mappings.getNamespaceId(sourceNamespace)))) + .map(method -> method.getName("named")) + .orElse(null); + + if (remappedMethod != null) { + output.set(i, matcher.group(1) + remapped + matcher.group(3).replace("'" + optionalMethod + "'", "'" + remappedMethod + "'")); + } else { + output.set(i, matcher.group(1) + remapped + matcher.group(3)); + } + } + } } } diff --git a/src/main/java/net/fabricmc/loom/util/srg/SrgMerger.java b/src/main/java/net/fabricmc/loom/util/srg/ForgeMappingsMerger.java similarity index 60% rename from src/main/java/net/fabricmc/loom/util/srg/SrgMerger.java rename to src/main/java/net/fabricmc/loom/util/srg/ForgeMappingsMerger.java index 378b0fa33..dd57907dc 100644 --- a/src/main/java/net/fabricmc/loom/util/srg/SrgMerger.java +++ b/src/main/java/net/fabricmc/loom/util/srg/ForgeMappingsMerger.java @@ -34,56 +34,57 @@ import java.util.Map; import java.util.Set; import java.util.function.Consumer; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import org.jetbrains.annotations.Nullable; +import net.fabricmc.loom.api.mappings.layered.MappingContext; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.configuration.providers.forge.SrgProvider; import net.fabricmc.loom.util.MappingException; import net.fabricmc.loom.util.function.CollectionUtil; import net.fabricmc.mappingio.FlatMappingVisitor; import net.fabricmc.mappingio.MappingReader; import net.fabricmc.mappingio.MappingVisitor; +import net.fabricmc.mappingio.adapter.ForwardingMappingVisitor; import net.fabricmc.mappingio.adapter.MappingNsRenamer; import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; import net.fabricmc.mappingio.adapter.RegularAsFlatMappingVisitor; import net.fabricmc.mappingio.format.MappingFormat; -import net.fabricmc.mappingio.format.Tiny2Writer; import net.fabricmc.mappingio.format.TsrgReader; import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.mappingio.tree.MappingTreeView; import net.fabricmc.mappingio.tree.MemoryMappingTree; /** - * Merges a Tiny file with an SRG file. + * Merges a Tiny file with a new namespace. * * @author Juuz */ -public final class SrgMerger { +public final class ForgeMappingsMerger { private static final List INPUT_NAMESPACES = List.of("official", "intermediary", "named"); - private final MemoryMappingTree srg; + private final MemoryMappingTree newNs; private final MemoryMappingTree src; private final MemoryMappingTree output; private final FlatMappingVisitor flatOutput; private final boolean lenient; private final @Nullable MemoryMappingTree extra; - private final ListMultimap methodsBySrgName; + private final ListMultimap methodsByNewNs; - private SrgMerger(Path srg, Path tiny, @Nullable ExtraMappings extraMappings, boolean lenient) throws IOException { - this.srg = readSrg(srg); - this.src = new MemoryMappingTree(); + private ForgeMappingsMerger(MemoryMappingTree newNs, MemoryMappingTree src, @Nullable ExtraMappings extraMappings, boolean lenient) throws IOException { + this.newNs = newNs; + Preconditions.checkArgument(this.newNs.getDstNamespaces().size() == 1, "New namespace must have exactly one destination namespace"); + this.src = src; this.output = new MemoryMappingTree(); this.flatOutput = new RegularAsFlatMappingVisitor(output); this.lenient = lenient; - this.methodsBySrgName = ArrayListMultimap.create(); + this.methodsByNewNs = ArrayListMultimap.create(); if (extraMappings != null) { this.extra = new MemoryMappingTree(); - MappingSourceNsSwitch nsSwitch = new MappingSourceNsSwitch(extra, "official"); - MappingVisitor visitor = nsSwitch; + MappingVisitor visitor = new MappingSourceNsSwitch(this.extra, MappingsNamespace.OFFICIAL.toString()); if (!extraMappings.hasCorrectNamespaces()) { Map namespaces = Map.of( @@ -98,33 +99,37 @@ private SrgMerger(Path srg, Path tiny, @Nullable ExtraMappings extraMappings, bo this.extra = null; } - MappingReader.read(tiny, this.src); - checkInputNamespaces(tiny); - - this.output.visitNamespaces(this.src.getSrcNamespace(), Stream.concat(Stream.of("srg"), this.src.getDstNamespaces().stream()).collect(Collectors.toList())); + var newDstNamespaces = new ArrayList(); + newDstNamespaces.add(this.newNs.getDstNamespaces().get(0)); + newDstNamespaces.addAll(this.src.getDstNamespaces()); + this.output.visitNamespaces(this.src.getSrcNamespace(), newDstNamespaces); } - private void checkInputNamespaces(Path tiny) { - List inputNamespaces = new ArrayList<>(this.src.getDstNamespaces()); - inputNamespaces.add(0, this.src.getSrcNamespace()); + private static MemoryMappingTree readInput(Path tiny) throws IOException { + MemoryMappingTree src = new MemoryMappingTree(); + MappingReader.read(tiny, src); + List inputNamespaces = new ArrayList<>(src.getDstNamespaces()); + inputNamespaces.add(0, src.getSrcNamespace()); if (!inputNamespaces.equals(INPUT_NAMESPACES)) { throw new MappingException("Mapping file " + tiny + " does not have 'official, intermediary, named' as its namespaces! Found: " + inputNamespaces); } + + return src; } /** * Creates a destination name array where the first element - * will be the srg name of the mapping element. + * will be the new namespace name of the mapping element. */ - private String[] createDstNameArray(MappingTree.ElementMappingView srg) { + private String[] createDstNameArray(MappingTree.ElementMappingView newNs) { String[] dstNames = new String[output.getDstNamespaces().size()]; - dstNames[0] = srg.getDstName(0); + dstNames[0] = newNs.getDstName(0); return dstNames; } /** - * Copies all the non-srg destination names from an element. + * Copies all the original destination names from an element. */ private void copyDstNames(String[] dstNames, MappingTreeView.ElementMappingView from) { for (int i = 1; i < dstNames.length; i++) { @@ -135,37 +140,37 @@ private void copyDstNames(String[] dstNames, MappingTreeView.ElementMappingView /** * Fills in an array of destination names with the element's source name. */ - private void fillMappings(String[] names, MappingTree.ElementMappingView srg) { + private void fillMappings(String[] names, MappingTree.ElementMappingView newNs) { for (int i = 1; i < names.length; i++) { - names[i] = srg.getSrcName(); + names[i] = newNs.getSrcName(); } } public MemoryMappingTree merge() throws IOException { - for (MappingTree.ClassMapping srgClass : srg.getClasses()) { - String[] dstNames = createDstNameArray(srgClass); - MappingTree.ClassMapping tinyClass = src.getClass(srgClass.getSrcName()); + for (MappingTree.ClassMapping newNsClass : newNs.getClasses()) { + String[] dstNames = createDstNameArray(newNsClass); + MappingTree.ClassMapping tinyClass = src.getClass(newNsClass.getSrcName()); String comment = null; if (tinyClass != null) { copyDstNames(dstNames, tinyClass); comment = tinyClass.getComment(); } else if (lenient) { - // Tiny class not found, we'll just use srg names - fillMappings(dstNames, srgClass); + // Tiny class not found, we'll just use new namespace names + fillMappings(dstNames, newNsClass); } else { - throw new MappingException("Could not find class " + srgClass.getSrcName() + "|" + srgClass.getDstName(0)); + throw new MappingException("Could not find class " + newNsClass.getSrcName() + "|" + newNsClass.getDstName(0)); } - flatOutput.visitClass(srgClass.getSrcName(), dstNames); - if (comment != null) flatOutput.visitClassComment(srgClass.getSrcName(), comment); + flatOutput.visitClass(newNsClass.getSrcName(), dstNames); + if (comment != null) flatOutput.visitClassComment(newNsClass.getSrcName(), comment); - for (MappingTree.FieldMapping field : srgClass.getFields()) { - mergeField(srgClass, field, tinyClass); + for (MappingTree.FieldMapping field : newNsClass.getFields()) { + mergeField(newNsClass, field, tinyClass); } - for (MappingTree.MethodMapping method : srgClass.getMethods()) { - mergeMethod(srgClass, method, tinyClass); + for (MappingTree.MethodMapping method : newNsClass.getMethods()) { + mergeMethod(newNsClass, method, tinyClass); } } @@ -174,20 +179,20 @@ public MemoryMappingTree merge() throws IOException { return output; } - private void mergeField(MappingTree.ClassMapping srgClass, MappingTree.FieldMapping srgField, @Nullable MappingTree.ClassMapping tinyClass) throws IOException { - String[] dstNames = createDstNameArray(srgField); + private void mergeField(MappingTree.ClassMapping newNsClass, MappingTree.FieldMapping newNsField, @Nullable MappingTree.ClassMapping tinyClass) throws IOException { + String[] dstNames = createDstNameArray(newNsField); MappingTree.FieldMapping tinyField = null; - String srcDesc = srgField.getSrcDesc(); + String srcDesc = newNsField.getSrcDesc(); String comment = null; if (tinyClass != null) { if (srcDesc != null) { - tinyField = tinyClass.getField(srgField.getSrcName(), srgField.getSrcDesc()); + tinyField = tinyClass.getField(newNsField.getSrcName(), newNsField.getSrcDesc()); } else { - tinyField = CollectionUtil.find(tinyClass.getFields(), field -> field.getSrcName().equals(srgField.getSrcName())).orElse(null); + tinyField = CollectionUtil.find(tinyClass.getFields(), field -> field.getSrcName().equals(newNsField.getSrcName())).orElse(null); } } else if (!lenient) { - throw new MappingException("Could not find field " + srgClass.getDstName(0) + '.' + srgField.getDstName(0) + ' ' + srgField.getDstDesc(0)); + throw new MappingException("Could not find field " + newNsClass.getDstName(0) + '.' + newNsField.getDstName(0) + ' ' + newNsField.getDstDesc(0)); } if (tinyField != null) { @@ -195,27 +200,27 @@ private void mergeField(MappingTree.ClassMapping srgClass, MappingTree.FieldMapp srcDesc = tinyField.getSrcDesc(); comment = tinyField.getComment(); } else { - fillMappings(dstNames, srgField); + fillMappings(dstNames, newNsField); } if (srcDesc != null) { - flatOutput.visitField(srgClass.getSrcName(), srgField.getSrcName(), srcDesc, dstNames); - if (comment != null) flatOutput.visitFieldComment(srgClass.getSrcName(), srgField.getSrcName(), srcDesc, comment); + flatOutput.visitField(newNsClass.getSrcName(), newNsField.getSrcName(), srcDesc, dstNames); + if (comment != null) flatOutput.visitFieldComment(newNsClass.getSrcName(), newNsField.getSrcName(), srcDesc, comment); } else if (!lenient) { - throw new MappingException("Could not find descriptor for field " + srgClass.getDstName(0) + '.' + srgField.getDstName(0)); + throw new MappingException("Could not find descriptor for field " + newNsClass.getDstName(0) + '.' + newNsField.getDstName(0)); } } - private void mergeMethod(MappingTree.ClassMapping srgClass, MappingTree.MethodMapping srgMethod, @Nullable MappingTree.ClassMapping tinyClass) throws IOException { - String[] dstNames = createDstNameArray(srgMethod); + private void mergeMethod(MappingTree.ClassMapping newNsClass, MappingTree.MethodMapping newNsMethod, @Nullable MappingTree.ClassMapping tinyClass) throws IOException { + String[] dstNames = createDstNameArray(newNsMethod); MappingTree.MethodMapping tinyMethod = null; String intermediaryName, namedName; String comment = null; if (tinyClass != null) { - tinyMethod = tinyClass.getMethod(srgMethod.getSrcName(), srgMethod.getSrcDesc()); + tinyMethod = tinyClass.getMethod(newNsMethod.getSrcName(), newNsMethod.getSrcDesc()); } else if (!lenient) { - throw new MappingException("Could not find method " + srgClass.getDstName(0) + '.' + srgMethod.getDstName(0) + ' ' + srgMethod.getDstDesc(0)); + throw new MappingException("Could not find method " + newNsClass.getDstName(0) + '.' + newNsMethod.getDstName(0) + ' ' + newNsMethod.getDstDesc(0)); } if (tinyMethod != null) { @@ -224,7 +229,7 @@ private void mergeMethod(MappingTree.ClassMapping srgClass, MappingTree.MethodMa namedName = tinyMethod.getName("named"); comment = tinyMethod.getComment(); } else { - if (srgMethod.getSrcName().equals(srgMethod.getDstName(0))) { + if (newNsMethod.getSrcName().equals(newNsMethod.getDstName(0))) { // These are only methods like or toString which have the same name in every NS. // We can safely ignore those. return; @@ -233,7 +238,7 @@ private void mergeMethod(MappingTree.ClassMapping srgClass, MappingTree.MethodMa @Nullable MappingTree.MethodMapping fillMethod = null; if (extra != null) { - MappingTree.MethodMapping extraMethod = extra.getMethod(srgClass.getSrcName(), srgMethod.getSrcName(), srgMethod.getSrcDesc()); + MappingTree.MethodMapping extraMethod = extra.getMethod(newNsClass.getSrcName(), newNsMethod.getSrcName(), newNsMethod.getSrcDesc()); if (extraMethod != null && extraMethod.getSrcName().equals(extraMethod.getDstName(0))) { fillMethod = extraMethod; @@ -246,33 +251,33 @@ private void mergeMethod(MappingTree.ClassMapping srgClass, MappingTree.MethodMa } else { // Do not allow missing methods as these are typically subclass methods and cause issues where // class B extends A, and overrides a method from the superclass. Then the subclass method - // DOES get a srg name but not a yarn/intermediary name. + // DOES get a new namespace name but not a yarn/intermediary name. return; } } - if (!srgMethod.getSrcName().equals(dstNames[0])) { // ignore and the likes - methodsBySrgName.put( - new SrgMethodKey(dstNames[0], srgMethod.getSrcDesc()), - new MethodData(srgClass.getSrcName(), srgMethod.getSrcName(), srgMethod.getSrcDesc(), tinyMethod != null, intermediaryName, namedName) + if (!newNsMethod.getSrcName().equals(dstNames[0])) { // ignore and the likes + methodsByNewNs.put( + new MethodKey(dstNames[0], newNsMethod.getSrcDesc()), + new MethodData(newNsClass.getSrcName(), newNsMethod.getSrcName(), newNsMethod.getSrcDesc(), tinyMethod != null, intermediaryName, namedName) ); } - flatOutput.visitMethod(srgClass.getSrcName(), srgMethod.getSrcName(), srgMethod.getSrcDesc(), dstNames); - if (comment != null) flatOutput.visitMethodComment(srgClass.getSrcName(), srgMethod.getSrcName(), srgMethod.getSrcDesc(), comment); + flatOutput.visitMethod(newNsClass.getSrcName(), newNsMethod.getSrcName(), newNsMethod.getSrcDesc(), dstNames); + if (comment != null) flatOutput.visitMethodComment(newNsClass.getSrcName(), newNsMethod.getSrcName(), newNsMethod.getSrcDesc(), comment); if (tinyMethod != null) { for (MappingTree.MethodArgMapping arg : tinyMethod.getArgs()) { String[] argDstNames = new String[output.getDstNamespaces().size()]; copyDstNames(argDstNames, arg); flatOutput.visitMethodArg( - srgClass.getSrcName(), srgMethod.getSrcName(), srgMethod.getSrcDesc(), + newNsClass.getSrcName(), newNsMethod.getSrcName(), newNsMethod.getSrcDesc(), arg.getArgPosition(), arg.getLvIndex(), arg.getSrcName(), argDstNames ); if (arg.getComment() != null) { flatOutput.visitMethodArgComment( - srgClass.getSrcName(), srgMethod.getSrcName(), srgMethod.getSrcDesc(), + newNsClass.getSrcName(), newNsMethod.getSrcName(), newNsMethod.getSrcDesc(), arg.getArgPosition(), arg.getLvIndex(), arg.getSrcName(), arg.getComment() ); @@ -282,14 +287,14 @@ private void mergeMethod(MappingTree.ClassMapping srgClass, MappingTree.MethodMa } /** - * Resolves conflicts where multiple methods map to an SRG method. + * Resolves conflicts where multiple methods map to a method in the new namespace. * We will prefer the ones with the Tiny mappings. */ private void resolveConflicts() { List conflicts = new ArrayList<>(); - for (SrgMethodKey srg : methodsBySrgName.keySet()) { - List methods = methodsBySrgName.get(srg); + for (MethodKey methodKey : methodsByNewNs.keySet()) { + List methods = methodsByNewNs.get(methodKey); if (methods.size() == 1) continue; // Determine whether the names conflict @@ -326,8 +331,8 @@ private void resolveConflicts() { List hasTiny = CollectionUtil.filter(methods, MethodData::hasTiny); // Record conflicts if needed - if (hasTiny.size() > 1) { // Multiple methods map to this SRG name - // Sometimes unrelated methods share an SRG name. Probably overrides from a past version? + if (hasTiny.size() > 1) { // Multiple methods map to this new name + // Sometimes unrelated methods share a SRG name. Probably overrides from a past version? Set intermediaryNames = new HashSet<>(); for (MethodData method : methods) { @@ -337,7 +342,7 @@ private void resolveConflicts() { // Only record a conflict if we map one intermediary name with multiple named names if (intermediaryNames.size() == 1) { StringBuilder message = new StringBuilder(); - message.append("- multiple preferred methods for ").append(srg).append(':'); + message.append("- multiple preferred methods for ").append(newNs).append(':'); for (MethodData preferred : hasTiny) { message.append("\n\t> ").append(preferred); @@ -347,50 +352,14 @@ private void resolveConflicts() { } return null; - } else if (hasTiny.isEmpty()) { // No methods map to this SRG name - conflictReporter.accept("- no preferred methods found for " + srg + ", available: " + methods); + } else if (hasTiny.isEmpty()) { // No methods map to this new name + conflictReporter.accept("- no preferred methods found for " + newNs + ", available: " + methods); return null; } return hasTiny.get(0); } - /** - * Merges SRG mappings with a tiny mappings tree through the obf names. - * This overload writes the mappings into a file. - * - *

The namespaces in the tiny file should be {@code official, intermediary, named}. - * The SRG names will add a new namespace called {@code srg} so that the final namespaces - * are {@code official, srg, intermediary, named}. - * - *

This method does not include local variables in the output. - * It can, however, be used for remapping the game jar since it has all - * classes, methods, fields, parameters and javadoc comments. - * - *

If {@code lenient} is true, the merger will not error when encountering names not present - * in the tiny mappings. Instead, the names will be filled from the {@code official} namespace. - * - * @param srg the SRG file in .tsrg format - * @param tiny the tiny file - * @param out the output file, will be in tiny v2 - * @param extraMappings an extra mappings file that will be used to determine - * whether an unobfuscated name is needed in the result file - * @param lenient whether lenient mode is enabled - * @throws IOException if an IO error occurs while reading or writing the mappings - * @throws MappingException if the input tiny mappings' namespaces are incorrect - * or if an element mentioned in the SRG file does not have tiny mappings in non-lenient mode - */ - public static void mergeSrg(Path srg, Path tiny, Path out, @Nullable ExtraMappings extraMappings, boolean lenient) - throws IOException, MappingException { - MemoryMappingTree tree = mergeSrg(srg, tiny, extraMappings, lenient); - - try (Tiny2Writer writer = new Tiny2Writer(Files.newBufferedWriter(out), false)) { - tree.accept(writer); - } catch (IOException e) { - e.printStackTrace(); - } - } - /** * Merges SRG mappings with a tiny mappings tree through the obf names. * This overload returns a {@link MemoryMappingTree} of the merged mappings. @@ -418,20 +387,29 @@ public static void mergeSrg(Path srg, Path tiny, Path out, @Nullable ExtraMappin */ public static MemoryMappingTree mergeSrg(Path srg, Path tiny, @Nullable ExtraMappings extraMappings, boolean lenient) throws IOException, MappingException { - return new SrgMerger(srg, tiny, extraMappings, lenient).merge(); + return new ForgeMappingsMerger(readSrg(srg), readInput(tiny), extraMappings, lenient).merge(); + } + + public static MemoryMappingTree mergeMojang(MappingContext context, Path tiny, @Nullable ExtraMappings extraMappings, boolean lenient) + throws IOException, MappingException { + MemoryMappingTree mojang = new MemoryMappingTree(); + SrgProvider.visitMojangMappings(new MappingNsRenamer(mojang, Map.of(MappingsNamespace.NAMED.toString(), MappingsNamespace.MOJANG.toString())), context); + return new ForgeMappingsMerger(mojang, readInput(tiny), extraMappings, lenient).merge(); } - private MemoryMappingTree readSrg(Path srg) throws IOException { + private static MemoryMappingTree readSrg(Path srg) throws IOException { try (BufferedReader reader = Files.newBufferedReader(srg)) { - MemoryMappingTree tsrg = new MemoryMappingTree(); - MemoryMappingTree temp = new MemoryMappingTree(); - TsrgReader.read(reader, temp); - Map namespaces = Map.of( - temp.getSrcNamespace(), MappingsNamespace.OFFICIAL.toString(), - temp.getDstNamespaces().get(0), MappingsNamespace.SRG.toString() - ); - temp.accept(new MappingNsRenamer(tsrg, namespaces)); - return tsrg; + MemoryMappingTree output = new MemoryMappingTree(); + TsrgReader.read(reader, new ForwardingMappingVisitor(output) { + // Override the namespaces to be official -> srg + @Override + public void visitNamespaces(String srcNamespace, List dstNamespaces) throws IOException { + List newDstNamespaces = new ArrayList<>(dstNamespaces); + newDstNamespaces.set(0, MappingsNamespace.SRG.toString()); + super.visitNamespaces(MappingsNamespace.OFFICIAL.toString(), newDstNamespaces); + } + }); + return output; } } @@ -445,7 +423,7 @@ public static ExtraMappings ofMojmapTsrg(Path path) { } } - private record SrgMethodKey(String name, String desc) { + private record MethodKey(String name, String desc) { @Override public String toString() { return name + desc; diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeRunConfigTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeRunConfigTest.groovy index 102480140..43e9b447f 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeRunConfigTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeRunConfigTest.groovy @@ -40,6 +40,9 @@ class ForgeRunConfigTest extends Specification implements GradleProjectTestTrait gradle.buildGradle.text = gradle.buildGradle.text.replace('@MCVERSION@', mcVersion) .replace('@FORGEVERSION@', forgeVersion) .replace('@MAPPINGS@', 'loom.officialMojangMappings()') + .replace('@REPOSITORIES@', '') + .replace('@PACKAGE@', 'net.minecraftforge:forge') + .replace('@JAVA_VERSION@', javaVersion) gradle.buildGradle << """ tasks.register('verifyRunConfigs') { doLast { @@ -62,12 +65,12 @@ class ForgeRunConfigTest extends Specification implements GradleProjectTestTrait result.task(":verifyRunConfigs").outcome == SUCCESS where: - mcVersion | forgeVersion | mainClass - '1.19.4' | "45.0.43" | 'cpw.mods.bootstraplauncher.BootstrapLauncher' - '1.18.1' | "39.0.63" | 'cpw.mods.bootstraplauncher.BootstrapLauncher' - '1.17.1' | "37.0.67" | 'cpw.mods.bootstraplauncher.BootstrapLauncher' - '1.16.5' | "36.2.4" | 'net.minecraftforge.userdev.LaunchTesting' - '1.14.4' | "28.2.23" | 'net.minecraftforge.userdev.LaunchTesting' + mcVersion | forgeVersion | javaVersion | mainClass + '1.19.4' | "45.0.43" | '17' | 'cpw.mods.bootstraplauncher.BootstrapLauncher' + '1.18.1' | "39.0.63" | '17' | 'cpw.mods.bootstraplauncher.BootstrapLauncher' + '1.17.1' | "37.0.67" | '16' | 'cpw.mods.bootstraplauncher.BootstrapLauncher' + '1.16.5' | "36.2.4" | '8' | 'net.minecraftforge.userdev.LaunchTesting' + '1.14.4' | "28.2.23" | '8' | 'net.minecraftforge.userdev.LaunchTesting' } def "verify mod classes"() { @@ -76,6 +79,9 @@ class ForgeRunConfigTest extends Specification implements GradleProjectTestTrait gradle.buildGradle.text = gradle.buildGradle.text.replace('@MCVERSION@', '1.19.4') .replace('@FORGEVERSION@', "45.0.43") .replace('@MAPPINGS@', 'loom.officialMojangMappings()') + .replace('@REPOSITORIES@', '') + .replace('@PACKAGE@', 'net.minecraftforge:forge') + .replace('@JAVA_VERSION@', '17') gradle.buildGradle << ''' sourceSets { testMod {} 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 7bd14046b..c4c0edebf 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 @@ -42,6 +42,7 @@ class ForgeTest extends Specification implements GradleProjectTestTrait { .replace('@MAPPINGS@', mappings) .replace('@REPOSITORIES@', '') .replace('@PACKAGE@', 'net.minecraftforge:forge') + .replace('@JAVA_VERSION@', javaVersion) when: def result = gradle.run(task: "build") @@ -50,19 +51,19 @@ class ForgeTest extends Specification implements GradleProjectTestTrait { result.task(":build").outcome == SUCCESS where: - mcVersion | forgeVersion | mappings - '1.20.1' | "47.2.1" | "loom.officialMojangMappings()" - '1.20.1' | "47.2.1" | "'net.fabricmc:yarn:1.19.4+build.2:v2'" - '1.19.4' | "45.0.43" | "loom.officialMojangMappings()" - '1.19.4' | "45.0.43" | "'net.fabricmc:yarn:1.19.4+build.2:v2'" - '1.18.1' | "39.0.63" | "loom.officialMojangMappings()" - '1.18.1' | "39.0.63" | '"net.fabricmc:yarn:1.18.1+build.22:v2"' - '1.17.1' | "37.0.67" | "loom.officialMojangMappings()" - '1.17.1' | "37.0.67" | '"net.fabricmc:yarn:1.17.1+build.61:v2"' - '1.16.5' | "36.2.4" | "loom.officialMojangMappings()" - '1.16.5' | "36.2.4" | '"net.fabricmc:yarn:1.16.5+build.5:v2"' - '1.16.5' | '36.2.4' | '"de.oceanlabs.mcp:mcp_snapshot:20210309-1.16.5"' - '1.14.4' | "28.2.23" | "loom.officialMojangMappings()" - '1.14.4' | "28.2.23" | '"net.fabricmc:yarn:1.14.4+build.18:v2"' + mcVersion | forgeVersion | javaVersion | mappings + '1.20.1' | "47.2.1" | '17' | "loom.officialMojangMappings()" + '1.20.1' | "47.2.1" | '17' | "'net.fabricmc:yarn:1.19.4+build.2:v2'" + '1.19.4' | "45.0.43" | '17' | "loom.officialMojangMappings()" + '1.19.4' | "45.0.43" | '17' | "'net.fabricmc:yarn:1.19.4+build.2:v2'" + '1.18.1' | "39.0.63" | '17' | "loom.officialMojangMappings()" + '1.18.1' | "39.0.63" | '17' | '"net.fabricmc:yarn:1.18.1+build.22:v2"' + '1.17.1' | "37.0.67" | '16' | "loom.officialMojangMappings()" + '1.17.1' | "37.0.67" | '16' | '"net.fabricmc:yarn:1.17.1+build.61:v2"' + '1.16.5' | "36.2.4" | '8' | "loom.officialMojangMappings()" + '1.16.5' | "36.2.4" | '8' | '"net.fabricmc:yarn:1.16.5+build.5:v2"' + '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"' } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/forge/PatchedDecompileTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/forge/PatchedDecompileTest.groovy index e9fcb8fdf..38d68cafc 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/forge/PatchedDecompileTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/forge/PatchedDecompileTest.groovy @@ -40,6 +40,9 @@ class PatchedDecompileTest extends Specification implements GradleProjectTestTra gradle.buildGradle.text = gradle.buildGradle.text.replace('@MCVERSION@', mcVersion) .replace('@FORGEVERSION@', forgeVersion) .replace('@MAPPINGS@', 'loom.officialMojangMappings()') + .replace('@REPOSITORIES@', '') + .replace('@PACKAGE@', 'net.minecraftforge:forge') + .replace('@JAVA_VERSION@', javaVersion) when: def result = gradle.run(task: "genForgePatchedSources") @@ -48,9 +51,9 @@ class PatchedDecompileTest extends Specification implements GradleProjectTestTra result.task(":genForgePatchedSources").outcome == SUCCESS where: - mcVersion | forgeVersion - '1.19.2' | "43.1.1" - '1.18.1' | "39.0.63" - '1.17.1' | "37.0.67" + mcVersion | forgeVersion | javaVersion + '1.19.2' | "43.1.1" | '17' + '1.18.1' | "39.0.63" | '17' + '1.17.1' | "37.0.67" | '16' } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/neoforge/NeoForge1201Test.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/neoforge/NeoForge1201Test.groovy index ca10dd186..6c5a82c1b 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/neoforge/NeoForge1201Test.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/neoforge/NeoForge1201Test.groovy @@ -42,6 +42,7 @@ class NeoForge1201Test extends Specification implements GradleProjectTestTrait { .replace('@MAPPINGS@', mappings) .replace('@REPOSITORIES@', 'maven { url "https://maven.neoforged.net/releases/" }') .replace('@PACKAGE@', 'net.neoforged:forge') + .replace('@JAVA_VERSION@', '17') when: def result = gradle.run(task: "build") diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/neoforge/SimpleNeoForgeTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/neoforge/SimpleNeoForgeTest.groovy new file mode 100644 index 000000000..3f385de62 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/neoforge/SimpleNeoForgeTest.groovy @@ -0,0 +1,55 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2023 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.test.integration.neoforge + +import spock.lang.Specification +import spock.lang.Unroll + +import net.fabricmc.loom.test.util.GradleProjectTestTrait + +import static net.fabricmc.loom.test.LoomTestConstants.DEFAULT_GRADLE +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class SimpleNeoForgeTest extends Specification implements GradleProjectTestTrait { + @Unroll + def "build #mcVersion #neoforgeVersion #mappings"() { + setup: + def gradle = gradleProject(project: "neoforge/simple", version: DEFAULT_GRADLE) + gradle.buildGradle.text = gradle.buildGradle.text.replace('@MCVERSION@', mcVersion) + .replace('@NEOFORGEVERSION@', neoforgeVersion) + .replace('@MAPPINGS@', mappings) + + when: + def result = gradle.run(task: "build") + + then: + result.task(":build").outcome == SUCCESS + + where: + mcVersion | neoforgeVersion | mappings + '1.20.2' | '20.2.51-beta' | 'loom.officialMojangMappings()' + '1.20.2' | '20.2.51-beta' | '"net.fabricmc:yarn:1.20.2+build.4:v2"' + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/architectury/MappingOptionTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/architectury/MappingOptionTest.groovy new file mode 100644 index 000000000..f24f04ebf --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/architectury/MappingOptionTest.groovy @@ -0,0 +1,49 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2023 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.test.unit.architectury + +import spock.lang.Specification + +import static dev.architectury.loom.util.MappingOption.* + +class MappingOptionTest extends Specification { + def "namespace filtering with empty array should not change mapping option"() { + when: + def filtered = mappingOption.forNamespaces(namespaces as String[]) + then: + filtered == expected + where: + mappingOption | namespaces | expected + DEFAULT | [] | DEFAULT + WITH_MOJANG | [] | DEFAULT + WITH_SRG | [] | DEFAULT + DEFAULT | ['a', 'srg'] | DEFAULT + WITH_SRG | ['a', 'srg'] | WITH_SRG + WITH_MOJANG | ['a', 'srg'] | DEFAULT + DEFAULT | ['mojang', 'a'] | DEFAULT + WITH_SRG | ['mojang', 'a'] | DEFAULT + WITH_MOJANG | ['mojang', 'a'] | WITH_MOJANG + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/forge/ForgeRunTemplateTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/forge/ForgeRunTemplateTest.groovy index 2d9eaa066..4ec2b3cce 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/forge/ForgeRunTemplateTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/forge/ForgeRunTemplateTest.groovy @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2022 FabricMC + * Copyright (c) 2022-2023 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 @@ -29,6 +29,7 @@ import java.nio.file.Path import com.google.gson.Gson import com.google.gson.JsonObject +import com.mojang.serialization.JsonOps import spock.lang.Specification import spock.lang.TempDir import spock.lang.Unroll @@ -56,7 +57,8 @@ class ForgeRunTemplateTest extends Specification { def json = downloadForgeConfig(gameVersion, forgeVersion) when: - def template = ForgeRunTemplate.fromJson(json.getAsJsonObject("runs").getAsJsonObject("client")) + def result = ForgeRunTemplate.CODEC.parse(JsonOps.INSTANCE, json.getAsJsonObject("runs").getAsJsonObject("client")) + def template = result.getOrThrow(false) { } then: template.name == template.name() // check that the name gradle sees matches the name read from the json diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/forge/SrgMergerTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/forge/SrgMergerTest.groovy index d3dd0e1ab..76fc06dff 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/forge/SrgMergerTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/forge/SrgMergerTest.groovy @@ -30,9 +30,10 @@ import java.nio.file.Path import spock.lang.Specification import spock.lang.TempDir -import net.fabricmc.loom.util.srg.SrgMerger +import net.fabricmc.loom.util.srg.ForgeMappingsMerger import net.fabricmc.mappingio.MappingUtil import net.fabricmc.mappingio.format.MappingFormat +import net.fabricmc.mappingio.format.Tiny2Writer class SrgMergerTest extends Specification { @TempDir @@ -42,7 +43,7 @@ class SrgMergerTest extends Specification { def output = mappingsDir.resolve("output.tiny") def expected = readTestData("expectedOutput.tiny") def proguardInput = extractTempFile("proguard.txt") - def extraMappings = new SrgMerger.ExtraMappings(proguardInput, MappingFormat.PROGUARD, MappingUtil.NS_TARGET_FALLBACK, MappingUtil.NS_SOURCE_FALLBACK) + def extraMappings = new ForgeMappingsMerger.ExtraMappings(proguardInput, MappingFormat.PROGUARD, MappingUtil.NS_TARGET_FALLBACK, MappingUtil.NS_SOURCE_FALLBACK) when: merge(extraMappings, output) @@ -55,7 +56,7 @@ class SrgMergerTest extends Specification { def output = mappingsDir.resolve("output.tiny") def expected = readTestData("expectedOutput.tiny") def extraInput = extractTempFile("extraInput.tsrg") - def extraMappings = SrgMerger.ExtraMappings.ofMojmapTsrg(extraInput) + def extraMappings = ForgeMappingsMerger.ExtraMappings.ofMojmapTsrg(extraInput) when: merge(extraMappings, output) @@ -64,10 +65,13 @@ class SrgMergerTest extends Specification { Files.readAllLines(output) == expected } - private def merge(SrgMerger.ExtraMappings extraMappings, Path output) { + private def merge(ForgeMappingsMerger.ExtraMappings extraMappings, Path output) { def srgInput = extractTempFile("srgInput.tsrg") def tinyInput = extractTempFile("tinyInput.tiny") - SrgMerger.mergeSrg(srgInput, tinyInput, output, extraMappings, true) + + new Tiny2Writer(Files.newBufferedWriter(output), false).withCloseable { writer -> + ForgeMappingsMerger.mergeSrg(srgInput, tinyInput, extraMappings, true).accept(writer) + } } private InputStream openTestDataStream(String path) { diff --git a/src/test/resources/projects/forge/simple/build.gradle b/src/test/resources/projects/forge/simple/build.gradle index 895a65ce2..844f05b6b 100644 --- a/src/test/resources/projects/forge/simple/build.gradle +++ b/src/test/resources/projects/forge/simple/build.gradle @@ -3,9 +3,11 @@ plugins { id 'maven-publish' } +def javaVersion = @JAVA_VERSION@ + java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.toVersion(javaVersion) + targetCompatibility = JavaVersion.toVersion(javaVersion) } base { @@ -45,10 +47,7 @@ tasks.withType(JavaCompile).configureEach { // The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too // JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used. // We'll use that if it's available, but otherwise we'll use the older option. - def targetVersion = 8 - if (JavaVersion.current().isJava9Compatible()) { - it.options.release = targetVersion - } + it.options.release = javaVersion } java { diff --git a/src/test/resources/projects/neoforge/simple/build.gradle b/src/test/resources/projects/neoforge/simple/build.gradle new file mode 100644 index 000000000..03a3d7c10 --- /dev/null +++ b/src/test/resources/projects/neoforge/simple/build.gradle @@ -0,0 +1,75 @@ +plugins { + id 'dev.architectury.loom' + id 'maven-publish' +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +base { + archivesName = project.archives_base_name +} + +version = project.mod_version +group = project.maven_group + +def mcVersion = "@MCVERSION@" +def neoforgeVersion = "@NEOFORGEVERSION@" + +repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. + maven { url "https://maven.neoforged.net/releases/" } +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:$mcVersion" + mappings @MAPPINGS@ + neoForge "net.neoforged:neoforge:$neoforgeVersion" +} + +tasks.withType(JavaCompile).configureEach { + it.options.release = 17 +} + +java { + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() +} + +jar { + from("LICENSE") { + rename { "${it}_${project.archivesBaseName}"} + } +} + +// configure the maven publication +publishing { + publications { + mavenJava(MavenPublication) { + // add all the jars that should be included when publishing to maven + artifact(remapJar) { + builtBy remapJar + } + artifact(sourcesJar) { + builtBy remapSourcesJar + } + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } +} diff --git a/src/test/resources/projects/neoforge/simple/gradle.properties b/src/test/resources/projects/neoforge/simple/gradle.properties new file mode 100644 index 000000000..7fb259a90 --- /dev/null +++ b/src/test/resources/projects/neoforge/simple/gradle.properties @@ -0,0 +1,11 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G + +# Mod Properties +mod_version = 1.0.0 +maven_group = com.example +archives_base_name = fabric-example-mod + +# Dependencies +# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api +loom.platform = neoforge diff --git a/src/test/resources/projects/neoforge/simple/settings.gradle b/src/test/resources/projects/neoforge/simple/settings.gradle new file mode 100644 index 000000000..c162c363e --- /dev/null +++ b/src/test/resources/projects/neoforge/simple/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = "fabric-example-mod" +