From f39becf005f98cdec0e09d590e3531cdb8309cb4 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 14 Dec 2025 14:07:36 -0500 Subject: [PATCH 01/15] WIP --- build.gradle | 21 ++++++++++++------- .../neoform/runtime/engine/NeoFormEngine.java | 8 +++++-- .../runtime/engine/ProcessGeneration.java | 19 +++++++++++++++++ 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 26246aea..365e2c19 100644 --- a/build.gradle +++ b/build.gradle @@ -166,17 +166,17 @@ idea { } "Run Neoforge 1.21.6 (joined) + Parchment"(Application) { mainClass = mainClassName - programParameters = "run --dist joined --neoforge net.neoforged:neoforge:21.6.1-beta:userdev --add-repository=https://maven.parchmentmc.org --parchment-data=org.parchmentmc.data:parchment-1.21.5:2025.06.15@zip --parchment-conflict-prefix=p_ --write-result=compiled:build/minecraft-1.21.6.jar --write-result=clientResources:build/client-extra-1.21.6.jar --write-result=sources:build/minecraft-sources-1.21.6.jar" + programParameters = "run --dist joined --neoforge net.neoforged:neoforge:21.6.1-beta:userdev --add-repository=https://maven.parchmentmc.org --parchment-data=org.parchmentmc.data:parchment-1.21.5:2025.06.15@zip --parchment-conflict-prefix=p_ --write-result=gameJar:build/minecraft-1.21.6.jar --write-result=clientResources:build/client-extra-1.21.6.jar --write-result=gameSources:build/minecraft-sources-1.21.6.jar" moduleRef(project, sourceSets.main) } "Run Neoforge 1.21 (joined) + Parchment"(Application) { mainClass = mainClassName - programParameters = "run --dist joined --neoforge net.neoforged:neoforge:21.0.0-beta:userdev --add-repository=https://maven.parchmentmc.org --parchment-data=org.parchmentmc.data:parchment-1.21:2024.06.23@zip --parchment-conflict-prefix=p_ --write-result=compiled:build/minecraft-1.21.jar --write-result=clientResources:build/client-extra-1.21.jar --write-result=sources:build/minecraft-sources-1.21.jar" + programParameters = "run --dist joined --neoforge net.neoforged:neoforge:21.0.0-beta:userdev --add-repository=https://maven.parchmentmc.org --parchment-data=org.parchmentmc.data:parchment-1.21:2024.06.23@zip --parchment-conflict-prefix=p_ --write-result=gameJar:build/minecraft-1.21.jar --write-result=clientResources:build/client-extra-1.21.jar --write-result=gameSources:build/minecraft-sources-1.21.jar" moduleRef(project, sourceSets.main) } "Run Neoforge 1.21 (joined)"(Application) { mainClass = mainClassName - programParameters = "run --dist joined --neoforge net.neoforged:neoforge:21.0.0-beta:userdev --write-result=compiled:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=sources:build/minecraft-sources.jar" + programParameters = "run --dist joined --neoforge net.neoforged:neoforge:21.0.0-beta:userdev --write-result=gameJar:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=gameSources:build/minecraft-sources.jar" moduleRef(project, sourceSets.main) } "Run Neoforge 1.21 (joined, no sources)"(Application) { @@ -186,22 +186,22 @@ idea { } "Run Neoform 1.21 (joined)"(Application) { mainClass = mainClassName - programParameters = "run --dist joined --neoform net.neoforged:neoform:1.21-20240613.152323@zip --write-result=compiled:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=sources:build/minecraft-sources.jar" + programParameters = "run --dist joined --neoform net.neoforged:neoform:1.21-20240613.152323@zip --write-result=gameJar:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=gameSources:build/minecraft-sources.jar" moduleRef(project, sourceSets.main) } "Run Neoforge 1.20.6 (joined) + Parchment"(Application) { mainClass = mainClassName - programParameters = "run --dist joined --neoforge net.neoforged:neoforge:20.6.72-beta:userdev --add-repository=https://maven.parchmentmc.org --parchment-data=org.parchmentmc.data:parchment-1.20.6:2024.05.01:checked@zip --write-result=compiled:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=sources:build/minecraft-sources.jar" + programParameters = "run --dist joined --neoforge net.neoforged:neoforge:20.6.72-beta:userdev --add-repository=https://maven.parchmentmc.org --parchment-data=org.parchmentmc.data:parchment-1.20.6:2024.05.01:checked@zip --write-result=gameJar:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=gameSources:build/minecraft-sources.jar" moduleRef(project, sourceSets.main) } "Run Neoforge 1.20.6 (joined)"(Application) { mainClass = mainClassName - programParameters = "run --dist joined --neoforge net.neoforged:neoforge:20.6.72-beta:userdev --write-result=compiled:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=sources:build/minecraft-sources.jar" + programParameters = "run --dist joined --neoforge net.neoforged:neoforge:20.6.72-beta:userdev --write-result=gameJar:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=gameSources:build/minecraft-sources.jar" moduleRef(project, sourceSets.main) } "Run Neoform 1.20.6 (joined)"(Application) { mainClass = mainClassName - programParameters = "run --dist joined --neoform net.neoforged:neoform:1.20.6-20240429.153634@zip --write-result=compiled:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=sources:build/minecraft-sources.jar" + programParameters = "run --dist joined --neoform net.neoforged:neoform:1.20.6-20240429.153634@zip --write-result=gameJar:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=gameSources:build/minecraft-sources.jar" moduleRef(project, sourceSets.main) } "Run NeoForge 1.20.1 (joined)"(Application) { @@ -219,9 +219,14 @@ idea { programParameters = "run --dist joined --add-repository https://maven.minecraftforge.net --neoforge net.minecraftforge:forge:1.17.1-37.1.1:userdev --write-result=compiledWithNeoForge:build/1.17.1-minecraft.jar --write-result=clientResources:build/1.17.1-client-extra.jar --write-result=sourcesWithNeoForge:build/1.17.1-minecraft-sources.jar" moduleRef(project, sourceSets.main) } + "Run Neoform 1.12.2 (joined)"(Application) { + mainClass = mainClassName + programParameters = "run --dist joined --neoform de.oceanlabs.mcp:mcp_config:1.12.2@zip --write-result=gameJar:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=gameSources:build/minecraft-sources.jar --java-executable=/usr/lib/jvm/java-8-openjdk/bin/java" + moduleRef(project, sourceSets.main) + } "Run Neoform 1.19.2 (joined)"(Application) { mainClass = mainClassName - programParameters = "run --dist joined --neoform de.oceanlabs.mcp:mcp_config:1.19.2@zip --write-result=compiled:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=sources:build/minecraft-sources.jar" + programParameters = "run --dist joined --neoform de.oceanlabs.mcp:mcp_config:1.19.2@zip --write-result=gameJar:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=gameSources:build/minecraft-sources.jar" moduleRef(project, sourceSets.main) } "Run Neoform 1.19.2 (joined, no sources)"(Application) { diff --git a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java index 16c42ed7..620bacbc 100644 --- a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java +++ b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java @@ -221,7 +221,7 @@ public void loadNeoFormProcess(NeoFormDistConfig distConfig) { // If we're running NeoForm for 1.20.1 or earlier, the sources after patches use // SRG method and field names, and need to be remapped. - if (processGeneration.sourcesUseIntermediaryNames()) { + if (false && processGeneration.sourcesUseIntermediaryNames()) { if (!graph.hasOutput("mergeMappings", "output") || !graph.hasOutput("downloadClientMappings", "output")) { throw new IllegalStateException("NFRT currently does not support MCP versions that did not make use of official Mojang mappings (pre 1.17)."); @@ -283,7 +283,11 @@ private NodeOutput addRecompileStep(NeoFormDistConfig distConfig, NodeOutput sou compileAction = new RecompileSourcesActionWithJDK(); } - compileAction.setTargetJavaVersion(distConfig.javaVersion()); + int javaVersion = distConfig.javaVersion(); + if (javaVersion == 0) { + javaVersion = 8; + } + compileAction.setTargetJavaVersion(javaVersion); // Add NeoForm libraries or apply overridden classpath fully compileAction.getClasspath().setOverriddenClasspath(buildOptions.getOverriddenCompileClasspath()); diff --git a/src/main/java/net/neoforged/neoform/runtime/engine/ProcessGeneration.java b/src/main/java/net/neoforged/neoform/runtime/engine/ProcessGeneration.java index 004b0275..f620ff86 100644 --- a/src/main/java/net/neoforged/neoform/runtime/engine/ProcessGeneration.java +++ b/src/main/java/net/neoforged/neoform/runtime/engine/ProcessGeneration.java @@ -41,6 +41,7 @@ public int compareTo(@NotNull ProcessGeneration.MinecraftReleaseVersion o) { } } + private static final MinecraftReleaseVersion MC_1_16_5 = new MinecraftReleaseVersion(1, 16, 5); private static final MinecraftReleaseVersion MC_1_17_1 = new MinecraftReleaseVersion(1, 17, 1); private static final MinecraftReleaseVersion MC_1_20_1 = new MinecraftReleaseVersion(1, 20, 1); private static final MinecraftReleaseVersion MC_1_21_6 = new MinecraftReleaseVersion(1, 21, 6); @@ -59,6 +60,14 @@ public int compareTo(@NotNull ProcessGeneration.MinecraftReleaseVersion o) { */ private boolean sourcesUseIntermediaryNames; + /** + * Indicates that the classes produced by MCP/NeoForm in this version use MCP names instead + * of Mojang-mapped names. In these versions, we apply an additional remapping step on the + * sources (before the SRG remap) that translates all the classnames from MCP->Mojmap, so that + * the names line up with what is expected on newer versions. + */ + private boolean classesUseMCPNames; + /** * SAS was used in Forge 1.20.1 and earlier to remove the "OnlyIn" annotation from client-only classes * that we'd want to be able to use on the server as well. @@ -101,6 +110,9 @@ static ProcessGeneration fromMinecraftVersion(String minecraftVersion) { // In 1.20.2 and later, NeoForge switched to Mojmap at runtime and sources defined in Mojmap result.sourcesUseIntermediaryNames = isLessThanOrEqualTo(releaseVersion, MC_1_20_1); + // In 1.17 and later, Forge switched to Mojmap classnames at runtime instead of MCP classnames + result.classesUseMCPNames = isLessThanOrEqualTo(releaseVersion, MC_1_16_5); + // In 1.21.6 and later, manifest entries should be generated as they may be used instead of RuntimeDistCleaner result.generateDistSourceManifest = isGreaterThanOrEqualTo(releaseVersion, MC_1_21_6); @@ -130,6 +142,13 @@ public boolean sourcesUseIntermediaryNames() { return sourcesUseIntermediaryNames; } + /** + * Does the Minecraft source code that MCP/NeoForm creates use MCP names instead of Mojmap names? + */ + public boolean classesUseMCPNames() { + return classesUseMCPNames; + } + /** * We only support side annotation stripping for Forge 1.20.1 and earlier. */ From 75ac32414d29828f80955fbff9c73a788a7927ba Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 14 Dec 2025 15:11:09 -0500 Subject: [PATCH 02/15] Fix recomp failure on older MCPConfigs --- .../neoform/runtime/actions/RecompileSourcesActionWithECJ.java | 2 +- .../neoform/runtime/actions/RecompileSourcesActionWithJDK.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/RecompileSourcesActionWithECJ.java b/src/main/java/net/neoforged/neoform/runtime/actions/RecompileSourcesActionWithECJ.java index 92c5e509..94b7ed34 100644 --- a/src/main/java/net/neoforged/neoform/runtime/actions/RecompileSourcesActionWithECJ.java +++ b/src/main/java/net/neoforged/neoform/runtime/actions/RecompileSourcesActionWithECJ.java @@ -146,7 +146,7 @@ public void acceptResult(CompilationResult result) { while (entries.hasMoreElements()) { var entry = entries.nextElement(); if (!entry.isDirectory()) { - if (entry.getName().endsWith(".java")) { + if (entry.getName().endsWith(".java") && !entry.getName().equals("package-info-template.java")) { futures.add(executor.submit(() -> { // TODO This is copy heavy and should be optimized try (var in = sourcesZip.getInputStream(entry)) { diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/RecompileSourcesActionWithJDK.java b/src/main/java/net/neoforged/neoform/runtime/actions/RecompileSourcesActionWithJDK.java index 64c86212..28370892 100644 --- a/src/main/java/net/neoforged/neoform/runtime/actions/RecompileSourcesActionWithJDK.java +++ b/src/main/java/net/neoforged/neoform/runtime/actions/RecompileSourcesActionWithJDK.java @@ -40,7 +40,7 @@ public void run(ProcessingEnvironment environment) throws IOException, Interrupt try (var stream = Files.walk(sourceRoot).filter(Files::isRegularFile)) { stream.forEach(path -> { var filename = path.getFileName().toString(); - if (filename.endsWith(".java")) { + if (filename.endsWith(".java") && !filename.equals("package-info-template.java")) { sourcePaths.add(path); } else { nonSourcePaths.add(path); From 801658c2dd76343b57a7b3b9aadb3557ed632231 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:01:56 -0500 Subject: [PATCH 03/15] Up to recomp --- .../actions/RemapSrgClassesAction.java | 2 +- .../actions/RemapSrgClassesToMcpAction.java | 52 +++++++++ .../actions/RemapSrgSourcesAction.java | 21 +++- .../actions/RemapSrgSourcesToMcpAction.java | 30 ++++++ .../runtime/artifacts/ArtifactManager.java | 3 +- .../runtime/cli/RunNeoFormCommand.java | 37 ++++++- .../config/neoforge/NeoForgeConfig.java | 5 +- .../runtime/config/neoform/NeoFormConfig.java | 5 + .../neoform/runtime/engine/NeoFormEngine.java | 100 ++++++++++++------ .../runtime/graph/ExecutionNodeBuilder.java | 4 + .../neoform/runtime/utils/MCPMappingsZip.java | 59 +++++++++++ 11 files changed, 271 insertions(+), 47 deletions(-) create mode 100644 src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java create mode 100644 src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesToMcpAction.java create mode 100644 src/main/java/net/neoforged/neoform/runtime/utils/MCPMappingsZip.java diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesAction.java index ad95eaf9..3942a90a 100644 --- a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesAction.java +++ b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesAction.java @@ -14,7 +14,7 @@ public RemapSrgClassesAction() { @Override public void run(ProcessingEnvironment environment) throws IOException, InterruptedException { - var srgToOfficial = RemapSrgSourcesAction.buildSrgToOfficialMappings(environment); + var srgToOfficial = RemapSrgSourcesAction.buildSrgToOfficialMappingFile(environment); var mappingsFile = environment.getWorkspace().resolve("mappings.tsrg2"); srgToOfficial.write(mappingsFile, IMappingFile.Format.TSRG2, false); diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java new file mode 100644 index 00000000..53bff066 --- /dev/null +++ b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java @@ -0,0 +1,52 @@ +package net.neoforged.neoform.runtime.actions; + +import net.neoforged.neoform.runtime.engine.ProcessingEnvironment; +import net.neoforged.neoform.runtime.utils.MCPMappingsZip; +import net.neoforged.neoform.runtime.utils.ToolCoordinate; +import net.neoforged.srgutils.IMappingFile; +import net.neoforged.srgutils.IRenamer; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; + +public class RemapSrgClassesToMcpAction extends ExternalJavaToolAction { + private final Path mcpMappingsData; + + public RemapSrgClassesToMcpAction(Path mcpMappingsData) { + super(ToolCoordinate.AUTO_RENAMING_TOOL); + Objects.requireNonNull(mcpMappingsData, "MCP mapping data not provided"); + this.mcpMappingsData = mcpMappingsData; + } + + @Override + public void run(ProcessingEnvironment environment) throws IOException, InterruptedException { + // We need to generate a srg->mcp mappings file. To do this we use the obf->srg mappings, reverse them, + // and then replace the obf names with the mcp ones. + var obfToSrgFile = environment.extractData("mappings"); + var mappingsZip = MCPMappingsZip.from(mcpMappingsData); + + var srgToMcpMappings = IMappingFile.load(obfToSrgFile.toFile()).reverse().rename(new IRenamer() { + @Override + public String rename(IMappingFile.IField value) { + return mappingsZip.fieldMappings().getOrDefault(value.getOriginal(), value.getOriginal()); + } + + @Override + public String rename(IMappingFile.IMethod value) { + return mappingsZip.methodMappings().getOrDefault(value.getOriginal(), value.getOriginal()); + } + }); + + var mappingsFile = environment.getWorkspace().resolve("mappings.tsrg2"); + srgToMcpMappings.write(mappingsFile, IMappingFile.Format.TSRG2, false); + + setArgs(List.of( + "--input", "{input}", + "--output", "{output}", + "--map", environment.getPathArgument(mappingsFile.toAbsolutePath()) + )); + super.run(environment); + } +} diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java index da71da53..ca1e0b54 100644 --- a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java +++ b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java @@ -10,6 +10,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.HashMap; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipInputStream; @@ -23,7 +24,7 @@ public class RemapSrgSourcesAction implements ExecutionNodeAction { private static final Pattern SRG_FINDER = Pattern.compile("[fF]unc_\\d+_[a-zA-Z_]+|m_\\d+_|[fF]ield_\\d+_[a-zA-Z_]+|f_\\d+_"); - static IMappingFile buildSrgToOfficialMappings(ProcessingEnvironment environment) throws IOException { + static IMappingFile buildSrgToOfficialMappingFile(ProcessingEnvironment environment) throws IOException { var officialMappingsPath = environment.getRequiredInputPath("officialMappings"); var mergeMappingsPath = environment.getRequiredInputPath("mergedMappings"); @@ -34,9 +35,8 @@ static IMappingFile buildSrgToOfficialMappings(ProcessingEnvironment environment return srgMappings.chain(officialMappings); } - @Override - public void run(ProcessingEnvironment environment) throws IOException, InterruptedException { - var srgToOfficial = buildSrgToOfficialMappings(environment); + protected Map buildSrgToOfficialMap(ProcessingEnvironment environment) throws IOException { + var srgToOfficial = buildSrgToOfficialMappingFile(environment); var srgNamesToOfficial = new HashMap(); for (var mappedClass : srgToOfficial.getClasses()) { for (var mappedField : mappedClass.getFields()) { @@ -46,6 +46,12 @@ public void run(ProcessingEnvironment environment) throws IOException, Interrupt srgNamesToOfficial.put(mappedMethod.getOriginal(), mappedMethod.getMapped()); } } + return srgNamesToOfficial; + } + + @Override + public void run(ProcessingEnvironment environment) throws IOException, InterruptedException { + var srgNamesToOfficial = this.buildSrgToOfficialMap(environment); var sourcesPath = environment.getRequiredInputPath("sources"); var outputPath = environment.getOutputPath("output"); @@ -67,7 +73,7 @@ public void run(ProcessingEnvironment environment) throws IOException, Interrupt } } - private static String mapSourceCode(String sourceCode, HashMap srgNamesToOfficial) { + private static String mapSourceCode(String sourceCode, Map srgNamesToOfficial) { var m = SRG_FINDER.matcher(sourceCode); return m.replaceAll(matchResult -> { var matched = matchResult.group(); @@ -76,4 +82,9 @@ private static String mapSourceCode(String sourceCode, HashMap s return Matcher.quoteReplacement(mapped); }); } + + @FunctionalInterface + public interface MappingProvider { + Map getMappings(ProcessingEnvironment environment) throws IOException; + } } diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesToMcpAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesToMcpAction.java new file mode 100644 index 00000000..3407fd14 --- /dev/null +++ b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesToMcpAction.java @@ -0,0 +1,30 @@ +package net.neoforged.neoform.runtime.actions; + +import net.neoforged.neoform.runtime.cache.CacheKeyBuilder; +import net.neoforged.neoform.runtime.engine.ProcessingEnvironment; +import net.neoforged.neoform.runtime.utils.MCPMappingsZip; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Map; +import java.util.Objects; + +public class RemapSrgSourcesToMcpAction extends RemapSrgSourcesAction { + private final Path mcpMappingsData; + + public RemapSrgSourcesToMcpAction(Path mcpMappingsData) { + Objects.requireNonNull(mcpMappingsData, "MCP mappings data not provided"); + this.mcpMappingsData = mcpMappingsData; + } + + @Override + protected Map buildSrgToOfficialMap(ProcessingEnvironment environment) throws IOException { + return MCPMappingsZip.from(mcpMappingsData).combinedMappings(); + } + + @Override + public void computeCacheKey(CacheKeyBuilder ck) { + super.computeCacheKey(ck); + ck.addPath("mcp mappings data", this.mcpMappingsData); + } +} diff --git a/src/main/java/net/neoforged/neoform/runtime/artifacts/ArtifactManager.java b/src/main/java/net/neoforged/neoform/runtime/artifacts/ArtifactManager.java index 8c7fbead..aa78b126 100644 --- a/src/main/java/net/neoforged/neoform/runtime/artifacts/ArtifactManager.java +++ b/src/main/java/net/neoforged/neoform/runtime/artifacts/ArtifactManager.java @@ -128,7 +128,8 @@ public Artifact get(MavenCoordinate mavenCoordinate) throws IOException { var finalLocation = artifactsCache.resolve(mavenCoordinate.toRelativeRepositoryPath()); // Special case: NeoForge reference libraries that are only available via the Mojang download server - if (mavenCoordinate.groupId().equals("com.mojang") && mavenCoordinate.artifactId().equals("logging")) { + if ((mavenCoordinate.groupId().equals("com.mojang") && mavenCoordinate.artifactId().equals("logging")) + || (mavenCoordinate.groupId().equals("net.minecraft") && mavenCoordinate.artifactId().equals("launchwrapper"))) { return get(mavenCoordinate, MINECRAFT_LIBRARIES_URI); } diff --git a/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java b/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java index c4c390e4..31fdd294 100644 --- a/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java +++ b/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java @@ -45,6 +45,7 @@ import java.util.Base64; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.function.Consumer; import java.util.jar.JarFile; @@ -85,6 +86,9 @@ public class RunNeoFormCommand extends NeoFormEngineCommand { @CommandLine.Option(names = "--parchment-conflict-prefix", description = "Setting this option enables automatic Parchment parameter conflict resolution and uses this prefix for parameter names that clash.") String parchmentConflictPrefix; + @CommandLine.Option(names = "--mcp-mapping-data", description = "Path or Maven coordinates of MCP mapping data to use for pre-1.17 Minecraft") + String mcpMappingData; + static class SourceArtifacts { @CommandLine.Option(names = "--neoform") String neoform; @@ -96,6 +100,10 @@ static class SourceArtifacts { protected void runWithNeoFormEngine(NeoFormEngine engine, List closables) throws IOException, InterruptedException { var artifactManager = engine.getArtifactManager(); + if (mcpMappingData != null) { + engine.setMcpMappingsData(artifactManager.get(mcpMappingData).path()); + } + if (sourceArtifacts.neoforge != null) { var neoforgeArtifact = artifactManager.get(sourceArtifacts.neoforge); var neoforgeZipFile = engine.addManagedResource(new JarFile(neoforgeArtifact.path().toFile())); @@ -248,8 +256,28 @@ private static void applyNeoForgeProcessTransforms(NeoFormEngine engine, JarFile } } + // Source post-processors were used to post-process the decompiler output before applying the NF patches. + // Example version: 1.12.2. + var nfPatchesInputNode = "patch"; + var sourcePreProcessor = neoforgeConfig.sourcePreProcessor(); + if (sourcePreProcessor != null) { + engine.applyTransform(new ReplaceNodeOutput( + "patch", "output", "applyUserdevSourcePreprocessor", + (builder, previousOutput) -> { + var newOutput = engine.applyFunctionToNode(neoforgeConfig.libraries(), Map.of( + // Provide the output of patch as the input + "input", "{patchOutput}" + ), NodeOutputType.ZIP, sourcePreProcessor, builder); + return Objects.requireNonNull(newOutput); + } + ) + ); + // Patches now need to use this node as input + nfPatchesInputNode = "applyUserdevSourcePreprocessor"; + } + // Append a patch step to the NeoForge patches - engine.applyTransform(new ReplaceNodeOutput("patch", "output", "applyNeoforgePatches", + engine.applyTransform(new ReplaceNodeOutput(nfPatchesInputNode, "output", "applyNeoforgePatches", (builder, previousOutput) -> { return PatchActionFactory.makeAction(builder, new DataSource(neoforgeZipFile, neoforgeConfig.patchesFolder(), engine.getFileHashingService()), @@ -482,9 +510,10 @@ private static ReplaceNodeOutput.NodeFactory sourceTransform(NeoFormEngine engin builder.input("input", previousNodeOutput.asInput()); builder.inputFromNodeOutput("versionManifest", "downloadJson", "output"); var action = new ApplySourceTransformAction(); - // Copy listLibraries classpath from rename action - var renameAction = (ExternalJavaToolAction) engine.getGraph().getNode("rename").action(); - action.getListLibraries().setClasspath(renameAction.getListLibraries().getClasspath().copy()); + // Copy listLibraries classpath from decompile action + // We used to use rename for this, but older MCPConfigs do not provide libraries there + var decompileAction = (ExternalJavaToolAction) engine.getGraph().getNode("decompile").action(); + action.getListLibraries().setClasspath(decompileAction.getListLibraries().getClasspath().copy()); builder.action(action); actionConsumer.accept(action); builder.output("stubs", NodeOutputType.JAR, "Additional stubs (resulted as part of interface injection) to add to the recompilation classpath"); diff --git a/src/main/java/net/neoforged/neoform/runtime/config/neoforge/NeoForgeConfig.java b/src/main/java/net/neoforged/neoform/runtime/config/neoforge/NeoForgeConfig.java index a27c2b99..d8bdafe0 100644 --- a/src/main/java/net/neoforged/neoform/runtime/config/neoforge/NeoForgeConfig.java +++ b/src/main/java/net/neoforged/neoform/runtime/config/neoforge/NeoForgeConfig.java @@ -5,6 +5,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonSyntaxException; import com.google.gson.annotations.SerializedName; +import net.neoforged.neoform.runtime.config.neoform.NeoFormFunction; import net.neoforged.neoform.runtime.utils.FilenameUtil; import net.neoforged.neoform.runtime.utils.MavenCoordinate; import org.jetbrains.annotations.Nullable; @@ -31,7 +32,9 @@ public record NeoForgeConfig( Map runs, List libraries, List modules, - @SerializedName("sass") List sideAnnotationStrippers + @SerializedName("sass") List sideAnnotationStrippers, + // This was used in older MC versions (i.e. 1.12.2) + @SerializedName("processor") @Nullable NeoFormFunction sourcePreProcessor ) { public static NeoForgeConfig from(ZipFile zipFile) throws IOException { byte[] configContent; diff --git a/src/main/java/net/neoforged/neoform/runtime/config/neoform/NeoFormConfig.java b/src/main/java/net/neoforged/neoform/runtime/config/neoform/NeoFormConfig.java index a0f54c8e..897bcac4 100644 --- a/src/main/java/net/neoforged/neoform/runtime/config/neoform/NeoFormConfig.java +++ b/src/main/java/net/neoforged/neoform/runtime/config/neoform/NeoFormConfig.java @@ -23,6 +23,11 @@ public record NeoFormConfig(int spec, Map> steps, Map functions, Map> libraries) { + public NeoFormConfig { + if (javaVersion == 0) { + javaVersion = 8; // Older versions did not explicitly specify 8 + } + } public NeoFormDistConfig getDistConfig(String dist) { if (!steps.containsKey(dist)) { diff --git a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java index 34a6132e..e76df189 100644 --- a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java +++ b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java @@ -14,7 +14,9 @@ import net.neoforged.neoform.runtime.actions.RecompileSourcesActionWithECJ; import net.neoforged.neoform.runtime.actions.RecompileSourcesActionWithJDK; import net.neoforged.neoform.runtime.actions.RemapSrgClassesAction; +import net.neoforged.neoform.runtime.actions.RemapSrgClassesToMcpAction; import net.neoforged.neoform.runtime.actions.RemapSrgSourcesAction; +import net.neoforged.neoform.runtime.actions.RemapSrgSourcesToMcpAction; import net.neoforged.neoform.runtime.actions.SplitResourcesFromClassesAction; import net.neoforged.neoform.runtime.artifacts.ArtifactManager; import net.neoforged.neoform.runtime.cache.CacheKeyBuilder; @@ -80,6 +82,7 @@ public class NeoFormEngine implements AutoCloseable { private final BuildOptions buildOptions = new BuildOptions(); private boolean verbose; private ProcessGeneration processGeneration; + private Path mcpMappingsData; /** * Nodes can reference certain configuration data (access transformers, patches, etc.) which come @@ -221,10 +224,10 @@ public void loadNeoFormProcess(NeoFormDistConfig distConfig) { // If we're running NeoForm for 1.20.1 or earlier, the sources after patches use // SRG method and field names, and need to be remapped. - if (false && processGeneration.sourcesUseIntermediaryNames()) { - if (!graph.hasOutput("mergeMappings", "output") - || !graph.hasOutput("downloadClientMappings", "output")) { - throw new IllegalStateException("NFRT currently does not support MCP versions that did not make use of official Mojang mappings (pre 1.17)."); + if (processGeneration.sourcesUseIntermediaryNames()) { + boolean hasMojmapSteps = graph.hasOutput("mergeMappings", "output") && graph.hasOutput("downloadClientMappings", "output"); + if (!hasMojmapSteps && !processGeneration.classesUseMCPNames()) { + throw new IllegalStateException("The NeoForm/MCP process has no nodes for mergeMappings/downloadClientMappings!"); } applyTransforms(List.of( @@ -234,39 +237,51 @@ public void loadNeoFormProcess(NeoFormDistConfig distConfig) { "remapSrgSourcesToOfficial", (builder, previousNodeOutput) -> { builder.input("sources", previousNodeOutput.asInput()); - builder.input("mergedMappings", graph.getRequiredOutput("mergeMappings", "output").asInput()); - builder.input("officialMappings", graph.getRequiredOutput("downloadClientMappings", "output").asInput()); - var action = new RemapSrgSourcesAction(); + RemapSrgSourcesAction action; + if (hasMojmapSteps) { + builder.input("mergedMappings", graph.getRequiredOutput("mergeMappings", "output").asInput()); + builder.input("officialMappings", graph.getRequiredOutput("downloadClientMappings", "output").asInput()); + action = new RemapSrgSourcesAction(); + } else { + action = new RemapSrgSourcesToMcpAction(mcpMappingsData); + } builder.action(action); return builder.output("output", NodeOutputType.ZIP, "Sources with SRG method and field names remapped to official."); } ) )); + // TODO: Implement binpatching rename for MCP { var builder = graph.nodeBuilder("remapSrgClassesToOfficial"); builder.input("input", graph.getRequiredOutput("rename", "output").asInput()); - builder.input("mergedMappings", graph.getRequiredOutput("mergeMappings", "output").asInput()); - builder.input("officialMappings", graph.getRequiredOutput("downloadClientMappings", "output").asInput()); var officialOutput = builder.output("output", NodeOutputType.JAR, "Classes with SRG method and field names remapped to official."); - builder.action(new RemapSrgClassesAction()); + if (hasMojmapSteps) { + builder.input("mergedMappings", graph.getRequiredOutput("mergeMappings", "output").asInput()); + builder.input("officialMappings", graph.getRequiredOutput("downloadClientMappings", "output").asInput()); + builder.action(new RemapSrgClassesAction()); + } else { + builder.action(new RemapSrgClassesToMcpAction(this.mcpMappingsData)); + } builder.build(); graph.setResult(ResultIds.GAME_JAR_NO_RECOMP, officialOutput); } - // We also expose a few results for mappings in different formats - var createMappings = graph.nodeBuilder("createMappings"); - // official -> obf - createMappings.inputFromNodeOutput("officialToObf", "downloadClientMappings", "output"); - // obf -> srg - createMappings.inputFromNodeOutput("obfToSrg", "mergeMappings", "output"); - var action = new CreateLegacyMappingsAction(); - createMappings.action(action); - graph.setResult(ResultIds.NAMED_TO_INTERMEDIARY_MAPPING, createMappings.output("officialToSrg", NodeOutputType.TSRG, "A mapping file that maps user-facing (Mojang, MCP) names to intermediary (SRG)")); - graph.setResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING, createMappings.output("srgToOfficial", NodeOutputType.SRG, "A mapping file that maps intermediary (SRG) names to user-facing (Mojang, MCP) names")); - graph.setResult(ResultIds.CSV_MAPPING, createMappings.output("csvMappings", NodeOutputType.ZIP, "A zip containing csv files with SRG to official mappings")); - createMappings.build(); + if (!processGeneration.classesUseMCPNames()) { + // We also expose a few results for mappings in different formats + var createMappings = graph.nodeBuilder("createMappings"); + // official -> obf + createMappings.inputFromNodeOutput("officialToObf", "downloadClientMappings", "output"); + // obf -> srg + createMappings.inputFromNodeOutput("obfToSrg", "mergeMappings", "output"); + var action = new CreateLegacyMappingsAction(); + createMappings.action(action); + graph.setResult(ResultIds.NAMED_TO_INTERMEDIARY_MAPPING, createMappings.output("officialToSrg", NodeOutputType.TSRG, "A mapping file that maps user-facing (Mojang, MCP) names to intermediary (SRG)")); + graph.setResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING, createMappings.output("srgToOfficial", NodeOutputType.SRG, "A mapping file that maps intermediary (SRG) names to user-facing (Mojang, MCP) names")); + graph.setResult(ResultIds.CSV_MAPPING, createMappings.output("csvMappings", NodeOutputType.ZIP, "A zip containing csv files with SRG to official mappings")); + createMappings.build(); + } } } @@ -284,9 +299,6 @@ private NodeOutput addRecompileStep(NeoFormDistConfig distConfig, NodeOutput sou } int javaVersion = distConfig.javaVersion(); - if (javaVersion == 0) { - javaVersion = 8; - } compileAction.setTargetJavaVersion(javaVersion); // Add NeoForm libraries or apply overridden classpath fully @@ -426,13 +438,28 @@ private DataSource getRequiredDataSource(String dataId) { } private void applyFunctionToNode(NeoFormDistConfig config, NeoFormStep step, NeoFormFunction function, ExecutionNodeBuilder builder) { + var type = switch (step.type()) { + case "mergeMappings" -> NodeOutputType.TSRG; + case "generateSplitManifest" -> NodeOutputType.JAR_MANIFEST; + default -> NodeOutputType.JAR; + }; + + applyFunctionToNode(config.libraries(), step.values(), type, function, builder); + } + + @Nullable + public NodeOutput applyFunctionToNode(List libraries, + Map placeholders, + NodeOutputType outputType, + NeoFormFunction function, + ExecutionNodeBuilder builder) { var resolvedJvmArgs = new ArrayList<>(Objects.requireNonNullElse(function.jvmargs(), List.of())); var resolvedArgs = new ArrayList<>(Objects.requireNonNullElse(function.args(), List.of())); // At runtime, {placeholder} in the function arguments can refer to node inputs, node outputs or data (see ProcessingEnvironment#interpolateString). // Variables assigned to outputs of other nodes have already been added as node inputs and can be used directly. // Any constants or references to data need to be eagerly resolved since those are not supported as node inputs. - for (var entry : step.values().entrySet()) { + for (var entry : placeholders.entrySet()) { if (builder.hasInput(entry.getKey())) { continue; // This placeholder was already declared as a node input (and referes to the output of another node) } @@ -441,6 +468,8 @@ private void applyFunctionToNode(NeoFormDistConfig config, NeoFormStep step, Neo resolvedArgs.replaceAll(resolver); } + NodeOutput[] mainOutput = new NodeOutput[1]; + // Now resolve the remaining placeholders. Set dataSourcesUsed = new HashSet<>(); boolean[] usesListLibraries = new boolean[]{false}; @@ -452,13 +481,8 @@ private void applyFunctionToNode(NeoFormDistConfig config, NeoFormStep step, Neo // Handle the "magic" output variable. In NeoForm JSON, it's impossible to know which // variables are truly intended to be outputs. if ("output".equals(variable)) { - var type = switch (step.type()) { - case "mergeMappings" -> NodeOutputType.TSRG; - case "generateSplitManifest" -> NodeOutputType.JAR_MANIFEST; - default -> NodeOutputType.JAR; - }; if (!builder.hasOutput(variable)) { - builder.output(variable, type, "Output of step " + step.type()); + mainOutput[0] = builder.output(variable, outputType, "Output of step " + outputType); } } else if (dataSources.containsKey(variable)) { // It likely refers to data from the NeoForm zip, this will be handled by the runtime later @@ -476,7 +500,7 @@ private void applyFunctionToNode(NeoFormDistConfig config, NeoFormStep step, Neo } else if (builder.hasInput(variable)) { // The variable was already set by the step and added as an input to the node, so we can safely use it } else { - throw new IllegalArgumentException("Unsupported variable " + variable + " used by step " + step.getId()); + throw new IllegalArgumentException("Unsupported variable " + variable + " used by step " + builder.id()); } } }; @@ -487,7 +511,7 @@ private void applyFunctionToNode(NeoFormDistConfig config, NeoFormStep step, Neo try { toolArtifactCoordinate = MavenCoordinate.parse(function.toolArtifact()); } catch (Exception e) { - throw new IllegalArgumentException("Function for step " + step + " has invalid tool: " + function.toolArtifact()); + throw new IllegalArgumentException("Function for step " + builder.id() + " has invalid tool: " + function.toolArtifact()); } var action = new ExternalJavaToolAction(toolArtifactCoordinate); @@ -505,9 +529,11 @@ private void applyFunctionToNode(NeoFormDistConfig config, NeoFormStep step, Neo builder.inputFromNodeOutput("versionManifest", "downloadJson", "output"); var listLibraries = new CreateLibrariesOptionsFile(); listLibraries.getClasspath().setOverriddenClasspath(buildOptions.getOverriddenCompileClasspath()); - listLibraries.getClasspath().addMavenLibraries(config.libraries()); + listLibraries.getClasspath().addMavenLibraries(libraries); action.setListLibraries(listLibraries); } + + return mainOutput[0]; } private void createDownloadFromVersionManifest(ExecutionNodeBuilder builder, String manifestEntry, NodeOutputType jar, String description) { @@ -857,4 +883,8 @@ public ProblemReporter getProblemReporter() { return problemReporter; } } + + public void setMcpMappingsData(Path mcpMappingsData) { + this.mcpMappingsData = mcpMappingsData; + } } diff --git a/src/main/java/net/neoforged/neoform/runtime/graph/ExecutionNodeBuilder.java b/src/main/java/net/neoforged/neoform/runtime/graph/ExecutionNodeBuilder.java index a8a17488..5f12368e 100644 --- a/src/main/java/net/neoforged/neoform/runtime/graph/ExecutionNodeBuilder.java +++ b/src/main/java/net/neoforged/neoform/runtime/graph/ExecutionNodeBuilder.java @@ -16,6 +16,10 @@ public ExecutionNodeBuilder(ExecutionGraph graph, String id) { this.id = Objects.requireNonNull(id, "id"); } + public String id() { + return this.id; + } + public boolean hasInput(String inputId) { return nodeInputs.containsKey(inputId); } diff --git a/src/main/java/net/neoforged/neoform/runtime/utils/MCPMappingsZip.java b/src/main/java/net/neoforged/neoform/runtime/utils/MCPMappingsZip.java new file mode 100644 index 00000000..36ba814c --- /dev/null +++ b/src/main/java/net/neoforged/neoform/runtime/utils/MCPMappingsZip.java @@ -0,0 +1,59 @@ +package net.neoforged.neoform.runtime.utils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.ZipFile; + +public record MCPMappingsZip(Map fieldMappings, Map methodMappings) { + public static MCPMappingsZip from(Path zipFilePath) throws IOException { + try (var zf = new ZipFile(zipFilePath.toFile())) { + var fields = loadMappingsCsv(zf, "fields.csv"); + var methods = loadMappingsCsv(zf, "methods.csv"); + return new MCPMappingsZip(fields, methods); + } + } + + // A very rudimentary parser for the CSV files inside the mappings ZIP. + // Ignores everything except the first two columns. + // Example: + // searge,name,side,desc + // field_100013_f,isPotionDurationMax,0,"True if potion effect duration is at maximum, false otherwise." + private static Map loadMappingsCsv(ZipFile zf, String filename) throws IOException { + Map mappings = new HashMap<>(); + + var entry = zf.getEntry(filename); + if (entry == null) { + throw new IllegalStateException("MCP mappings ZIP file is missing entry " + filename); + } + + try (var reader = new BufferedReader(new InputStreamReader(zf.getInputStream(entry)))) { + var header = reader.readLine(); + if (!header.startsWith("searge,name,")) { + throw new IOException("Invalid header for Mappings CSV: " + filename); + } + String line; + while ((line = reader.readLine()) != null) { + var parts = line.split(",", 3); + if (parts.length < 2) { + continue; + } + String seargeName = parts[0]; + String name = parts[1]; + mappings.put(seargeName, name); + } + } + + return Map.copyOf(mappings); + } + + public Map combinedMappings() { + Map combinedMap = HashMap.newHashMap(fieldMappings.size() + methodMappings.size()); + combinedMap.putAll(fieldMappings); + combinedMap.putAll(methodMappings); + return combinedMap; + } +} From a4693f8222a1eaebf6eb93f7f1b1e5e15dee8153 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:04:44 -0500 Subject: [PATCH 04/15] Add support for dynamic version references --- .../runtime/artifacts/ArtifactManager.java | 28 ++++++ .../runtime/artifacts/MavenMetadata.java | 92 +++++++++++++++++++ .../runtime/utils/MavenCoordinate.java | 15 +++ 3 files changed, 135 insertions(+) create mode 100644 src/main/java/net/neoforged/neoform/runtime/artifacts/MavenMetadata.java diff --git a/src/main/java/net/neoforged/neoform/runtime/artifacts/ArtifactManager.java b/src/main/java/net/neoforged/neoform/runtime/artifacts/ArtifactManager.java index aa78b126..4df9bef8 100644 --- a/src/main/java/net/neoforged/neoform/runtime/artifacts/ArtifactManager.java +++ b/src/main/java/net/neoforged/neoform/runtime/artifacts/ArtifactManager.java @@ -125,6 +125,23 @@ public Artifact get(MavenCoordinate mavenCoordinate) throws IOException { return externalArtifact; } + // Yet another special case: dynamic versions! + // Used in 1.12.1, for example. And yes, this will be very slow. + if (mavenCoordinate.isDynamicVersion()) { + var availableVersions = MavenMetadata.gatherVersions( + downloadManager, + repositoryBaseUrls, + mavenCoordinate.groupId(), + mavenCoordinate.artifactId() + ); + for (var availableVersion : availableVersions) { + if (mavenCoordinate.matchesVersion(availableVersion.version())) { + var concreteMavenCoordinate = mavenCoordinate.withVersion(availableVersion.version()); + return get(concreteMavenCoordinate, availableVersion.repositoryUrl()); + } + } + } + var finalLocation = artifactsCache.resolve(mavenCoordinate.toRelativeRepositoryPath()); // Special case: NeoForge reference libraries that are only available via the Mojang download server @@ -259,11 +276,22 @@ public interface DownloadAction { } private Artifact getFromExternalManifest(MavenCoordinate artifactCoordinate) { + // Try direct match first var artifact = externallyProvided.get(artifactCoordinate); if (artifact != null) { return artifact; } + // Find any manifest entry for the same group/artifact/classifier and evaluate if it matches a dynamic version constraint + if (artifactCoordinate.isDynamicVersion()) { + for (var entry : externallyProvided.entrySet()) { + if (artifactCoordinate.equalsWithoutVersion(entry.getKey()) + && artifactCoordinate.matchesVersion(entry.getKey().version())) { + return entry.getValue(); + } + } + } + // Fall back to looking up a wildcard version for dependency replacement in includeBuild scenarios if (!"*".equals(artifactCoordinate.version())) { artifact = externallyProvided.get(artifactCoordinate.withVersion("*")); diff --git a/src/main/java/net/neoforged/neoform/runtime/artifacts/MavenMetadata.java b/src/main/java/net/neoforged/neoform/runtime/artifacts/MavenMetadata.java new file mode 100644 index 00000000..88048364 --- /dev/null +++ b/src/main/java/net/neoforged/neoform/runtime/artifacts/MavenMetadata.java @@ -0,0 +1,92 @@ +package net.neoforged.neoform.runtime.artifacts; + +import net.neoforged.neoform.runtime.downloads.DownloadManager; +import net.neoforged.neoform.runtime.utils.Logger; +import org.w3c.dom.Element; + +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.ByteArrayInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +/** + * Support class for querying maven metadata from a remote repository. + * The format is documented here: https://maven.apache.org/repositories/metadata.html + * We only deal with A-level metadata since we're interested in listing the versions of + * a specific artifact. + */ +final class MavenMetadata { + private static final Logger LOG = Logger.create(); + + private MavenMetadata() { + } + + static List gatherVersions(DownloadManager downloadManager, + List repositoryBaseUrls, + String groupId, + String artifactId) throws IOException { + var versions = new ArrayList(); + for (var repositoryBaseUrl : repositoryBaseUrls) { + versions.addAll(gatherVersions(downloadManager, repositoryBaseUrl, groupId, artifactId)); + } + return versions; + } + + static List gatherVersions(DownloadManager downloadManager, + URI repositoryBaseUrl, + String groupId, + String artifactId) throws IOException { + var metadataUri = repositoryBaseUrl.toString(); + if (!metadataUri.endsWith("/")) { + metadataUri += "/"; + } + metadataUri += groupId.replace('.', '/') + "/" + artifactId + "/maven-metadata.xml"; + + byte[] metadataContent; + + var tempFile = Files.createTempFile("maven-metadata", ".xml"); + try { + Files.deleteIfExists(tempFile); // The downloader should assume it does not exist yet + downloadManager.download(URI.create(metadataUri), tempFile); + metadataContent = Files.readAllBytes(tempFile); + } catch (FileNotFoundException fnf) { + return List.of(); // Repository doesn't have artifact + } finally { + Files.deleteIfExists(tempFile); + } + + try (var in = new ByteArrayInputStream(metadataContent)) { + var result = new ArrayList(); + var documentBuilder = DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder(); + var document = documentBuilder.parse(in).getDocumentElement(); + var nodes = document.getChildNodes(); + for (var i = 0; i < nodes.getLength(); i++) { + if (nodes.item(i) instanceof Element versioningEl && "versioning".equals(versioningEl.getTagName())) { + for (var versions = versioningEl.getFirstChild(); versions != null; versions = versions.getNextSibling()) { + if (versions instanceof Element versionsEl && "versions".equals(versionsEl.getTagName())) { + for (var child = versionsEl.getFirstChild(); child != null; child = child.getNextSibling()) { + if (child instanceof Element childEl && "version".equals(childEl.getTagName())) { + result.add(new AvailableVersion( + repositoryBaseUrl, + childEl.getTextContent().trim() + )); + } + } + } + } + } + } + return result; + } catch (Exception e) { + LOG.println("Failed to parse Maven metadata from " + metadataUri + ": " + e); + throw new RuntimeException(e); + } + } + + record AvailableVersion(URI repositoryUrl, String version) { + } +} \ No newline at end of file diff --git a/src/main/java/net/neoforged/neoform/runtime/utils/MavenCoordinate.java b/src/main/java/net/neoforged/neoform/runtime/utils/MavenCoordinate.java index 94902767..6d031f5f 100644 --- a/src/main/java/net/neoforged/neoform/runtime/utils/MavenCoordinate.java +++ b/src/main/java/net/neoforged/neoform/runtime/utils/MavenCoordinate.java @@ -151,4 +151,19 @@ public boolean equalsWithoutVersion(MavenCoordinate other) { && Objects.equals(classifier, other.classifier) && Objects.equals(extension, other.extension); } + + public boolean isDynamicVersion() { + // We only support extremely simple cases right now. + return version.endsWith("+"); + } + + public boolean matchesVersion(String version) { + // "+" acts as a prefix match according to Gradle + // https://docs.gradle.org/current/userguide/dependency_versions.html#sec:single-version-declarations + if (this.version.endsWith("+")) { + return version.startsWith(this.version.substring(0, this.version.length() - 1)); + } else { + return this.version.equals(version); + } + } } From 3855b4b8b69d6d82423b1a3ba912d3d1755863e7 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:15:59 -0500 Subject: [PATCH 05/15] Changes for MDG --- .../actions/GenerateMCPSrgFilesAction.java | 46 +++++++++++++++++++ .../actions/RemapSrgClassesToMcpAction.java | 28 +---------- .../neoform/runtime/engine/NeoFormEngine.java | 31 +++++++------ 3 files changed, 65 insertions(+), 40 deletions(-) create mode 100644 src/main/java/net/neoforged/neoform/runtime/actions/GenerateMCPSrgFilesAction.java diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/GenerateMCPSrgFilesAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/GenerateMCPSrgFilesAction.java new file mode 100644 index 00000000..20581fac --- /dev/null +++ b/src/main/java/net/neoforged/neoform/runtime/actions/GenerateMCPSrgFilesAction.java @@ -0,0 +1,46 @@ +package net.neoforged.neoform.runtime.actions; + +import net.neoforged.neoform.runtime.engine.ProcessingEnvironment; +import net.neoforged.neoform.runtime.graph.ExecutionNodeAction; +import net.neoforged.neoform.runtime.utils.MCPMappingsZip; +import net.neoforged.srgutils.IMappingFile; +import net.neoforged.srgutils.IRenamer; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +public class GenerateMCPSrgFilesAction implements ExecutionNodeAction { + private final Path mcpMappingsData; + + public GenerateMCPSrgFilesAction(Path mcpMappingsData) { + Objects.requireNonNull(mcpMappingsData); + this.mcpMappingsData = mcpMappingsData; + } + + @Override + public void run(ProcessingEnvironment environment) throws IOException, InterruptedException { + // We need to generate a srg->mcp mappings file. To do this we use the obf->srg mappings, reverse them, + // and then replace the obf names with the mcp ones. + var obfToSrgFile = environment.extractData("mappings"); + var mappingsZip = MCPMappingsZip.from(mcpMappingsData); + + var srgToMcpMappings = IMappingFile.load(obfToSrgFile.toFile()).reverse().rename(new IRenamer() { + @Override + public String rename(IMappingFile.IField value) { + return mappingsZip.fieldMappings().getOrDefault(value.getOriginal(), value.getOriginal()); + } + + @Override + public String rename(IMappingFile.IMethod value) { + return mappingsZip.methodMappings().getOrDefault(value.getOriginal(), value.getOriginal()); + } + }); + + // Now create the expected toolchain outputs + srgToMcpMappings.reverse().write(environment.getOutputPath("officialToSrg"), IMappingFile.Format.TSRG, false); + srgToMcpMappings.write(environment.getOutputPath("srgToOfficial"), IMappingFile.Format.SRG, false); + Files.copy(mcpMappingsData, environment.getOutputPath("csvMappings")); + } +} diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java index 53bff066..dafcb9fb 100644 --- a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java +++ b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java @@ -1,10 +1,7 @@ package net.neoforged.neoform.runtime.actions; import net.neoforged.neoform.runtime.engine.ProcessingEnvironment; -import net.neoforged.neoform.runtime.utils.MCPMappingsZip; import net.neoforged.neoform.runtime.utils.ToolCoordinate; -import net.neoforged.srgutils.IMappingFile; -import net.neoforged.srgutils.IRenamer; import java.io.IOException; import java.nio.file.Path; @@ -12,40 +9,17 @@ import java.util.Objects; public class RemapSrgClassesToMcpAction extends ExternalJavaToolAction { - private final Path mcpMappingsData; - public RemapSrgClassesToMcpAction(Path mcpMappingsData) { super(ToolCoordinate.AUTO_RENAMING_TOOL); Objects.requireNonNull(mcpMappingsData, "MCP mapping data not provided"); - this.mcpMappingsData = mcpMappingsData; } @Override public void run(ProcessingEnvironment environment) throws IOException, InterruptedException { - // We need to generate a srg->mcp mappings file. To do this we use the obf->srg mappings, reverse them, - // and then replace the obf names with the mcp ones. - var obfToSrgFile = environment.extractData("mappings"); - var mappingsZip = MCPMappingsZip.from(mcpMappingsData); - - var srgToMcpMappings = IMappingFile.load(obfToSrgFile.toFile()).reverse().rename(new IRenamer() { - @Override - public String rename(IMappingFile.IField value) { - return mappingsZip.fieldMappings().getOrDefault(value.getOriginal(), value.getOriginal()); - } - - @Override - public String rename(IMappingFile.IMethod value) { - return mappingsZip.methodMappings().getOrDefault(value.getOriginal(), value.getOriginal()); - } - }); - - var mappingsFile = environment.getWorkspace().resolve("mappings.tsrg2"); - srgToMcpMappings.write(mappingsFile, IMappingFile.Format.TSRG2, false); - setArgs(List.of( "--input", "{input}", "--output", "{output}", - "--map", environment.getPathArgument(mappingsFile.toAbsolutePath()) + "--map", environment.getInputPath("srgToMcpMappings").toAbsolutePath().toString() )); super.run(environment); } diff --git a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java index 5d675784..30e51444 100644 --- a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java +++ b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java @@ -6,6 +6,7 @@ import net.neoforged.neoform.runtime.actions.DownloadLauncherManifestAction; import net.neoforged.neoform.runtime.actions.DownloadVersionManifestAction; import net.neoforged.neoform.runtime.actions.ExternalJavaToolAction; +import net.neoforged.neoform.runtime.actions.GenerateMCPSrgFilesAction; import net.neoforged.neoform.runtime.actions.InjectFromZipFileSource; import net.neoforged.neoform.runtime.actions.InjectZipContentAction; import net.neoforged.neoform.runtime.actions.MergeWithSourcesAction; @@ -249,6 +250,21 @@ public void loadNeoFormProcess(NeoFormDistConfig distConfig) { ) )); + // We also expose a few results for mappings in different formats + var createMappings = graph.nodeBuilder("createMappings"); + if (hasMojmapSteps) { + createMappings.inputFromNodeOutput("officialToObf", "downloadClientMappings", "output"); + createMappings.inputFromNodeOutput("obfToSrg", "mergeMappings", "output"); + var action = new CreateLegacyMappingsAction(); + createMappings.action(action); + } else { + createMappings.action(new GenerateMCPSrgFilesAction(mcpMappingsData)); + } + graph.setResult(ResultIds.NAMED_TO_INTERMEDIARY_MAPPING, createMappings.output("officialToSrg", NodeOutputType.TSRG, "A mapping file that maps user-facing (Mojang, MCP) names to intermediary (SRG)")); + graph.setResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING, createMappings.output("srgToOfficial", NodeOutputType.SRG, "A mapping file that maps intermediary (SRG) names to user-facing (Mojang, MCP) names")); + graph.setResult(ResultIds.CSV_MAPPING, createMappings.output("csvMappings", NodeOutputType.ZIP, "A zip containing csv files with SRG to official mappings")); + createMappings.build(); + // If intermediary is in use, the game jar has to be remapped to developer-facing names to be usable { var builder = graph.nodeBuilder("remapSrgClassesToOfficial"); @@ -259,25 +275,14 @@ public void loadNeoFormProcess(NeoFormDistConfig distConfig) { builder.input("officialMappings", graph.getRequiredOutput("downloadClientMappings", "output").asInput()); builder.action(new RemapSrgClassesAction()); } else { + // We can provide the SRG->MCP mappings generated by the earlier task + builder.input("srgToMcpMappings", graph.getResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING).asInput()); builder.action(new RemapSrgClassesToMcpAction(this.mcpMappingsData)); } builder.build(); graph.setResult(ResultIds.GAME_JAR_NO_RECOMP, officialOutput); } - - if (!processGeneration.classesUseMCPNames()) { - // We also expose a few results for mappings in different formats - var createMappings = graph.nodeBuilder("createMappings"); - createMappings.inputFromNodeOutput("officialToObf", "downloadClientMappings", "output"); - createMappings.inputFromNodeOutput("obfToSrg", "mergeMappings", "output"); - var action = new CreateLegacyMappingsAction(); - createMappings.action(action); - graph.setResult(ResultIds.NAMED_TO_INTERMEDIARY_MAPPING, createMappings.output("officialToSrg", NodeOutputType.TSRG, "A mapping file that maps user-facing (Mojang, MCP) names to intermediary (SRG)")); - graph.setResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING, createMappings.output("srgToOfficial", NodeOutputType.SRG, "A mapping file that maps intermediary (SRG) names to user-facing (Mojang, MCP) names")); - graph.setResult(ResultIds.CSV_MAPPING, createMappings.output("csvMappings", NodeOutputType.ZIP, "A zip containing csv files with SRG to official mappings")); - createMappings.build(); - } } else { // Without the presence of further patching or renaming, the game jar without recompilation is the deobfuscated vanilla jar graph.setResultFromCurrentInput(ResultIds.GAME_JAR_NO_RECOMP, decompileInput); From 2bb960798d452dc9bed6508505ec4645f779e1e5 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:16:58 -0500 Subject: [PATCH 06/15] Remove runs that don't actually work These need --mcp-mapping-data and --java-executable provided in order to work correctly --- build.gradle | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/build.gradle b/build.gradle index ed2098a0..a1b1fed9 100644 --- a/build.gradle +++ b/build.gradle @@ -209,21 +209,11 @@ idea { programParameters = "run --dist joined --neoforge net.neoforged:forge:1.20.1-47.1.54:userdev --parchment-data=org.parchmentmc.data:parchment-1.20.1:2023.09.03@zip --parchment-conflict-prefix=p_ --write-result=namedToIntermediaryMapping:build/1.20.1.tsrg --write-result=gameJarWithNeoForge:build/1.20.1-minecraft.jar --write-result=clientResources:build/1.20.1-client-extra.jar --write-result=gameSourcesWithNeoForge:build/1.20.1-minecraft-sources.jar --problems-report=build/1.20.1-problems.json" moduleRef(project, sourceSets.main) } - "Run Forge 1.12.2 (joined)"(Application) { - mainClass = mainClassName - programParameters = "run --dist joined --add-repository https://maven.minecraftforge.net --neoforge net.minecraftforge:forge:1.12.2-14.23.5.2860:userdev3 --write-result=gameJarWithNeoForge:build/1.12.2-minecraft.jar --write-result=clientResources:build/1.12.2-client-extra.jar --write-result=gameSourcesWithNeoForge:build/1.12.2-minecraft-sources.jar" - moduleRef(project, sourceSets.main) - } "Run Forge 1.17.1 (joined)"(Application) { mainClass = mainClassName programParameters = "run --dist joined --add-repository https://maven.minecraftforge.net --neoforge net.minecraftforge:forge:1.17.1-37.1.1:userdev --write-result=gameJarWithNeoForge:build/1.17.1-minecraft.jar --write-result=clientResources:build/1.17.1-client-extra.jar --write-result=gameSourcesWithNeoForge:build/1.17.1-minecraft-sources.jar" moduleRef(project, sourceSets.main) } - "Run Neoform 1.12.2 (joined)"(Application) { - mainClass = mainClassName - programParameters = "run --dist joined --neoform de.oceanlabs.mcp:mcp_config:1.12.2@zip --write-result=gameJar:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=gameSources:build/minecraft-sources.jar --java-executable=/usr/lib/jvm/java-8-openjdk/bin/java" - moduleRef(project, sourceSets.main) - } "Run Neoform 1.19.2 (joined)"(Application) { mainClass = mainClassName programParameters = "run --dist joined --neoform de.oceanlabs.mcp:mcp_config:1.19.2@zip --write-result=gameJar:build/minecraft.jar --write-result=clientResources:build/client-extra.jar --write-result=gameSources:build/minecraft-sources.jar" From 027f512db03914f71721d811489fd26f229c170f Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:25:26 -0500 Subject: [PATCH 07/15] Revert refactor --- .../net/neoforged/neoform/runtime/engine/NeoFormEngine.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java index 30e51444..dafacdc6 100644 --- a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java +++ b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java @@ -302,8 +302,7 @@ private NodeOutput addRecompileStep(NeoFormDistConfig distConfig, NodeOutput sou compileAction = new RecompileSourcesActionWithJDK(); } - int javaVersion = distConfig.javaVersion(); - compileAction.setTargetJavaVersion(javaVersion); + compileAction.setTargetJavaVersion(distConfig.javaVersion()); // Add NeoForm libraries or apply overridden classpath fully compileAction.getClasspath().setOverriddenClasspath(buildOptions.getOverriddenCompileClasspath()); From bc5303c1c1a71cb4a76400e2ff87b9456b8fe6e2 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 16 May 2026 15:39:59 -0400 Subject: [PATCH 08/15] Support `inject` in userdev config --- .../neoforged/neoform/runtime/cli/RunNeoFormCommand.java | 8 ++++++++ .../neoform/runtime/config/neoforge/NeoForgeConfig.java | 1 + 2 files changed, 9 insertions(+) diff --git a/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java b/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java index 862a922f..24b9585a 100644 --- a/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java +++ b/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java @@ -223,6 +223,14 @@ private static void applyNeoForgeProcessTransforms(NeoFormEngine engine, JarFile // The MCF sources have a bogus MANIFEST that should be ignored Pattern.compile("^(?!META-INF/MANIFEST.MF$).*") )); + // Older Forge versions ship pre-compiled dev-only classes and service registrations + // (e.g. ILaunchHandlerService) under inject/ in the userdev jar. + if (neoforgeConfig.injectFolder() != null) { + action.getInjectedSources().add(new InjectFromZipFileSource( + neoforgeZipFile, + neoforgeConfig.injectFolder() + )); + } } )); } diff --git a/src/main/java/net/neoforged/neoform/runtime/config/neoforge/NeoForgeConfig.java b/src/main/java/net/neoforged/neoform/runtime/config/neoforge/NeoForgeConfig.java index d8bdafe0..1d4ea273 100644 --- a/src/main/java/net/neoforged/neoform/runtime/config/neoforge/NeoForgeConfig.java +++ b/src/main/java/net/neoforged/neoform/runtime/config/neoforge/NeoForgeConfig.java @@ -29,6 +29,7 @@ public record NeoForgeConfig( @SerializedName("universal") String universalArtifact, @SerializedName("patchesOriginalPrefix") @Nullable String basePathPrefix, @SerializedName("patchesModifiedPrefix") @Nullable String modifiedPathPrefix, + @SerializedName("inject") @Nullable String injectFolder, Map runs, List libraries, List modules, From 9981fc52e4249a27885c7d8a8dfaa601a85846ec Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 16 May 2026 15:42:54 -0400 Subject: [PATCH 09/15] Fix duplicate class files being injected --- .../net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java b/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java index 24b9585a..5b9aba6a 100644 --- a/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java +++ b/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java @@ -228,7 +228,8 @@ private static void applyNeoForgeProcessTransforms(NeoFormEngine engine, JarFile if (neoforgeConfig.injectFolder() != null) { action.getInjectedSources().add(new InjectFromZipFileSource( neoforgeZipFile, - neoforgeConfig.injectFolder() + neoforgeConfig.injectFolder(), + Pattern.compile("^(?!.*\\.class$).*") )); } } From e5f7956d7b1c7ee81dfb43be9cbad022fa0c4329 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 16 May 2026 15:52:21 -0400 Subject: [PATCH 10/15] Fix code review issues --- .../neoform/runtime/actions/RemapSrgClassesToMcpAction.java | 5 +---- .../neoform/runtime/actions/RemapSrgSourcesAction.java | 5 ----- .../neoforged/neoform/runtime/artifacts/ArtifactManager.java | 2 ++ .../net/neoforged/neoform/runtime/engine/NeoFormEngine.java | 2 +- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java index dafcb9fb..96026610 100644 --- a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java +++ b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java @@ -4,14 +4,11 @@ import net.neoforged.neoform.runtime.utils.ToolCoordinate; import java.io.IOException; -import java.nio.file.Path; import java.util.List; -import java.util.Objects; public class RemapSrgClassesToMcpAction extends ExternalJavaToolAction { - public RemapSrgClassesToMcpAction(Path mcpMappingsData) { + public RemapSrgClassesToMcpAction() { super(ToolCoordinate.AUTO_RENAMING_TOOL); - Objects.requireNonNull(mcpMappingsData, "MCP mapping data not provided"); } @Override diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java index ca1e0b54..d5e30f3f 100644 --- a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java +++ b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java @@ -82,9 +82,4 @@ private static String mapSourceCode(String sourceCode, Map srgNa return Matcher.quoteReplacement(mapped); }); } - - @FunctionalInterface - public interface MappingProvider { - Map getMappings(ProcessingEnvironment environment) throws IOException; - } } diff --git a/src/main/java/net/neoforged/neoform/runtime/artifacts/ArtifactManager.java b/src/main/java/net/neoforged/neoform/runtime/artifacts/ArtifactManager.java index 4df9bef8..92aa08c5 100644 --- a/src/main/java/net/neoforged/neoform/runtime/artifacts/ArtifactManager.java +++ b/src/main/java/net/neoforged/neoform/runtime/artifacts/ArtifactManager.java @@ -140,6 +140,8 @@ public Artifact get(MavenCoordinate mavenCoordinate) throws IOException { return get(concreteMavenCoordinate, availableVersion.repositoryUrl()); } } + + throw new FileNotFoundException("Could not find " + mavenCoordinate + " in any repository."); } var finalLocation = artifactsCache.resolve(mavenCoordinate.toRelativeRepositoryPath()); diff --git a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java index dafacdc6..969e3f65 100644 --- a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java +++ b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java @@ -485,7 +485,7 @@ public NodeOutput applyFunctionToNode(List libraries, // variables are truly intended to be outputs. if ("output".equals(variable)) { if (!builder.hasOutput(variable)) { - mainOutput[0] = builder.output(variable, outputType, "Output of step " + outputType); + mainOutput[0] = builder.output(variable, outputType, "Output of step " + builder.id()); } } else if (dataSources.containsKey(variable)) { // It likely refers to data from the NeoForm zip, this will be handled by the runtime later From 6c52379ef35594904b160deba406bbb5c748e2dc Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 16 May 2026 16:07:11 -0400 Subject: [PATCH 11/15] Fix compile error --- .../net/neoforged/neoform/runtime/engine/NeoFormEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java index 969e3f65..dddeb7f3 100644 --- a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java +++ b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java @@ -277,7 +277,7 @@ public void loadNeoFormProcess(NeoFormDistConfig distConfig) { } else { // We can provide the SRG->MCP mappings generated by the earlier task builder.input("srgToMcpMappings", graph.getResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING).asInput()); - builder.action(new RemapSrgClassesToMcpAction(this.mcpMappingsData)); + builder.action(new RemapSrgClassesToMcpAction()); } builder.build(); From 58042a7b8e4336865308614a20865c39f24f6d8c Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 16 May 2026 16:34:32 -0400 Subject: [PATCH 12/15] Use Forge official-ish Mojmap naming on 1.16.5 --- .../actions/CreateLegacyMappingsAction.java | 7 +- .../actions/ExtractNeoFormDataAction.java | 31 ++++ .../neoform/runtime/engine/NeoFormEngine.java | 138 ++++++++++++------ .../runtime/engine/ProcessGeneration.java | 22 +-- 4 files changed, 142 insertions(+), 56 deletions(-) create mode 100644 src/main/java/net/neoforged/neoform/runtime/actions/ExtractNeoFormDataAction.java diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/CreateLegacyMappingsAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/CreateLegacyMappingsAction.java index 2d4468dc..7e0c2423 100644 --- a/src/main/java/net/neoforged/neoform/runtime/actions/CreateLegacyMappingsAction.java +++ b/src/main/java/net/neoforged/neoform/runtime/actions/CreateLegacyMappingsAction.java @@ -37,10 +37,13 @@ public void run(ProcessingEnvironment environment) throws IOException, Interrupt srgToOfficial.write(environment.getOutputPath("srgToOfficial"), IMappingFile.Format.SRG, false); try (var zipCsv = new ZipOutputStream(Files.newOutputStream(environment.getOutputPath("csvMappings")))) { + // Match both modern intermediary names (m_/f_, 1.17+) and legacy SRG names (func_/field_, pre-1.17) writeCsv(zipCsv, "methods.csv", srgToOfficial.getClasses().stream() - .flatMap(c -> c.getMethods().stream()).filter(c -> c.getOriginal().startsWith("m_"))); + .flatMap(c -> c.getMethods().stream()) + .filter(c -> c.getOriginal().startsWith("m_") || c.getOriginal().startsWith("func_"))); writeCsv(zipCsv, "fields.csv", srgToOfficial.getClasses().stream() - .flatMap(c -> c.getFields().stream()).filter(c -> c.getOriginal().startsWith("f_"))); + .flatMap(c -> c.getFields().stream()) + .filter(c -> c.getOriginal().startsWith("f_") || c.getOriginal().startsWith("field_"))); } } diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/ExtractNeoFormDataAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/ExtractNeoFormDataAction.java new file mode 100644 index 00000000..157506b3 --- /dev/null +++ b/src/main/java/net/neoforged/neoform/runtime/actions/ExtractNeoFormDataAction.java @@ -0,0 +1,31 @@ +package net.neoforged.neoform.runtime.actions; + +import net.neoforged.neoform.runtime.cache.CacheKeyBuilder; +import net.neoforged.neoform.runtime.engine.ProcessingEnvironment; +import net.neoforged.neoform.runtime.graph.ExecutionNodeAction; + +import java.io.IOException; +import java.nio.file.Files; + +/** + * Copies a NeoForm data source (a file embedded in the NeoForm config zip) to a node output, + * making it available as an input to downstream nodes in the execution graph. + */ +public class ExtractNeoFormDataAction implements ExecutionNodeAction { + private final String dataKey; + + public ExtractNeoFormDataAction(String dataKey) { + this.dataKey = dataKey; + } + + @Override + public void run(ProcessingEnvironment environment) throws IOException, InterruptedException { + Files.copy(environment.extractData(dataKey), environment.getOutputPath("output")); + } + + @Override + public void computeCacheKey(CacheKeyBuilder ck) { + ExecutionNodeAction.super.computeCacheKey(ck); + ck.add("data key", dataKey); + } +} diff --git a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java index dddeb7f3..616ec40c 100644 --- a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java +++ b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java @@ -6,6 +6,7 @@ import net.neoforged.neoform.runtime.actions.DownloadLauncherManifestAction; import net.neoforged.neoform.runtime.actions.DownloadVersionManifestAction; import net.neoforged.neoform.runtime.actions.ExternalJavaToolAction; +import net.neoforged.neoform.runtime.actions.ExtractNeoFormDataAction; import net.neoforged.neoform.runtime.actions.GenerateMCPSrgFilesAction; import net.neoforged.neoform.runtime.actions.InjectFromZipFileSource; import net.neoforged.neoform.runtime.actions.InjectZipContentAction; @@ -224,61 +225,54 @@ public void loadNeoFormProcess(NeoFormDistConfig distConfig) { // If we're running NeoForm for 1.20.1 or earlier, the sources after patches use // SRG method and field names, and need to be remapped. if (processGeneration.sourcesUseIntermediaryNames()) { - boolean hasMojmapSteps = graph.hasOutput("mergeMappings", "output") && graph.hasOutput("downloadClientMappings", "output"); - if (!hasMojmapSteps && !processGeneration.classesUseMCPNames()) { - throw new IllegalStateException("The NeoForm/MCP process has no nodes for mergeMappings/downloadClientMappings!"); + if (!graph.hasOutput("mergeMappings", "output")) { + if (processGeneration.hasProguardMappings()) { + // 1.14.4–1.16.5: ProGuard mappings exist in the version manifest but the MCP + // pipeline predates the mergeMappings/downloadClientMappings steps. Synthesize + // equivalent nodes so the unified path below works for all versions. + synthesizeMojmapNodes(); + } else if (mcpMappingsData != null) { + // pre-1.14.4 (e.g. 1.12.2): no ProGuard mappings; use user-supplied MCP CSV data + setupMcpRemapping(); + } else { + throw new IllegalStateException("This MCP config requires --mcp-mapping-data (Mojang ProGuard mappings are not available for this Minecraft version)."); + } } - applyTransforms(List.of( - new ReplaceNodeOutput( - "patch", - "output", - "remapSrgSourcesToOfficial", - (builder, previousNodeOutput) -> { - builder.input("sources", previousNodeOutput.asInput()); - RemapSrgSourcesAction action; - if (hasMojmapSteps) { + // Unified path: mergeMappings and downloadClientMappings are always present here, + // either natively (1.18+) or synthesized above (1.14.4–1.16.5). + // Not reached when the pre-1.14.4 MCP path was taken above. + if (graph.hasOutput("mergeMappings", "output")) { + applyTransforms(List.of( + new ReplaceNodeOutput( + "patch", + "output", + "remapSrgSourcesToOfficial", + (builder, previousNodeOutput) -> { + builder.input("sources", previousNodeOutput.asInput()); builder.input("mergedMappings", graph.getRequiredOutput("mergeMappings", "output").asInput()); builder.input("officialMappings", graph.getRequiredOutput("downloadClientMappings", "output").asInput()); - action = new RemapSrgSourcesAction(); - } else { - action = new RemapSrgSourcesToMcpAction(mcpMappingsData); + builder.action(new RemapSrgSourcesAction()); + return builder.output("output", NodeOutputType.ZIP, "Sources with SRG method and field names remapped to official."); } - builder.action(action); - return builder.output("output", NodeOutputType.ZIP, "Sources with SRG method and field names remapped to official."); - } - ) - )); + ) + )); - // We also expose a few results for mappings in different formats - var createMappings = graph.nodeBuilder("createMappings"); - if (hasMojmapSteps) { + var createMappings = graph.nodeBuilder("createMappings"); createMappings.inputFromNodeOutput("officialToObf", "downloadClientMappings", "output"); createMappings.inputFromNodeOutput("obfToSrg", "mergeMappings", "output"); - var action = new CreateLegacyMappingsAction(); - createMappings.action(action); - } else { - createMappings.action(new GenerateMCPSrgFilesAction(mcpMappingsData)); - } - graph.setResult(ResultIds.NAMED_TO_INTERMEDIARY_MAPPING, createMappings.output("officialToSrg", NodeOutputType.TSRG, "A mapping file that maps user-facing (Mojang, MCP) names to intermediary (SRG)")); - graph.setResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING, createMappings.output("srgToOfficial", NodeOutputType.SRG, "A mapping file that maps intermediary (SRG) names to user-facing (Mojang, MCP) names")); - graph.setResult(ResultIds.CSV_MAPPING, createMappings.output("csvMappings", NodeOutputType.ZIP, "A zip containing csv files with SRG to official mappings")); - createMappings.build(); + createMappings.action(new CreateLegacyMappingsAction()); + graph.setResult(ResultIds.NAMED_TO_INTERMEDIARY_MAPPING, createMappings.output("officialToSrg", NodeOutputType.TSRG, "A mapping file that maps user-facing (Mojang, MCP) names to intermediary (SRG)")); + graph.setResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING, createMappings.output("srgToOfficial", NodeOutputType.SRG, "A mapping file that maps intermediary (SRG) names to user-facing (Mojang, MCP) names")); + graph.setResult(ResultIds.CSV_MAPPING, createMappings.output("csvMappings", NodeOutputType.ZIP, "A zip containing csv files with SRG to official mappings")); + createMappings.build(); - // If intermediary is in use, the game jar has to be remapped to developer-facing names to be usable - { var builder = graph.nodeBuilder("remapSrgClassesToOfficial"); builder.input("input", decompileInput.copy()); + builder.input("mergedMappings", graph.getRequiredOutput("mergeMappings", "output").asInput()); + builder.input("officialMappings", graph.getRequiredOutput("downloadClientMappings", "output").asInput()); var officialOutput = builder.output("output", NodeOutputType.JAR, "Classes with SRG method and field names remapped to official."); - if (hasMojmapSteps) { - builder.input("mergedMappings", graph.getRequiredOutput("mergeMappings", "output").asInput()); - builder.input("officialMappings", graph.getRequiredOutput("downloadClientMappings", "output").asInput()); - builder.action(new RemapSrgClassesAction()); - } else { - // We can provide the SRG->MCP mappings generated by the earlier task - builder.input("srgToMcpMappings", graph.getResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING).asInput()); - builder.action(new RemapSrgClassesToMcpAction()); - } + builder.action(new RemapSrgClassesAction()); builder.build(); graph.setResult(ResultIds.GAME_JAR_NO_RECOMP, officialOutput); @@ -289,6 +283,64 @@ public void loadNeoFormProcess(NeoFormDistConfig distConfig) { } } + /** + * Synthesizes the {@code downloadClientMappings} and {@code mergeMappings} graph nodes for + * MCP configs that predate those pipeline steps (1.14.4–1.16.5). ProGuard mappings are + * available from the version manifest for these versions, so the same unified remapping path + * used for 1.17+ can be applied after synthesis. + */ + private void synthesizeMojmapNodes() { + var dlBuilder = graph.nodeBuilder("downloadClientMappings"); + dlBuilder.inputFromNodeOutput("versionManifest", "downloadJson", "output"); + dlBuilder.output("output", NodeOutputType.TXT, "Official mappings for the Minecraft client jar-file."); + dlBuilder.action(new DownloadFromVersionManifestAction(artifactManager, "client_mappings")); + dlBuilder.build(); + + // The MCP config TSRG (config/joined.tsrg) is already in obf→srg format, identical to + // what the native mergeMappings step produces. Expose it as a node output. + var mergeBuilder = graph.nodeBuilder("mergeMappings"); + mergeBuilder.output("output", NodeOutputType.TSRG, "Obfuscated-to-SRG mappings from MCP config."); + mergeBuilder.action(new ExtractNeoFormDataAction("mappings")); + mergeBuilder.build(); + } + + /** + * Sets up the remapping pipeline for pre-1.14.4 MCP versions (e.g. 1.12.2) where Mojang + * ProGuard mappings are not available and user-supplied MCP CSV data is used instead. + */ + private void setupMcpRemapping() { + var decompile = graph.getRequiredInput("decompile", "input"); + + applyTransforms(List.of( + new ReplaceNodeOutput( + "patch", + "output", + "remapSrgSourcesToOfficial", + (builder, previousNodeOutput) -> { + builder.input("sources", previousNodeOutput.asInput()); + builder.action(new RemapSrgSourcesToMcpAction(mcpMappingsData)); + return builder.output("output", NodeOutputType.ZIP, "Sources with SRG method and field names remapped to official."); + } + ) + )); + + var createMappings = graph.nodeBuilder("createMappings"); + createMappings.action(new GenerateMCPSrgFilesAction(mcpMappingsData)); + graph.setResult(ResultIds.NAMED_TO_INTERMEDIARY_MAPPING, createMappings.output("officialToSrg", NodeOutputType.TSRG, "A mapping file that maps user-facing (MCP) names to intermediary (SRG)")); + graph.setResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING, createMappings.output("srgToOfficial", NodeOutputType.SRG, "A mapping file that maps intermediary (SRG) names to user-facing (MCP) names")); + graph.setResult(ResultIds.CSV_MAPPING, createMappings.output("csvMappings", NodeOutputType.ZIP, "A zip containing csv files with SRG to MCP mappings")); + createMappings.build(); + + var builder = graph.nodeBuilder("remapSrgClassesToOfficial"); + builder.input("input", decompile.copy()); + builder.input("srgToMcpMappings", graph.getResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING).asInput()); + var officialOutput = builder.output("output", NodeOutputType.JAR, "Classes with SRG method and field names remapped to official."); + builder.action(new RemapSrgClassesToMcpAction()); + builder.build(); + + graph.setResult(ResultIds.GAME_JAR_NO_RECOMP, officialOutput); + } + private NodeOutput addRecompileStep(NeoFormDistConfig distConfig, NodeOutput sourcesOutput) { // Add a recompile step var builder = graph.nodeBuilder("recompile"); diff --git a/src/main/java/net/neoforged/neoform/runtime/engine/ProcessGeneration.java b/src/main/java/net/neoforged/neoform/runtime/engine/ProcessGeneration.java index f620ff86..2e132872 100644 --- a/src/main/java/net/neoforged/neoform/runtime/engine/ProcessGeneration.java +++ b/src/main/java/net/neoforged/neoform/runtime/engine/ProcessGeneration.java @@ -41,7 +41,7 @@ public int compareTo(@NotNull ProcessGeneration.MinecraftReleaseVersion o) { } } - private static final MinecraftReleaseVersion MC_1_16_5 = new MinecraftReleaseVersion(1, 16, 5); + private static final MinecraftReleaseVersion MC_1_14_4 = new MinecraftReleaseVersion(1, 14, 4); private static final MinecraftReleaseVersion MC_1_17_1 = new MinecraftReleaseVersion(1, 17, 1); private static final MinecraftReleaseVersion MC_1_20_1 = new MinecraftReleaseVersion(1, 20, 1); private static final MinecraftReleaseVersion MC_1_21_6 = new MinecraftReleaseVersion(1, 21, 6); @@ -61,12 +61,11 @@ public int compareTo(@NotNull ProcessGeneration.MinecraftReleaseVersion o) { private boolean sourcesUseIntermediaryNames; /** - * Indicates that the classes produced by MCP/NeoForm in this version use MCP names instead - * of Mojang-mapped names. In these versions, we apply an additional remapping step on the - * sources (before the SRG remap) that translates all the classnames from MCP->Mojmap, so that - * the names line up with what is expected on newer versions. + * Indicates that Mojang publishes ProGuard mappings for this version, allowing SRG names + * to be remapped to official names without user-supplied MCP CSV data. + * ProGuard mappings have been published since Minecraft 1.14.4. */ - private boolean classesUseMCPNames; + private boolean hasProguardMappings; /** * SAS was used in Forge 1.20.1 and earlier to remove the "OnlyIn" annotation from client-only classes @@ -110,8 +109,8 @@ static ProcessGeneration fromMinecraftVersion(String minecraftVersion) { // In 1.20.2 and later, NeoForge switched to Mojmap at runtime and sources defined in Mojmap result.sourcesUseIntermediaryNames = isLessThanOrEqualTo(releaseVersion, MC_1_20_1); - // In 1.17 and later, Forge switched to Mojmap classnames at runtime instead of MCP classnames - result.classesUseMCPNames = isLessThanOrEqualTo(releaseVersion, MC_1_16_5); + // Mojang started publishing ProGuard mappings with 1.14.4 + result.hasProguardMappings = isGreaterThanOrEqualTo(releaseVersion, MC_1_14_4); // In 1.21.6 and later, manifest entries should be generated as they may be used instead of RuntimeDistCleaner result.generateDistSourceManifest = isGreaterThanOrEqualTo(releaseVersion, MC_1_21_6); @@ -143,10 +142,11 @@ public boolean sourcesUseIntermediaryNames() { } /** - * Does the Minecraft source code that MCP/NeoForm creates use MCP names instead of Mojmap names? + * Does Mojang publish ProGuard mappings for this Minecraft version? + * When false, SRG-to-official remapping requires user-supplied MCP CSV data. */ - public boolean classesUseMCPNames() { - return classesUseMCPNames; + public boolean hasProguardMappings() { + return hasProguardMappings; } /** From f3746a5fb2b978c5329dfc6b3365fe5e217684df Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 16 May 2026 16:37:56 -0400 Subject: [PATCH 13/15] Remove support for using legacy MCP mappings and require Mojmap on 1.16 --- .../actions/GenerateMCPSrgFilesAction.java | 46 -------------- .../actions/RemapSrgClassesToMcpAction.java | 23 ------- .../actions/RemapSrgSourcesAction.java | 9 +-- .../actions/RemapSrgSourcesToMcpAction.java | 30 ---------- .../runtime/cli/RunNeoFormCommand.java | 7 --- .../neoform/runtime/engine/NeoFormEngine.java | 60 ++----------------- .../neoform/runtime/utils/MCPMappingsZip.java | 59 ------------------ 7 files changed, 8 insertions(+), 226 deletions(-) delete mode 100644 src/main/java/net/neoforged/neoform/runtime/actions/GenerateMCPSrgFilesAction.java delete mode 100644 src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java delete mode 100644 src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesToMcpAction.java delete mode 100644 src/main/java/net/neoforged/neoform/runtime/utils/MCPMappingsZip.java diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/GenerateMCPSrgFilesAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/GenerateMCPSrgFilesAction.java deleted file mode 100644 index 20581fac..00000000 --- a/src/main/java/net/neoforged/neoform/runtime/actions/GenerateMCPSrgFilesAction.java +++ /dev/null @@ -1,46 +0,0 @@ -package net.neoforged.neoform.runtime.actions; - -import net.neoforged.neoform.runtime.engine.ProcessingEnvironment; -import net.neoforged.neoform.runtime.graph.ExecutionNodeAction; -import net.neoforged.neoform.runtime.utils.MCPMappingsZip; -import net.neoforged.srgutils.IMappingFile; -import net.neoforged.srgutils.IRenamer; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Objects; - -public class GenerateMCPSrgFilesAction implements ExecutionNodeAction { - private final Path mcpMappingsData; - - public GenerateMCPSrgFilesAction(Path mcpMappingsData) { - Objects.requireNonNull(mcpMappingsData); - this.mcpMappingsData = mcpMappingsData; - } - - @Override - public void run(ProcessingEnvironment environment) throws IOException, InterruptedException { - // We need to generate a srg->mcp mappings file. To do this we use the obf->srg mappings, reverse them, - // and then replace the obf names with the mcp ones. - var obfToSrgFile = environment.extractData("mappings"); - var mappingsZip = MCPMappingsZip.from(mcpMappingsData); - - var srgToMcpMappings = IMappingFile.load(obfToSrgFile.toFile()).reverse().rename(new IRenamer() { - @Override - public String rename(IMappingFile.IField value) { - return mappingsZip.fieldMappings().getOrDefault(value.getOriginal(), value.getOriginal()); - } - - @Override - public String rename(IMappingFile.IMethod value) { - return mappingsZip.methodMappings().getOrDefault(value.getOriginal(), value.getOriginal()); - } - }); - - // Now create the expected toolchain outputs - srgToMcpMappings.reverse().write(environment.getOutputPath("officialToSrg"), IMappingFile.Format.TSRG, false); - srgToMcpMappings.write(environment.getOutputPath("srgToOfficial"), IMappingFile.Format.SRG, false); - Files.copy(mcpMappingsData, environment.getOutputPath("csvMappings")); - } -} diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java deleted file mode 100644 index 96026610..00000000 --- a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesToMcpAction.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.neoforged.neoform.runtime.actions; - -import net.neoforged.neoform.runtime.engine.ProcessingEnvironment; -import net.neoforged.neoform.runtime.utils.ToolCoordinate; - -import java.io.IOException; -import java.util.List; - -public class RemapSrgClassesToMcpAction extends ExternalJavaToolAction { - public RemapSrgClassesToMcpAction() { - super(ToolCoordinate.AUTO_RENAMING_TOOL); - } - - @Override - public void run(ProcessingEnvironment environment) throws IOException, InterruptedException { - setArgs(List.of( - "--input", "{input}", - "--output", "{output}", - "--map", environment.getInputPath("srgToMcpMappings").toAbsolutePath().toString() - )); - super.run(environment); - } -} diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java index d5e30f3f..d9b4c8aa 100644 --- a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java +++ b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java @@ -35,7 +35,8 @@ static IMappingFile buildSrgToOfficialMappingFile(ProcessingEnvironment environm return srgMappings.chain(officialMappings); } - protected Map buildSrgToOfficialMap(ProcessingEnvironment environment) throws IOException { + @Override + public void run(ProcessingEnvironment environment) throws IOException, InterruptedException { var srgToOfficial = buildSrgToOfficialMappingFile(environment); var srgNamesToOfficial = new HashMap(); for (var mappedClass : srgToOfficial.getClasses()) { @@ -46,12 +47,6 @@ protected Map buildSrgToOfficialMap(ProcessingEnvironment enviro srgNamesToOfficial.put(mappedMethod.getOriginal(), mappedMethod.getMapped()); } } - return srgNamesToOfficial; - } - - @Override - public void run(ProcessingEnvironment environment) throws IOException, InterruptedException { - var srgNamesToOfficial = this.buildSrgToOfficialMap(environment); var sourcesPath = environment.getRequiredInputPath("sources"); var outputPath = environment.getOutputPath("output"); diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesToMcpAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesToMcpAction.java deleted file mode 100644 index 3407fd14..00000000 --- a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesToMcpAction.java +++ /dev/null @@ -1,30 +0,0 @@ -package net.neoforged.neoform.runtime.actions; - -import net.neoforged.neoform.runtime.cache.CacheKeyBuilder; -import net.neoforged.neoform.runtime.engine.ProcessingEnvironment; -import net.neoforged.neoform.runtime.utils.MCPMappingsZip; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Map; -import java.util.Objects; - -public class RemapSrgSourcesToMcpAction extends RemapSrgSourcesAction { - private final Path mcpMappingsData; - - public RemapSrgSourcesToMcpAction(Path mcpMappingsData) { - Objects.requireNonNull(mcpMappingsData, "MCP mappings data not provided"); - this.mcpMappingsData = mcpMappingsData; - } - - @Override - protected Map buildSrgToOfficialMap(ProcessingEnvironment environment) throws IOException { - return MCPMappingsZip.from(mcpMappingsData).combinedMappings(); - } - - @Override - public void computeCacheKey(CacheKeyBuilder ck) { - super.computeCacheKey(ck); - ck.addPath("mcp mappings data", this.mcpMappingsData); - } -} diff --git a/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java b/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java index 5ae6602d..b6cabf9d 100644 --- a/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java +++ b/src/main/java/net/neoforged/neoform/runtime/cli/RunNeoFormCommand.java @@ -86,9 +86,6 @@ public class RunNeoFormCommand extends NeoFormEngineCommand { @CommandLine.Option(names = "--parchment-conflict-prefix", description = "Setting this option enables automatic Parchment parameter conflict resolution and uses this prefix for parameter names that clash.") String parchmentConflictPrefix; - @CommandLine.Option(names = "--mcp-mapping-data", description = "Path or Maven coordinates of MCP mapping data to use for pre-1.17 Minecraft") - String mcpMappingData; - static class SourceArtifacts { @CommandLine.Option(names = "--neoform") String neoform; @@ -100,10 +97,6 @@ static class SourceArtifacts { protected void runWithNeoFormEngine(NeoFormEngine engine, List closables) throws IOException, InterruptedException { var artifactManager = engine.getArtifactManager(); - if (mcpMappingData != null) { - engine.setMcpMappingsData(artifactManager.get(mcpMappingData).path()); - } - if (sourceArtifacts.neoforge != null) { var neoforgeArtifact = artifactManager.get(sourceArtifacts.neoforge); var neoforgeZipFile = engine.addManagedResource(new JarFile(neoforgeArtifact.path().toFile())); diff --git a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java index 616ec40c..bd44ea63 100644 --- a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java +++ b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java @@ -7,7 +7,6 @@ import net.neoforged.neoform.runtime.actions.DownloadVersionManifestAction; import net.neoforged.neoform.runtime.actions.ExternalJavaToolAction; import net.neoforged.neoform.runtime.actions.ExtractNeoFormDataAction; -import net.neoforged.neoform.runtime.actions.GenerateMCPSrgFilesAction; import net.neoforged.neoform.runtime.actions.InjectFromZipFileSource; import net.neoforged.neoform.runtime.actions.InjectZipContentAction; import net.neoforged.neoform.runtime.actions.MergeWithSourcesAction; @@ -16,9 +15,7 @@ import net.neoforged.neoform.runtime.actions.RecompileSourcesActionWithECJ; import net.neoforged.neoform.runtime.actions.RecompileSourcesActionWithJDK; import net.neoforged.neoform.runtime.actions.RemapSrgClassesAction; -import net.neoforged.neoform.runtime.actions.RemapSrgClassesToMcpAction; import net.neoforged.neoform.runtime.actions.RemapSrgSourcesAction; -import net.neoforged.neoform.runtime.actions.RemapSrgSourcesToMcpAction; import net.neoforged.neoform.runtime.actions.SplitResourcesFromClassesAction; import net.neoforged.neoform.runtime.artifacts.ArtifactManager; import net.neoforged.neoform.runtime.cache.CacheKeyBuilder; @@ -84,7 +81,6 @@ public class NeoFormEngine implements AutoCloseable { private final BuildOptions buildOptions = new BuildOptions(); private boolean verbose; private ProcessGeneration processGeneration; - private Path mcpMappingsData; /** * Nodes can reference certain configuration data (access transformers, patches, etc.) which come @@ -226,17 +222,13 @@ public void loadNeoFormProcess(NeoFormDistConfig distConfig) { // SRG method and field names, and need to be remapped. if (processGeneration.sourcesUseIntermediaryNames()) { if (!graph.hasOutput("mergeMappings", "output")) { - if (processGeneration.hasProguardMappings()) { - // 1.14.4–1.16.5: ProGuard mappings exist in the version manifest but the MCP - // pipeline predates the mergeMappings/downloadClientMappings steps. Synthesize - // equivalent nodes so the unified path below works for all versions. - synthesizeMojmapNodes(); - } else if (mcpMappingsData != null) { - // pre-1.14.4 (e.g. 1.12.2): no ProGuard mappings; use user-supplied MCP CSV data - setupMcpRemapping(); - } else { - throw new IllegalStateException("This MCP config requires --mcp-mapping-data (Mojang ProGuard mappings are not available for this Minecraft version)."); + if (!processGeneration.hasProguardMappings()) { + throw new IllegalStateException("MCP versions predating Mojang's ProGuard mappings (before 1.14.4) are not supported."); } + // 1.14.4–1.16.5: ProGuard mappings exist in the version manifest but the MCP + // pipeline predates the mergeMappings/downloadClientMappings steps. Synthesize + // equivalent nodes so the unified path below works for all versions. + synthesizeMojmapNodes(); } // Unified path: mergeMappings and downloadClientMappings are always present here, @@ -304,43 +296,6 @@ private void synthesizeMojmapNodes() { mergeBuilder.build(); } - /** - * Sets up the remapping pipeline for pre-1.14.4 MCP versions (e.g. 1.12.2) where Mojang - * ProGuard mappings are not available and user-supplied MCP CSV data is used instead. - */ - private void setupMcpRemapping() { - var decompile = graph.getRequiredInput("decompile", "input"); - - applyTransforms(List.of( - new ReplaceNodeOutput( - "patch", - "output", - "remapSrgSourcesToOfficial", - (builder, previousNodeOutput) -> { - builder.input("sources", previousNodeOutput.asInput()); - builder.action(new RemapSrgSourcesToMcpAction(mcpMappingsData)); - return builder.output("output", NodeOutputType.ZIP, "Sources with SRG method and field names remapped to official."); - } - ) - )); - - var createMappings = graph.nodeBuilder("createMappings"); - createMappings.action(new GenerateMCPSrgFilesAction(mcpMappingsData)); - graph.setResult(ResultIds.NAMED_TO_INTERMEDIARY_MAPPING, createMappings.output("officialToSrg", NodeOutputType.TSRG, "A mapping file that maps user-facing (MCP) names to intermediary (SRG)")); - graph.setResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING, createMappings.output("srgToOfficial", NodeOutputType.SRG, "A mapping file that maps intermediary (SRG) names to user-facing (MCP) names")); - graph.setResult(ResultIds.CSV_MAPPING, createMappings.output("csvMappings", NodeOutputType.ZIP, "A zip containing csv files with SRG to MCP mappings")); - createMappings.build(); - - var builder = graph.nodeBuilder("remapSrgClassesToOfficial"); - builder.input("input", decompile.copy()); - builder.input("srgToMcpMappings", graph.getResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING).asInput()); - var officialOutput = builder.output("output", NodeOutputType.JAR, "Classes with SRG method and field names remapped to official."); - builder.action(new RemapSrgClassesToMcpAction()); - builder.build(); - - graph.setResult(ResultIds.GAME_JAR_NO_RECOMP, officialOutput); - } - private NodeOutput addRecompileStep(NeoFormDistConfig distConfig, NodeOutput sourcesOutput) { // Add a recompile step var builder = graph.nodeBuilder("recompile"); @@ -965,7 +920,4 @@ public ProblemReporter getProblemReporter() { } } - public void setMcpMappingsData(Path mcpMappingsData) { - this.mcpMappingsData = mcpMappingsData; - } } diff --git a/src/main/java/net/neoforged/neoform/runtime/utils/MCPMappingsZip.java b/src/main/java/net/neoforged/neoform/runtime/utils/MCPMappingsZip.java deleted file mode 100644 index 36ba814c..00000000 --- a/src/main/java/net/neoforged/neoform/runtime/utils/MCPMappingsZip.java +++ /dev/null @@ -1,59 +0,0 @@ -package net.neoforged.neoform.runtime.utils; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; -import java.util.zip.ZipFile; - -public record MCPMappingsZip(Map fieldMappings, Map methodMappings) { - public static MCPMappingsZip from(Path zipFilePath) throws IOException { - try (var zf = new ZipFile(zipFilePath.toFile())) { - var fields = loadMappingsCsv(zf, "fields.csv"); - var methods = loadMappingsCsv(zf, "methods.csv"); - return new MCPMappingsZip(fields, methods); - } - } - - // A very rudimentary parser for the CSV files inside the mappings ZIP. - // Ignores everything except the first two columns. - // Example: - // searge,name,side,desc - // field_100013_f,isPotionDurationMax,0,"True if potion effect duration is at maximum, false otherwise." - private static Map loadMappingsCsv(ZipFile zf, String filename) throws IOException { - Map mappings = new HashMap<>(); - - var entry = zf.getEntry(filename); - if (entry == null) { - throw new IllegalStateException("MCP mappings ZIP file is missing entry " + filename); - } - - try (var reader = new BufferedReader(new InputStreamReader(zf.getInputStream(entry)))) { - var header = reader.readLine(); - if (!header.startsWith("searge,name,")) { - throw new IOException("Invalid header for Mappings CSV: " + filename); - } - String line; - while ((line = reader.readLine()) != null) { - var parts = line.split(",", 3); - if (parts.length < 2) { - continue; - } - String seargeName = parts[0]; - String name = parts[1]; - mappings.put(seargeName, name); - } - } - - return Map.copyOf(mappings); - } - - public Map combinedMappings() { - Map combinedMap = HashMap.newHashMap(fieldMappings.size() + methodMappings.size()); - combinedMap.putAll(fieldMappings); - combinedMap.putAll(methodMappings); - return combinedMap; - } -} From ce75efacac50b47f7d655ec2d612fa2200850d18 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 16 May 2026 16:39:51 -0400 Subject: [PATCH 14/15] Revert diff noise --- .../neoform/runtime/actions/RemapSrgClassesAction.java | 2 +- .../neoform/runtime/actions/RemapSrgSourcesAction.java | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesAction.java index 3942a90a..ad95eaf9 100644 --- a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesAction.java +++ b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgClassesAction.java @@ -14,7 +14,7 @@ public RemapSrgClassesAction() { @Override public void run(ProcessingEnvironment environment) throws IOException, InterruptedException { - var srgToOfficial = RemapSrgSourcesAction.buildSrgToOfficialMappingFile(environment); + var srgToOfficial = RemapSrgSourcesAction.buildSrgToOfficialMappings(environment); var mappingsFile = environment.getWorkspace().resolve("mappings.tsrg2"); srgToOfficial.write(mappingsFile, IMappingFile.Format.TSRG2, false); diff --git a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java index d9b4c8aa..da71da53 100644 --- a/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java +++ b/src/main/java/net/neoforged/neoform/runtime/actions/RemapSrgSourcesAction.java @@ -10,7 +10,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.HashMap; -import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipInputStream; @@ -24,7 +23,7 @@ public class RemapSrgSourcesAction implements ExecutionNodeAction { private static final Pattern SRG_FINDER = Pattern.compile("[fF]unc_\\d+_[a-zA-Z_]+|m_\\d+_|[fF]ield_\\d+_[a-zA-Z_]+|f_\\d+_"); - static IMappingFile buildSrgToOfficialMappingFile(ProcessingEnvironment environment) throws IOException { + static IMappingFile buildSrgToOfficialMappings(ProcessingEnvironment environment) throws IOException { var officialMappingsPath = environment.getRequiredInputPath("officialMappings"); var mergeMappingsPath = environment.getRequiredInputPath("mergedMappings"); @@ -37,7 +36,7 @@ static IMappingFile buildSrgToOfficialMappingFile(ProcessingEnvironment environm @Override public void run(ProcessingEnvironment environment) throws IOException, InterruptedException { - var srgToOfficial = buildSrgToOfficialMappingFile(environment); + var srgToOfficial = buildSrgToOfficialMappings(environment); var srgNamesToOfficial = new HashMap(); for (var mappedClass : srgToOfficial.getClasses()) { for (var mappedField : mappedClass.getFields()) { @@ -68,7 +67,7 @@ public void run(ProcessingEnvironment environment) throws IOException, Interrupt } } - private static String mapSourceCode(String sourceCode, Map srgNamesToOfficial) { + private static String mapSourceCode(String sourceCode, HashMap srgNamesToOfficial) { var m = SRG_FINDER.matcher(sourceCode); return m.replaceAll(matchResult -> { var matched = matchResult.group(); From 1818f76bfa5a8f7a01af28f3765d5484d690a194 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 16 May 2026 16:41:43 -0400 Subject: [PATCH 15/15] clean --- .../neoform/runtime/engine/NeoFormEngine.java | 55 ++++++++++--------- .../runtime/engine/ProcessGeneration.java | 4 +- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java index bd44ea63..f6aaa3ba 100644 --- a/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java +++ b/src/main/java/net/neoforged/neoform/runtime/engine/NeoFormEngine.java @@ -231,34 +231,24 @@ public void loadNeoFormProcess(NeoFormDistConfig distConfig) { synthesizeMojmapNodes(); } - // Unified path: mergeMappings and downloadClientMappings are always present here, - // either natively (1.18+) or synthesized above (1.14.4–1.16.5). - // Not reached when the pre-1.14.4 MCP path was taken above. - if (graph.hasOutput("mergeMappings", "output")) { - applyTransforms(List.of( - new ReplaceNodeOutput( - "patch", - "output", - "remapSrgSourcesToOfficial", - (builder, previousNodeOutput) -> { - builder.input("sources", previousNodeOutput.asInput()); - builder.input("mergedMappings", graph.getRequiredOutput("mergeMappings", "output").asInput()); - builder.input("officialMappings", graph.getRequiredOutput("downloadClientMappings", "output").asInput()); - builder.action(new RemapSrgSourcesAction()); - return builder.output("output", NodeOutputType.ZIP, "Sources with SRG method and field names remapped to official."); - } - ) - )); - - var createMappings = graph.nodeBuilder("createMappings"); - createMappings.inputFromNodeOutput("officialToObf", "downloadClientMappings", "output"); - createMappings.inputFromNodeOutput("obfToSrg", "mergeMappings", "output"); - createMappings.action(new CreateLegacyMappingsAction()); - graph.setResult(ResultIds.NAMED_TO_INTERMEDIARY_MAPPING, createMappings.output("officialToSrg", NodeOutputType.TSRG, "A mapping file that maps user-facing (Mojang, MCP) names to intermediary (SRG)")); - graph.setResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING, createMappings.output("srgToOfficial", NodeOutputType.SRG, "A mapping file that maps intermediary (SRG) names to user-facing (Mojang, MCP) names")); - graph.setResult(ResultIds.CSV_MAPPING, createMappings.output("csvMappings", NodeOutputType.ZIP, "A zip containing csv files with SRG to official mappings")); - createMappings.build(); + applyTransforms(List.of( + new ReplaceNodeOutput( + "patch", + "output", + "remapSrgSourcesToOfficial", + (builder, previousNodeOutput) -> { + builder.input("sources", previousNodeOutput.asInput()); + builder.input("mergedMappings", graph.getRequiredOutput("mergeMappings", "output").asInput()); + builder.input("officialMappings", graph.getRequiredOutput("downloadClientMappings", "output").asInput()); + var action = new RemapSrgSourcesAction(); + builder.action(action); + return builder.output("output", NodeOutputType.ZIP, "Sources with SRG method and field names remapped to official."); + } + ) + )); + // If intermediary is in use, the game jar has to be remapped to developer-facing names to be usable + { var builder = graph.nodeBuilder("remapSrgClassesToOfficial"); builder.input("input", decompileInput.copy()); builder.input("mergedMappings", graph.getRequiredOutput("mergeMappings", "output").asInput()); @@ -269,6 +259,17 @@ public void loadNeoFormProcess(NeoFormDistConfig distConfig) { graph.setResult(ResultIds.GAME_JAR_NO_RECOMP, officialOutput); } + + // We also expose a few results for mappings in different formats + var createMappings = graph.nodeBuilder("createMappings"); + createMappings.inputFromNodeOutput("officialToObf", "downloadClientMappings", "output"); + createMappings.inputFromNodeOutput("obfToSrg", "mergeMappings", "output"); + var action = new CreateLegacyMappingsAction(); + createMappings.action(action); + graph.setResult(ResultIds.NAMED_TO_INTERMEDIARY_MAPPING, createMappings.output("officialToSrg", NodeOutputType.TSRG, "A mapping file that maps user-facing (Mojang, MCP) names to intermediary (SRG)")); + graph.setResult(ResultIds.INTERMEDIARY_TO_NAMED_MAPPING, createMappings.output("srgToOfficial", NodeOutputType.SRG, "A mapping file that maps intermediary (SRG) names to user-facing (Mojang, MCP) names")); + graph.setResult(ResultIds.CSV_MAPPING, createMappings.output("csvMappings", NodeOutputType.ZIP, "A zip containing csv files with SRG to official mappings")); + createMappings.build(); } else { // Without the presence of further patching or renaming, the game jar without recompilation is the deobfuscated vanilla jar graph.setResultFromCurrentInput(ResultIds.GAME_JAR_NO_RECOMP, decompileInput); diff --git a/src/main/java/net/neoforged/neoform/runtime/engine/ProcessGeneration.java b/src/main/java/net/neoforged/neoform/runtime/engine/ProcessGeneration.java index 2e132872..156a609f 100644 --- a/src/main/java/net/neoforged/neoform/runtime/engine/ProcessGeneration.java +++ b/src/main/java/net/neoforged/neoform/runtime/engine/ProcessGeneration.java @@ -61,8 +61,7 @@ public int compareTo(@NotNull ProcessGeneration.MinecraftReleaseVersion o) { private boolean sourcesUseIntermediaryNames; /** - * Indicates that Mojang publishes ProGuard mappings for this version, allowing SRG names - * to be remapped to official names without user-supplied MCP CSV data. + * Indicates that Mojang publishes ProGuard mappings for this version. * ProGuard mappings have been published since Minecraft 1.14.4. */ private boolean hasProguardMappings; @@ -143,7 +142,6 @@ public boolean sourcesUseIntermediaryNames() { /** * Does Mojang publish ProGuard mappings for this Minecraft version? - * When false, SRG-to-official remapping requires user-supplied MCP CSV data. */ public boolean hasProguardMappings() { return hasProguardMappings;