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