Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Very Legacy (basic support for 1.16.5 and older) #51

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
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
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
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.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
* Takes a zip-file of Java sources and replaces SRG identifiers with
* identifiers from a mapping file.
* This version of {@link RemapSrgSourcesAction} uses MCP Mappings data for
* the intermediate->named mapping.
* The mappings are loaded from an MCP mappings ZIP, which contains CSV files with mappings for SRG->Named.
*/
public class RemapSrgSourcesWithMcpAction 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+_");

private final Path mcpMappingsData;

public RemapSrgSourcesWithMcpAction(Path mcpMappingsData) {
this.mcpMappingsData = mcpMappingsData;
}

@Override
public void run(ProcessingEnvironment environment) throws IOException, InterruptedException {
var mappings = new HashMap<String, String>();

try (var zf = new ZipFile(mcpMappingsData.toFile())) {
loadMappingsCsv(zf, "fields.csv", mappings);
loadMappingsCsv(zf, "methods.csv", mappings);
}

var sourcesPath = environment.getRequiredInputPath("sources");
var outputPath = environment.getOutputPath("output");

try (var zipIn = new ZipInputStream(new BufferedInputStream(Files.newInputStream(sourcesPath)));
var zipOut = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(outputPath)))) {
for (var entry = zipIn.getNextEntry(); entry != null; entry = zipIn.getNextEntry()) {
zipOut.putNextEntry(entry);

if (entry.getName().endsWith(".java")) {
var sourceCode = new String(zipIn.readAllBytes(), StandardCharsets.UTF_8);
var mappedSource = mapSourceCode(sourceCode, mappings);
zipOut.write(mappedSource.getBytes(StandardCharsets.UTF_8));
} else {
zipIn.transferTo(zipOut);
}
zipOut.closeEntry();
}
}
}

// 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 void loadMappingsCsv(ZipFile zf, String filename, Map<String, String> mappings) throws IOException {
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);
}
}
}

@Override
public void computeCacheKey(CacheKeyBuilder ck) {
ExecutionNodeAction.super.computeCacheKey(ck);
ck.addPath("mcp mappings data", mcpMappingsData);
}

private static String mapSourceCode(String sourceCode, Map<String, String> srgNamesToOfficial) {
var m = SRG_FINDER.matcher(sourceCode);
return m.replaceAll(matchResult -> {
var matched = matchResult.group();
// Some will be unmapped
var mapped = srgNamesToOfficial.getOrDefault(matched, matched);
return Matcher.quoteReplacement(mapped);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,28 @@ 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
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);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
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;

final class MavenMetadata {
private static final Logger LOG = Logger.create();

private MavenMetadata() {
}

static List<AvailableVersion> gatherVersions(DownloadManager downloadManager,
List<URI> repositoryBaseUrls,
String groupId,
String artifactId) throws IOException {
var versions = new ArrayList<AvailableVersion>();
for (var repositoryBaseUrl : repositoryBaseUrls) {
versions.addAll(gatherVersions(downloadManager, repositoryBaseUrl, groupId, artifactId));
}
return versions;
}

static List<AvailableVersion> 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<AvailableVersion>();
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) {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.jar.JarFile;
Expand Down Expand Up @@ -68,8 +69,11 @@ 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.ArgGroup(multiplicity = "1")
@CommandLine.ArgGroup
NeoFormArtifact neoform;
@CommandLine.Option(names = "--neoforge")
String neoforge;
Expand All @@ -86,17 +90,21 @@ static class NeoFormArtifact {
protected void runWithNeoFormEngine(NeoFormEngine engine, List<AutoCloseable> 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()));
var neoforgeConfig = NeoForgeConfig.from(neoforgeZipFile);

// Allow it to be overridden with local or remote data
Path neoformArtifact;
if (sourceArtifacts.neoform.file != null) {
if (sourceArtifacts.neoform != null && sourceArtifacts.neoform.file != null) {
LOG.println("Overriding NeoForm version " + neoforgeConfig.neoformArtifact() + " with NeoForm file " + sourceArtifacts.neoform.file);
neoformArtifact = sourceArtifacts.neoform.file;
} else if (sourceArtifacts.neoform.artifact != null) {
} else if (sourceArtifacts.neoform != null && sourceArtifacts.neoform.artifact != null) {
LOG.println("Overriding NeoForm version " + neoforgeConfig.neoformArtifact() + " with CLI argument " + sourceArtifacts.neoform.artifact);
neoformArtifact = artifactManager.get(MavenCoordinate.parse(sourceArtifacts.neoform.artifact)).path();
} else {
Expand Down Expand Up @@ -162,8 +170,28 @@ protected void runWithNeoFormEngine(NeoFormEngine engine, List<AutoCloseable> cl
}
));

// 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) {
transforms.add(new ReplaceNodeOutput(
"patch", "output", "applyUserdevSourcePreprocessor",
(builder, previousOutput) -> {
var newOutput = engine.applyFunctionToNode(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
transforms.add(new ReplaceNodeOutput("patch", "output", "applyNeoforgePatches",
transforms.add(new ReplaceNodeOutput(nfPatchesInputNode, "output", "applyNeoforgePatches",
(builder, previousOutput) -> {
return PatchActionFactory.makeAction(builder,
neoforgeArtifact.path(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,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;
Expand All @@ -27,7 +28,9 @@ public record NeoForgeConfig(
@SerializedName("patchesModifiedPrefix") @Nullable String modifiedPathPrefix,
Map<String, JsonObject> runs,
List<MavenCoordinate> libraries,
List<String> modules
List<String> modules,
// 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public record NeoFormConfig(int spec,
Map<String, NeoFormFunction> functions,
Map<String, List<MavenCoordinate>> libraries) {

public NeoFormConfig {
if (javaVersion == 0) {
javaVersion = 8; // Old versions didn't specify, but require Java 8
}
}

public NeoFormDistConfig getDistConfig(String dist) {
if (!steps.containsKey(dist)) {
throw new IllegalArgumentException("This configuration does not include the distribution "
Expand Down
Loading
Loading