Compare commits

..
8 Commits
32 changed files with 305 additions and 98 deletions

No files matched your search

@@ -62,7 +62,7 @@ public final class LauncherHelper {
if (account == null)
throw new IllegalStateException("No account");
Version version = repository.getVersion(selectedVersion);
Version version = repository.getResolvedVersion(selectedVersion);
VersionSetting setting = profile.getVersionSetting(selectedVersion);
Platform.runLater(() -> {
@@ -530,8 +530,8 @@ public class Settings {
}
private void onProfileChanged() {
getSelectedProfile().getRepository().refreshVersionsAsync().start();
EventBus.EVENT_BUS.fireEvent(new ProfileChangedEvent(SETTINGS, getSelectedProfile()));
getSelectedProfile().getRepository().refreshVersionsAsync().subscribe(() ->
EventBus.EVENT_BUS.fireEvent(new ProfileChangedEvent(SETTINGS, getSelectedProfile())));
}
private void profileNameChanged(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
@@ -80,8 +80,6 @@ public final class MainPage extends StackPane implements DecoratorPage {
FXUtils.loadFXML(this, "/assets/fxml/main.fxml");
EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(() -> Platform.runLater(this::loadVersions));
EventBus.EVENT_BUS.channel(ProfileLoadingEvent.class).register(this::onProfilesLoading);
EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(this::onProfileChanged);
versionPopup = new JFXPopup(versionList);
getChildren().remove(versionList);
@@ -178,14 +176,6 @@ public final class MainPage extends StackPane implements DecoratorPage {
return item;
}
public void onProfilesLoading() {
// TODO: Profiles
}
public void onProfileChanged(ProfileChangedEvent event) {
Platform.runLater(() -> loadVersions(event.getProfile()));
}
private void loadVersions() {
loadVersions(Settings.INSTANCE.getSelectedProfile());
}
@@ -193,8 +183,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
private void loadVersions(Profile profile) {
this.profile = profile;
List<Node> children = new LinkedList<>();
List<Version> versions = new LinkedList<>(profile.getRepository().getVersions());
for (Version version : versions) {
for (Version version : profile.getRepository().getVersions()) {
children.add(buildNode(profile, version.getId(), GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version.getId())).orElse("Unknown")));
}
FXUtils.resetChildren(masonryPane, children);
@@ -18,6 +18,8 @@
package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.*;
import com.jfoenix.effects.JFXDepthManager;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
@@ -41,7 +43,7 @@ import org.jackhuang.hmcl.util.Lang;
import java.net.Proxy;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
public final class SettingsPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("settings.launcher"));
@@ -77,7 +79,7 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
@FXML
private MultiFileItem backgroundItem;
@FXML
private MultiColorItem themeItem;
private StackPane themeColorPickerContainer;
@FXML
private JFXRadioButton chkNoProxy;
@FXML
@@ -193,38 +195,17 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
Settings.INSTANCE.setBackgroundImageType((EnumBackgroundImage) newValue.getUserData()));
// theme
themeItem.loadChildren(Arrays.asList(
themeItem.createChildren(Main.i18n("color.blue"), Theme.BLUE),
themeItem.createChildren(Main.i18n("color.dark_blue"), Theme.DARK_BLUE),
themeItem.createChildren(Main.i18n("color.green"), Theme.GREEN),
themeItem.createChildren(Main.i18n("color.orange"), Theme.ORANGE),
themeItem.createChildren(Main.i18n("color.purple"), Theme.PURPLE),
themeItem.createChildren(Main.i18n("color.red"), Theme.RED)
));
if (Settings.INSTANCE.getTheme().isCustom())
themeItem.setColor(Color.web(Settings.INSTANCE.getTheme().getColor()));
themeItem.setToggleSelectedListener(newValue -> {
if (newValue.getUserData() != null) {
Settings.INSTANCE.setTheme((Theme) newValue.getUserData());
themeItem.setOnColorPickerChanged(null);
} else {
themeItem.setOnColorPickerChanged(color ->
Settings.INSTANCE.setTheme(Theme.custom(Theme.getColorDisplayName(color))));
}
});
themeItem.getGroup().getToggles().stream().filter(it -> Settings.INSTANCE.getTheme() == it.getUserData() || Settings.INSTANCE.getTheme().isCustom() && themeItem.isCustomToggle(it)).findFirst().ifPresent(it -> it.setSelected(true));
Settings.INSTANCE.themeProperty().setChangedListenerAndOperate(it -> {
if (it.isCustom())
themeItem.setSubtitle(it.getName());
else
themeItem.setSubtitle(Main.i18n("color." + it.getName().toLowerCase()));
Controllers.getScene().getStylesheets().setAll(it.getStylesheets());
JFXColorPicker picker = new JFXColorPicker(Color.web(Settings.INSTANCE.getTheme().getColor()), null);
picker.setCustomColorText(Main.i18n("color.custom"));
picker.setRecentColorsText(Main.i18n("color.recent"));
picker.getCustomColors().setAll(Arrays.stream(Theme.VALUES).map(Theme::getColor).map(Color::web).collect(Collectors.toList()));
picker.setOnAction(e -> {
Theme theme = Theme.custom(Theme.getColorDisplayName(picker.getValue()));
Settings.INSTANCE.setTheme(theme);
Controllers.getScene().getStylesheets().setAll(theme.getStylesheets());
});
themeColorPickerContainer.getChildren().setAll(picker);
Platform.runLater(() -> JFXDepthManager.setDepth(picker, 0));
}
private void initBackgroundItemSubtitle() {
@@ -31,10 +31,7 @@ import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask;
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.game.HMCLModpackExportTask;
import org.jackhuang.hmcl.game.HMCLModpackInstallTask;
import org.jackhuang.hmcl.mod.CurseCompletionTask;
import org.jackhuang.hmcl.mod.CurseInstallTask;
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
import org.jackhuang.hmcl.mod.MultiMCModpackInstallTask;
import org.jackhuang.hmcl.mod.*;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.task.TaskListener;
@@ -60,7 +57,7 @@ public final class TaskListPane extends StackPane {
}
@Override
public void onReady(Task task) {
public void onRunning(Task task) {
if (!task.getSignificance().shouldShow())
return;
@@ -76,6 +73,8 @@ public final class TaskListPane extends StackPane {
task.setName(Main.i18n("install.installer.install", Main.i18n("install.installer.optifine")));
} else if (task instanceof CurseCompletionTask) {
task.setName(Main.i18n("modpack.type.curse.completion"));
} else if (task instanceof ModpackInstallTask) {
task.setName(Main.i18n("modpack.installing"));
} else if (task instanceof CurseInstallTask) {
task.setName(Main.i18n("modpack.install", Main.i18n("modpack.type.curse")));
} else if (task instanceof MultiMCModpackInstallTask) {
@@ -108,7 +108,14 @@
</VBox>
</ComponentList>
<MultiColorItem fx:id="themeItem" title="%settings.launcher.theme" hasSubtitle="true" />
<BorderPane>
<left>
<Label text="%settings.launcher.theme" BorderPane.alignment="CENTER_LEFT" />
</left>
<right>
<StackPane fx:id="themeColorPickerContainer" minHeight="30" />
</right>
</BorderPane>
<VBox spacing="5">
<BorderPane><left><Label text="%settings.launcher.log_font" BorderPane.alignment="CENTER_LEFT" /></left><right><HBox spacing="3"><FontComboBox fontSize="12" enableStyle="false" fx:id="cboFont" /><JFXTextField fx:id="txtFontSize" maxWidth="50" minWidth="50" /></HBox></right></BorderPane>
@@ -89,6 +89,8 @@ color.green=Green
color.orange=Orange
color.purple=Purple
color.red=Red
color.recent=Recommended
color.custom=Custom Color
crash.NoClassDefFound=Please check "HMCL" software is complete.
crash.advice.ClassNotFoundException=Minecraft or mods are incomplete. Retry if there are some libraries that have not downloaded or update your game and mods! Or you can try Game Settings -> Manage (Version) -> Delete library files to solve the problem.
@@ -89,6 +89,8 @@ color.green=绿色
color.orange=橙色
color.purple=紫色
color.red=红色
color.recent=推荐
color.custom=自定义颜色
crash.NoClassDefFound=请确认HMCL本体是否完整
crash.advice.ClassNotFoundException=Minecraft不完整或Mod冲突,如果有未能下载的文件请下载成功后重试或是客户端损坏请重试请重新制作客户端或下载整合包解决问题,另可尝试游戏设置->(版本)管理->删除库文件解决问题
@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.download.game.*;
import org.jackhuang.hmcl.game.SimpleVersionProvider;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
import org.jackhuang.hmcl.task.Task;
@@ -55,7 +55,7 @@ public final class GameAssetDownloadTask extends Task {
*/
public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version.requireResolved();
this.version = version;
this.refreshTask = new GameAssetRefreshTask(dependencyManager, version);
this.dependents.add(refreshTask);
}
@@ -49,7 +49,7 @@ public final class GameAssetIndexDownloadTask extends Task {
*/
public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version.requireResolved();
this.version = version;
setSignificance(TaskSignificance.MODERATE);
}
@@ -54,7 +54,7 @@ public final class GameAssetRefreshTask extends TaskResult<Collection<Pair<File,
*/
public GameAssetRefreshTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version.requireResolved();
this.version = version;
this.assetIndexInfo = version.getAssetIndex();
this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId());
@@ -48,7 +48,7 @@ public final class GameLibrariesTask extends Task {
*/
public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version.requireResolved();
this.version = version;
setSignificance(TaskSignificance.MODERATE);
}
@@ -62,9 +62,7 @@ public final class GameLibrariesTask extends Task {
version.getLibraries().stream().filter(Library::appliesToCurrentEnvironment).forEach(library -> {
File file = dependencyManager.getGameRepository().getLibraryFile(version, library);
if (!file.exists())
dependencies.add(new FileDownloadTask(
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(library.getDownload().getUrl())),
file, dependencyManager.getProxy(), library.getDownload().getSha1()));
dependencies.add(new LibraryDownloadTask(dependencyManager, file, library));
});
}
@@ -49,7 +49,7 @@ public final class GameLoggingDownloadTask extends Task {
*/
public GameLoggingDownloadTask(DependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager;
this.version = version.requireResolved();
this.version = version;
setSignificance(TaskSignificance.MODERATE);
}
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.download.game;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.SimpleMultimap;
import org.jackhuang.hmcl.util.VersionNumber;
@@ -49,6 +50,7 @@ public class LibrariesUniqueTask extends TaskResult<Version> {
for (Library library : libraries) {
String id = library.getGroupId() + ":" + library.getArtifactId();
VersionNumber number = VersionNumber.asVersion(library.getVersion());
String serialized = Constants.GSON.toJson(library);
if (versionMap.containsKey(id)) {
VersionNumber otherNumber = versionMap.get(id);
@@ -56,8 +58,23 @@ public class LibrariesUniqueTask extends TaskResult<Version> {
multimap.removeKey(id);
versionMap.put(id, number);
multimap.put(id, library);
} else if (number.compareTo(otherNumber) == 0) {
multimap.put(id, library);
} else if (number.compareTo(otherNumber) == 0) { // same library id.
boolean flag = false;
// prevent from duplicated libraries
for (Library otherLibrary : multimap.get(id))
if (library.equals(otherLibrary)) {
String otherSerialized = Constants.GSON.toJson(otherLibrary);
// A trick, the library that has more information is better, which can be
// considered whose serialized JSON text will be longer.
if (serialized.length() <= otherSerialized.length()) {
flag = true;
break;
} else {
multimap.removeValue(id, otherLibrary);
}
}
if (!flag)
multimap.put(id, library);
}
} else {
versionMap.put(id, number);
@@ -0,0 +1,168 @@
package org.jackhuang.hmcl.download.game;
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
import org.jackhuang.hmcl.download.AbstractDependencyManager;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Scheduler;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.*;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;
public final class LibraryDownloadTask extends Task {
private final FileDownloadTask xzTask;
private final FileDownloadTask task;
private final File jar;
private final File xzFile;
private final Library library;
private boolean downloaded = false;
public LibraryDownloadTask(AbstractDependencyManager dependencyManager, File file, Library library) {
if (library.is("net.minecraftforge", "forge"))
library = library.setClassifier("universal");
this.library = library;
String url = dependencyManager.getDownloadProvider().injectURL(library.getDownload().getUrl());
jar = file;
xzFile = new File(file.getAbsoluteFile().getParentFile(), file.getName() + ".pack.xz");
xzTask = new FileDownloadTask(NetworkUtils.toURL(url + ".pack.xz"),
xzFile, dependencyManager.getProxy(), null, 1);
xzTask.setSignificance(TaskSignificance.MINOR);
setSignificance(TaskSignificance.MODERATE);
task = new FileDownloadTask(NetworkUtils.toURL(url),
file, dependencyManager.getProxy(), library.getDownload().getSha1());
}
@Override
public Collection<? extends Task> getDependents() {
return library.getChecksums() != null ? Collections.singleton(xzTask) : Collections.emptySet();
}
@Override
public boolean isRelyingOnDependents() {
return false;
}
@Override
public void execute() throws Exception {
if (isDependentsSucceeded() && library.getChecksums() != null) {
unpackLibrary(jar, FileUtils.readBytes(xzFile));
if (!checksumValid(jar, library.getChecksums()))
throw new IOException("Checksum failed for " + library);
downloaded = true;
}
}
@Override
public Collection<? extends Task> getDependencies() {
return downloaded ? Collections.emptySet() : Collections.singleton(task);
}
private static boolean checksumValid(File libPath, List<String> checksums) {
try {
if ((checksums == null) || (checksums.isEmpty())) {
return true;
}
byte[] fileData = FileUtils.readBytes(libPath);
boolean valid = checksums.contains(DigestUtils.sha1Hex(fileData));
if ((!valid) && (libPath.getName().endsWith(".jar"))) {
}
return validateJar(fileData, checksums);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
private static boolean validateJar(byte[] data, List<String> checksums) throws IOException {
HashMap<String, String> files = new HashMap<>();
String[] hashes = null;
JarInputStream jar = new JarInputStream(new ByteArrayInputStream(data));
JarEntry entry = jar.getNextJarEntry();
while (entry != null) {
byte[] eData = IOUtils.readFullyAsByteArray(jar);
if (entry.getName().equals("checksums.sha1")) {
hashes = new String(eData, Charset.forName("UTF-8")).split("\n");
}
if (!entry.isDirectory()) {
files.put(entry.getName(), DigestUtils.sha1Hex(eData));
}
entry = jar.getNextJarEntry();
}
jar.close();
if (hashes != null) {
boolean passed = !checksums.contains(files.get("checksums.sha1"));
if (passed) {
for (String hash : hashes) {
if ((!hash.trim().equals("")) && (hash.contains(" "))) {
String[] e = hash.split(" ");
String validChecksum = e[0];
String target = hash.substring(validChecksum.length() + 1);
String checksum = files.get(target);
if ((!files.containsKey(target)) || (checksum == null)) {
Logging.LOG.warning(" " + target + " : missing");
passed = false;
break;
} else if (!checksum.equals(validChecksum)) {
Logging.LOG.warning(" " + target + " : failed (" + checksum + ", " + validChecksum + ")");
passed = false;
break;
}
}
}
}
return passed;
}
return false;
}
private static void unpackLibrary(File dest, byte[] src) throws IOException {
if (dest.exists())
if (!dest.delete())
throw new IOException("Unable to delete file " + dest);
byte[] decompressed = IOUtils.readFullyAsByteArray(new XZCompressorInputStream(new ByteArrayInputStream(src)));
String end = new String(decompressed, decompressed.length - 4, 4);
if (!end.equals("SIGN"))
throw new IOException("Unpacking failed, signature missing " + end);
int x = decompressed.length;
int len = decompressed[(x - 8)] & 0xFF | (decompressed[(x - 7)] & 0xFF) << 8 | (decompressed[(x - 6)] & 0xFF) << 16 | (decompressed[(x - 5)] & 0xFF) << 24;
File temp = FileUtils.createTempFile("minecraft", ".pack");
byte[] checksums = Arrays.copyOfRange(decompressed, decompressed.length - len - 8, decompressed.length - 8);
OutputStream out = new FileOutputStream(temp);
out.write(decompressed, 0, decompressed.length - len - 8);
out.close();
try (FileOutputStream jarBytes = new FileOutputStream(dest); JarOutputStream jos = new JarOutputStream(jarBytes)) {
Pack200.newUnpacker().unpack(temp, jos);
JarEntry checksumsFile = new JarEntry("checksums.sha1");
checksumsFile.setTime(0L);
jos.putNextEntry(checksumsFile);
jos.write(checksums);
jos.closeEntry();
}
temp.delete();
}
}
@@ -40,11 +40,11 @@ public final class VersionJsonSaveTask extends Task {
* Constructor.
*
* @param repository the game repository
* @param version the **resolved** version
* @param version the game version
*/
public VersionJsonSaveTask(DefaultGameRepository repository, Version version) {
this.repository = repository;
this.version = version.requireResolved();
this.version = version;
setSignificance(TaskSignificance.MODERATE);
}
@@ -40,7 +40,7 @@ public class ClassicVersion extends Version {
public ClassicLibrary(String name) {
super("", "", "", null, null,
new LibrariesDownloadInfo(new LibraryDownloadInfo("bin/" + name + ".jar"), null),
false, null, null, null);
false, null, null, null, null);
}
}
@@ -38,7 +38,7 @@ import java.util.logging.Level;
public class DefaultGameRepository implements GameRepository {
private File baseDirectory;
protected final Map<String, Version> versions = new TreeMap<>();
protected Map<String, Version> versions;
protected boolean loaded = false;
public DefaultGameRepository(File baseDirectory) {
@@ -143,6 +143,8 @@ public class DefaultGameRepository implements GameRepository {
}
public boolean removeVersionFromDisk(String id) {
if (!versions.containsKey(id))
return true;
File file = getVersionRoot(id);
if (!file.exists())
return true;
@@ -151,7 +153,7 @@ public class DefaultGameRepository implements GameRepository {
}
protected void refreshVersionsImpl() {
versions.clear();
Map<String, Version> versions = new TreeMap<>();
if (ClassicVersion.hasClassicVersion(getBaseDirectory())) {
Version version = new ClassicVersion();
@@ -172,7 +174,10 @@ public class DefaultGameRepository implements GameRepository {
if (!json.exists()) {
List<File> jsons = FileUtils.listFilesByExtension(dir, "json");
if (jsons.size() == 1)
jsons.get(0).renameTo(json);
if (!jsons.get(0).renameTo(json)) {
Logging.LOG.warning("Cannot rename json file " + jsons.get(0) + " to " + json + ", ignoring version " + id);
continue;
}
}
Version version;
@@ -216,6 +221,7 @@ public class DefaultGameRepository implements GameRepository {
}
}
this.versions = versions;
loaded = true;
}
@@ -48,6 +48,7 @@ public class Library implements Comparable<Library> {
private final boolean lateload;
private final Map<OperatingSystem, String> natives;
private final List<CompatibilityRule> rules;
private final List<String> checksums;
private final String path;
@@ -60,10 +61,10 @@ public class Library implements Comparable<Library> {
}
public Library(String groupId, String artifactId, String version, String classifier, String url, LibrariesDownloadInfo downloads, boolean lateload) {
this(groupId, artifactId, version, classifier, url, downloads, lateload, null, null, null);
this(groupId, artifactId, version, classifier, url, downloads, lateload, null, null, null, null);
}
public Library(String groupId, String artifactId, String version, String classifier, String url, LibrariesDownloadInfo downloads, boolean lateload, ExtractRules extract, Map<OperatingSystem, String> natives, List<CompatibilityRule> rules) {
public Library(String groupId, String artifactId, String version, String classifier, String url, LibrariesDownloadInfo downloads, boolean lateload, List<String> checksums, ExtractRules extract, Map<OperatingSystem, String> natives, List<CompatibilityRule> rules) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
@@ -80,6 +81,7 @@ public class Library implements Comparable<Library> {
this.lateload = lateload;
this.natives = natives;
this.rules = rules;
this.checksums = checksums;
LibraryDownloadInfo temp = null;
if (downloads != null)
@@ -141,6 +143,14 @@ public class Library implements Comparable<Library> {
return lateload;
}
public List<String> getChecksums() {
return checksums;
}
public boolean is(String groupId, String artifactId) {
return this.groupId.equals(groupId) && this.artifactId.equals(artifactId);
}
@Override
public String toString() {
return new ToStringBuilder(this).append("name", getName()).toString();
@@ -168,16 +178,20 @@ public class Library implements Comparable<Library> {
return Objects.hash(getName(), isNative());
}
public static Library fromName(String name) {
return fromName(name, null, null, null, null, null);
public Library setClassifier(String classifier) {
return new Library(groupId, artifactId, version, classifier, url, downloads, lateload, checksums, extract, natives, rules);
}
public static Library fromName(String name, String url, LibrariesDownloadInfo downloads, ExtractRules extract, Map<OperatingSystem, String> natives, List<CompatibilityRule> rules) {
public static Library fromName(String name) {
return fromName(name, null, null, null, null, null, null);
}
public static Library fromName(String name, String url, LibrariesDownloadInfo downloads, List<String> checksums, ExtractRules extract, Map<OperatingSystem, String> natives, List<CompatibilityRule> rules) {
String[] arr = name.split(":", 4);
if (arr.length != 3 && arr.length != 4)
throw new IllegalArgumentException("Library name is malformed. Correct example: group:artifact:version(:classifier).");
return new Library(arr[0].replace("\\", "/"), arr[1], arr[2], arr.length >= 4 ? arr[3] : null, url, downloads, false, extract, natives, rules);
return new Library(arr[0].replace("\\", "/"), arr[1], arr[2], arr.length >= 4 ? arr[3] : null, url, downloads, false, checksums, extract, natives, rules);
}
public static class Serializer implements JsonDeserializer<Library>, JsonSerializer<Library> {
@@ -198,6 +212,8 @@ public class Library implements Comparable<Library> {
jsonObject.get("name").getAsString(),
jsonObject.has("url") ? jsonObject.get("url").getAsString() : null,
context.deserialize(jsonObject.get("downloads"), LibrariesDownloadInfo.class),
context.deserialize(jsonObject.get("checksums"), new TypeToken<List<String>>() {
}.getType()),
context.deserialize(jsonObject.get("extract"), ExtractRules.class),
context.deserialize(jsonObject.get("natives"), new TypeToken<Map<OperatingSystem, String>>() {
}.getType()),
@@ -143,13 +143,6 @@ public class Version implements Comparable<Version>, Validation {
return false;
}
public Version requireResolved() {
if (!resolved)
throw new RuntimeException("Version not resolved");
return this;
}
/**
* Resolve given version
*/
@@ -220,7 +220,6 @@ public class FileDownloadTask extends Task {
} catch (IOException | IllegalStateException e) {
if (temp != null)
temp.delete();
Logging.LOG.log(Level.WARNING, "Unable to download file " + currentURL, e);
exception = e;
} finally {
closeFiles();
@@ -228,7 +227,7 @@ public class FileDownloadTask extends Task {
}
if (exception != null)
throw exception;
throw new IOException("Unable to download file " + currentURL, exception);
}
}
@@ -36,7 +36,10 @@ class SchedulerExecutorService extends Scheduler {
@Override
public Future<?> schedule(ExceptionalRunnable<?> block) {
return executorService.submit(block.toCallable());
if (executorService.isShutdown() || executorService.isTerminated())
return Schedulers.NONE.schedule(block);
else
return executorService.submit(block.toCallable());
}
}
@@ -17,6 +17,8 @@
*/
package org.jackhuang.hmcl.task;
import org.jackhuang.hmcl.util.Constants;
import java.util.concurrent.*;
/**
@@ -112,6 +114,8 @@ public final class Schedulers {
return newThread();
}
static final Scheduler NONE = new SchedulerImpl(Constants.emptyConsumer());
public static synchronized void shutdown() {
if (CACHED_EXECUTOR != null)
CACHED_EXECUTOR.shutdown();
@@ -133,6 +133,7 @@ public abstract class Task {
/**
* The collection of sub-tasks that should execute **after** this task running.
* Will not be executed if execution fails.
*/
public Collection<? extends Task> getDependencies() {
return Collections.emptySet();
@@ -350,6 +351,7 @@ public abstract class Task {
public enum TaskState {
READY,
RUNNING,
EXECUTED,
SUCCEEDED,
FAILED
}
@@ -131,7 +131,7 @@ public final class TaskExecutor {
return false;
}
task.setState(Task.TaskState.RUNNING);
task.setState(Task.TaskState.READY);
if (task.getSignificance().shouldLog())
Logging.LOG.log(Level.FINE, "Executing task: {0}", task.getName());
@@ -150,10 +150,18 @@ public final class TaskExecutor {
task.setVariables(variables);
task.setState(Task.TaskState.RUNNING);
taskListeners.forEach(it -> it.onRunning(task));
try {
task.getScheduler().schedule(task::execute).get();
} catch (ExecutionException e) {
throw (Exception) e.getCause();
if (e.getCause() instanceof Exception)
throw (Exception) e.getCause();
else
throw e;
} finally {
task.setState(Task.TaskState.EXECUTED);
}
if (task instanceof TaskResult<?>) {
@@ -169,22 +177,24 @@ public final class TaskExecutor {
flag = true;
if (task.getSignificance().shouldLog()) {
Logging.LOG.log(Level.FINER, "Task finished: {0}", task.getName());
task.onDone().fireEvent(new TaskEvent(this, task, false));
taskListeners.forEach(it -> it.onFinished(task));
}
task.onDone().fireEvent(new TaskEvent(this, task, false));
taskListeners.forEach(it -> it.onFinished(task));
} catch (InterruptedException e) {
if (task.getSignificance().shouldLog()) {
Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName());
task.onDone().fireEvent(new TaskEvent(this, task, true));
taskListeners.forEach(it -> it.onFailed(task, e));
}
task.onDone().fireEvent(new TaskEvent(this, task, true));
taskListeners.forEach(it -> it.onFailed(task, e));
} catch (SilentException | RejectedExecutionException e) {
// do nothing
} catch (Exception e) {
lastException = e;
variables.set(LAST_EXCEPTION_ID, e);
Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e);
if (task.getSignificance().shouldLog()) {
Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e);
}
task.onDone().fireEvent(new TaskEvent(this, task, true));
taskListeners.forEach(it -> it.onFailed(task, e));
} finally {
@@ -31,6 +31,9 @@ public abstract class TaskListener implements EventListener {
public void onReady(Task task) {
}
public void onRunning(Task task) {
}
public void onFinished(Task task) {
}
@@ -74,9 +74,15 @@ public final class SimpleMultimap<K, V> {
return map.remove(key);
}
public void removeValue(V value) {
public boolean removeValue(V value) {
boolean flag = false;
for (Collection<V> c : map.values())
c.remove(value);
flag |= c.remove(value);
return flag;
}
public boolean removeValue(K key, V value) {
return get(key).remove(value);
}
public void clear() {
@@ -13,7 +13,7 @@ public class ToStringBuilder {
if (!first)
stringBuilder.append(", ");
first = false;
stringBuilder.append(name).append('=').append(content.toString());
stringBuilder.append(name).append('=').append(content);
return this;
}
+1
View File
@@ -49,6 +49,7 @@ allprojects {
dependencies {
compile "com.google.code.gson:gson:2.8.2"
compile "org.apache.commons:commons-compress:1.15"
compile "org.tukaani:xz:1.2"
testCompile group: 'junit', name: 'junit', version: '4.12'
}
+1 -1
View File
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.5.1-bin.zip
BIN
View File
Binary file not shown.