Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package net.neoforged.neoform.runtime.actions;

import net.neoforged.neoform.runtime.engine.ProcessingEnvironment;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
* In older NeoForm processes, the output of the process is not a usable Minecraft mod jar.
* <p>
* The old process strips resources early (in the strip/stripClient/stripServer step), and then only deals with
* classes. In the new process, resources are never stripped, and the Jar pre-processor will also add both the
* Side-Manifest entries to the Jar Manifest, and add a neoforge.mods.toml to the Jar.
*/
public class CreateMinecraftModJarAction extends BuiltInAction {
private static final String MOD_MANIFEST_NAME = "META-INF/neoforge.mods.toml";

static final long STABLE_TIMESTAMP = 0x386D4380; //01/01/2000 00:00:00 java 8 breaks when using 0.

private final String minecraftVersion;

public CreateMinecraftModJarAction(String minecraftVersion) {
this.minecraftVersion = minecraftVersion;
}

@Override
public void run(ProcessingEnvironment environment) throws IOException, InterruptedException {

var classesFile = environment.getRequiredInputPath("classes");
var resourcesFile = environment.getInputPath("resources");
var output = environment.getOutputPath("output");

try (var os = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(output)))) {
boolean modManifestCopied = false;

try (var in = new ZipInputStream(new BufferedInputStream(Files.newInputStream(classesFile)))) {
for (var entry = in.getNextEntry(); entry != null; entry = in.getNextEntry()) {
os.putNextEntry(entry);
in.transferTo(os);
os.closeEntry();
if (entry.getName().equals(MOD_MANIFEST_NAME)) {
modManifestCopied = true;
}
}
}

if (resourcesFile != null) {
try (var in = new ZipInputStream(new BufferedInputStream(Files.newInputStream(resourcesFile)))) {
for (var entry = in.getNextEntry(); entry != null; entry = in.getNextEntry()) {
os.putNextEntry(entry);
in.transferTo(os);
os.closeEntry();
if (entry.getName().equals(MOD_MANIFEST_NAME)) {
modManifestCopied = true;
}
}
}
}

// If no META-INF/neoforge.mods.toml was copied over, we now inject a new one.
if (!modManifestCopied) {
appendModManifest(os);
}
}
}

// This is based on installertools.
private void appendModManifest(ZipOutputStream os) throws IOException {
String modManifest = "modLoader=\"minecraft\"\n" +
"license=\"Minecraft EULA\"\n" +
"[[mods]]\n" +
"modId=\"minecraft\"\n" +
"version=\"" + minecraftVersion + "\"\n" +
"displayName=\"Minecraft\"\n" +
"authors=\"Mojang Studios\"\n" +
"description=\"\"\n";

var entry = new ZipEntry(MOD_MANIFEST_NAME);
entry.setTime(STABLE_TIMESTAMP);
os.putNextEntry(entry);
os.write(modManifest.getBytes(StandardCharsets.UTF_8));
os.closeEntry();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import net.neoforged.neoform.runtime.actions.ExternalJavaToolAction;
import net.neoforged.neoform.runtime.actions.InjectFromZipFileSource;
import net.neoforged.neoform.runtime.actions.InjectZipContentAction;
import net.neoforged.neoform.runtime.actions.CreateMinecraftModJarAction;
import net.neoforged.neoform.runtime.actions.MergeWithSourcesAction;
import net.neoforged.neoform.runtime.actions.PatchActionFactory;
import net.neoforged.neoform.runtime.actions.RecompileSourcesAction;
Expand Down Expand Up @@ -202,7 +203,17 @@ public void loadNeoFormProcess(NeoFormDistConfig distConfig) {

// The split-off resources must also be made available. The steps are not consistently named across dists
if (graph.hasOutput("stripClient", "resourcesOutput")) {
graph.setResult("clientResources", graph.getRequiredOutput("stripClient", "resourcesOutput"));
var resourceOutput = graph.getRequiredOutput("stripClient", "resourcesOutput");
graph.setResult("clientResources", resourceOutput);

// In NeoForm versions that use the old process, we manually created nodes that merge the resources back into the jar, and create the neoforge.mods.toml
createMinecraftModJar(distConfig.minecraftVersion(), "minecraftModJar", compiledOutput.asInput(), resourceOutput.asInput());
createMinecraftModJar(distConfig.minecraftVersion(), "minecraftModJarWithSources", sourcesAndCompiledOutput.asInput(), resourceOutput.asInput());
} else {
// In newer versions of the process where resources aren't stripped, we're likely to also have a neoforge.mods.toml already
// Then we just alias the outputs
graph.setResult("minecraftModJar", compiledOutput);
graph.setResult("minecraftModJarWithSources", sourcesAndCompiledOutput);
}
if (graph.hasOutput("stripServer", "resourcesOutput")) {
graph.setResult("serverResources", graph.getRequiredOutput("stripServer", "resourcesOutput"));
Expand Down Expand Up @@ -486,6 +497,19 @@ private void createDownloadFromVersionManifest(ExecutionNodeBuilder builder, Str
builder.action(new DownloadFromVersionManifestAction(artifactManager, manifestEntry));
}

private void createMinecraftModJar(String minecraftVersion, String withResourcesId, NodeInput classesInput, @Nullable NodeInput resourceInput) {
var builder = graph.nodeBuilder(withResourcesId);
builder.input("classes", classesInput);
if (resourceInput != null) {
builder.input("resources", resourceInput);
}
builder.action(new CreateMinecraftModJarAction(minecraftVersion));
var output = builder.output("output", NodeOutputType.JAR, "Result of " + classesInput.getId() + " with Minecraft resources added into the same jar.");
builder.build();

graph.setResult(withResourcesId, output);
}

private void triggerAndWait(Collection<ExecutionNode> nodes) throws InterruptedException {
record Pair(ExecutionNode node, CompletableFuture<Void> future) {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public NodeOutput getResult(String id) {
var outputId = matcher.group(2);
return getRequiredOutput(step, outputId);
}
return null;
throw new IllegalArgumentException("Unknown result: " + id + ". Available results: " + results.keySet());
}

public Map<String, NodeOutput> getResults() {
Expand Down