diff --git a/launcher-builder/src/main/java/com/skcraft/launcher/builder/PackageBuilder.java b/launcher-builder/src/main/java/com/skcraft/launcher/builder/PackageBuilder.java index 01f54ce..1a48e0b 100644 --- a/launcher-builder/src/main/java/com/skcraft/launcher/builder/PackageBuilder.java +++ b/launcher-builder/src/main/java/com/skcraft/launcher/builder/PackageBuilder.java @@ -19,10 +19,7 @@ import com.google.common.io.Files; import com.skcraft.launcher.Launcher; import com.skcraft.launcher.LauncherUtils; -import com.skcraft.launcher.builder.loaders.ILoaderProcessor; -import com.skcraft.launcher.builder.loaders.LoaderResult; -import com.skcraft.launcher.builder.loaders.ModernForgeLoaderProcessor; -import com.skcraft.launcher.builder.loaders.OldForgeLoaderProcessor; +import com.skcraft.launcher.builder.loaders.*; import com.skcraft.launcher.model.loader.BasicInstallProfile; import com.skcraft.launcher.model.minecraft.Library; import com.skcraft.launcher.model.minecraft.ReleaseList; @@ -171,6 +168,8 @@ } else if (basicProfile.getProfile().equalsIgnoreCase("forge")) { processor = new ModernForgeLoaderProcessor(); } + } else if (BuilderUtils.getZipEntry(jarFile, "fabric-installer.json") != null) { + processor = new FabricLoaderProcessor(); } } finally { closer.close(); @@ -200,6 +199,7 @@ Files.createParentDirs(outputPath); boolean found = false; + // Try just the URL, it might be a full URL to the file if (!artifact.getUrl().isEmpty()) { found = tryDownloadLibrary(library, artifact, artifact.getUrl(), outputPath); } @@ -212,6 +212,12 @@ } } + // Assume artifact URL is a maven repository URL and try that + if (!found) { + URL url = LauncherUtils.concat(url(artifact.getUrl()), artifact.getPath()); + found = tryDownloadLibrary(library, artifact, url.toString(), outputPath); + } + // Try each repository if not found yet if (!found) { for (String baseUrl : mavenRepos) { diff --git a/launcher-builder/src/main/java/com/skcraft/launcher/builder/loaders/FabricLoaderProcessor.java b/launcher-builder/src/main/java/com/skcraft/launcher/builder/loaders/FabricLoaderProcessor.java new file mode 100644 index 0000000..4d52840 --- /dev/null +++ b/launcher-builder/src/main/java/com/skcraft/launcher/builder/loaders/FabricLoaderProcessor.java @@ -0,0 +1,113 @@ +package com.skcraft.launcher.builder.loaders; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Iterables; +import com.google.common.io.Closer; +import com.skcraft.launcher.builder.BuilderUtils; +import com.skcraft.launcher.model.loader.MavenUrl; +import com.skcraft.launcher.model.loader.profiles.FabricInstallProfile; +import com.skcraft.launcher.model.minecraft.Library; +import com.skcraft.launcher.model.modpack.Manifest; +import com.skcraft.launcher.util.HttpRequest; +import lombok.extern.java.Log; +import org.apache.commons.io.FilenameUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.List; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; + +@Log +public class FabricLoaderProcessor implements ILoaderProcessor { + @Override + public LoaderResult process(File loaderJar, Manifest manifest, ObjectMapper mapper, File baseDir) throws IOException { + JarFile jarFile = new JarFile(loaderJar); + LoaderResult result = new LoaderResult(); + Closer closer = Closer.create(); + + try { + ZipEntry installerEntry = BuilderUtils.getZipEntry(jarFile, "fabric-installer.json"); + + if (installerEntry != null) { + InputStreamReader reader = new InputStreamReader(jarFile.getInputStream(installerEntry)); + FabricInstallProfile profile = mapper.readValue( + BuilderUtils.readStringFromStream(closer.register(reader)), FabricInstallProfile.class); + + // Check version + if (profile.getVersion() != 1) { + log.warning(String.format("Fabric installer metadata version is %d - we expect version 1.", + profile.getVersion())); + } + + // Add libraries (TODO: Add server-only libraries to somewhere) + Iterable libraries = Iterables.concat(profile.getLibraries().getClient(), + profile.getLibraries().getCommon()); + for (Library library : libraries) { + result.getLoaderLibraries().add(library); + log.info("Adding loader library " + library.getName()); + } + + // Add actual loader jar into library path + if (profile.getLoader() != null) { + result.getLoaderLibraries().add(profile.getLoader()); + log.info(String.format("Adding Fabric Loader '%s'", profile.getLoader().getName())); + } else { + log.warning("Fabric loader metadata is missing a `loader` section, making up a fake library"); + Library loader = new Library(); + loader.setName("faked:loader:" + FilenameUtils.getBaseName(loaderJar.getName())); + + Library.Downloads downloads = new Library.Downloads(); + downloads.setArtifact(new Library.Artifact()); + downloads.getArtifact().setPath(loaderJar.getName()); + downloads.getArtifact().setUrl(""); + loader.setDownloads(downloads); + + result.getLoaderLibraries().add(loader); + // Little bit of a hack here, pretending the filesystem is a maven + result.getJarMavens().add(new URL("file:" + loaderJar.getParentFile().getAbsolutePath() + "/")); + } + + // Set main class + String mainClass = profile.getMainClass().getClient(); + if (mainClass != null) { + manifest.getVersionManifest().setMainClass(mainClass); + log.info("Using main class " + mainClass); + } + + // Add intermediary library + log.info("Downloading fabric metadata..."); + URL url = HttpRequest.url("https://meta.fabricmc.net/v2/versions/intermediary/" + + manifest.getVersionManifest().getId()); + List versions = HttpRequest.get(url) + .execute() + .expectResponseCode(200) + .returnContent() + .asJson(new TypeReference>() {}); + + if (versions != null && versions.size() > 0) { + MavenUrl intermediaryLib = versions.get(0); + + if (intermediaryLib.getUrl() == null) { + // FIXME temporary hack since maven URL is missing, hopefully can go away soon + // waiting on PR FabricMC/fabric-meta#9 + intermediaryLib.setUrl("https://maven.fabricmc.net/"); + } + + result.getLoaderLibraries().add(intermediaryLib.toLibrary()); + log.info("Added intermediary " + intermediaryLib.getName()); + } + } + } catch (InterruptedException e) { + log.warning("HTTP request to fabric metadata API was interrupted, this will probably not work!"); + } finally { + closer.close(); + jarFile.close(); + } + + return result; + } +} diff --git a/launcher-builder/src/main/java/com/skcraft/launcher/builder/loaders/ModernForgeLoaderProcessor.java b/launcher-builder/src/main/java/com/skcraft/launcher/builder/loaders/ModernForgeLoaderProcessor.java index 96f0175..3149b59 100644 --- a/launcher-builder/src/main/java/com/skcraft/launcher/builder/loaders/ModernForgeLoaderProcessor.java +++ b/launcher-builder/src/main/java/com/skcraft/launcher/builder/loaders/ModernForgeLoaderProcessor.java @@ -51,7 +51,7 @@ loaderName = version.getId(); } - // Copy tweak class arguments + // Copy game arguments List gameArguments = info.getArguments().getGameArguments(); if (gameArguments != null) { version.getArguments().getGameArguments().addAll(gameArguments); diff --git a/launcher/src/main/java/com/skcraft/launcher/model/loader/ExtendedSidedData.java b/launcher/src/main/java/com/skcraft/launcher/model/loader/ExtendedSidedData.java new file mode 100644 index 0000000..d83fc6b --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/model/loader/ExtendedSidedData.java @@ -0,0 +1,10 @@ +package com.skcraft.launcher.model.loader; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class ExtendedSidedData extends SidedData { + private T common; +} diff --git a/launcher/src/main/java/com/skcraft/launcher/model/loader/LoaderManifest.java b/launcher/src/main/java/com/skcraft/launcher/model/loader/LoaderManifest.java index 9c53586..c1b567c 100644 --- a/launcher/src/main/java/com/skcraft/launcher/model/loader/LoaderManifest.java +++ b/launcher/src/main/java/com/skcraft/launcher/model/loader/LoaderManifest.java @@ -14,7 +14,7 @@ @NoArgsConstructor public class LoaderManifest { private List libraries; - private Map sidedData; + private Map> sidedData; private List downloadableFiles; public Library findLibrary(String name) { diff --git a/launcher/src/main/java/com/skcraft/launcher/model/loader/LoaderSubResolver.java b/launcher/src/main/java/com/skcraft/launcher/model/loader/LoaderSubResolver.java index 27072dc..e3ab1ca 100644 --- a/launcher/src/main/java/com/skcraft/launcher/model/loader/LoaderSubResolver.java +++ b/launcher/src/main/java/com/skcraft/launcher/model/loader/LoaderSubResolver.java @@ -35,7 +35,7 @@ char end = arg.charAt(bound); if (start == '{' && end == '}') { - SidedData sidedData = loader.getSidedData().get(arg.substring(1, bound)); + SidedData sidedData = loader.getSidedData().get(arg.substring(1, bound)); if (sidedData != null) { arg = sidedData.resolveFor(side); } diff --git a/launcher/src/main/java/com/skcraft/launcher/model/loader/MavenUrl.java b/launcher/src/main/java/com/skcraft/launcher/model/loader/MavenUrl.java new file mode 100644 index 0000000..fdc6ca4 --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/model/loader/MavenUrl.java @@ -0,0 +1,24 @@ +package com.skcraft.launcher.model.loader; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.skcraft.launcher.model.minecraft.Library; +import lombok.Data; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class MavenUrl { + @JsonProperty("maven") + private String name; + private String url; + private String version; + private boolean stable; + + public Library toLibrary() { + Library library = new Library(); + library.setName(name); + library.setUrl(url); + + return library; + } +} diff --git a/launcher/src/main/java/com/skcraft/launcher/model/loader/SidedData.java b/launcher/src/main/java/com/skcraft/launcher/model/loader/SidedData.java index 807b444..ea06805 100644 --- a/launcher/src/main/java/com/skcraft/launcher/model/loader/SidedData.java +++ b/launcher/src/main/java/com/skcraft/launcher/model/loader/SidedData.java @@ -10,11 +10,11 @@ @AllArgsConstructor(staticName = "create") @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -public class SidedData { - private String client; - private String server; +public class SidedData { + private T client; + private T server; - public String resolveFor(Side side) { + public T resolveFor(Side side) { switch (side) { case CLIENT: return client; case SERVER: return server; @@ -22,7 +22,7 @@ } } - public static SidedData of(String singleValue) { - return new SidedData(singleValue, singleValue); + public static SidedData of(T singleValue) { + return new SidedData(singleValue, singleValue); } } diff --git a/launcher/src/main/java/com/skcraft/launcher/model/loader/profiles/FabricInstallProfile.java b/launcher/src/main/java/com/skcraft/launcher/model/loader/profiles/FabricInstallProfile.java new file mode 100644 index 0000000..3219ebd --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/model/loader/profiles/FabricInstallProfile.java @@ -0,0 +1,18 @@ +package com.skcraft.launcher.model.loader.profiles; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.skcraft.launcher.model.loader.ExtendedSidedData; +import com.skcraft.launcher.model.loader.SidedData; +import com.skcraft.launcher.model.minecraft.Library; +import lombok.Data; + +import java.util.List; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class FabricInstallProfile { + private int version; + private Library loader; + private ExtendedSidedData> libraries; + private SidedData mainClass; +} diff --git a/launcher/src/main/java/com/skcraft/launcher/model/loader/profiles/ModernForgeInstallProfile.java b/launcher/src/main/java/com/skcraft/launcher/model/loader/profiles/ModernForgeInstallProfile.java index 2f3a645..93594af 100644 --- a/launcher/src/main/java/com/skcraft/launcher/model/loader/profiles/ModernForgeInstallProfile.java +++ b/launcher/src/main/java/com/skcraft/launcher/model/loader/profiles/ModernForgeInstallProfile.java @@ -23,7 +23,7 @@ public class ModernForgeInstallProfile { private List libraries; private List processors; - private Map data; + private Map> data; public List toProcessorEntries(final String loaderName) { return Lists.transform(getProcessors(), new Function() { diff --git a/launcher/src/main/java/com/skcraft/launcher/model/minecraft/VersionManifest.java b/launcher/src/main/java/com/skcraft/launcher/model/minecraft/VersionManifest.java index 06d452d..d0fad1a 100644 --- a/launcher/src/main/java/com/skcraft/launcher/model/minecraft/VersionManifest.java +++ b/launcher/src/main/java/com/skcraft/launcher/model/minecraft/VersionManifest.java @@ -55,9 +55,6 @@ } setArguments(result); - - // TODO: Possibly do some cheaty side-effects here to add parameters missing from early game versions. - // Either that or use a proper version adapter. } @Data diff --git a/launcher/src/main/resources/com/skcraft/launcher/maven_repos.json b/launcher/src/main/resources/com/skcraft/launcher/maven_repos.json index fda4311..ec8063e 100644 --- a/launcher/src/main/resources/com/skcraft/launcher/maven_repos.json +++ b/launcher/src/main/resources/com/skcraft/launcher/maven_repos.json @@ -2,5 +2,6 @@ "https://libraries.minecraft.net/", "https://repo1.maven.org/maven2/", "https://files.minecraftforge.net/maven/", + "https://maven.fabricmc.net/", "http://maven.apache.org/" ]