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

Java 8 Compatibility #18

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
20 changes: 3 additions & 17 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import java.net.URI

plugins {
java
`java-library`
`maven-publish`
// application
// application

id("net.kyori.blossom") version "1.3.1"
id("com.diffplug.spotless") version "6.19.0"
Expand All @@ -31,10 +30,6 @@ repositories {
}
}

sourceSets {
create("java8")
}

dependencies {
implementation("org.quiltmc.parsers:json:0.2.1")
compileOnly("org.jetbrains:annotations:20.1.0")
Expand All @@ -53,24 +48,16 @@ blossom {
}

tasks.compileJava {
options.release.set(17)
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
}

tasks.getByName("compileJava8Java", JavaCompile::class) {
options.release.set(8)
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
// Cannot use application for the time being because shadow does not like mainClass being set for some reason.
// There is a PR which has fixed this, so update shadow probably when 6.10.1 or 6.11 is out
//application {
// mainClass.set("org.quiltmc.installer.Main")
//}

tasks.jar.get().dependsOn(tasks["compileJava8Java"])
tasks.jar {
manifest {
attributes["Implementation-Title"] = "Ornithe-Installer"
Expand All @@ -88,7 +75,6 @@ tasks.shadowJar {
// Compiler does not know which set method we are targeting with null value
val classifier: String? = null;
archiveClassifier.set(classifier)
from(sourceSets["java8"].output)
}

tasks.assemble {
Expand Down
6 changes: 0 additions & 6 deletions src/main/java/module-info.java

This file was deleted.

8 changes: 2 additions & 6 deletions src/main/java/org/quiltmc/installer/CliInstaller.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@

package org.quiltmc.installer;

import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.*;

import org.jetbrains.annotations.Nullable;
import org.quiltmc.installer.action.Action;
Expand Down Expand Up @@ -333,7 +329,7 @@ private static Action<?> parse(String input) {
}

private static String fetchIntermediary(GameSide side, String minecraftVersion) {
return OrnitheMeta.create(OrnitheMeta.ORNITHE_META_URL, Set.of(OrnitheMeta.INTERMEDIARY_VERSIONS_ENDPOINT)).thenApply(meta -> {
return OrnitheMeta.create(OrnitheMeta.ORNITHE_META_URL, new HashSet<OrnitheMeta.Endpoint<?>>() {{add(OrnitheMeta.INTERMEDIARY_VERSIONS_ENDPOINT);}}).thenApply(meta -> {
VersionManifest manifest = VersionManifest.create().join();
VersionManifest.Version version = manifest.getVersion(minecraftVersion);
return meta.getEndpoint(OrnitheMeta.INTERMEDIARY_VERSIONS_ENDPOINT).get(version.id(side));
Expand Down
40 changes: 18 additions & 22 deletions src/main/java/org/quiltmc/installer/LaunchJson.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.concurrent.CompletableFuture;

public final class LaunchJson {
private static final List<String> BEACON_ISSUE_VERSIONS = Arrays.asList("0.19.2", "0.19.3", "0.19.4");

@SuppressWarnings("unchecked")
public static CompletableFuture<String> getMmcJson(VersionManifest.Version gameVersion){
Expand All @@ -43,10 +44,10 @@ public static CompletableFuture<String> getMmcJson(VersionManifest.Version gameV
Map<String,Map<String, String>> downloads = (Map<String, Map<String, String>>) vanillaMap.get("downloads");
Map<String, String> client = downloads.get("client");

Map<String, Object> mainJar = Map.of(
"downloads", Map.of("artifact",client),
"name",clientName
);
Map<String, Object> mainJar = new HashMap<String, Object>() {{
put("downloads", new HashMap<Object, Object>(){{put("artifact", client);}});
put("name", clientName);
}};

List<Map<String, String>> vanillaLibraries = (List<Map<String, String>>) vanillaMap.get("libraries");
vanillaLibraries.removeIf(lib -> {
Expand Down Expand Up @@ -113,20 +114,20 @@ private static Map<String, Object> buildPackJsonMap(
}

moddedJsonMap.put("assetIndex",vanilaMap.get("assetIndex"));
moddedJsonMap.put("compatibleJavaMajors", List.of(8));
moddedJsonMap.put("compatibleJavaMajors", new ArrayList<Object>(){{add(8);}});
moddedJsonMap.put("formatVersion", 1);
moddedJsonMap.put("libraries", modifiedLibraries);
moddedJsonMap.put("mainClass", vanilaMap.get("mainClass"));
moddedJsonMap.put("mainJar", mainJar);
moddedJsonMap.put("minecraftArguments", minecraftArguments);
moddedJsonMap.put("name", "Minecraft");
moddedJsonMap.put("releaseTime", vanilaMap.get("releaseTime"));
moddedJsonMap.put("requires",List.of(
Map.of(
"suggests", "${lwjgl_version}",
"uid", "${lwjgl_uid}"
)
));
moddedJsonMap.put("requires", new ArrayList<Object>() {{
add(new HashMap<Object, Object>() {{
put("suggests", "${lwjgl_version}");
put("uid", "${lwjgl_uid}");
}});
}});
moddedJsonMap.put("type", vanilaMap.get("type"));
moddedJsonMap.put("uid", "net.minecraft");
moddedJsonMap.put("version", gameVersion);
Expand Down Expand Up @@ -234,17 +235,12 @@ public static CompletableFuture<String> get(GameSide side, VersionManifest.Versi

if (type == LoaderType.QUILT) {
// Prevents a log warning about being unable to reach the active user beacon on stable versions.
switch (loaderVersion) {
case "0.19.2", "0.19.3", "0.19.4" -> {
@SuppressWarnings("unchecked")
Map<String, List<Object>> arguments = (Map<String,List<Object>>)map.get("arguments");
arguments
.computeIfAbsent("jvm", (key) -> new ArrayList<>())
.add("-Dloader.disable_beacon=true");
}
default -> {
// do nothing
}
if (BEACON_ISSUE_VERSIONS.contains(loaderVersion)) {
@SuppressWarnings("unchecked")
Map<String, List<Object>> arguments = (Map<String,List<Object>>)map.get("arguments");
arguments
.computeIfAbsent("jvm", (key) -> new ArrayList<>())
.add("-Dloader.disable_beacon=true");
}
}

Expand Down
40 changes: 29 additions & 11 deletions src/main/java/org/quiltmc/installer/MmcPackCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.*;
import java.util.zip.*;

public class MmcPackCreator {
private static final String ENV_WRAPPER_COMMAND = "WrapperCommand=env __GL_THREADED_OPTIMIZATIONS=0";
Expand Down Expand Up @@ -167,9 +168,13 @@ private static String addCommonLibraries(Path instanceZipRoot, String gameVersio
String libName = name.substring(name.indexOf(':')+1, name.lastIndexOf(':'));
String version = name.substring(name.lastIndexOf(':')+1);

Files.writeString(instanceZipRoot.resolve("patches").resolve(uid + ".json"),
String.format(patch, name, url, libName, uid, version));
components.add(Map.of("cachedName", libName,"cachedVersion", version,"uid", uid));
Files.write(instanceZipRoot.resolve("patches").resolve(uid + ".json"),
String.format(patch, name, url, libName, uid, version).getBytes(StandardCharsets.UTF_8));
components.add(new HashMap<String, Object>() {{
put("cachedName", libName);
put("cachedVersion", version);
put("uid", uid);
}});
}


Expand Down Expand Up @@ -220,16 +225,29 @@ public static void compileMmcZip(Path outPutDir, String gameVersion, LoaderType
Path zipFile = outPutDir.resolve("Ornithe-" + gameVersion + ".zip");
Files.deleteIfExists(zipFile);

try (FileSystem fs = FileSystems.newFileSystem(zipFile, Map.of("create", "true"))) {
// This is a god awful workaround, because paths can't be cleanly converted to URIs in j8, and for some reason, you can't pass parameters into newFileSystem with a path argument.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they can be converted, it's just

URI.create("jar:" + path.toUri())

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't work on windows, cause:

  • Windows uses \, which is an illegal char.
  • Windows also has the drive name, which itself is illegal to have in a URI.
  • I can't replace \ with / and then urlencode it, because it then encodes /, which then breaks the path.
  • I can't just manually escape space and colon (the two main offenders of breaking path), cause the path component is still somehow undefined.

I love J8 path API on windows :)

Copy link

@wagyourtail wagyourtail Sep 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, path.toUri on windows looks like file:///C:/path/to/file on windows. and is a valid uri
and adding jar: on the front makes FileSystems recognize it as a zip properly on java 8 as the scheme becomes jar:file:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still ends up at the exact same "path component is undefined" error.

// Thanks Java :)
ZipOutputStream dummyZipOutputStream = new ZipOutputStream(Files.newOutputStream(zipFile.toFile().toPath()));
// I need to put an entry inside, or it creates a 0-byte file, which filesystem doesn't like
dummyZipOutputStream.putNextEntry(new ZipEntry("mmc-pack.json"));
dummyZipOutputStream.write("if you see this, this didn't work".getBytes(StandardCharsets.UTF_8));
dummyZipOutputStream.closeEntry();
dummyZipOutputStream.close();
// End god awful workaround

//noinspection RedundantCast Don't remove the classloader cast unless you want to break builds
try (FileSystem fs = FileSystems.newFileSystem(zipFile, (ClassLoader) null)) {
Files.copy(MmcPackCreator.class.getResourceAsStream(examplePackDir + "/" + iconPath), fs.getPath(iconPath));
Files.writeString(fs.getPath(instanceCfgPath), transformedInstanceCfg);
Files.write(fs.getPath(instanceCfgPath), transformedInstanceCfg.getBytes(StandardCharsets.UTF_8));
Files.createDirectory(fs.getPath("patches"));
Files.writeString(fs.getPath(intermediaryJsonPath), transformedIntermediaryJson);
Files.writeString(fs.getPath(minecraftPatchPath), transformedMinecraftJson);
Files.write(fs.getPath(intermediaryJsonPath), transformedIntermediaryJson.getBytes(StandardCharsets.UTF_8));
Files.write(fs.getPath(minecraftPatchPath), transformedMinecraftJson.getBytes(StandardCharsets.UTF_8));
String packJsonWithLibraries = addCommonLibraries(fs.getPath("/"), intermediaryVersion,
loaderType, loaderVersion, transformedPackJson);

Files.writeString(fs.getPath(packJsonPath), packJsonWithLibraries);
// Remove the workaround file and replace it
Files.delete(fs.getPath(packJsonPath));
Files.write(fs.getPath(packJsonPath), packJsonWithLibraries.getBytes(StandardCharsets.UTF_8));
}

if (copyProfilePath) {
Expand All @@ -248,7 +266,7 @@ private static String readResource(String dir, String path) throws IOException {
for (int length; (length = resource.read(buffer)) != -1; ) {
os.write(buffer, 0, length);
}
return os.toString(StandardCharsets.UTF_8);
return os.toString(StandardCharsets.UTF_8.name());
}

static {
Expand Down
27 changes: 18 additions & 9 deletions src/main/java/org/quiltmc/installer/OrnitheMeta.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,18 @@ public final class OrnitheMeta {

@SuppressWarnings("unchecked")
public static Endpoint<List<Map<String, String>>> profileLibrariesEndpoint(String version, LoaderType type, String loaderVersion){
String loader = switch (type){
case FABRIC -> "fabric-loader";
case QUILT -> "quilt-loader";
};
String loader;
switch (type) {
case FABRIC:
loader = "fabric-loader";
break;
case QUILT:
loader = "quilt-loader";
break;
default:
throw new IllegalStateException("Unexpected value: " + type);
}

return new Endpoint<>(String.format("/v3/versions/%s/%s/%s/profile/json", loader, version, loaderVersion), reader -> {

List<Map<String, String>> libraries = new ArrayList<>();
Expand Down Expand Up @@ -96,19 +104,20 @@ public static Endpoint<List<String>> loaderVersionsEndpoint(LoaderType type) {

while (reader.hasNext()) {
switch (reader.nextName()) {
case "version" -> {
case "version":
if (reader.peek() != JsonToken.STRING) {
throw new ParseException("Version must be a string", reader);
}
version = reader.nextString();
}
case "maven" -> {
break;
case "maven":
if (reader.peek() != JsonToken.STRING) {
throw new ParseException("maven must be a string", reader);
}
maven = reader.nextString();
}
case "stable" -> reader.nextBoolean(); // TODO
break;
case "stable":
reader.nextBoolean(); // TODO
}
}

Expand Down
Loading