Skip to content
Merged
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
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