mirror of
https://github.com/SKCraft/Launcher.git
synced 2024-11-30 13:13:58 +01:00
Initial 1.16 update
oh lord okay - Update to new launchermeta endpoint for version manifests - Fix library handling (libraries now seem to give us the URL most of the time) - Write entire new system for handling the forge installer process - Fix asset handling, mostly (still TODO is remove all traces of assetSource, it's given to us by launchermeta now)
This commit is contained in:
parent
40b5fb838c
commit
410031a86f
@ -42,7 +42,7 @@ public final class BuilderUtils {
|
||||
}
|
||||
|
||||
public static List<Compressor> getCompressors(String repoUrl) {
|
||||
if (repoUrl.matches("^https?://files.minecraftforge.net/maven/")) {
|
||||
if (repoUrl.matches("^https?://files.minecraftforge.net/maven/?")) {
|
||||
return Lists.newArrayList(
|
||||
new Compressor("xz", CompressorStreamFactory.XZ),
|
||||
new Compressor("pack", CompressorStreamFactory.PACK200));
|
||||
|
@ -12,6 +12,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.io.ByteStreams;
|
||||
@ -21,24 +22,32 @@ 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.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.DownloadableFile;
|
||||
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;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.java.Log;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
@ -61,8 +70,13 @@ public class PackageBuilder {
|
||||
private final PropertiesApplicator applicator;
|
||||
@Getter
|
||||
private boolean prettyPrint = false;
|
||||
|
||||
@Getter @Setter
|
||||
private File baseDir;
|
||||
|
||||
private List<Library> loaderLibraries = Lists.newArrayList();
|
||||
private List<String> mavenRepos;
|
||||
private List<URL> jarMavens = Lists.newArrayList();
|
||||
|
||||
/**
|
||||
* Create a new package builder.
|
||||
@ -144,67 +158,101 @@ public class PackageBuilder {
|
||||
Closer closer = Closer.create();
|
||||
|
||||
try {
|
||||
ZipEntry profileEntry = BuilderUtils.getZipEntry(jarFile, "install_profile.json");
|
||||
ZipEntry manifestEntry = BuilderUtils.getZipEntry(jarFile, "version.json");
|
||||
String loaderName = file.getName();
|
||||
|
||||
if (profileEntry != null) {
|
||||
InputStream stream = jarFile.getInputStream(profileEntry);
|
||||
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
|
||||
|
||||
InstallProfile profile = mapper.readValue(data, InstallProfile.class);
|
||||
VersionInfo info = mapper.readValue(data, VersionInfo.class);
|
||||
VersionManifest version = manifest.getVersionManifest();
|
||||
|
||||
if (version.getId() != null) {
|
||||
loaderName = version.getId();
|
||||
}
|
||||
|
||||
// Copy tweak class arguments
|
||||
String args = profile.getVersionInfo().getMinecraftArguments();
|
||||
if (args != null) {
|
||||
List<String> gameArguments = info.getMinecraftArguments().getGameArguments();
|
||||
if (gameArguments != null) {
|
||||
String args = Joiner.on(' ').join(gameArguments);
|
||||
String existingArgs = Strings.nullToEmpty(version.getMinecraftArguments());
|
||||
|
||||
Matcher m = TWEAK_CLASS_ARG.matcher(args);
|
||||
while (m.find()) {
|
||||
version.setMinecraftArguments(existingArgs + " " + m.group());
|
||||
log.info("Adding " + m.group() + " to launch arguments");
|
||||
}
|
||||
version.setMinecraftArguments(Joiner.on(' ').join(existingArgs, args));
|
||||
}
|
||||
|
||||
// Add libraries
|
||||
List<Library> libraries = profile.getVersionInfo().getLibraries();
|
||||
List<Library> libraries = info.getLibraries();
|
||||
if (libraries != null) {
|
||||
for (Library library : libraries) {
|
||||
if (!version.getLibraries().contains(library)) {
|
||||
loaderLibraries.add(library);
|
||||
}
|
||||
loaderLibraries.add(library);
|
||||
log.info("Adding loader library " + library.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// Copy main class
|
||||
String mainClass = profile.getVersionInfo().getMainClass();
|
||||
String mainClass = info.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 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 " + file.getAbsolutePath() + ", which means that this mod loader will not work correctly");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warning("The file at " + file.getAbsolutePath() + " did not appear to have an " +
|
||||
"install_profile.json file inside -- is it actually an installer for a mod loader?");
|
||||
"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*\\}", "}");
|
||||
|
||||
InstallProfile profile = mapper.readValue(data, InstallProfile.class);
|
||||
|
||||
// Import the libraries for the installer
|
||||
loaderLibraries.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");
|
||||
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");
|
||||
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.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();
|
||||
@ -219,53 +267,31 @@ public class PackageBuilder {
|
||||
Environment env = Environment.getInstance();
|
||||
|
||||
for (Library library : loaderLibraries) {
|
||||
File outputPath = new File(librariesDir, library.getPath(env));
|
||||
Library.Artifact artifact = library.getArtifact(env);
|
||||
File outputPath = new File(librariesDir, artifact.getPath());
|
||||
|
||||
if (!outputPath.exists()) {
|
||||
Files.createParentDirs(outputPath);
|
||||
boolean found = false;
|
||||
|
||||
// Gather a list of repositories to download from
|
||||
List<String> sources = Lists.newArrayList();
|
||||
if (library.getBaseUrl() != null) {
|
||||
sources.add(library.getBaseUrl());
|
||||
if (!artifact.getUrl().isEmpty()) {
|
||||
found = tryDownloadLibrary(library, artifact, artifact.getUrl(), outputPath);
|
||||
}
|
||||
sources.addAll(mavenRepos);
|
||||
|
||||
// Try each repository
|
||||
for (String baseUrl : sources) {
|
||||
String pathname = library.getPath(env);
|
||||
|
||||
// Some repositories compress their files
|
||||
List<Compressor> compressors = BuilderUtils.getCompressors(baseUrl);
|
||||
for (Compressor compressor : Lists.reverse(compressors)) {
|
||||
pathname = compressor.transformPathname(pathname);
|
||||
// Look inside the loader JARs
|
||||
if (!found) {
|
||||
for (URL base : jarMavens) {
|
||||
found = tryFetchLibrary(library, new URL(base, artifact.getPath()), outputPath);
|
||||
if (found) break;
|
||||
}
|
||||
}
|
||||
|
||||
URL url = new URL(baseUrl + pathname);
|
||||
File tempFile = File.createTempFile("launcherlib", null);
|
||||
|
||||
try {
|
||||
log.info("Downloading library " + library.getName() + " from " + url + "...");
|
||||
HttpRequest.get(url).execute().expectResponseCode(200).saveContent(tempFile);
|
||||
} catch (IOException e) {
|
||||
log.info("Could not get file from " + url + ": " + e.getMessage());
|
||||
continue;
|
||||
// Try each repository if not found yet
|
||||
if (!found) {
|
||||
for (String baseUrl : mavenRepos) {
|
||||
found = tryDownloadLibrary(library, artifact, baseUrl + artifact.getPath(), outputPath);
|
||||
if (found) break;
|
||||
}
|
||||
|
||||
// Decompress (if needed) and write to file
|
||||
Closer closer = Closer.create();
|
||||
InputStream inputStream = closer.register(new FileInputStream(tempFile));
|
||||
inputStream = closer.register(new BufferedInputStream(inputStream));
|
||||
for (Compressor compressor : compressors) {
|
||||
inputStream = closer.register(compressor.createInputStream(inputStream));
|
||||
}
|
||||
ByteStreams.copy(inputStream, closer.register(new FileOutputStream(outputPath)));
|
||||
|
||||
tempFile.delete();
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
@ -275,6 +301,65 @@ public class PackageBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean tryDownloadLibrary(Library library, Library.Artifact artifact, String baseUrl, File outputPath)
|
||||
throws IOException, InterruptedException {
|
||||
URL url = new URL(baseUrl);
|
||||
File tempFile = File.createTempFile("launcherlib", null);
|
||||
|
||||
// Some repositories compress their files
|
||||
List<Compressor> compressors = BuilderUtils.getCompressors(baseUrl);
|
||||
for (Compressor compressor : Lists.reverse(compressors)) {
|
||||
url = new URL(url, compressor.transformPathname(artifact.getPath()));
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("Downloading library " + library.getName() + " from " + url + "...");
|
||||
HttpRequest.get(url).execute().expectResponseCode(200).saveContent(tempFile);
|
||||
} catch (IOException e) {
|
||||
log.info("Could not get file from " + url + ": " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
writeLibraryToFile(outputPath, tempFile, compressors);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean tryFetchLibrary(Library library, URL url, File outputPath)
|
||||
throws IOException {
|
||||
File tempFile = File.createTempFile("launcherlib", null);
|
||||
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
log.info("Reading library " + library.getName() + " from " + url.toString());
|
||||
InputStream stream = closer.register(url.openStream());
|
||||
stream = closer.register(new BufferedInputStream(stream));
|
||||
|
||||
ByteStreams.copy(stream, closer.register(new FileOutputStream(tempFile)));
|
||||
} catch (IOException e) {
|
||||
log.info("Could not get file from " + url + ": " + e.getMessage());
|
||||
return false;
|
||||
} finally {
|
||||
closer.close();
|
||||
}
|
||||
|
||||
writeLibraryToFile(outputPath, tempFile, Collections.<Compressor>emptyList());
|
||||
return true;
|
||||
}
|
||||
|
||||
private void writeLibraryToFile(File outputPath, File inputFile, List<Compressor> compressors) throws IOException {
|
||||
// Decompress (if needed) and write to file
|
||||
Closer closer = Closer.create();
|
||||
InputStream inputStream = closer.register(new FileInputStream(inputFile));
|
||||
inputStream = closer.register(new BufferedInputStream(inputStream));
|
||||
for (Compressor compressor : compressors) {
|
||||
inputStream = closer.register(compressor.createInputStream(inputStream));
|
||||
}
|
||||
ByteStreams.copy(inputStream, closer.register(new FileOutputStream(outputPath)));
|
||||
|
||||
inputFile.delete();
|
||||
closer.close();
|
||||
}
|
||||
|
||||
public void validateManifest() {
|
||||
checkNotNull(emptyToNull(manifest.getName()), "Package name is not defined");
|
||||
checkNotNull(emptyToNull(manifest.getGameVersion()), "Game version is not defined");
|
||||
@ -297,18 +382,24 @@ public class PackageBuilder {
|
||||
|
||||
log.info("Loaded version manifest from " + path.getAbsolutePath());
|
||||
} else {
|
||||
URL url = url(String.format(
|
||||
properties.getProperty("versionManifestUrl"),
|
||||
manifest.getGameVersion()));
|
||||
URL url = url(properties.getProperty("versionManifestUrl"));
|
||||
|
||||
log.info("Fetching version manifest from " + url + "...");
|
||||
|
||||
manifest.setVersionManifest(HttpRequest
|
||||
.get(url)
|
||||
ReleaseList releases = HttpRequest.get(url)
|
||||
.execute()
|
||||
.expectResponseCode(200)
|
||||
.returnContent()
|
||||
.asJson(VersionManifest.class));
|
||||
.asJson(ReleaseList.class);
|
||||
|
||||
Version version = releases.find(manifest.getGameVersion());
|
||||
VersionManifest versionManifest = HttpRequest.get(url(version.getUrl()))
|
||||
.execute()
|
||||
.expectResponseCode(200)
|
||||
.returnContent()
|
||||
.asJson(VersionManifest.class);
|
||||
|
||||
manifest.setVersionManifest(versionManifest);
|
||||
}
|
||||
}
|
||||
|
||||
@ -379,6 +470,7 @@ public class PackageBuilder {
|
||||
// From config
|
||||
builder.readConfig(options.getConfigPath());
|
||||
builder.readVersionManifest(options.getVersionManifestPath());
|
||||
builder.setBaseDir(options.getOutputPath());
|
||||
|
||||
// From options
|
||||
manifest.updateName(options.getName());
|
||||
|
@ -50,7 +50,7 @@ public class AssetsRoot {
|
||||
* @return the file, which may not exist
|
||||
*/
|
||||
public File getIndexPath(VersionManifest versionManifest) {
|
||||
return new File(dir, "indexes/" + versionManifest.getAssetsIndex() + ".json");
|
||||
return new File(dir, "indexes/" + versionManifest.getAssetId() + ".json");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,7 +75,7 @@ public class AssetsRoot {
|
||||
* @throws LauncherException
|
||||
*/
|
||||
public AssetsTreeBuilder createAssetsBuilder(@NonNull VersionManifest versionManifest) throws LauncherException {
|
||||
String indexId = versionManifest.getAssetsIndex();
|
||||
String indexId = versionManifest.getAssetId();
|
||||
File path = getIndexPath(versionManifest);
|
||||
AssetsIndex index = Persistence.read(path, AssetsIndex.class, true);
|
||||
if (index == null || index.getObjects() == null) {
|
||||
|
@ -16,10 +16,12 @@ import com.skcraft.launcher.auth.AccountList;
|
||||
import com.skcraft.launcher.auth.LoginService;
|
||||
import com.skcraft.launcher.auth.YggdrasilLoginService;
|
||||
import com.skcraft.launcher.launch.LaunchSupervisor;
|
||||
import com.skcraft.launcher.model.minecraft.Library;
|
||||
import com.skcraft.launcher.model.minecraft.VersionManifest;
|
||||
import com.skcraft.launcher.persistence.Persistence;
|
||||
import com.skcraft.launcher.swing.SwingHelper;
|
||||
import com.skcraft.launcher.update.UpdateManager;
|
||||
import com.skcraft.launcher.util.Environment;
|
||||
import com.skcraft.launcher.util.HttpRequest;
|
||||
import com.skcraft.launcher.util.SharedLocale;
|
||||
import com.skcraft.launcher.util.SimpleLogFormatter;
|
||||
@ -264,6 +266,16 @@ public final class Launcher {
|
||||
return new File(getCommonDataDir(), "libraries");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a library file.
|
||||
* @param library Library to fetch
|
||||
* @return File pointing to the library on disk.
|
||||
*/
|
||||
public File getLibraryFile(Library library) {
|
||||
Environment env = Environment.getInstance();
|
||||
return new File(getLibrariesDir(), library.getPath(env));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the directory to store versions.
|
||||
*
|
||||
|
@ -7,6 +7,7 @@
|
||||
package com.skcraft.launcher.install;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import com.skcraft.launcher.Launcher;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.java.Log;
|
||||
|
||||
@ -28,7 +29,7 @@ public class FileCopy implements InstallTask {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws IOException {
|
||||
public void execute(Launcher launcher) throws IOException {
|
||||
log.log(Level.INFO, "Copying to {0} (from {1})...", new Object[]{to.getAbsoluteFile(), from.getName()});
|
||||
to.getParentFile().mkdirs();
|
||||
Files.copy(from, to);
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
package com.skcraft.launcher.install;
|
||||
|
||||
import com.skcraft.launcher.Launcher;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.java.Log;
|
||||
|
||||
@ -27,7 +28,7 @@ public class FileMover implements InstallTask {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws IOException {
|
||||
public void execute(Launcher launcher) throws IOException {
|
||||
log.log(Level.INFO, "Moving to {0} (from {1})...", new Object[]{to.getAbsoluteFile(), from.getName()});
|
||||
to.getParentFile().mkdirs();
|
||||
to.delete();
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
package com.skcraft.launcher.install;
|
||||
|
||||
import com.skcraft.launcher.Launcher;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.java.Log;
|
||||
|
||||
@ -29,7 +30,7 @@ public class InstallLogFileMover implements InstallTask {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws IOException {
|
||||
public void execute(Launcher launcher) throws IOException {
|
||||
InstallLogFileMover.log.log(Level.INFO, "Installing to {0} (from {1})...", new Object[]{to.getAbsoluteFile(), from.getName()});
|
||||
to.getParentFile().mkdirs();
|
||||
to.delete();
|
||||
|
@ -7,9 +7,10 @@
|
||||
package com.skcraft.launcher.install;
|
||||
|
||||
import com.skcraft.concurrency.ProgressObservable;
|
||||
import com.skcraft.launcher.Launcher;
|
||||
|
||||
public interface InstallTask extends ProgressObservable {
|
||||
|
||||
void execute() throws Exception;
|
||||
void execute(Launcher launcher) throws Exception;
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
package com.skcraft.launcher.install;
|
||||
|
||||
import com.skcraft.concurrency.ProgressObservable;
|
||||
import com.skcraft.launcher.Launcher;
|
||||
import com.skcraft.launcher.util.SharedLocale;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
@ -26,11 +27,11 @@ public class Installer implements ProgressObservable {
|
||||
|
||||
@Getter private final File tempDir;
|
||||
private final HttpDownloader downloader;
|
||||
private InstallTask running;
|
||||
private int count = 0;
|
||||
private int finished = 0;
|
||||
|
||||
private List<InstallTask> queue = new ArrayList<InstallTask>();
|
||||
private TaskQueue mainQueue = new TaskQueue();
|
||||
private TaskQueue lateQueue = new TaskQueue();
|
||||
|
||||
private transient TaskQueue activeQueue;
|
||||
|
||||
public Installer(@NonNull File tempDir) {
|
||||
this.tempDir = tempDir;
|
||||
@ -38,27 +39,27 @@ public class Installer implements ProgressObservable {
|
||||
}
|
||||
|
||||
public synchronized void queue(@NonNull InstallTask runnable) {
|
||||
queue.add(runnable);
|
||||
count++;
|
||||
mainQueue.queue(runnable);
|
||||
}
|
||||
|
||||
public synchronized void queueLate(@NonNull InstallTask runnable) {
|
||||
lateQueue.queue(runnable);
|
||||
}
|
||||
|
||||
public void download() throws IOException, InterruptedException {
|
||||
downloader.execute();
|
||||
}
|
||||
|
||||
public synchronized void execute() throws Exception {
|
||||
queue = Collections.unmodifiableList(queue);
|
||||
public synchronized void execute(Launcher launcher) throws Exception {
|
||||
activeQueue = mainQueue;
|
||||
mainQueue.execute(launcher);
|
||||
activeQueue = null;
|
||||
}
|
||||
|
||||
try {
|
||||
for (InstallTask runnable : queue) {
|
||||
checkInterrupted();
|
||||
running = runnable;
|
||||
runnable.execute();
|
||||
finished++;
|
||||
}
|
||||
} finally {
|
||||
running = null;
|
||||
}
|
||||
public synchronized void executeLate(Launcher launcher) throws Exception {
|
||||
activeQueue = lateQueue;
|
||||
lateQueue.execute(launcher);
|
||||
activeQueue = null;
|
||||
}
|
||||
|
||||
public Downloader getDownloader() {
|
||||
@ -67,20 +68,50 @@ public class Installer implements ProgressObservable {
|
||||
|
||||
@Override
|
||||
public double getProgress() {
|
||||
return finished / (double) count;
|
||||
if (activeQueue == null) return 0.0;
|
||||
|
||||
return activeQueue.finished / (double) activeQueue.count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatus() {
|
||||
InstallTask running = this.running;
|
||||
if (running != null) {
|
||||
if (activeQueue != null && activeQueue.running != null) {
|
||||
InstallTask running = activeQueue.running;
|
||||
String status = running.getStatus();
|
||||
if (status == null) {
|
||||
status = running.toString();
|
||||
}
|
||||
return tr("installer.executing", count - finished) + "\n" + status;
|
||||
return tr("installer.executing", activeQueue.count - activeQueue.finished) + "\n" + status;
|
||||
} else {
|
||||
return SharedLocale.tr("installer.installing");
|
||||
}
|
||||
}
|
||||
|
||||
public static class TaskQueue {
|
||||
private List<InstallTask> queue = new ArrayList<InstallTask>();
|
||||
|
||||
private int count = 0;
|
||||
private int finished = 0;
|
||||
private InstallTask running;
|
||||
|
||||
public synchronized void queue(@NonNull InstallTask runnable) {
|
||||
queue.add(runnable);
|
||||
count++;
|
||||
}
|
||||
|
||||
public synchronized void execute(Launcher launcher) throws Exception {
|
||||
queue = Collections.unmodifiableList(queue);
|
||||
|
||||
try {
|
||||
for (InstallTask runnable : queue) {
|
||||
checkInterrupted();
|
||||
running = runnable;
|
||||
runnable.execute(launcher);
|
||||
finished++;
|
||||
}
|
||||
} finally {
|
||||
running = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,129 @@
|
||||
package com.skcraft.launcher.install;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.skcraft.launcher.Launcher;
|
||||
import com.skcraft.launcher.model.loader.InstallProcessor;
|
||||
import com.skcraft.launcher.model.loader.LoaderManifest;
|
||||
import com.skcraft.launcher.model.loader.LoaderSubResolver;
|
||||
import com.skcraft.launcher.model.loader.SidedData;
|
||||
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.Environment;
|
||||
import com.skcraft.launcher.util.FileUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.java.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import static com.skcraft.launcher.util.SharedLocale.tr;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Log
|
||||
public class ProcessorTask implements InstallTask {
|
||||
private final InstallProcessor processor;
|
||||
private final LoaderManifest loaderManifest;
|
||||
private final Manifest manifest;
|
||||
private final HashMap<String, DownloadableFile.LocalFile> localFiles;
|
||||
|
||||
private transient String message = "";
|
||||
private transient double progress = 0;
|
||||
|
||||
@Override
|
||||
public void execute(Launcher launcher) throws Exception {
|
||||
VersionManifest versionManifest = manifest.getVersionManifest();
|
||||
loaderManifest.getSidedData().put("MINECRAFT_JAR", SidedData.of(launcher.getJarPath(versionManifest).getAbsolutePath()));
|
||||
|
||||
LoaderSubResolver resolver = new LoaderSubResolver(manifest, loaderManifest,
|
||||
Environment.getInstance(), Side.CLIENT, launcher.getBaseDir(), localFiles);
|
||||
|
||||
message = "Resolving parameters";
|
||||
List<String> programArgs = processor.resolveArgs(resolver);
|
||||
Map<String, String> outputs = processor.resolveOutputs(resolver);
|
||||
|
||||
message = "Finding libraries";
|
||||
Library execFile = versionManifest.findLibrary(processor.getJar());
|
||||
File jar = launcher.getLibraryFile(execFile);
|
||||
|
||||
JarFile jarFile = new JarFile(jar);
|
||||
String mainClass = jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
|
||||
jarFile.close();
|
||||
|
||||
if (mainClass == null || mainClass.isEmpty()) {
|
||||
throw new RuntimeException(String.format("Processor jar file '%s' has no main class!", processor.getJar()));
|
||||
}
|
||||
|
||||
List<URL> classpath = Lists.newArrayList(jar.toURI().toURL());
|
||||
int i = 0;
|
||||
int total = processor.getClasspath().size();
|
||||
for (String libraryName : processor.getClasspath()) {
|
||||
message = "Adding library " + libraryName;
|
||||
File libraryFile = launcher.getLibraryFile(versionManifest.findLibrary(libraryName));
|
||||
if (!libraryFile.exists()) {
|
||||
throw new RuntimeException(String.format("Missing library '%s' for processor '%s'",
|
||||
libraryName, processor.getJar()));
|
||||
}
|
||||
|
||||
classpath.add(libraryFile.toURI().toURL());
|
||||
i++;
|
||||
progress = (double) i / total;
|
||||
}
|
||||
|
||||
progress = 0.0;
|
||||
message = "Executing";
|
||||
|
||||
log.info(String.format("Running processor '%s' with %d args", processor.getJar(), programArgs.size()));
|
||||
|
||||
ClassLoader cl = new URLClassLoader(classpath.toArray(new URL[0]), null);
|
||||
try {
|
||||
Class<?> mainClazz = Class.forName(mainClass, true, cl);
|
||||
Method main = mainClazz.getDeclaredMethod("main", String[].class);
|
||||
main.invoke(null, (Object) programArgs.toArray(new String[0]));
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
message = "Verifying";
|
||||
progress = 1.0;
|
||||
|
||||
if (!outputs.isEmpty()) {
|
||||
progress = 0.0;
|
||||
i = 0;
|
||||
total = outputs.size();
|
||||
for (Map.Entry<String, String> output : outputs.entrySet()) {
|
||||
File artifact = new File(output.getKey());
|
||||
|
||||
if (!artifact.exists()) {
|
||||
throw new RuntimeException(String.format("Artifact '%s' missing", output.getKey()));
|
||||
}
|
||||
|
||||
if (!FileUtils.getShaHash(artifact).equals(output.getValue())) {
|
||||
throw new RuntimeException(String.format("Artifact '%s' has invalid hash!", output.getKey()));
|
||||
}
|
||||
|
||||
i++;
|
||||
progress = (double) i / total;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatus() {
|
||||
return tr("installer.runningProcessor", processor.getJar(), message);
|
||||
}
|
||||
}
|
@ -371,7 +371,7 @@ public class Runner implements Callable<Process>, ProgressObservable {
|
||||
map.put("game_directory", instance.getContentDir().getAbsolutePath());
|
||||
map.put("game_assets", virtualAssetsDir.getAbsolutePath());
|
||||
map.put("assets_root", launcher.getAssets().getDir().getAbsolutePath());
|
||||
map.put("assets_index_name", versionManifest.getAssetsIndex());
|
||||
map.put("assets_index_name", versionManifest.getAssetId());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
package com.skcraft.launcher.model.loader;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class InstallProcessor {
|
||||
private String jar;
|
||||
private List<String> classpath;
|
||||
private List<String> args;
|
||||
private Map<String, String> outputs;
|
||||
|
||||
public List<String> resolveArgs(final LoaderSubResolver resolver) {
|
||||
return Lists.transform(getArgs(), new Function<String, String>() {
|
||||
@Override
|
||||
public String apply(String input) {
|
||||
return resolver.transform(input);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Map<String, String> resolveOutputs(final LoaderSubResolver resolver) {
|
||||
if (getOutputs() == null) return Collections.emptyMap();
|
||||
|
||||
HashMap<String, String> result = new HashMap<String, String>();
|
||||
|
||||
for (Map.Entry<String, String> entry : getOutputs().entrySet()) {
|
||||
result.put(resolver.transform(entry.getKey()), resolver.transform(entry.getValue()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -7,15 +7,27 @@
|
||||
package com.skcraft.launcher.model.loader;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.skcraft.launcher.model.minecraft.Library;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class InstallProfile {
|
||||
private List<Library> libraries;
|
||||
private List<InstallProcessor> processors;
|
||||
private Map<String, SidedData> data;
|
||||
|
||||
@JsonProperty("install")
|
||||
private InstallData installData;
|
||||
private VersionInfo versionInfo;
|
||||
|
||||
public List<ProcessorEntry> toProcessorEntries(final String loaderName) {
|
||||
return Lists.transform(getProcessors(), new Function<InstallProcessor, ProcessorEntry>() {
|
||||
@Override
|
||||
public ProcessorEntry apply(InstallProcessor input) {
|
||||
return new ProcessorEntry(loaderName, input);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
package com.skcraft.launcher.model.loader;
|
||||
|
||||
import com.skcraft.launcher.model.modpack.DownloadableFile;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class LoaderManifest {
|
||||
private Map<String, SidedData> sidedData;
|
||||
private List<DownloadableFile> downloadableFiles;
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package com.skcraft.launcher.model.loader;
|
||||
|
||||
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.Environment;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class LoaderSubResolver {
|
||||
private final Manifest manifest;
|
||||
private final LoaderManifest loader;
|
||||
private final Environment env;
|
||||
private final Side side;
|
||||
private final File baseDir;
|
||||
private final HashMap<String, DownloadableFile.LocalFile> localFiles;
|
||||
|
||||
public String getPathOf(String... rest) {
|
||||
File file = baseDir;
|
||||
for (String part : rest) {
|
||||
file = new File(file, part);
|
||||
}
|
||||
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
public String transform(String arg) {
|
||||
VersionManifest version = manifest.getVersionManifest();
|
||||
|
||||
while (true) {
|
||||
char start = arg.charAt(0);
|
||||
int bound = arg.length() - 1;
|
||||
char end = arg.charAt(bound);
|
||||
|
||||
if (start == '{' && end == '}') {
|
||||
SidedData sidedData = loader.getSidedData().get(arg.substring(1, bound));
|
||||
if (sidedData != null) {
|
||||
arg = sidedData.resolveFor(side);
|
||||
}
|
||||
} else if (start == '[' && end == ']') {
|
||||
String libraryName = arg.substring(1, bound);
|
||||
Library library = version.findLibrary(libraryName);
|
||||
if (library != null) {
|
||||
arg = getPathOf(manifest.getLibrariesLocation(), library.getPath(env));
|
||||
} else {
|
||||
arg = getPathOf(manifest.getLibrariesLocation(), Library.mavenNameToPath(libraryName));
|
||||
}
|
||||
} else if (start == '&' && end == '&') {
|
||||
String localFileName = arg.substring(1, bound);
|
||||
|
||||
if (localFiles.containsKey(localFileName)) {
|
||||
arg = localFiles.get(localFileName).getLocation().getAbsolutePath();
|
||||
} else {
|
||||
arg = localFileName;
|
||||
}
|
||||
} else if (start == '\'' && end == '\'') {
|
||||
arg = arg.substring(1, bound);
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.skcraft.launcher.model.loader;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.skcraft.launcher.install.InstallLog;
|
||||
import com.skcraft.launcher.install.Installer;
|
||||
import com.skcraft.launcher.install.ProcessorTask;
|
||||
import com.skcraft.launcher.install.UpdateCache;
|
||||
import com.skcraft.launcher.model.modpack.DownloadableFile;
|
||||
import com.skcraft.launcher.model.modpack.ManifestEntry;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ProcessorEntry extends ManifestEntry {
|
||||
private String loaderName;
|
||||
private InstallProcessor processor;
|
||||
|
||||
@Override
|
||||
public void install(Installer installer, InstallLog log, UpdateCache cache, File contentDir) throws Exception {
|
||||
LoaderManifest loaderManifest = getManifest().getLoaders().get(loaderName);
|
||||
|
||||
HashMap<String, DownloadableFile.LocalFile> localFilesMap = Maps.newHashMap();
|
||||
for (DownloadableFile downloadableFile : loaderManifest.getDownloadableFiles()) {
|
||||
DownloadableFile.LocalFile localFile = downloadableFile.download(installer, getManifest());
|
||||
|
||||
localFilesMap.put(localFile.getName(), localFile);
|
||||
}
|
||||
|
||||
installer.queueLate(new ProcessorTask(processor, loaderManifest, getManifest(), localFilesMap));
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.skcraft.launcher.model.loader;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.skcraft.launcher.model.minecraft.Side;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor(staticName = "create")
|
||||
@NoArgsConstructor
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class SidedData {
|
||||
private String client;
|
||||
private String server;
|
||||
|
||||
public String resolveFor(Side side) {
|
||||
switch (side) {
|
||||
case CLIENT: return client;
|
||||
case SERVER: return server;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static SidedData of(String singleValue) {
|
||||
return new SidedData(singleValue, singleValue);
|
||||
}
|
||||
}
|
@ -7,7 +7,9 @@
|
||||
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 com.skcraft.launcher.model.minecraft.MinecraftArguments;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
@ -15,8 +17,9 @@ import java.util.List;
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class VersionInfo {
|
||||
|
||||
private String minecraftArguments;
|
||||
private String id;
|
||||
@JsonProperty("arguments")
|
||||
private MinecraftArguments minecraftArguments;
|
||||
private String mainClass;
|
||||
private List<Library> libraries;
|
||||
|
||||
|
@ -6,9 +6,14 @@
|
||||
|
||||
package com.skcraft.launcher.model.minecraft;
|
||||
|
||||
import com.fasterxml.jackson.annotation.*;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.skcraft.launcher.util.Environment;
|
||||
import com.skcraft.launcher.util.Platform;
|
||||
import lombok.Data;
|
||||
@ -22,11 +27,7 @@ import java.util.regex.Pattern;
|
||||
public class Library {
|
||||
|
||||
private String name;
|
||||
private transient String group;
|
||||
private transient String artifact;
|
||||
private transient String version;
|
||||
@JsonProperty("url")
|
||||
private String baseUrl;
|
||||
private Downloads downloads;
|
||||
private Map<String, String> natives;
|
||||
private Extract extract;
|
||||
private List<Rule> rules;
|
||||
@ -37,21 +38,6 @@ public class Library {
|
||||
// Custom
|
||||
private boolean locallyAvailable;
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
|
||||
if (name != null) {
|
||||
String[] parts = name.split(":");
|
||||
this.group = parts[0];
|
||||
this.artifact = parts[1];
|
||||
this.version = parts[2];
|
||||
} else {
|
||||
this.group = null;
|
||||
this.artifact = null;
|
||||
this.version = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matches(Environment environment) {
|
||||
boolean allow = false;
|
||||
|
||||
@ -68,21 +54,6 @@ public class Library {
|
||||
return allow;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getArtifact() {
|
||||
return artifact;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getNativeString(Platform platform) {
|
||||
if (getNatives() != null) {
|
||||
switch (platform) {
|
||||
@ -100,28 +71,18 @@ public class Library {
|
||||
}
|
||||
}
|
||||
|
||||
public String getFilename(Environment environment) {
|
||||
public Artifact getArtifact(Environment environment) {
|
||||
String nativeString = getNativeString(environment.getPlatform());
|
||||
if (nativeString != null) {
|
||||
return String.format("%s-%s-%s.jar",
|
||||
getArtifact(), getVersion(), nativeString);
|
||||
}
|
||||
|
||||
return String.format("%s-%s.jar", getArtifact(), getVersion());
|
||||
if (nativeString != null) {
|
||||
return getDownloads().getClassifiers().get(nativeString);
|
||||
} else {
|
||||
return getDownloads().getArtifact();
|
||||
}
|
||||
}
|
||||
|
||||
public String getPath(Environment environment) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(getGroup().replace('.', '/'));
|
||||
builder.append("/");
|
||||
builder.append(getArtifact());
|
||||
builder.append("/");
|
||||
builder.append(getVersion());
|
||||
builder.append("/");
|
||||
builder.append(getFilename(environment));
|
||||
String path = builder.toString();
|
||||
path = path.replace("${arch}", environment.getArchBits());
|
||||
return path;
|
||||
return getArtifact(environment).getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -179,6 +140,21 @@ public class Library {
|
||||
private List<String> exclude;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class Artifact {
|
||||
private String path;
|
||||
private String url;
|
||||
private String sha1;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class Downloads {
|
||||
private Artifact artifact;
|
||||
private Map<String, Artifact> classifiers;
|
||||
}
|
||||
|
||||
private enum Action {
|
||||
ALLOW,
|
||||
DISALLOW;
|
||||
@ -194,4 +170,32 @@ public class Library {
|
||||
}
|
||||
}
|
||||
|
||||
public static String mavenNameToPath(String mavenName) {
|
||||
List<String> split = Splitter.on(':').splitToList(mavenName);
|
||||
int size = split.size();
|
||||
|
||||
String group = split.get(0);
|
||||
String name = split.get(1);
|
||||
String version = split.get(2);
|
||||
String extension = "jar";
|
||||
|
||||
String fileName = name + "-" + version;
|
||||
|
||||
if (size > 3) {
|
||||
String classifier = split.get(3);
|
||||
|
||||
if (classifier.indexOf("@") != -1) {
|
||||
List<String> parts = Splitter.on('@').splitToList(classifier);
|
||||
|
||||
classifier = parts.get(0);
|
||||
extension = parts.get(1);
|
||||
}
|
||||
|
||||
fileName += "-" + classifier;
|
||||
}
|
||||
|
||||
fileName += "." + extension;
|
||||
|
||||
return Joiner.on('/').join(group.replace('.', '/'), name, version, fileName);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
package com.skcraft.launcher.model.minecraft;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class MinecraftArguments {
|
||||
@JsonProperty("game")
|
||||
private List<String> gameArguments;
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
package com.skcraft.launcher.model.minecraft;
|
||||
|
||||
public enum Side {
|
||||
CLIENT("client"),
|
||||
SERVER("server");
|
||||
|
||||
private String name;
|
||||
|
||||
Side(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
@ -20,6 +20,11 @@ public class Version {
|
||||
@NonNull
|
||||
private String id;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NonNull
|
||||
private String url;
|
||||
|
||||
public Version() {
|
||||
}
|
||||
|
||||
|
@ -6,12 +6,14 @@
|
||||
|
||||
package com.skcraft.launcher.model.minecraft;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@ -21,16 +23,45 @@ public class VersionManifest {
|
||||
private Date time;
|
||||
private Date releaseTime;
|
||||
private String assets;
|
||||
private AssetIndex assetIndex;
|
||||
private String type;
|
||||
private String processArguments;
|
||||
private String minecraftArguments;
|
||||
private String mainClass;
|
||||
private int minimumLauncherVersion;
|
||||
private LinkedHashSet<Library> libraries;
|
||||
private Map<String, Artifact> downloads = new HashMap<String, Artifact>();
|
||||
|
||||
@JsonIgnore
|
||||
public String getAssetsIndex() {
|
||||
return getAssets() != null ? getAssets() : "legacy";
|
||||
public String getAssetId() {
|
||||
return getAssetIndex() != null
|
||||
? getAssetIndex().getId()
|
||||
: "legacy";
|
||||
}
|
||||
|
||||
public Library findLibrary(String name) {
|
||||
for (Library library : getLibraries()) {
|
||||
if (library.getName().equals(name)) {
|
||||
return library;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class Artifact {
|
||||
private String url;
|
||||
private int size;
|
||||
|
||||
@JsonProperty("sha1")
|
||||
private String hash;
|
||||
}
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class AssetIndex {
|
||||
private String id;
|
||||
private String url;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
package com.skcraft.launcher.model.modpack;
|
||||
|
||||
import com.skcraft.launcher.install.Installer;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import static com.skcraft.launcher.LauncherUtils.concat;
|
||||
|
||||
@Data
|
||||
public class DownloadableFile {
|
||||
private String name;
|
||||
private String hash;
|
||||
private String location;
|
||||
private int size;
|
||||
|
||||
public LocalFile download(@NonNull Installer installer, Manifest manifest) throws MalformedURLException {
|
||||
URL url = concat(manifest.getObjectsUrl(), getLocation());
|
||||
|
||||
File local = installer.getDownloader().download(url, hash, size, name);
|
||||
return new LocalFile(local, name);
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class LocalFile {
|
||||
private final File location;
|
||||
private final String name;
|
||||
}
|
||||
}
|
@ -12,8 +12,9 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.base.Strings;
|
||||
import com.skcraft.launcher.Instance;
|
||||
import com.skcraft.launcher.LauncherUtils;
|
||||
import com.skcraft.launcher.model.minecraft.VersionManifest;
|
||||
import com.skcraft.launcher.install.Installer;
|
||||
import com.skcraft.launcher.model.loader.LoaderManifest;
|
||||
import com.skcraft.launcher.model.minecraft.VersionManifest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
@ -22,7 +23,9 @@ import lombok.Setter;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ -43,6 +46,7 @@ public class Manifest extends BaseManifest {
|
||||
@Getter @Setter @JsonIgnore
|
||||
private Installer installer;
|
||||
private VersionManifest versionManifest;
|
||||
private Map<String, LoaderManifest> loaders = new HashMap<String, LoaderManifest>();
|
||||
|
||||
@JsonIgnore
|
||||
public URL getLibrariesUrl() {
|
||||
|
@ -12,6 +12,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.skcraft.launcher.install.InstallLog;
|
||||
import com.skcraft.launcher.install.Installer;
|
||||
import com.skcraft.launcher.install.UpdateCache;
|
||||
import com.skcraft.launcher.model.loader.ProcessorEntry;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
@ -23,7 +24,8 @@ import java.io.File;
|
||||
property = "type",
|
||||
defaultImpl = FileInstall.class)
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value = FileInstall.class, name = "file")
|
||||
@JsonSubTypes.Type(value = FileInstall.class, name = "file"),
|
||||
@JsonSubTypes.Type(value = ProcessorEntry.class, name = "process")
|
||||
})
|
||||
@Data
|
||||
@ToString(exclude = "manifest")
|
||||
|
@ -48,7 +48,7 @@ public class SelfUpdater implements Callable<File>, ProgressObservable {
|
||||
installer.queue(new FileMover(tempFile, file));
|
||||
|
||||
progress = installer;
|
||||
installer.execute();
|
||||
installer.execute(launcher);
|
||||
|
||||
return file;
|
||||
} finally {
|
||||
|
@ -224,8 +224,8 @@ public abstract class BaseUpdater {
|
||||
|
||||
File tempFile = installer.getDownloader().download(urls, "", LIBRARY_SIZE_ESTIMATE,
|
||||
library.getName() + ".jar");
|
||||
installer.queue(new FileMover( tempFile, targetFile));
|
||||
log.info("Fetching " + path + " from " + urls);
|
||||
installer.queue(new FileMover( tempFile, targetFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ public class Updater extends BaseUpdater implements Callable<Instance>, Progress
|
||||
|
||||
// Install the .jar
|
||||
File jarPath = launcher.getJarPath(version);
|
||||
URL jarSource = launcher.propUrl("jarUrl", version.getId());
|
||||
URL jarSource = url(version.getDownloads().get("client").getUrl());
|
||||
log.info("JAR at " + jarPath.getAbsolutePath() + ", fetched from " + jarSource);
|
||||
installJar(installer, jarPath, jarSource);
|
||||
|
||||
@ -162,7 +162,7 @@ public class Updater extends BaseUpdater implements Callable<Instance>, Progress
|
||||
URL url = manifest.getLibrariesUrl();
|
||||
if (url != null) {
|
||||
log.info("Added library source: " + url);
|
||||
librarySources.add(url);
|
||||
librarySources.add(0, url);
|
||||
}
|
||||
|
||||
progress = new DefaultProgress(-1, SharedLocale.tr("instanceUpdater.collectingLibraries"));
|
||||
@ -171,7 +171,7 @@ public class Updater extends BaseUpdater implements Callable<Instance>, Progress
|
||||
// Download assets
|
||||
log.info("Enumerating assets to download...");
|
||||
progress = new DefaultProgress(-1, SharedLocale.tr("instanceUpdater.collectingAssets"));
|
||||
installAssets(installer, version, launcher.propUrl("assetsIndexUrl", version.getAssetsIndex()), assetsSources);
|
||||
installAssets(installer, version, url(version.getAssetIndex().getUrl()), assetsSources);
|
||||
|
||||
log.info("Executing download phase...");
|
||||
progress = ProgressFilter.between(installer.getDownloader(), 0, 0.98);
|
||||
@ -179,7 +179,9 @@ public class Updater extends BaseUpdater implements Callable<Instance>, Progress
|
||||
|
||||
log.info("Executing install phase...");
|
||||
progress = ProgressFilter.between(installer, 0.98, 1);
|
||||
installer.execute();
|
||||
installer.execute(launcher);
|
||||
|
||||
installer.executeLate(launcher);
|
||||
|
||||
log.info("Completing...");
|
||||
complete();
|
||||
|
@ -0,0 +1,43 @@
|
||||
package com.skcraft.launcher.util;
|
||||
|
||||
import com.google.common.hash.HashFunction;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.Files;
|
||||
import com.skcraft.launcher.model.modpack.DownloadableFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class FileUtils {
|
||||
public static DownloadableFile saveStreamToObjectsDir(InputStream stream, File outputDir) throws IOException {
|
||||
byte[] input = ByteStreams.toByteArray(stream);
|
||||
HashFunction hf = Hashing.sha1();
|
||||
|
||||
String fileHash = hf.hashBytes(input).toString();
|
||||
String filePath = fileHash.substring(0, 2) + "/" + fileHash.substring(2, 4) + "/" + fileHash;
|
||||
|
||||
File dest = new File(outputDir, filePath);
|
||||
dest.getParentFile().mkdirs();
|
||||
|
||||
Files.write(input, dest);
|
||||
|
||||
DownloadableFile entry = new DownloadableFile();
|
||||
entry.setLocation(filePath);
|
||||
entry.setHash(fileHash);
|
||||
entry.setSize(input.length);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
public static String getShaHash(File file) throws IOException {
|
||||
FileInputStream stream = new FileInputStream(file);
|
||||
byte[] input = ByteStreams.toByteArray(stream);
|
||||
String res = Hashing.sha1().hashBytes(input).toString();
|
||||
|
||||
stream.close();
|
||||
return res;
|
||||
}
|
||||
}
|
@ -146,6 +146,7 @@ installer.installing=Installing...
|
||||
installer.executing=Executing tasks... ({0} remaining)
|
||||
installer.copyingFile=Copying from {0} to {1}
|
||||
installer.movingFile=Moving {0} to {1}
|
||||
installer.runningProcessor=Running processor {0}: {1}
|
||||
|
||||
updater.updating=Updating launcher...
|
||||
updater.updateRequiredButOffline=An update is required but you need to be in online mode.
|
||||
|
@ -8,7 +8,7 @@ version=${project.version}
|
||||
agentName=Minecraft
|
||||
offlinePlayerName=Player
|
||||
|
||||
versionManifestUrl=https://s3.amazonaws.com/Minecraft.Download/versions/%1$s/%1$s.json
|
||||
versionManifestUrl=https://launchermeta.mojang.com/mc/game/version_manifest.json
|
||||
librariesSource=https://libraries.minecraft.net/
|
||||
jarUrl=https://s3.amazonaws.com/Minecraft.Download/versions/%1$s/%1$s.jar
|
||||
assetsIndexUrl=https://s3.amazonaws.com/Minecraft.Download/indexes/%s.json
|
||||
|
@ -1,5 +1,6 @@
|
||||
[
|
||||
"https://libraries.minecraft.net/",
|
||||
"https://repo1.maven.org/maven2/",
|
||||
"https://files.minecraftforge.net/maven/",
|
||||
"http://maven.apache.org/"
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user