1
0
mirror of https://github.com/SKCraft/Launcher.git synced 2025-01-07 19:29:03 +01:00

Refactor loader processing into subclasses for greater flexibility

This commit is contained in:
Henry Le Grys 2020-12-02 02:29:52 +00:00
parent 8b6f3abaf9
commit aeedf09f8c
12 changed files with 404 additions and 122 deletions

View File

@ -7,8 +7,10 @@
package com.skcraft.launcher.builder;
import com.beust.jcommander.internal.Lists;
import com.google.common.io.CharStreams;
import org.apache.commons.compress.compressors.CompressorStreamFactory;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
@ -41,6 +43,13 @@ public final class BuilderUtils {
return null;
}
public static String readStringFromStream(Readable r) throws IOException {
String data = CharStreams.toString(r);
data = data.replaceAll(",\\s*\\}", "}"); // Fix issues with trailing commas
return data;
}
public static List<Compressor> getCompressors(String repoUrl) {
if (repoUrl.matches("^https?://files.minecraftforge.net/maven/?")) {
return Lists.newArrayList(

View File

@ -15,20 +15,21 @@ import com.fasterxml.jackson.databind.ObjectWriter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
import com.google.common.io.Closer;
import com.google.common.io.Files;
import com.skcraft.launcher.Launcher;
import com.skcraft.launcher.LauncherUtils;
import com.skcraft.launcher.model.loader.InstallProfile;
import com.skcraft.launcher.model.loader.LoaderManifest;
import com.skcraft.launcher.model.loader.SidedData;
import com.skcraft.launcher.model.loader.VersionInfo;
import com.skcraft.launcher.model.minecraft.*;
import com.skcraft.launcher.model.modpack.DownloadableFile;
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.model.loader.BasicInstallProfile;
import com.skcraft.launcher.model.minecraft.Library;
import com.skcraft.launcher.model.minecraft.ReleaseList;
import com.skcraft.launcher.model.minecraft.Version;
import com.skcraft.launcher.model.minecraft.VersionManifest;
import com.skcraft.launcher.model.modpack.Manifest;
import com.skcraft.launcher.util.Environment;
import com.skcraft.launcher.util.FileUtils;
import com.skcraft.launcher.util.HttpRequest;
import com.skcraft.launcher.util.SimpleLogFormatter;
import lombok.Getter;
@ -153,107 +154,39 @@ public class PackageBuilder {
JarFile jarFile = new JarFile(file);
Closer closer = Closer.create();
ILoaderProcessor processor = null;
try {
ZipEntry manifestEntry = BuilderUtils.getZipEntry(jarFile, "version.json");
String loaderName = file.getName();
if (manifestEntry != null) {
InputStream stream = jarFile.getInputStream(manifestEntry);
// Read file
String data = CharStreams.toString(closer.register(new InputStreamReader(stream)));
data = data.replaceAll(",\\s*\\}", "}"); // Fix issues with trailing commas
VersionInfo info = mapper.readValue(data, VersionInfo.class);
VersionManifest version = manifest.getVersionManifest();
if (version.getId() != null) {
loaderName = version.getId();
}
// Copy tweak class arguments
List<GameArgument> gameArguments = info.getMinecraftArguments().getGameArguments();
if (gameArguments != null) {
version.getArguments().getGameArguments().addAll(gameArguments);
}
// Add libraries
List<Library> libraries = info.getLibraries();
if (libraries != null) {
for (Library library : libraries) {
loaderLibraries.add(library);
log.info("Adding loader library " + library.getName());
}
}
// Copy main class
String mainClass = info.getMainClass();
if (mainClass != null) {
version.setMainClass(mainClass);
log.info("Using " + mainClass + " as the main class");
}
} else {
log.warning("The file at " + file.getAbsolutePath() + " did not appear to have an " +
"version.json file inside -- is it actually an installer for a mod loader?");
}
ZipEntry profileEntry = BuilderUtils.getZipEntry(jarFile, "install_profile.json");
if (profileEntry != null) {
InputStream stream = jarFile.getInputStream(profileEntry);
String data = CharStreams.toString(closer.register(new InputStreamReader(stream)));
data = data.replace(",\\s*\\}", "}");
InputStreamReader reader = closer.register(new InputStreamReader(stream));
InstallProfile profile = mapper.readValue(data, InstallProfile.class);
BasicInstallProfile basicProfile = mapper.readValue(BuilderUtils.readStringFromStream(reader),
BasicInstallProfile.class);
String profileName = basicProfile.resolveProfileName();
// Import the libraries for the installer
installerLibraries.addAll(profile.getLibraries());
// Extract the data files
List<DownloadableFile> extraFiles = Lists.newArrayList();
ZipEntry clientBinpatch = BuilderUtils.getZipEntry(jarFile, "data/client.lzma");
if (clientBinpatch != null) {
DownloadableFile entry = FileUtils.saveStreamToObjectsDir(
closer.register(jarFile.getInputStream(clientBinpatch)),
new File(baseDir, manifest.getObjectsLocation()));
entry.setName("client.lzma");
entry.setSide(Side.CLIENT);
extraFiles.add(entry);
profile.getData().get("BINPATCH").setClient("&" + entry.getName() + "&");
if (profileName.equalsIgnoreCase("forge")) {
if (basicProfile.isLegacy()) {
processor = new OldForgeLoaderProcessor();
} else {
processor = new ModernForgeLoaderProcessor();
}
}
ZipEntry serverBinpatch = BuilderUtils.getZipEntry(jarFile, "data/server.lzma");
if (serverBinpatch != null) {
DownloadableFile entry = FileUtils.saveStreamToObjectsDir(
closer.register(jarFile.getInputStream(serverBinpatch)),
new File(baseDir, manifest.getObjectsLocation()));
entry.setName("server.lzma");
entry.setSide(Side.SERVER);
extraFiles.add(entry);
profile.getData().get("BINPATCH").setServer("&" + entry.getName() + "&");
}
// Add extra sided data
profile.getData().put("SIDE", SidedData.create("client", "server"));
// Add loader manifest to the map
manifest.getLoaders().put(loaderName, new LoaderManifest(profile.getLibraries(), profile.getData(), extraFiles));
// Add processors
manifest.getTasks().addAll(profile.toProcessorEntries(loaderName));
}
ZipEntry mavenEntry = BuilderUtils.getZipEntry(jarFile, "maven/");
if (mavenEntry != null) {
URL jarUrl = new URL("jar:file:" + file.getAbsolutePath() + "!/");
jarMavens.add(new URL(jarUrl, "/maven/"));
}
} finally {
closer.close();
jarFile.close();
}
if (processor != null) {
LoaderResult result = processor.process(file, manifest, mapper, baseDir);
loaderLibraries.addAll(result.getLoaderLibraries());
installerLibraries.addAll(result.getProcessorLibraries());
jarMavens.addAll(result.getJarMavens());
}
}
public void downloadLibraries(File librariesDir) throws IOException, InterruptedException {

View File

@ -0,0 +1,11 @@
package com.skcraft.launcher.builder.loaders;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.skcraft.launcher.model.modpack.Manifest;
import java.io.File;
import java.io.IOException;
public interface ILoaderProcessor {
LoaderResult process(File loaderJar, Manifest manifest, ObjectMapper mapper, File baseDir) throws IOException;
}

View File

@ -0,0 +1,15 @@
package com.skcraft.launcher.builder.loaders;
import com.google.common.collect.Lists;
import com.skcraft.launcher.model.minecraft.Library;
import lombok.Data;
import java.net.URL;
import java.util.List;
@Data
public class LoaderResult {
private final List<Library> loaderLibraries = Lists.newArrayList();
private final List<Library> processorLibraries = Lists.newArrayList();
private final List<URL> jarMavens = Lists.newArrayList();
}

View File

@ -0,0 +1,139 @@
package com.skcraft.launcher.builder.loaders;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.google.common.io.CharStreams;
import com.google.common.io.Closer;
import com.skcraft.launcher.builder.BuilderUtils;
import com.skcraft.launcher.model.loader.LoaderManifest;
import com.skcraft.launcher.model.loader.SidedData;
import com.skcraft.launcher.model.loader.VersionInfo;
import com.skcraft.launcher.model.loader.profiles.ModernForgeInstallProfile;
import com.skcraft.launcher.model.minecraft.GameArgument;
import com.skcraft.launcher.model.minecraft.Library;
import com.skcraft.launcher.model.minecraft.Side;
import com.skcraft.launcher.model.minecraft.VersionManifest;
import com.skcraft.launcher.model.modpack.DownloadableFile;
import com.skcraft.launcher.model.modpack.Manifest;
import com.skcraft.launcher.util.FileUtils;
import lombok.extern.java.Log;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
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 ModernForgeLoaderProcessor implements ILoaderProcessor {
@Override
public LoaderResult process(File loaderJar, Manifest manifest, ObjectMapper mapper, File baseDir) throws IOException {
JarFile jarFile = new JarFile(loaderJar);
Closer closer = Closer.create();
LoaderResult result = new LoaderResult();
try {
ZipEntry versionEntry = BuilderUtils.getZipEntry(jarFile, "version.json");
String loaderName = jarFile.getName();
if (versionEntry != null) {
InputStream stream = jarFile.getInputStream(versionEntry);
VersionInfo info = mapper.readValue(
BuilderUtils.readStringFromStream(closer.register(new InputStreamReader(stream))),
VersionInfo.class);
VersionManifest version = manifest.getVersionManifest();
if (version.getId() != null) {
loaderName = version.getId();
}
// Copy tweak class arguments
List<GameArgument> gameArguments = info.getMinecraftArguments().getGameArguments();
if (gameArguments != null) {
version.getArguments().getGameArguments().addAll(gameArguments);
}
// Add libraries
List<Library> libraries = info.getLibraries();
if (libraries != null) {
for (Library library : libraries) {
result.getLoaderLibraries().add(library);
log.info("Adding loader library " + library.getName());
}
}
// Copy main class
String mainClass = info.getMainClass();
if (mainClass != null) {
version.setMainClass(mainClass);
log.info("Using " + mainClass + " as the main class");
}
} else {
log.warning("The loader " + loaderJar.getAbsolutePath() + " does not appear to have a " +
"version.json file inside -- is it actually an installer for Forge?");
}
ZipEntry profileEntry = BuilderUtils.getZipEntry(jarFile, "install_profile.json");
if (profileEntry != null) {
InputStream stream = jarFile.getInputStream(profileEntry);
String data = CharStreams.toString(closer.register(new InputStreamReader(stream)));
data = data.replace(",\\s*\\}", "}");
ModernForgeInstallProfile profile = mapper.readValue(data, ModernForgeInstallProfile.class);
// Import the libraries for the installer
result.getProcessorLibraries().addAll(profile.getLibraries());
// Extract the data files
List<DownloadableFile> extraFiles = Lists.newArrayList();
ZipEntry clientBinpatch = BuilderUtils.getZipEntry(jarFile, "data/client.lzma");
if (clientBinpatch != null) {
DownloadableFile entry = FileUtils.saveStreamToObjectsDir(
closer.register(jarFile.getInputStream(clientBinpatch)),
new File(baseDir, manifest.getObjectsLocation()));
entry.setName("client.lzma");
entry.setSide(Side.CLIENT);
extraFiles.add(entry);
profile.getData().get("BINPATCH").setClient("&" + entry.getName() + "&");
}
ZipEntry serverBinpatch = BuilderUtils.getZipEntry(jarFile, "data/server.lzma");
if (serverBinpatch != null) {
DownloadableFile entry = FileUtils.saveStreamToObjectsDir(
closer.register(jarFile.getInputStream(serverBinpatch)),
new File(baseDir, manifest.getObjectsLocation()));
entry.setName("server.lzma");
entry.setSide(Side.SERVER);
extraFiles.add(entry);
profile.getData().get("BINPATCH").setServer("&" + entry.getName() + "&");
}
// Add extra sided data
profile.getData().put("SIDE", SidedData.create("client", "server"));
// Add loader manifest to the map
manifest.getLoaders().put(loaderName, new LoaderManifest(profile.getLibraries(), profile.getData(), extraFiles));
// Add processors
manifest.getTasks().addAll(profile.toProcessorEntries(loaderName));
}
ZipEntry mavenEntry = BuilderUtils.getZipEntry(jarFile, "maven/");
if (mavenEntry != null) {
URL jarUrl = new URL("jar:file:" + loaderJar.getAbsolutePath() + "!/");
result.getJarMavens().add(new URL(jarUrl, "/maven/"));
}
} finally {
closer.close();
jarFile.close();
}
return result;
}
}

View File

@ -0,0 +1,116 @@
package com.skcraft.launcher.builder.loaders;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import com.google.common.io.Files;
import com.skcraft.launcher.builder.BuilderUtils;
import com.skcraft.launcher.model.loader.profiles.LegacyInstallProfile;
import com.skcraft.launcher.model.minecraft.GameArgument;
import com.skcraft.launcher.model.minecraft.Library;
import com.skcraft.launcher.model.minecraft.MinecraftArguments;
import com.skcraft.launcher.model.minecraft.VersionManifest;
import com.skcraft.launcher.model.modpack.Manifest;
import com.skcraft.launcher.util.Environment;
import lombok.extern.java.Log;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.List;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
@Log
public class OldForgeLoaderProcessor 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 profileEntry = BuilderUtils.getZipEntry(jarFile, "install_profile.json");
if (profileEntry != null) {
InputStream stream = jarFile.getInputStream(profileEntry);
// Read file
String data = BuilderUtils.readStringFromStream(closer.register(new InputStreamReader(stream)));
LegacyInstallProfile profile = mapper.readValue(data, LegacyInstallProfile.class);
VersionManifest version = manifest.getVersionManifest();
// Copy tweak class arguments
MinecraftArguments args = profile.getVersionInfo().getMinecraftArguments();
if (args != null) {
Iterator<GameArgument> iter = args.getGameArguments().iterator();
while (iter.hasNext()) {
GameArgument cur = iter.next();
if (cur.getValues().contains("--tweakClass")) {
String tweakClass = cur.getValues().size() > 1
? cur.getValues().get(1)
: iter.next().getJoinedValue();
GameArgument tweakArg = new GameArgument(Lists.newArrayList("--tweakClass", tweakClass));
manifest.getVersionManifest().getArguments().getGameArguments().add(tweakArg);
log.info(String.format("Adding tweak class '%s' to arguments", tweakClass));
}
}
}
// Add libraries
List<Library> libraries = profile.getVersionInfo().getLibraries();
if (libraries != null) {
for (Library library : libraries) {
if (!version.getLibraries().contains(library)) {
result.getLoaderLibraries().add(library);
}
}
}
// Copy main class
String mainClass = profile.getVersionInfo().getMainClass();
if (mainClass != null) {
version.setMainClass(mainClass);
log.info("Using " + mainClass + " as the main class");
}
// Extract the library
String filePath = profile.getInstallData().getFilePath();
String libraryPath = profile.getInstallData().getPath();
if (filePath != null && libraryPath != null) {
ZipEntry libraryEntry = BuilderUtils.getZipEntry(jarFile, filePath);
if (libraryEntry != null) {
Library library = new Library();
library.setName(libraryPath);
File librariesDir = new File(baseDir, manifest.getLibrariesLocation());
File extractPath = new File(librariesDir, library.getPath(Environment.getInstance()));
Files.createParentDirs(extractPath);
ByteStreams.copy(closer.register(jarFile.getInputStream(libraryEntry)),
Files.newOutputStreamSupplier(extractPath));
} else {
log.warning("Could not find the file '" + filePath + "' in "
+ loaderJar.getAbsolutePath()
+ ", which means that this mod loader will not work correctly");
}
}
} else {
log.warning("The file at " + loaderJar.getAbsolutePath() + " did not appear to have an " +
"install_profile.json file inside -- is it actually an installer for a mod loader?");
}
} finally {
closer.close();
jarFile.close();
}
return result;
}
}

View File

@ -0,0 +1,34 @@
package com.skcraft.launcher.model.loader;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class BasicInstallProfile {
private String profile;
private int spec;
@JsonProperty("install")
private Legacy legacyProfile;
@JsonIgnore
public boolean isLegacy() {
return getLegacyProfile() != null;
}
public String resolveProfileName() {
if (isLegacy()) {
return getLegacyProfile().getProfileName();
} else {
return getProfile();
}
}
@Data
public static class Legacy {
private String profileName;
}
}

View File

@ -1,19 +0,0 @@
/*
* SK's Minecraft Launcher
* Copyright (C) 2010-2014 Albert Pham <http://www.sk89q.com> and contributors
* Please see LICENSE.txt for license information.
*/
package com.skcraft.launcher.model.loader;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class InstallData {
private String path;
private String filePath;
}

View File

@ -8,10 +8,13 @@ package com.skcraft.launcher.model.loader;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Splitter;
import com.skcraft.launcher.model.minecraft.GameArgument;
import com.skcraft.launcher.model.minecraft.Library;
import com.skcraft.launcher.model.minecraft.MinecraftArguments;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
@ -23,4 +26,18 @@ public class VersionInfo {
private String mainClass;
private List<Library> libraries;
public void setMinecraftArguments(MinecraftArguments arguments) {
this.minecraftArguments = arguments;
}
public void setMinecraftArguments(String argumentString) {
MinecraftArguments minecraftArguments = new MinecraftArguments();
minecraftArguments.setGameArguments(new ArrayList<GameArgument>());
for (String arg : Splitter.on(' ').split(argumentString)) {
minecraftArguments.getGameArguments().add(new GameArgument(arg));
}
setMinecraftArguments(minecraftArguments);
}
}

View File

@ -0,0 +1,21 @@
package com.skcraft.launcher.model.loader.profiles;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.skcraft.launcher.model.loader.VersionInfo;
import lombok.Data;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class LegacyInstallProfile {
@JsonProperty("install")
private InstallData installData;
private VersionInfo versionInfo;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public static class InstallData {
private String path;
private String filePath;
}
}

View File

@ -4,11 +4,14 @@
* Please see LICENSE.txt for license information.
*/
package com.skcraft.launcher.model.loader;
package com.skcraft.launcher.model.loader.profiles;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.skcraft.launcher.model.loader.InstallProcessor;
import com.skcraft.launcher.model.loader.ProcessorEntry;
import com.skcraft.launcher.model.loader.SidedData;
import com.skcraft.launcher.model.minecraft.Library;
import lombok.Data;
@ -17,7 +20,7 @@ import java.util.Map;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class InstallProfile {
public class ModernForgeInstallProfile {
private List<Library> libraries;
private List<InstallProcessor> processors;
private Map<String, SidedData> data;

View File

@ -11,10 +11,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Splitter;
import lombok.Data;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.*;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@ -49,10 +46,16 @@ public class VersionManifest {
}
public void setMinecraftArguments(String minecraftArguments) {
MinecraftArguments result = new MinecraftArguments();
result.setGameArguments(new ArrayList<GameArgument>());
result.setJvmArguments(new ArrayList<GameArgument>());
for (String arg : Splitter.on(' ').split(minecraftArguments)) {
this.arguments.getGameArguments().add(new GameArgument(arg));
result.getGameArguments().add(new GameArgument(arg));
}
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.
}