feat: balanced download provider & show release date in download versions page & download page ui refactor.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -60,11 +60,11 @@ public final class DownloadProviders {
|
||||
|
||||
AdaptedDownloadProvider fileProvider = new AdaptedDownloadProvider();
|
||||
fileProvider.setDownloadProviderCandidates(Arrays.asList(MCBBS, BMCLAPI, MOJANG));
|
||||
// BalancedDownloadProvider balanced = new BalancedDownloadProvider();
|
||||
BalancedDownloadProvider balanced = new BalancedDownloadProvider(Arrays.asList(MCBBS, BMCLAPI, MOJANG));
|
||||
|
||||
providersById = mapOf(
|
||||
pair("official", new AutoDownloadProvider(MOJANG, fileProvider)),
|
||||
pair("balanced", new AutoDownloadProvider(MCBBS, fileProvider)),
|
||||
pair("balanced", new AutoDownloadProvider(balanced, fileProvider)),
|
||||
pair("mirror", new AutoDownloadProvider(MCBBS, fileProvider)));
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ import org.jackhuang.hmcl.download.forge.ForgeRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.game.GameRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||
@@ -47,9 +46,12 @@ import org.jackhuang.hmcl.ui.construct.RipplerContainer;
|
||||
import org.jackhuang.hmcl.ui.wizard.Refreshable;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.i18n.Locales;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -86,7 +88,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
private StackPane center;
|
||||
|
||||
private VersionList<?> versionList;
|
||||
private TaskExecutor executor;
|
||||
private CompletableFuture<?> executor;
|
||||
|
||||
public VersionsPage(WizardController controller, String title, String gameVersion, DownloadProvider downloadProvider, String libraryId, Runnable callback) {
|
||||
this.title = title;
|
||||
@@ -129,35 +131,55 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
setGraphic(pane);
|
||||
|
||||
content.setTitle(remoteVersion.getSelfVersion());
|
||||
content.setSubtitle(remoteVersion.getGameVersion());
|
||||
if (remoteVersion.getReleaseDate() != null) {
|
||||
content.setSubtitle(Locales.DATE_TIME_FORMATTER.get().format(remoteVersion.getReleaseDate()));
|
||||
} else {
|
||||
content.setSubtitle("");
|
||||
}
|
||||
|
||||
if (remoteVersion instanceof GameRemoteVersion) {
|
||||
switch (remoteVersion.getVersionType()) {
|
||||
case RELEASE:
|
||||
content.setSubtitle(i18n("version.game.release"));
|
||||
content.getTags().setAll(i18n("version.game.release"));
|
||||
content.setImage(new Image("/assets/img/grass.png", 32, 32, false, true));
|
||||
break;
|
||||
case SNAPSHOT:
|
||||
content.setSubtitle(i18n("version.game.snapshot"));
|
||||
content.getTags().setAll(i18n("version.game.snapshot"));
|
||||
content.setImage(new Image("/assets/img/command.png", 32, 32, false, true));
|
||||
break;
|
||||
default:
|
||||
content.setSubtitle(i18n("version.game.old"));
|
||||
content.getTags().setAll(i18n("version.game.old"));
|
||||
content.setImage(new Image("/assets/img/craft_table.png", 32, 32, false, true));
|
||||
break;
|
||||
}
|
||||
} else if (remoteVersion instanceof LiteLoaderRemoteVersion) {
|
||||
content.setImage(new Image("/assets/img/chicken.png", 32, 32, false, true));
|
||||
content.setSubtitle(remoteVersion.getGameVersion());
|
||||
if (StringUtils.isNotBlank(content.getSubtitle())) {
|
||||
content.getTags().setAll(remoteVersion.getGameVersion());
|
||||
} else {
|
||||
content.setSubtitle(remoteVersion.getGameVersion());
|
||||
}
|
||||
} else if (remoteVersion instanceof OptiFineRemoteVersion) {
|
||||
content.setImage(new Image("/assets/img/command.png", 32, 32, false, true));
|
||||
content.setSubtitle(remoteVersion.getGameVersion());
|
||||
if (StringUtils.isNotBlank(content.getSubtitle())) {
|
||||
content.getTags().setAll(remoteVersion.getGameVersion());
|
||||
} else {
|
||||
content.setSubtitle(remoteVersion.getGameVersion());
|
||||
}
|
||||
} else if (remoteVersion instanceof ForgeRemoteVersion) {
|
||||
content.setImage(new Image("/assets/img/forge.png", 32, 32, false, true));
|
||||
content.setSubtitle(remoteVersion.getGameVersion());
|
||||
if (StringUtils.isNotBlank(content.getSubtitle())) {
|
||||
content.getTags().setAll(remoteVersion.getGameVersion());
|
||||
} else {
|
||||
content.setSubtitle(remoteVersion.getGameVersion());
|
||||
}
|
||||
} else if (remoteVersion instanceof FabricRemoteVersion) {
|
||||
content.setImage(new Image("/assets/img/fabric.png", 32, 32, false, true));
|
||||
content.setSubtitle(remoteVersion.getGameVersion());
|
||||
if (StringUtils.isNotBlank(content.getSubtitle())) {
|
||||
content.getTags().setAll(remoteVersion.getGameVersion());
|
||||
} else {
|
||||
content.setSubtitle(remoteVersion.getGameVersion());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -193,7 +215,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
public void refresh() {
|
||||
VersionList<?> currentVersionList = versionList;
|
||||
root.setContent(spinner, ContainerAnimations.FADE.getAnimationProducer());
|
||||
executor = currentVersionList.refreshAsync(gameVersion).whenComplete(exception -> {
|
||||
executor = currentVersionList.refreshAsync(gameVersion).whenComplete((result, exception) -> {
|
||||
if (exception == null) {
|
||||
List<RemoteVersion> items = loadVersions();
|
||||
|
||||
@@ -222,7 +244,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
|
||||
// https://github.com/huanghongxun/HMCL/issues/938
|
||||
System.gc();
|
||||
}).executor().start();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -234,7 +256,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
public void cleanup(Map<String, Object> settings) {
|
||||
settings.remove(libraryId);
|
||||
if (executor != null)
|
||||
executor.cancel();
|
||||
executor.cancel(true);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -29,10 +29,10 @@ import org.jackhuang.hmcl.game.World;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.wizard.SinglePageWizardProvider;
|
||||
import org.jackhuang.hmcl.util.i18n.Locales;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import static org.jackhuang.hmcl.util.StringUtils.parseColorEscapes;
|
||||
@@ -43,14 +43,13 @@ public class WorldListItem extends Control {
|
||||
private final StringProperty subtitle = new SimpleStringProperty();
|
||||
private final ObjectProperty<Image> image = new SimpleObjectProperty<>();
|
||||
private final World world;
|
||||
private final SimpleDateFormat simpleDateFormat;
|
||||
|
||||
public WorldListItem(World world) {
|
||||
this.world = world;
|
||||
this.simpleDateFormat = new SimpleDateFormat(i18n("world.time"));
|
||||
|
||||
title.set(parseColorEscapes(world.getWorldName()));
|
||||
subtitle.set(i18n("world.description", world.getFileName(), simpleDateFormat.format(new Date(world.getLastPlayed())), world.getGameVersion() == null ? i18n("message.unknown") : world.getGameVersion()));
|
||||
|
||||
subtitle.set(i18n("world.description", world.getFileName(), Locales.SIMPLE_DATE_FORMAT.get().format(new Date(world.getLastPlayed())), world.getGameVersion() == null ? i18n("message.unknown") : world.getGameVersion()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -21,12 +21,18 @@ import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Lazy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class Locales {
|
||||
private Locales() {
|
||||
}
|
||||
@@ -129,4 +135,7 @@ public final class Locales {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final Lazy<SimpleDateFormat> SIMPLE_DATE_FORMAT = new Lazy<>(() -> new SimpleDateFormat(i18n("world.time")));
|
||||
public static final Lazy<DateTimeFormatter> DATE_TIME_FORMATTER = new Lazy<>(() -> DateTimeFormatter.ofPattern(i18n("world.time")).withZone(ZoneId.systemDefault()));
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ public class MicrosoftService {
|
||||
public Optional<MinecraftProfileResponse> getCompleteProfile(String authorization) throws AuthenticationException {
|
||||
try {
|
||||
return Optional.ofNullable(
|
||||
HttpRequest.GET(NetworkUtils.toURL("https://api.minecraftservices.com/minecraft/profile"))
|
||||
HttpRequest.GET("https://api.minecraftservices.com/minecraft/profile")
|
||||
.authorization(authorization).getJson(MinecraftProfileResponse.class));
|
||||
} catch (IOException e) {
|
||||
throw new ServerDisconnectException(e);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -120,7 +120,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
if (baseVersion.isResolved()) throw new IllegalArgumentException("Version should not be resolved");
|
||||
|
||||
VersionList<?> versionList = getVersionList(libraryId);
|
||||
return versionList.loadAsync(gameVersion)
|
||||
return Task.fromCompletableFuture(versionList.loadAsync(gameVersion))
|
||||
.thenComposeAsync(() -> installLibraryAsync(baseVersion, versionList.getVersion(gameVersion, libraryVersion)
|
||||
.orElseThrow(() -> new IOException("Remote library " + libraryId + " has no version " + libraryVersion))))
|
||||
.withStage(String.format("hmcl.install.%s:%s", libraryId, libraryVersion));
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.ToStringBuilder;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -35,6 +36,7 @@ public class RemoteVersion implements Comparable<RemoteVersion> {
|
||||
private final String libraryId;
|
||||
private final String gameVersion;
|
||||
private final String selfVersion;
|
||||
private final Instant releaseDate;
|
||||
private final List<String> urls;
|
||||
private final Type type;
|
||||
|
||||
@@ -45,8 +47,8 @@ public class RemoteVersion implements Comparable<RemoteVersion> {
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param urls the installer or universal jar original URL.
|
||||
*/
|
||||
public RemoteVersion(String libraryId, String gameVersion, String selfVersion, List<String> urls) {
|
||||
this(libraryId, gameVersion, selfVersion, Type.UNCATEGORIZED, urls);
|
||||
public RemoteVersion(String libraryId, String gameVersion, String selfVersion, Instant releaseDate, List<String> urls) {
|
||||
this(libraryId, gameVersion, selfVersion, releaseDate, Type.UNCATEGORIZED, urls);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,10 +58,11 @@ public class RemoteVersion implements Comparable<RemoteVersion> {
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param urls the installer or universal jar URL.
|
||||
*/
|
||||
public RemoteVersion(String libraryId, String gameVersion, String selfVersion, Type type, List<String> urls) {
|
||||
public RemoteVersion(String libraryId, String gameVersion, String selfVersion, Instant releaseDate, Type type, List<String> urls) {
|
||||
this.libraryId = Objects.requireNonNull(libraryId);
|
||||
this.gameVersion = Objects.requireNonNull(gameVersion);
|
||||
this.selfVersion = Objects.requireNonNull(selfVersion);
|
||||
this.releaseDate = releaseDate;
|
||||
this.urls = Objects.requireNonNull(urls);
|
||||
this.type = Objects.requireNonNull(type);
|
||||
}
|
||||
@@ -76,6 +79,10 @@ public class RemoteVersion implements Comparable<RemoteVersion> {
|
||||
return selfVersion;
|
||||
}
|
||||
|
||||
public Instant getReleaseDate() {
|
||||
return releaseDate;
|
||||
}
|
||||
|
||||
public List<String> getUrls() {
|
||||
return urls;
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download;
|
||||
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.SimpleMultimap;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
@@ -61,42 +61,44 @@ public abstract class VersionList<T extends RemoteVersion> {
|
||||
/**
|
||||
* @return the task to reload the remote version list.
|
||||
*/
|
||||
public abstract Task<?> refreshAsync();
|
||||
public abstract CompletableFuture<?> refreshAsync();
|
||||
|
||||
/**
|
||||
* @param gameVersion the remote version depends on
|
||||
* @return the task to reload the remote version list.
|
||||
*/
|
||||
public Task<?> refreshAsync(String gameVersion) {
|
||||
public CompletableFuture<?> refreshAsync(String gameVersion) {
|
||||
return refreshAsync();
|
||||
}
|
||||
|
||||
public Task<?> loadAsync() {
|
||||
return Task.composeAsync(() -> {
|
||||
lock.readLock().lock();
|
||||
boolean loaded;
|
||||
public CompletableFuture<?> loadAsync() {
|
||||
return CompletableFuture.completedFuture(null)
|
||||
.thenComposeAsync(unused -> {
|
||||
lock.readLock().lock();
|
||||
boolean loaded;
|
||||
|
||||
try {
|
||||
loaded = isLoaded();
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
return loaded ? null : refreshAsync();
|
||||
});
|
||||
try {
|
||||
loaded = isLoaded();
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
return loaded ? CompletableFuture.completedFuture(null) : refreshAsync();
|
||||
});
|
||||
}
|
||||
|
||||
public Task<?> loadAsync(String gameVersion) {
|
||||
return Task.composeAsync(() -> {
|
||||
lock.readLock().lock();
|
||||
boolean loaded;
|
||||
public CompletableFuture<?> loadAsync(String gameVersion) {
|
||||
return CompletableFuture.completedFuture(null)
|
||||
.thenComposeAsync(unused -> {
|
||||
lock.readLock().lock();
|
||||
boolean loaded;
|
||||
|
||||
try {
|
||||
loaded = isLoaded(gameVersion);
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
return loaded ? null : refreshAsync(gameVersion);
|
||||
});
|
||||
try {
|
||||
loaded = isLoaded(gameVersion);
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
return loaded ? CompletableFuture.completedFuture(null) : refreshAsync(gameVersion);
|
||||
});
|
||||
}
|
||||
|
||||
protected Collection<T> getVersionsImpl(String gameVersion) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -34,7 +34,7 @@ public class FabricRemoteVersion extends RemoteVersion {
|
||||
* @param urls the installer or universal jar original URL.
|
||||
*/
|
||||
FabricRemoteVersion(String gameVersion, String selfVersion, List<String> urls) {
|
||||
super(LibraryAnalyzer.LibraryType.FABRIC.getPatchId(), gameVersion, selfVersion, urls);
|
||||
super(LibraryAnalyzer.LibraryType.FABRIC.getPatchId(), gameVersion, selfVersion, null, urls);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,7 +20,6 @@ package org.jackhuang.hmcl.download.fabric;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -29,8 +28,11 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.wrap;
|
||||
|
||||
public final class FabricVersionList extends VersionList<FabricRemoteVersion> {
|
||||
private final DownloadProvider downloadProvider;
|
||||
|
||||
@@ -44,25 +46,22 @@ public final class FabricVersionList extends VersionList<FabricRemoteVersion> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<?> refreshAsync() {
|
||||
return new Task<Void>() {
|
||||
@Override
|
||||
public void execute() throws IOException {
|
||||
List<String> gameVersions = getGameVersions(GAME_META_URL);
|
||||
List<String> loaderVersions = getGameVersions(LOADER_META_URL);
|
||||
public CompletableFuture<?> refreshAsync() {
|
||||
return CompletableFuture.runAsync(wrap(() -> {
|
||||
List<String> gameVersions = getGameVersions(GAME_META_URL);
|
||||
List<String> loaderVersions = getGameVersions(LOADER_META_URL);
|
||||
|
||||
lock.writeLock().lock();
|
||||
lock.writeLock().lock();
|
||||
|
||||
try {
|
||||
for (String gameVersion : gameVersions)
|
||||
for (String loaderVersion : loaderVersions)
|
||||
versions.put(gameVersion, new FabricRemoteVersion(gameVersion, loaderVersion,
|
||||
Collections.singletonList(getLaunchMetaUrl(gameVersion, loaderVersion))));
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
try {
|
||||
for (String gameVersion : gameVersions)
|
||||
for (String loaderVersion : loaderVersions)
|
||||
versions.put(gameVersion, new FabricRemoteVersion(gameVersion, loaderVersion,
|
||||
Collections.singletonList(getLaunchMetaUrl(gameVersion, loaderVersion))));
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
||||
private static final String LOADER_META_URL = "https://meta.fabricmc.net/v2/versions/loader";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,19 +20,26 @@ package org.jackhuang.hmcl.download.forge;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.task.GetTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.gson.Validation;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
import static org.jackhuang.hmcl.util.Lang.wrap;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
|
||||
public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion> {
|
||||
@@ -51,64 +58,66 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<?> loadAsync() {
|
||||
public CompletableFuture<?> loadAsync() {
|
||||
throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<?> refreshAsync() {
|
||||
public CompletableFuture<?> refreshAsync() {
|
||||
throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<?> refreshAsync(String gameVersion) {
|
||||
final GetTask task = new GetTask(NetworkUtils.toURL(apiRoot + "/forge/minecraft/" + gameVersion));
|
||||
return new Task<Void>() {
|
||||
@Override
|
||||
public Collection<Task<?>> getDependents() {
|
||||
return Collections.singleton(task);
|
||||
}
|
||||
public CompletableFuture<?> refreshAsync(String gameVersion) {
|
||||
return CompletableFuture.completedFuture(null)
|
||||
.thenApplyAsync(wrap(unused -> HttpRequest.GET(apiRoot + "/forge/minecraft/" + gameVersion).<List<ForgeVersion>>getJson(new TypeToken<List<ForgeVersion>>() {
|
||||
}.getType())))
|
||||
.thenAcceptAsync(forgeVersions -> {
|
||||
lock.writeLock().lock();
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
versions.clear(gameVersion);
|
||||
if (forgeVersions == null) return;
|
||||
for (ForgeVersion version : forgeVersions) {
|
||||
if (version == null)
|
||||
continue;
|
||||
List<String> urls = new ArrayList<>();
|
||||
for (ForgeVersion.File file : version.getFiles())
|
||||
if ("installer".equals(file.getCategory()) && "jar".equals(file.getFormat())) {
|
||||
String classifier = gameVersion + "-" + version.getVersion()
|
||||
+ (StringUtils.isNotBlank(version.getBranch()) ? "-" + version.getBranch() : "");
|
||||
String fileName1 = "forge-" + classifier + "-" + file.getCategory() + "." + file.getFormat();
|
||||
String fileName2 = "forge-" + classifier + "-" + gameVersion + "-" + file.getCategory() + "." + file.getFormat();
|
||||
urls.add("https://files.minecraftforge.net/maven/net/minecraftforge/forge/" + classifier + "/" + fileName1);
|
||||
urls.add("https://files.minecraftforge.net/maven/net/minecraftforge/forge/" + classifier + "-" + gameVersion + "/" + fileName2);
|
||||
urls.add(NetworkUtils.withQuery("https://bmclapi2.bangbang93.com/forge/download", mapOf(
|
||||
pair("mcversion", version.getGameVersion()),
|
||||
pair("version", version.getVersion()),
|
||||
pair("branch", version.getBranch()),
|
||||
pair("category", file.getCategory()),
|
||||
pair("format", file.getFormat())
|
||||
)));
|
||||
}
|
||||
|
||||
try {
|
||||
List<ForgeVersion> forgeVersions = JsonUtils.GSON.fromJson(task.getResult(), new TypeToken<List<ForgeVersion>>() {
|
||||
}.getType());
|
||||
versions.clear(gameVersion);
|
||||
if (forgeVersions == null) return;
|
||||
for (ForgeVersion version : forgeVersions) {
|
||||
if (version == null)
|
||||
continue;
|
||||
List<String> urls = new ArrayList<>();
|
||||
for (ForgeVersion.File file : version.getFiles())
|
||||
if ("installer".equals(file.getCategory()) && "jar".equals(file.getFormat())) {
|
||||
String classifier = gameVersion + "-" + version.getVersion()
|
||||
+ (StringUtils.isNotBlank(version.getBranch()) ? "-" + version.getBranch() : "");
|
||||
String fileName1 = "forge-" + classifier + "-" + file.getCategory() + "." + file.getFormat();
|
||||
String fileName2 = "forge-" + classifier + "-" + gameVersion + "-" + file.getCategory() + "." + file.getFormat();
|
||||
urls.add("https://files.minecraftforge.net/maven/net/minecraftforge/forge/" + classifier + "/" + fileName1);
|
||||
urls.add("https://files.minecraftforge.net/maven/net/minecraftforge/forge/" + classifier + "-" + gameVersion + "/" + fileName2);
|
||||
urls.add(NetworkUtils.withQuery("https://bmclapi2.bangbang93.com/forge/download", mapOf(
|
||||
pair("mcversion", version.getGameVersion()),
|
||||
pair("version", version.getVersion()),
|
||||
pair("branch", version.getBranch()),
|
||||
pair("category", file.getCategory()),
|
||||
pair("format", file.getFormat())
|
||||
)));
|
||||
if (urls.isEmpty())
|
||||
continue;
|
||||
|
||||
Instant releaseDate = null;
|
||||
if (version.getModified() != null) {
|
||||
try {
|
||||
releaseDate = Instant.parse(version.getModified());
|
||||
} catch (DateTimeParseException e) {
|
||||
LOG.log(Level.WARNING, "Failed to parse instant " + version.getModified(), e);
|
||||
}
|
||||
}
|
||||
|
||||
if (urls.isEmpty())
|
||||
continue;
|
||||
versions.put(gameVersion, new ForgeRemoteVersion(
|
||||
version.getGameVersion(), version.getVersion(), urls));
|
||||
versions.put(gameVersion, new ForgeRemoteVersion(
|
||||
version.getGameVersion(), version.getVersion(), releaseDate, urls));
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,7 +130,9 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
|
||||
public static final class ForgeVersion implements Validation {
|
||||
|
||||
private final String branch;
|
||||
private final int build;
|
||||
private final String mcversion;
|
||||
private final String modified;
|
||||
private final String version;
|
||||
private final List<File> files;
|
||||
|
||||
@@ -130,12 +141,14 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public ForgeVersion() {
|
||||
this(null, null, null, null);
|
||||
this(null, 0, "", null, "", Collections.emptyList());
|
||||
}
|
||||
|
||||
public ForgeVersion(String branch, String mcversion, String version, List<File> files) {
|
||||
public ForgeVersion(String branch, int build, String mcversion, String modified, String version, List<File> files) {
|
||||
this.branch = branch;
|
||||
this.build = build;
|
||||
this.mcversion = mcversion;
|
||||
this.modified = modified;
|
||||
this.version = version;
|
||||
this.files = files;
|
||||
}
|
||||
@@ -145,11 +158,20 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
|
||||
return branch;
|
||||
}
|
||||
|
||||
public int getBuild() {
|
||||
return build;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getGameVersion() {
|
||||
return mcversion;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getVersion() {
|
||||
return version;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -23,6 +23,7 @@ import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
public class ForgeRemoteVersion extends RemoteVersion {
|
||||
@@ -33,8 +34,8 @@ public class ForgeRemoteVersion extends RemoteVersion {
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param url the installer or universal jar original URL.
|
||||
*/
|
||||
public ForgeRemoteVersion(String gameVersion, String selfVersion, List<String> url) {
|
||||
super(LibraryAnalyzer.LibraryType.FORGE.getPatchId(), gameVersion, selfVersion, url);
|
||||
public ForgeRemoteVersion(String gameVersion, String selfVersion, Instant instant, List<String> url) {
|
||||
super(LibraryAnalyzer.LibraryType.FORGE.getPatchId(), gameVersion, selfVersion, instant, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,16 +19,13 @@ package org.jackhuang.hmcl.download.forge;
|
||||
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.task.GetTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -47,53 +44,42 @@ public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<?> refreshAsync() {
|
||||
final GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(FORGE_LIST)));
|
||||
return new Task<Void>() {
|
||||
public CompletableFuture<?> refreshAsync() {
|
||||
return HttpRequest.GET(downloadProvider.injectURL(FORGE_LIST)).getJsonAsync(ForgeVersionRoot.class)
|
||||
.thenAcceptAsync(root -> {
|
||||
lock.writeLock().lock();
|
||||
|
||||
@Override
|
||||
public Collection<Task<?>> getDependents() {
|
||||
return Collections.singleton(task);
|
||||
}
|
||||
try {
|
||||
if (root == null)
|
||||
return;
|
||||
versions.clear();
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
lock.writeLock().lock();
|
||||
for (Map.Entry<String, int[]> entry : root.getGameVersions().entrySet()) {
|
||||
String gameVersion = VersionNumber.normalize(entry.getKey());
|
||||
for (int v : entry.getValue()) {
|
||||
ForgeVersion version = root.getNumber().get(v);
|
||||
if (version == null)
|
||||
continue;
|
||||
String jar = null;
|
||||
for (String[] file : version.getFiles())
|
||||
if (file.length > 1 && "installer".equals(file[1])) {
|
||||
String classifier = version.getGameVersion() + "-" + version.getVersion()
|
||||
+ (StringUtils.isNotBlank(version.getBranch()) ? "-" + version.getBranch() : "");
|
||||
String fileName = root.getArtifact() + "-" + classifier + "-" + file[1] + "." + file[0];
|
||||
jar = root.getWebPath() + classifier + "/" + fileName;
|
||||
}
|
||||
|
||||
try {
|
||||
ForgeVersionRoot root = JsonUtils.GSON.fromJson(task.getResult(), ForgeVersionRoot.class);
|
||||
if (root == null)
|
||||
return;
|
||||
versions.clear();
|
||||
|
||||
for (Map.Entry<String, int[]> entry : root.getGameVersions().entrySet()) {
|
||||
String gameVersion = VersionNumber.normalize(entry.getKey());
|
||||
for (int v : entry.getValue()) {
|
||||
ForgeVersion version = root.getNumber().get(v);
|
||||
if (version == null)
|
||||
continue;
|
||||
String jar = null;
|
||||
for (String[] file : version.getFiles())
|
||||
if (file.length > 1 && "installer".equals(file[1])) {
|
||||
String classifier = version.getGameVersion() + "-" + version.getVersion()
|
||||
+ (StringUtils.isNotBlank(version.getBranch()) ? "-" + version.getBranch() : "");
|
||||
String fileName = root.getArtifact() + "-" + classifier + "-" + file[1] + "." + file[0];
|
||||
jar = root.getWebPath() + classifier + "/" + fileName;
|
||||
}
|
||||
|
||||
if (jar == null)
|
||||
continue;
|
||||
versions.put(gameVersion, new ForgeRemoteVersion(
|
||||
version.getGameVersion(), version.getVersion(), Collections.singletonList(jar)
|
||||
));
|
||||
if (jar == null)
|
||||
continue;
|
||||
versions.put(gameVersion, new ForgeRemoteVersion(
|
||||
version.getGameVersion(), version.getVersion(), null, Collections.singletonList(jar)
|
||||
));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public static final String FORGE_LIST = "https://files.minecraftforge.net/maven/net/minecraftforge/forge/json";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -25,7 +25,7 @@ import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -36,16 +36,10 @@ import java.util.List;
|
||||
public final class GameRemoteVersion extends RemoteVersion {
|
||||
|
||||
private final ReleaseType type;
|
||||
private final Date time;
|
||||
|
||||
public GameRemoteVersion(String gameVersion, String selfVersion, List<String> url, ReleaseType type, Date time) {
|
||||
super(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId(), gameVersion, selfVersion, getReleaseType(type), url);
|
||||
public GameRemoteVersion(String gameVersion, String selfVersion, List<String> url, ReleaseType type, Instant releaseDate) {
|
||||
super(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId(), gameVersion, selfVersion, releaseDate, getReleaseType(type), url);
|
||||
this.type = type;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public Date getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public ReleaseType getType() {
|
||||
@@ -62,7 +56,7 @@ public final class GameRemoteVersion extends RemoteVersion {
|
||||
if (!(o instanceof GameRemoteVersion))
|
||||
return 0;
|
||||
|
||||
return ((GameRemoteVersion) o).getTime().compareTo(getTime());
|
||||
return o.getReleaseDate().compareTo(getReleaseDate());
|
||||
}
|
||||
|
||||
private static Type getReleaseType(ReleaseType type) {
|
||||
|
||||
@@ -19,14 +19,11 @@ package org.jackhuang.hmcl.download.game;
|
||||
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.task.GetTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.jackhuang.hmcl.util.gson.JsonUtils.GSON;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -50,11 +47,9 @@ public final class GameVersionList extends VersionList<GameRemoteVersion> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<?> refreshAsync() {
|
||||
return new GetTask(NetworkUtils.toURL(downloadProvider.getVersionListURL()))
|
||||
.thenAcceptAsync(json -> {
|
||||
GameRemoteVersions root = GSON.fromJson(json, GameRemoteVersions.class);
|
||||
|
||||
public CompletableFuture<?> refreshAsync() {
|
||||
return HttpRequest.GET(downloadProvider.getVersionListURL()).getJsonAsync(GameRemoteVersions.class)
|
||||
.thenAcceptAsync(root -> {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
versions.clear();
|
||||
@@ -64,7 +59,7 @@ public final class GameVersionList extends VersionList<GameRemoteVersion> {
|
||||
remoteVersion.getGameVersion(),
|
||||
remoteVersion.getGameVersion(),
|
||||
Collections.singletonList(remoteVersion.getUrl()),
|
||||
remoteVersion.getType(), remoteVersion.getReleaseTime()));
|
||||
remoteVersion.getType(), remoteVersion.getReleaseTime().toInstant()));
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
|
||||
@@ -44,7 +44,7 @@ public final class VersionJsonDownloadTask extends Task<String> {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.gameVersionList = dependencyManager.getVersionList("game");
|
||||
|
||||
dependents.add(gameVersionList.loadAsync());
|
||||
dependents.add(Task.fromCompletableFuture(gameVersionList.loadAsync()));
|
||||
|
||||
setSignificance(TaskSignificance.MODERATE);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,10 +19,7 @@ package org.jackhuang.hmcl.download.liteloader;
|
||||
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.task.GetTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
@@ -30,9 +27,9 @@ import org.w3c.dom.Node;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -50,63 +47,54 @@ public final class LiteLoaderBMCLVersionList extends VersionList<LiteLoaderRemot
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<?> refreshAsync() {
|
||||
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(LITELOADER_LIST)));
|
||||
return new Task<Void>() {
|
||||
@Override
|
||||
public Collection<Task<?>> getDependents() {
|
||||
return Collections.singleton(task);
|
||||
}
|
||||
private void doBranch(String key, String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch, boolean snapshot) {
|
||||
if (branch == null || repository == null)
|
||||
return;
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
lock.writeLock().lock();
|
||||
for (Map.Entry<String, LiteLoaderVersion> entry : branch.getLiteLoader().entrySet()) {
|
||||
String branchName = entry.getKey();
|
||||
LiteLoaderVersion v = entry.getValue();
|
||||
if ("latest".equals(branchName))
|
||||
continue;
|
||||
|
||||
String version = v.getVersion();
|
||||
String url = "https://bmclapi2.bangbang93.com/liteloader/download?version=" + version;
|
||||
if (snapshot) {
|
||||
try {
|
||||
LiteLoaderVersionsRoot root = JsonUtils.GSON.fromJson(task.getResult(), LiteLoaderVersionsRoot.class);
|
||||
versions.clear();
|
||||
|
||||
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
|
||||
String gameVersion = entry.getKey();
|
||||
LiteLoaderGameVersions liteLoader = entry.getValue();
|
||||
|
||||
String gg = VersionNumber.normalize(gameVersion);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
version = version.replace("SNAPSHOT", getLatestSnapshotVersion(repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/"));
|
||||
url = repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/liteloader-" + version + "-release.jar";
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
private void doBranch(String key, String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch, boolean snapshot) {
|
||||
if (branch == null || repository == null)
|
||||
return;
|
||||
versions.put(key, new LiteLoaderRemoteVersion(gameVersion,
|
||||
version, Collections.singletonList(url),
|
||||
v.getTweakClass(), v.getLibraries()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, LiteLoaderVersion> entry : branch.getLiteLoader().entrySet()) {
|
||||
String branchName = entry.getKey();
|
||||
LiteLoaderVersion v = entry.getValue();
|
||||
if ("latest".equals(branchName))
|
||||
continue;
|
||||
@Override
|
||||
public CompletableFuture<?> refreshAsync() {
|
||||
return HttpRequest.GET(downloadProvider.injectURL(LITELOADER_LIST)).getJsonAsync(LiteLoaderVersionsRoot.class)
|
||||
.thenAcceptAsync(root -> {
|
||||
lock.writeLock().lock();
|
||||
|
||||
String version = v.getVersion();
|
||||
String url = "https://bmclapi2.bangbang93.com/liteloader/download?version=" + version;
|
||||
if (snapshot) {
|
||||
try {
|
||||
version = version.replace("SNAPSHOT", getLatestSnapshotVersion(repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/"));
|
||||
url = repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/liteloader-" + version + "-release.jar";
|
||||
} catch (Exception ignore) {
|
||||
try {
|
||||
versions.clear();
|
||||
|
||||
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
|
||||
String gameVersion = entry.getKey();
|
||||
LiteLoaderGameVersions liteLoader = entry.getValue();
|
||||
|
||||
String gg = VersionNumber.normalize(gameVersion);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
|
||||
versions.put(key, new LiteLoaderRemoteVersion(gameVersion,
|
||||
version, Collections.singletonList(url),
|
||||
v.getTweakClass(), v.getLibraries()
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public static final String LITELOADER_LIST = "http://dl.liteloader.com/versions/versions.json";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -38,7 +38,7 @@ public class LiteLoaderRemoteVersion extends RemoteVersion {
|
||||
* @param urls the installer or universal jar original URL.
|
||||
*/
|
||||
LiteLoaderRemoteVersion(String gameVersion, String selfVersion, List<String> urls, String tweakClass, Collection<Library> libraries) {
|
||||
super(LibraryAnalyzer.LibraryType.LITELOADER.getPatchId(), gameVersion, selfVersion, urls);
|
||||
super(LibraryAnalyzer.LibraryType.LITELOADER.getPatchId(), gameVersion, selfVersion, null, urls);
|
||||
|
||||
this.tweakClass = tweakClass;
|
||||
this.libraries = libraries;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,10 +19,7 @@ package org.jackhuang.hmcl.download.liteloader;
|
||||
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.task.GetTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
@@ -30,9 +27,9 @@ import org.w3c.dom.Node;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -51,63 +48,54 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<?> refreshAsync() {
|
||||
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(LITELOADER_LIST)));
|
||||
return new Task<Void>() {
|
||||
@Override
|
||||
public Collection<Task<?>> getDependents() {
|
||||
return Collections.singleton(task);
|
||||
}
|
||||
private void doBranch(String key, String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch, boolean snapshot) {
|
||||
if (branch == null || repository == null)
|
||||
return;
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
lock.writeLock().lock();
|
||||
for (Map.Entry<String, LiteLoaderVersion> entry : branch.getLiteLoader().entrySet()) {
|
||||
String branchName = entry.getKey();
|
||||
LiteLoaderVersion v = entry.getValue();
|
||||
if ("latest".equals(branchName))
|
||||
continue;
|
||||
|
||||
String version = v.getVersion();
|
||||
String url = repository.getUrl() + "com/mumfrey/liteloader/" + gameVersion + "/" + v.getFile();
|
||||
if (snapshot) {
|
||||
try {
|
||||
LiteLoaderVersionsRoot root = JsonUtils.GSON.fromJson(task.getResult(), LiteLoaderVersionsRoot.class);
|
||||
versions.clear();
|
||||
|
||||
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
|
||||
String gameVersion = entry.getKey();
|
||||
LiteLoaderGameVersions liteLoader = entry.getValue();
|
||||
|
||||
String gg = VersionNumber.normalize(gameVersion);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
version = version.replace("SNAPSHOT", getLatestSnapshotVersion(repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/"));
|
||||
url = repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/liteloader-" + version + "-release.jar";
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
private void doBranch(String key, String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch, boolean snapshot) {
|
||||
if (branch == null || repository == null)
|
||||
return;
|
||||
versions.put(key, new LiteLoaderRemoteVersion(gameVersion,
|
||||
version, Collections.singletonList(url),
|
||||
v.getTweakClass(), v.getLibraries()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, LiteLoaderVersion> entry : branch.getLiteLoader().entrySet()) {
|
||||
String branchName = entry.getKey();
|
||||
LiteLoaderVersion v = entry.getValue();
|
||||
if ("latest".equals(branchName))
|
||||
continue;
|
||||
@Override
|
||||
public CompletableFuture<?> refreshAsync() {
|
||||
return HttpRequest.GET(downloadProvider.injectURL(LITELOADER_LIST)).getJsonAsync(LiteLoaderVersionsRoot.class)
|
||||
.thenAcceptAsync(root -> {
|
||||
lock.writeLock().lock();
|
||||
|
||||
String version = v.getVersion();
|
||||
String url = repository.getUrl() + "com/mumfrey/liteloader/" + gameVersion + "/" + v.getFile();
|
||||
if (snapshot) {
|
||||
try {
|
||||
version = version.replace("SNAPSHOT", getLatestSnapshotVersion(repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/"));
|
||||
url = repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/liteloader-" + version + "-release.jar";
|
||||
} catch (Exception ignore) {
|
||||
try {
|
||||
versions.clear();
|
||||
|
||||
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
|
||||
String gameVersion = entry.getKey();
|
||||
LiteLoaderGameVersions liteLoader = entry.getValue();
|
||||
|
||||
String gg = VersionNumber.normalize(gameVersion);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
|
||||
versions.put(key, new LiteLoaderRemoteVersion(gameVersion,
|
||||
version, Collections.singletonList(url),
|
||||
v.getTweakClass(), v.getLibraries()
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public static final String LITELOADER_LIST = "http://dl.liteloader.com/versions/versions.json";
|
||||
|
||||
@@ -19,14 +19,15 @@ package org.jackhuang.hmcl.download.optifine;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.task.GetTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -48,42 +49,33 @@ public final class OptiFineBMCLVersionList extends VersionList<OptiFineRemoteVer
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<?> refreshAsync() {
|
||||
GetTask task = new GetTask(NetworkUtils.toURL(apiRoot + "/optifine/versionlist"));
|
||||
return new Task<Void>() {
|
||||
@Override
|
||||
public Collection<Task<?>> getDependents() {
|
||||
return Collections.singleton(task);
|
||||
}
|
||||
public CompletableFuture<?> refreshAsync() {
|
||||
return HttpRequest.GET(apiRoot + "/optifine/versionlist").<List<OptiFineVersion>>getJsonAsync(new TypeToken<List<OptiFineVersion>>() {
|
||||
}.getType())
|
||||
.thenAcceptAsync(root -> {
|
||||
lock.writeLock().lock();
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
versions.clear();
|
||||
Set<String> duplicates = new HashSet<>();
|
||||
for (OptiFineVersion element : root) {
|
||||
String version = element.getType() + "_" + element.getPatch();
|
||||
String mirror = "https://bmclapi2.bangbang93.com/optifine/" + element.getGameVersion() + "/" + element.getType() + "/" + element.getPatch();
|
||||
if (!duplicates.add(mirror))
|
||||
continue;
|
||||
|
||||
try {
|
||||
versions.clear();
|
||||
Set<String> duplicates = new HashSet<>();
|
||||
List<OptiFineVersion> root = JsonUtils.GSON.fromJson(task.getResult(), new TypeToken<List<OptiFineVersion>>() {
|
||||
}.getType());
|
||||
for (OptiFineVersion element : root) {
|
||||
String version = element.getType() + "_" + element.getPatch();
|
||||
String mirror = "https://bmclapi2.bangbang93.com/optifine/" + element.getGameVersion() + "/" + element.getType() + "/" + element.getPatch();
|
||||
if (!duplicates.add(mirror))
|
||||
continue;
|
||||
boolean isPre = element.getPatch() != null && (element.getPatch().startsWith("pre") || element.getPatch().startsWith("alpha"));
|
||||
|
||||
boolean isPre = element.getPatch() != null && (element.getPatch().startsWith("pre") || element.getPatch().startsWith("alpha"));
|
||||
if (StringUtils.isBlank(element.getGameVersion()))
|
||||
continue;
|
||||
|
||||
if (StringUtils.isBlank(element.getGameVersion()))
|
||||
continue;
|
||||
|
||||
String gameVersion = VersionNumber.normalize(element.getGameVersion());
|
||||
versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, Collections.singletonList(mirror), isPre));
|
||||
String gameVersion = VersionNumber.normalize(element.getGameVersion());
|
||||
versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, Collections.singletonList(mirror), isPre));
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import java.util.List;
|
||||
public class OptiFineRemoteVersion extends RemoteVersion {
|
||||
|
||||
public OptiFineRemoteVersion(String gameVersion, String selfVersion, List<String> urls, boolean snapshot) {
|
||||
super(LibraryAnalyzer.LibraryType.OPTIFINE.getPatchId(), gameVersion, selfVersion, snapshot ? Type.SNAPSHOT : Type.RELEASE, urls);
|
||||
super(LibraryAnalyzer.LibraryType.OPTIFINE.getPatchId(), gameVersion, selfVersion, null, snapshot ? Type.SNAPSHOT : Type.RELEASE, urls);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -51,6 +51,8 @@ import java.util.stream.Stream;
|
||||
|
||||
import static org.jackhuang.hmcl.util.DigestUtils.digest;
|
||||
import static org.jackhuang.hmcl.util.Hex.encodeHex;
|
||||
import static org.jackhuang.hmcl.util.Lang.wrap;
|
||||
import static org.jackhuang.hmcl.util.Lang.wrapConsumer;
|
||||
|
||||
public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
|
||||
|
||||
@@ -173,7 +175,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
|
||||
|
||||
manifest = remoteManifest.setFiles(newFiles);
|
||||
return executor.all(tasks.stream().filter(Objects::nonNull).collect(Collectors.toList()));
|
||||
})).thenAcceptAsync(wrap(unused1 -> {
|
||||
})).thenAcceptAsync(wrapConsumer(unused1 -> {
|
||||
File manifestFile = repository.getModpackConfiguration(version);
|
||||
FileUtils.writeText(manifestFile, JsonUtils.GSON.toJson(
|
||||
new ModpackConfiguration<>(manifest, this.configuration.getType(), this.manifest.getName(), this.manifest.getVersion(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,16 +20,15 @@ package org.jackhuang.hmcl.task;
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.rethrow;
|
||||
import static org.jackhuang.hmcl.util.Lang.wrap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
@@ -326,28 +325,6 @@ public final class AsyncTaskExecutor extends TaskExecutor {
|
||||
return e;
|
||||
}
|
||||
|
||||
private static void rethrow(Throwable e) {
|
||||
if (e == null)
|
||||
return;
|
||||
if (e instanceof ExecutionException || e instanceof CompletionException) { // including UncheckedException and UncheckedThrowable
|
||||
rethrow(e.getCause());
|
||||
} else if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
} else {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Runnable wrap(ExceptionalRunnable<?> runnable) {
|
||||
return () -> {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
rethrow(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void checkCancellation() {
|
||||
if (isCancelled()) {
|
||||
throw new CancellationException("Cancelled by user");
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.task;
|
||||
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalBiConsumer;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public abstract class CompletableFutureTask<T> extends Task<T> {
|
||||
|
||||
@@ -20,59 +29,6 @@ public abstract class CompletableFutureTask<T> extends Task<T> {
|
||||
|
||||
public abstract CompletableFuture<T> getFuture(TaskCompletableFuture executor);
|
||||
|
||||
protected static Runnable wrap(ExceptionalRunnable<?> runnable) {
|
||||
return () -> {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
rethrow(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected static <T, R> Function<T, R> wrap(ExceptionalFunction<T, R, ?> fn) {
|
||||
return t -> {
|
||||
try {
|
||||
return fn.apply(t);
|
||||
} catch (Exception e) {
|
||||
rethrow(e);
|
||||
throw new InternalError("Unreachable code");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected static <T> Consumer<T> wrap(ExceptionalConsumer<T, ?> fn) {
|
||||
return t -> {
|
||||
try {
|
||||
fn.accept(t);
|
||||
} catch (Exception e) {
|
||||
rethrow(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected static <T, E> BiConsumer<T, E> wrap(ExceptionalBiConsumer<T, E, ?> fn) {
|
||||
return (t, e) -> {
|
||||
try {
|
||||
fn.accept(t, e);
|
||||
} catch (Exception ex) {
|
||||
rethrow(ex);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected static void rethrow(Throwable e) {
|
||||
if (e == null)
|
||||
return;
|
||||
if (e instanceof ExecutionException || e instanceof CompletionException) { // including UncheckedException and UncheckedThrowable
|
||||
rethrow(e.getCause());
|
||||
} else if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
} else {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected static Throwable resolveException(Throwable e) {
|
||||
if (e instanceof ExecutionException || e instanceof CompletionException)
|
||||
return resolveException(e.getCause());
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
@@ -921,6 +922,15 @@ public abstract class Task<T> {
|
||||
return task;
|
||||
}
|
||||
|
||||
public static <T> Task<T> fromCompletableFuture(CompletableFuture<T> future) {
|
||||
return new CompletableFutureTask<T>() {
|
||||
@Override
|
||||
public CompletableFuture<T> getFuture(TaskCompletableFuture executor) {
|
||||
return future;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public enum TaskSignificance {
|
||||
MAJOR,
|
||||
MODERATE,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,17 +17,12 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
|
||||
import org.jackhuang.hmcl.util.function.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -256,6 +251,70 @@ public final class Lang {
|
||||
return t;
|
||||
}
|
||||
|
||||
public static void rethrow(Throwable e) {
|
||||
if (e == null)
|
||||
return;
|
||||
if (e instanceof ExecutionException || e instanceof CompletionException) { // including UncheckedException and UncheckedThrowable
|
||||
rethrow(e.getCause());
|
||||
} else if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
} else {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Runnable wrap(ExceptionalRunnable<?> runnable) {
|
||||
return () -> {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
rethrow(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> Supplier<T> wrap(ExceptionalSupplier<T, ?> supplier) {
|
||||
return () -> {
|
||||
try {
|
||||
return supplier.get();
|
||||
} catch (Exception e) {
|
||||
rethrow(e);
|
||||
throw new InternalError("Unreachable code");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T, R> Function<T, R> wrap(ExceptionalFunction<T, R, ?> fn) {
|
||||
return t -> {
|
||||
try {
|
||||
return fn.apply(t);
|
||||
} catch (Exception e) {
|
||||
rethrow(e);
|
||||
throw new InternalError("Unreachable code");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> Consumer<T> wrapConsumer(ExceptionalConsumer<T, ?> fn) {
|
||||
return t -> {
|
||||
try {
|
||||
fn.accept(t);
|
||||
} catch (Exception e) {
|
||||
rethrow(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T, E> BiConsumer<T, E> wrap(ExceptionalBiConsumer<T, E, ?> fn) {
|
||||
return (t, e) -> {
|
||||
try {
|
||||
fn.accept(t, e);
|
||||
} catch (Exception ex) {
|
||||
rethrow(ex);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a useful function to prevent exceptions being eaten when using CompletableFuture.
|
||||
* You can write:
|
||||
|
||||
@@ -70,6 +70,11 @@ public final class SimpleMultimap<K, V> {
|
||||
set.add(value);
|
||||
}
|
||||
|
||||
public void putAll(K key, Collection<? extends V> value) {
|
||||
Collection<V> set = get(key);
|
||||
set.addAll(value);
|
||||
}
|
||||
|
||||
public Collection<V> removeKey(K key) {
|
||||
return map.remove(key);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.jackhuang.hmcl.util.io;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalBiConsumer;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
@@ -31,20 +32,22 @@ import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
import static org.jackhuang.hmcl.util.Lang.wrap;
|
||||
import static org.jackhuang.hmcl.util.gson.JsonUtils.GSON;
|
||||
import static org.jackhuang.hmcl.util.io.NetworkUtils.createHttpConnection;
|
||||
import static org.jackhuang.hmcl.util.io.NetworkUtils.resolveConnection;
|
||||
|
||||
public abstract class HttpRequest {
|
||||
protected final URL url;
|
||||
protected final String url;
|
||||
protected final String method;
|
||||
protected final Map<String, String> headers = new HashMap<>();
|
||||
protected ExceptionalBiConsumer<URL, Integer, IOException> responseCodeTester;
|
||||
|
||||
private HttpRequest(URL url, String method) {
|
||||
private HttpRequest(String url, String method) {
|
||||
this.url = url;
|
||||
this.method = method;
|
||||
}
|
||||
@@ -64,6 +67,10 @@ public abstract class HttpRequest {
|
||||
|
||||
public abstract String getString() throws IOException;
|
||||
|
||||
public CompletableFuture<String> getStringAsync() {
|
||||
return CompletableFuture.supplyAsync(wrap(this::getString), Schedulers.io());
|
||||
}
|
||||
|
||||
public <T> T getJson(Class<T> typeOfT) throws IOException, JsonParseException {
|
||||
return JsonUtils.fromNonNullJson(getString(), typeOfT);
|
||||
}
|
||||
@@ -72,13 +79,21 @@ public abstract class HttpRequest {
|
||||
return JsonUtils.fromNonNullJson(getString(), type);
|
||||
}
|
||||
|
||||
public <T> CompletableFuture<T> getJsonAsync(Class<T> typeOfT) {
|
||||
return getStringAsync().thenApplyAsync(jsonString -> JsonUtils.fromNonNullJson(jsonString, typeOfT));
|
||||
}
|
||||
|
||||
public <T> CompletableFuture<T> getJsonAsync(Type type) {
|
||||
return getStringAsync().thenApplyAsync(jsonString -> JsonUtils.fromNonNullJson(jsonString, type));
|
||||
}
|
||||
|
||||
public HttpRequest filter(ExceptionalBiConsumer<URL, Integer, IOException> responseCodeTester) {
|
||||
this.responseCodeTester = responseCodeTester;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpURLConnection createConnection() throws IOException {
|
||||
HttpURLConnection con = createHttpConnection(url);
|
||||
HttpURLConnection con = createHttpConnection(new URL(url));
|
||||
con.setRequestMethod(method);
|
||||
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||
con.setRequestProperty(entry.getKey(), entry.getValue());
|
||||
@@ -87,7 +102,7 @@ public abstract class HttpRequest {
|
||||
}
|
||||
|
||||
public static class HttpGetRequest extends HttpRequest {
|
||||
public HttpGetRequest(URL url) {
|
||||
public HttpGetRequest(String url) {
|
||||
super(url, "GET");
|
||||
}
|
||||
|
||||
@@ -101,7 +116,7 @@ public abstract class HttpRequest {
|
||||
public static final class HttpPostRequest extends HttpRequest {
|
||||
private byte[] bytes;
|
||||
|
||||
public HttpPostRequest(URL url) {
|
||||
public HttpPostRequest(String url) {
|
||||
super(url, "POST");
|
||||
}
|
||||
|
||||
@@ -135,7 +150,7 @@ public abstract class HttpRequest {
|
||||
con.setDoOutput(true);
|
||||
|
||||
if (responseCodeTester != null) {
|
||||
responseCodeTester.accept(url, con.getResponseCode());
|
||||
responseCodeTester.accept(new URL(url), con.getResponseCode());
|
||||
}
|
||||
|
||||
try (OutputStream os = con.getOutputStream()) {
|
||||
@@ -145,24 +160,17 @@ public abstract class HttpRequest {
|
||||
}
|
||||
}
|
||||
|
||||
public static HttpGetRequest GET(String url) throws MalformedURLException {
|
||||
return GET(new URL(url));
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static HttpGetRequest GET(String url, Pair<String, String>... query) throws MalformedURLException {
|
||||
return GET(new URL(NetworkUtils.withQuery(url, mapOf(query))));
|
||||
}
|
||||
|
||||
public static HttpGetRequest GET(URL url) {
|
||||
public static HttpGetRequest GET(String url) {
|
||||
return new HttpGetRequest(url);
|
||||
}
|
||||
|
||||
public static HttpPostRequest POST(String url) throws MalformedURLException {
|
||||
return POST(new URL(url));
|
||||
@SafeVarargs
|
||||
public static HttpGetRequest GET(String url, Pair<String, String>... query) {
|
||||
return GET(NetworkUtils.withQuery(url, mapOf(query)));
|
||||
}
|
||||
|
||||
public static HttpPostRequest POST(URL url) {
|
||||
public static HttpPostRequest POST(String url) throws MalformedURLException {
|
||||
return new HttpPostRequest(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user