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