Skip to content

Commit

Permalink
forge: support CleanroomMC installer (#516)
Browse files Browse the repository at this point in the history
  • Loading branch information
itzg authored Feb 2, 2025
1 parent ed61115 commit 6e228be
Show file tree
Hide file tree
Showing 7 changed files with 2,571 additions and 32 deletions.
27 changes: 17 additions & 10 deletions src/main/java/me/itzg/helpers/forge/ForgeInstaller.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.NonNull;
Expand All @@ -30,11 +31,6 @@ public class ForgeInstaller {
"Exec:\\s+(?<exec>.+)"
+ "|The server installed successfully, you should now be able to run the file (?<universalJar>.+)");

private static final List<String> entryJarFormats = Arrays.asList(
"forge-%s-%s.jar",
"forge-%s-%s-shim.jar"
);

private final InstallerResolver installerResolver;

public ForgeInstaller(InstallerResolver installerResolver) {
Expand Down Expand Up @@ -96,7 +92,7 @@ else if (
final Path forgeInstallerJar = installerResolver.download(resolved.minecraft, resolved.forge, outputDir);

try {
newManifest = install(forgeInstallerJar, outputDir, resolved.minecraft, variant, resolved.forge);
newManifest = install(forgeInstallerJar, outputDir, resolved.minecraft, Optional.ofNullable(resolved.variantOverride).orElse(variant), resolved.forge);

} finally {

Expand Down Expand Up @@ -217,7 +213,7 @@ private ForgeManifest install(Path installerJar, Path outputDir, String minecraf
// A 1.12.2 style installer that doesn't report entry point in logs
// >= 1.20.4 where "Exec:" line is no longer included in logs
if (entryFile == null) {
final Path resolved = findEntryJar(outputDir, minecraftVersion, forgeVersion);
final Path resolved = findEntryJar(outputDir, variant, minecraftVersion, forgeVersion);
if (resolved != null) {
entryFile = resolved.toAbsolutePath();
}
Expand Down Expand Up @@ -254,9 +250,20 @@ private ForgeManifest install(Path installerJar, Path outputDir, String minecraf
}
}

private static Path findEntryJar(Path outputDir, String minecraftVersion, String forgeVersion) {
for (final String entryJarFormat : entryJarFormats) {
final Path path = outputDir.resolve(String.format(entryJarFormat, minecraftVersion, forgeVersion));
@FunctionalInterface
interface ServerJarNameBuilder {
String build(String variant, String minecraftVersion, String forgeVersion);
}

private final static List<ServerJarNameBuilder> serverJarNameBuilders = Arrays.asList(
(v, m, f) -> String.format("%s-%s-%s.jar", v, m, f),
(v,m, f) -> String.format("%s-%s-%s-shim.jar", v, m, f),
(v,m, f) -> String.format("%s-%s.jar", v, f)
);

private static Path findEntryJar(Path outputDir, String variant, String minecraftVersion, String forgeVersion) {
for (final ServerJarNameBuilder builder : serverJarNameBuilders) {
final Path path = outputDir.resolve(builder.build(variant.toLowerCase(), minecraftVersion, forgeVersion));
if (Files.exists(path)) {
return path;
}
Expand Down
59 changes: 41 additions & 18 deletions src/main/java/me/itzg/helpers/forge/ProvidedInstallerResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand All @@ -11,7 +12,12 @@
import me.itzg.helpers.json.ObjectMappers;

public class ProvidedInstallerResolver implements InstallerResolver {

private static final Pattern OLD_FORGE_ID_VERSION = Pattern.compile("forge(.+)", Pattern.CASE_INSENSITIVE);
public static final String PROP_ID = "id";
public static final String PROP_INHERITS_FROM = "inheritsFrom";
public static final String INSTALLER_ID_FORGE = "forge";
public static final String INSTALLER_ID_CLEANROOM = "cleanroom";

private final Path forgeInstaller;

Expand All @@ -31,7 +37,7 @@ public VersionPair resolve() {
throw new GenericException("Failed to locate version from provided installer file");
}

return new VersionPair(versions.minecraft, versions.forge);
return versions;
}

@Override
Expand All @@ -46,22 +52,10 @@ public void cleanup(Path forgeInstallerJar) {

private VersionPair extractVersion(Path forgeInstaller) throws IOException {

// Extract version from installer jar's version.json file
// where top level "id" field is used

final VersionPair fromVersionJson = IoStreams.readFileFromZip(forgeInstaller, "version.json", inputStream -> {
final ObjectNode parsed = ObjectMappers.defaultMapper()
.readValue(inputStream, ObjectNode.class);

final String id = parsed.get("id").asText("");

final String[] idParts = id.split("-");
if (idParts.length != 3 || !idParts[1].equals("forge")) {
throw new GenericException("Unexpected format of id from Forge installer's version.json: " + id);
}

return new VersionPair(idParts[0], idParts[2]);
});
final VersionPair fromVersionJson = IoStreams.readFileFromZip(forgeInstaller, "version.json",
ProvidedInstallerResolver::extractFromVersionJson
);
// will be null if version.json wasn't present
if (fromVersionJson != null) {
return fromVersionJson;
}
Expand All @@ -70,7 +64,7 @@ private VersionPair extractVersion(Path forgeInstaller) throws IOException {
final ObjectNode parsed = ObjectMappers.defaultMapper()
.readValue(inputStream, ObjectNode.class);

final JsonNode idNode = parsed.path("versionInfo").path("id");
final JsonNode idNode = parsed.path("versionInfo").path(PROP_ID);
if (idNode.isTextual()) {
final String[] idParts = idNode.asText().split("-");

Expand Down Expand Up @@ -100,4 +94,33 @@ private VersionPair extractVersion(Path forgeInstaller) throws IOException {
}
});
}

/**
* Extract version from installer jar's version.json file where top level "id" and "inheritedFrom" fields are used
* @throws GenericException if something wasn't right about the version.json
*/
public static VersionPair extractFromVersionJson(InputStream versionJsonIn) throws IOException {
final ObjectNode parsed = ObjectMappers.defaultMapper()
.readValue(versionJsonIn, ObjectNode.class);

final String id = parsed.get(PROP_ID).asText();
final JsonNode inheritsFromNode = parsed.get(PROP_INHERITS_FROM);
if (inheritsFromNode.isMissingNode()) {
throw new GenericException("Installer version.json is missing " + PROP_INHERITS_FROM);
}
final String minecraftVersion = inheritsFromNode.asText();

final String[] idParts = id.split("-");
if (idParts.length >= 3) {
if (idParts[1].equals(INSTALLER_ID_FORGE)) {
return new VersionPair(minecraftVersion, idParts[2]);
}
if (idParts[0].equals(INSTALLER_ID_CLEANROOM)) {
return new VersionPair(minecraftVersion, String.join("-", idParts[1], idParts[2]))
.setVariantOverride(INSTALLER_ID_CLEANROOM);
}
}

throw new GenericException("Unexpected format of id from Forge installer's version.json: " + id);
}
}
11 changes: 7 additions & 4 deletions src/main/java/me/itzg/helpers/forge/VersionPair.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package me.itzg.helpers.forge;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@AllArgsConstructor
@RequiredArgsConstructor
@ToString @EqualsAndHashCode
public class VersionPair {

String minecraft;
String forge;
final String minecraft;
final String forge;
@Setter
String variantOverride;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

class ProvidedInstallerResolverTest {

Expand All @@ -22,4 +28,28 @@ void resolvesVersionFromFile() throws URISyntaxException {
assertThat(versions.minecraft).isEqualTo("1.20.2");
assertThat(versions.forge).isEqualTo("48.1.0");
}

@ParameterizedTest
@MethodSource("resolvesIdVariantsArgs")
void resolvesIdVariants(String versionJsonName, String expectedMinecraftVersion, String expectedInstallerVersion)
throws IOException {
try (InputStream versionJsonStream = ProvidedInstallerResolverTest.class.getResourceAsStream(
"/forge/" + versionJsonName)) {
assertThat(versionJsonStream).isNotNull();

final VersionPair result = ProvidedInstallerResolver.extractFromVersionJson(versionJsonStream);
assertThat(result).isNotNull();
assertThat(result.minecraft).isEqualTo(expectedMinecraftVersion);
assertThat(result.forge).isEqualTo(expectedInstallerVersion);
}
}

public static Stream<Arguments> resolvesIdVariantsArgs() {
return Stream.of(
Arguments.arguments("version-forge-1.20.2.json", "1.20.2", "48.1.0"),
Arguments.arguments("version-forge-1.12.2.json", "1.12.2", "14.23.5.2860"),
Arguments.arguments("version-cleanroom.json", "1.12.2", "0.2.4-alpha")
);
}

}
Loading

0 comments on commit 6e228be

Please sign in to comment.