重构下载源选择逻辑 (#4856)
This commit is contained in:
@@ -493,7 +493,7 @@ public final class Config extends ObservableSetting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SerializedName("downloadType")
|
@SerializedName("downloadType")
|
||||||
private final StringProperty downloadType = new SimpleStringProperty(DownloadProviders.DEFAULT_RAW_PROVIDER_ID);
|
private final StringProperty downloadType = new SimpleStringProperty(DownloadProviders.DEFAULT_DIRECT_PROVIDER_ID);
|
||||||
|
|
||||||
public StringProperty downloadTypeProperty() {
|
public StringProperty downloadTypeProperty() {
|
||||||
return downloadType;
|
return downloadType;
|
||||||
@@ -523,7 +523,7 @@ public final class Config extends ObservableSetting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SerializedName("versionListSource")
|
@SerializedName("versionListSource")
|
||||||
private final StringProperty versionListSource = new SimpleStringProperty("balanced");
|
private final StringProperty versionListSource = new SimpleStringProperty(DownloadProviders.DEFAULT_AUTO_PROVIDER_ID);
|
||||||
|
|
||||||
public StringProperty versionListSourceProperty() {
|
public StringProperty versionListSourceProperty() {
|
||||||
return versionListSource;
|
return versionListSource;
|
||||||
|
|||||||
@@ -21,9 +21,10 @@ import javafx.beans.InvalidationListener;
|
|||||||
import org.jackhuang.hmcl.download.*;
|
import org.jackhuang.hmcl.download.*;
|
||||||
import org.jackhuang.hmcl.task.DownloadException;
|
import org.jackhuang.hmcl.task.DownloadException;
|
||||||
import org.jackhuang.hmcl.task.FetchTask;
|
import org.jackhuang.hmcl.task.FetchTask;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||||
|
import org.jackhuang.hmcl.util.i18n.LocaleUtils;
|
||||||
import org.jackhuang.hmcl.util.io.ResponseCodeException;
|
import org.jackhuang.hmcl.util.io.ResponseCodeException;
|
||||||
|
|
||||||
import javax.net.ssl.SSLHandshakeException;
|
import javax.net.ssl.SSLHandshakeException;
|
||||||
@@ -31,104 +32,88 @@ import java.io.FileNotFoundException;
|
|||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.file.AccessDeniedException;
|
import java.nio.file.AccessDeniedException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||||
import static org.jackhuang.hmcl.task.FetchTask.DEFAULT_CONCURRENCY;
|
import static org.jackhuang.hmcl.task.FetchTask.DEFAULT_CONCURRENCY;
|
||||||
|
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
public final class DownloadProviders {
|
public final class DownloadProviders {
|
||||||
private DownloadProviders() {
|
private DownloadProviders() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final DownloadProviderWrapper provider;
|
public static final String DEFAULT_AUTO_PROVIDER_ID = "balanced";
|
||||||
|
public static final String DEFAULT_DIRECT_PROVIDER_ID = "mojang";
|
||||||
|
|
||||||
public static final Map<String, DownloadProvider> providersById;
|
private static final DownloadProviderWrapper PROVIDER_WRAPPER;
|
||||||
public static final Map<String, DownloadProvider> rawProviders;
|
|
||||||
private static final AdaptedDownloadProvider fileDownloadProvider = new AdaptedDownloadProvider();
|
|
||||||
|
|
||||||
private static final MojangDownloadProvider MOJANG;
|
private static final DownloadProvider DEFAULT_PROVIDER;
|
||||||
private static final BMCLAPIDownloadProvider BMCLAPI;
|
public static final Map<String, DownloadProvider> DIRECT_PROVIDERS;
|
||||||
|
public static final Map<String, DownloadProvider> AUTO_PROVIDERS;
|
||||||
public static final String DEFAULT_PROVIDER_ID = "balanced";
|
|
||||||
public static final String DEFAULT_RAW_PROVIDER_ID = "bmclapi";
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private static final InvalidationListener observer;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
String bmclapiRoot = "https://bmclapi2.bangbang93.com";
|
String bmclapiRoot = System.getProperty("hmcl.bmclapi.override", "https://bmclapi2.bangbang93.com");
|
||||||
String bmclapiRootOverride = System.getProperty("hmcl.bmclapi.override");
|
BMCLAPIDownloadProvider bmclapiRaw = new BMCLAPIDownloadProvider(bmclapiRoot);
|
||||||
if (bmclapiRootOverride != null) bmclapiRoot = bmclapiRootOverride;
|
|
||||||
|
|
||||||
MOJANG = new MojangDownloadProvider();
|
DownloadProvider mojang = new MojangDownloadProvider();
|
||||||
BMCLAPI = new BMCLAPIDownloadProvider(bmclapiRoot);
|
DownloadProvider bmclapi = new AutoDownloadProvider(bmclapiRaw, mojang);
|
||||||
rawProviders = Map.of(
|
|
||||||
"mojang", MOJANG,
|
DEFAULT_PROVIDER = mojang;
|
||||||
"bmclapi", BMCLAPI
|
DIRECT_PROVIDERS = Lang.mapOf(
|
||||||
|
pair("mojang", mojang),
|
||||||
|
pair("bmclapi", bmclapi)
|
||||||
);
|
);
|
||||||
|
|
||||||
AdaptedDownloadProvider fileProvider = new AdaptedDownloadProvider();
|
AUTO_PROVIDERS = Lang.mapOf(
|
||||||
fileProvider.setDownloadProviderCandidates(List.of(BMCLAPI, MOJANG));
|
pair("balanced", LocaleUtils.IS_CHINA_MAINLAND ? bmclapi : mojang),
|
||||||
BalancedDownloadProvider balanced = new BalancedDownloadProvider(MOJANG, BMCLAPI);
|
pair("official", LocaleUtils.IS_CHINA_MAINLAND ? new AutoDownloadProvider(
|
||||||
|
List.of(mojang, bmclapiRaw),
|
||||||
|
List.of(bmclapiRaw, mojang)
|
||||||
|
) : mojang),
|
||||||
|
pair("mirror", bmclapi)
|
||||||
|
);
|
||||||
|
|
||||||
providersById = Map.of(
|
PROVIDER_WRAPPER = new DownloadProviderWrapper(DEFAULT_PROVIDER);
|
||||||
"official", new AutoDownloadProvider(MOJANG, fileProvider),
|
|
||||||
"balanced", new AutoDownloadProvider(balanced, fileProvider),
|
|
||||||
"mirror", new AutoDownloadProvider(BMCLAPI, fileProvider));
|
|
||||||
|
|
||||||
observer = FXUtils.observeWeak(() -> {
|
|
||||||
FetchTask.setDownloadExecutorConcurrency(
|
|
||||||
config().getAutoDownloadThreads() ? DEFAULT_CONCURRENCY : config().getDownloadThreads());
|
|
||||||
}, config().autoDownloadThreadsProperty(), config().downloadThreadsProperty());
|
|
||||||
|
|
||||||
provider = new DownloadProviderWrapper(MOJANG);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init() {
|
static void init() {
|
||||||
InvalidationListener onChangeDownloadSource = observable -> {
|
InvalidationListener onChangeDownloadThreads = observable -> {
|
||||||
String versionListSource = Objects.requireNonNullElse(config().getVersionListSource(), "");
|
FetchTask.setDownloadExecutorConcurrency(config().getAutoDownloadThreads()
|
||||||
if (config().isAutoChooseDownloadType()) {
|
? DEFAULT_CONCURRENCY
|
||||||
DownloadProvider currentDownloadProvider = providersById.get(versionListSource);
|
: config().getDownloadThreads());
|
||||||
if (currentDownloadProvider == null)
|
};
|
||||||
currentDownloadProvider = Objects.requireNonNull(providersById.get(DEFAULT_PROVIDER_ID),
|
config().autoDownloadThreadsProperty().addListener(onChangeDownloadThreads);
|
||||||
"default provider is null");
|
config().downloadThreadsProperty().addListener(onChangeDownloadThreads);
|
||||||
|
onChangeDownloadThreads.invalidated(null);
|
||||||
|
|
||||||
provider.setProvider(currentDownloadProvider);
|
InvalidationListener onChangeDownloadSource = observable -> {
|
||||||
|
if (config().isAutoChooseDownloadType()) {
|
||||||
|
String versionListSource = config().getVersionListSource();
|
||||||
|
DownloadProvider downloadProvider = versionListSource != null
|
||||||
|
? AUTO_PROVIDERS.getOrDefault(versionListSource, DEFAULT_PROVIDER)
|
||||||
|
: DEFAULT_PROVIDER;
|
||||||
|
PROVIDER_WRAPPER.setProvider(downloadProvider);
|
||||||
} else {
|
} else {
|
||||||
provider.setProvider(fileDownloadProvider);
|
String downloadType = config().getDownloadType();
|
||||||
|
PROVIDER_WRAPPER.setProvider(downloadType != null
|
||||||
|
? DIRECT_PROVIDERS.getOrDefault(downloadType, DEFAULT_PROVIDER)
|
||||||
|
: DEFAULT_PROVIDER);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
config().versionListSourceProperty().addListener(onChangeDownloadSource);
|
config().versionListSourceProperty().addListener(onChangeDownloadSource);
|
||||||
config().autoChooseDownloadTypeProperty().addListener(onChangeDownloadSource);
|
config().autoChooseDownloadTypeProperty().addListener(onChangeDownloadSource);
|
||||||
|
config().downloadTypeProperty().addListener(onChangeDownloadSource);
|
||||||
onChangeDownloadSource.invalidated(null);
|
onChangeDownloadSource.invalidated(null);
|
||||||
|
|
||||||
FXUtils.onChangeAndOperate(config().downloadTypeProperty(), downloadType -> {
|
|
||||||
DownloadProvider primary = Objects.requireNonNullElseGet(
|
|
||||||
rawProviders.get(Objects.requireNonNullElse(downloadType, "")),
|
|
||||||
() -> rawProviders.get(DEFAULT_RAW_PROVIDER_ID));
|
|
||||||
|
|
||||||
List<DownloadProvider> providers = new ArrayList<>(rawProviders.size());
|
|
||||||
providers.add(primary);
|
|
||||||
for (DownloadProvider provider : rawProviders.values()) {
|
|
||||||
if (provider != primary)
|
|
||||||
providers.add(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
fileDownloadProvider.setDownloadProviderCandidates(providers);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current primary preferred download provider
|
* Get current primary preferred download provider
|
||||||
*/
|
*/
|
||||||
public static DownloadProvider getDownloadProvider() {
|
public static DownloadProvider getDownloadProvider() {
|
||||||
return provider;
|
return PROVIDER_WRAPPER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String localizeErrorMessage(Throwable exception) {
|
public static String localizeErrorMessage(Throwable exception) {
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class DownloadSettingsPage extends StackPane {
|
|||||||
versionListSourcePane.setRight(cboVersionListSource);
|
versionListSourcePane.setRight(cboVersionListSource);
|
||||||
FXUtils.setLimitWidth(cboVersionListSource, 400);
|
FXUtils.setLimitWidth(cboVersionListSource, 400);
|
||||||
|
|
||||||
cboVersionListSource.getItems().setAll(DownloadProviders.providersById.keySet());
|
cboVersionListSource.getItems().setAll(DownloadProviders.AUTO_PROVIDERS.keySet());
|
||||||
selectedItemPropertyFor(cboVersionListSource).bindBidirectional(config().versionListSourceProperty());
|
selectedItemPropertyFor(cboVersionListSource).bindBidirectional(config().versionListSourceProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ public class DownloadSettingsPage extends StackPane {
|
|||||||
downloadSourcePane.setRight(cboDownloadSource);
|
downloadSourcePane.setRight(cboDownloadSource);
|
||||||
FXUtils.setLimitWidth(cboDownloadSource, 420);
|
FXUtils.setLimitWidth(cboDownloadSource, 420);
|
||||||
|
|
||||||
cboDownloadSource.getItems().setAll(DownloadProviders.rawProviders.keySet());
|
cboDownloadSource.getItems().setAll(DownloadProviders.DIRECT_PROVIDERS.keySet());
|
||||||
selectedItemPropertyFor(cboDownloadSource).bindBidirectional(config().downloadTypeProperty());
|
selectedItemPropertyFor(cboDownloadSource).bindBidirectional(config().downloadTypeProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
/*
|
|
||||||
* Hello Minecraft! Launcher
|
|
||||||
* Copyright (C) 2020 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.download;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Unmodifiable;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The download provider that changes the real download source in need.
|
|
||||||
*
|
|
||||||
* @author huangyuhui
|
|
||||||
*/
|
|
||||||
public class AdaptedDownloadProvider implements DownloadProvider {
|
|
||||||
|
|
||||||
private @Unmodifiable List<DownloadProvider> downloadProviderCandidates;
|
|
||||||
|
|
||||||
public void setDownloadProviderCandidates(List<DownloadProvider> downloadProviderCandidates) {
|
|
||||||
this.downloadProviderCandidates = List.copyOf(downloadProviderCandidates);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DownloadProvider getPreferredDownloadProvider() {
|
|
||||||
List<DownloadProvider> d = downloadProviderCandidates;
|
|
||||||
if (d == null || d.isEmpty()) {
|
|
||||||
throw new IllegalStateException("No download provider candidate");
|
|
||||||
}
|
|
||||||
return d.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getVersionListURL() {
|
|
||||||
return getPreferredDownloadProvider().getVersionListURL();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAssetBaseURL() {
|
|
||||||
return getPreferredDownloadProvider().getAssetBaseURL();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String injectURL(String baseURL) {
|
|
||||||
return getPreferredDownloadProvider().injectURL(baseURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<URI> getAssetObjectCandidates(String assetObjectLocation) {
|
|
||||||
return downloadProviderCandidates.stream()
|
|
||||||
.flatMap(d -> d.getAssetObjectCandidates(assetObjectLocation).stream())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<URI> injectURLWithCandidates(String baseURL) {
|
|
||||||
return downloadProviderCandidates.stream()
|
|
||||||
.flatMap(d -> d.injectURLWithCandidates(baseURL).stream())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<URI> injectURLsWithCandidates(List<String> urls) {
|
|
||||||
return downloadProviderCandidates.stream()
|
|
||||||
.flatMap(d -> d.injectURLsWithCandidates(urls).stream())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public VersionList<?> getVersionListById(String id) {
|
|
||||||
return getPreferredDownloadProvider().getVersionListById(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getConcurrency() {
|
|
||||||
return getPreferredDownloadProvider().getConcurrency();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,60 +18,99 @@
|
|||||||
package org.jackhuang.hmcl.download;
|
package org.jackhuang.hmcl.download;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/// @author huangyuhui
|
||||||
* Official Download Provider fetches version list from Mojang and
|
public final class AutoDownloadProvider implements DownloadProvider {
|
||||||
* download files from mcbbs.
|
private final List<DownloadProvider> versionListProviders;
|
||||||
*
|
private final List<DownloadProvider> fileProviders;
|
||||||
* @author huangyuhui
|
private final ConcurrentMap<String, VersionList<?>> versionLists = new ConcurrentHashMap<>();
|
||||||
*/
|
|
||||||
public class AutoDownloadProvider implements DownloadProvider {
|
|
||||||
private final DownloadProvider versionListProvider;
|
|
||||||
private final DownloadProvider fileProvider;
|
|
||||||
|
|
||||||
public AutoDownloadProvider(DownloadProvider versionListProvider, DownloadProvider fileProvider) {
|
public AutoDownloadProvider(
|
||||||
this.versionListProvider = versionListProvider;
|
List<DownloadProvider> versionListProviders,
|
||||||
this.fileProvider = fileProvider;
|
List<DownloadProvider> fileProviders) {
|
||||||
|
if (versionListProviders == null || versionListProviders.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("versionListProviders must not be null or empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileProviders == null || fileProviders.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("fileProviders must not be null or empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.versionListProviders = versionListProviders;
|
||||||
|
this.fileProviders = fileProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AutoDownloadProvider(DownloadProvider... downloadProviderCandidate) {
|
||||||
|
if (downloadProviderCandidate.length == 0) {
|
||||||
|
throw new IllegalArgumentException("Download provider must have at least one download provider");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.versionListProviders = List.of(downloadProviderCandidate);
|
||||||
|
this.fileProviders = versionListProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DownloadProvider getPreferredDownloadProvider() {
|
||||||
|
return fileProviders.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<URI> getAll(
|
||||||
|
List<DownloadProvider> providers,
|
||||||
|
Function<DownloadProvider, List<URI>> function) {
|
||||||
|
LinkedHashSet<URI> result = new LinkedHashSet<>();
|
||||||
|
for (DownloadProvider provider : providers) {
|
||||||
|
result.addAll(function.apply(provider));
|
||||||
|
}
|
||||||
|
return List.copyOf(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getVersionListURL() {
|
public List<URI> getVersionListURLs() {
|
||||||
return versionListProvider.getVersionListURL();
|
return getAll(versionListProviders, DownloadProvider::getVersionListURLs);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAssetBaseURL() {
|
|
||||||
return fileProvider.getAssetBaseURL();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String injectURL(String baseURL) {
|
public String injectURL(String baseURL) {
|
||||||
return fileProvider.injectURL(baseURL);
|
return getPreferredDownloadProvider().injectURL(baseURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<URI> getAssetObjectCandidates(String assetObjectLocation) {
|
public List<URI> getAssetObjectCandidates(String assetObjectLocation) {
|
||||||
return fileProvider.getAssetObjectCandidates(assetObjectLocation);
|
return getAll(fileProviders, provider -> provider.getAssetObjectCandidates(assetObjectLocation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<URI> injectURLWithCandidates(String baseURL) {
|
public List<URI> injectURLWithCandidates(String baseURL) {
|
||||||
return fileProvider.injectURLWithCandidates(baseURL);
|
return getAll(fileProviders, provider -> provider.injectURLWithCandidates(baseURL));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<URI> injectURLsWithCandidates(List<String> urls) {
|
public List<URI> injectURLsWithCandidates(List<String> urls) {
|
||||||
return fileProvider.injectURLsWithCandidates(urls);
|
return getAll(fileProviders, provider -> provider.injectURLsWithCandidates(urls));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VersionList<?> getVersionListById(String id) {
|
public VersionList<?> getVersionListById(String id) {
|
||||||
return versionListProvider.getVersionListById(id);
|
return versionLists.computeIfAbsent(id, value -> {
|
||||||
|
VersionList<?>[] lists = new VersionList<?>[versionListProviders.size()];
|
||||||
|
for (int i = 0; i < versionListProviders.size(); i++) {
|
||||||
|
lists[i] = versionListProviders.get(i).getVersionListById(value);
|
||||||
|
}
|
||||||
|
return new MultipleSourceVersionList(lists);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getConcurrency() {
|
public int getConcurrency() {
|
||||||
return fileProvider.getConcurrency();
|
return getPreferredDownloadProvider().getConcurrency();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AutoDownloadProvider[versionListProviders=%s, fileProviders=%s]".formatted(versionListProviders, fileProviders);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ import org.jackhuang.hmcl.download.optifine.OptiFineBMCLVersionList;
|
|||||||
import org.jackhuang.hmcl.download.quilt.QuiltAPIVersionList;
|
import org.jackhuang.hmcl.download.quilt.QuiltAPIVersionList;
|
||||||
import org.jackhuang.hmcl.download.quilt.QuiltVersionList;
|
import org.jackhuang.hmcl.download.quilt.QuiltVersionList;
|
||||||
import org.jackhuang.hmcl.util.Pair;
|
import org.jackhuang.hmcl.util.Pair;
|
||||||
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -99,13 +101,13 @@ public final class BMCLAPIDownloadProvider implements DownloadProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getVersionListURL() {
|
public List<URI> getVersionListURLs() {
|
||||||
return apiRoot + "/mc/game/version_manifest.json";
|
return List.of(URI.create(apiRoot + "/mc/game/version_manifest.json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAssetBaseURL() {
|
public List<URI> getAssetObjectCandidates(String assetObjectLocation) {
|
||||||
return apiRoot + "/assets/";
|
return List.of(NetworkUtils.toURI(apiRoot + "/assets/" + assetObjectLocation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.download;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Official Download Provider fetches version list from Mojang and
|
|
||||||
* download files from mcbbs.
|
|
||||||
*
|
|
||||||
* @author huangyuhui
|
|
||||||
*/
|
|
||||||
public final class BalancedDownloadProvider implements DownloadProvider {
|
|
||||||
private final DownloadProvider[] candidates;
|
|
||||||
private final Map<String, VersionList<?>> versionLists = new HashMap<>();
|
|
||||||
|
|
||||||
public BalancedDownloadProvider(DownloadProvider... candidates) {
|
|
||||||
this.candidates = candidates;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getVersionListURL() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAssetBaseURL() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String injectURL(String baseURL) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public VersionList<?> getVersionListById(String id) {
|
|
||||||
return versionLists.computeIfAbsent(id, value -> {
|
|
||||||
VersionList<?>[] lists = new VersionList<?>[candidates.length];
|
|
||||||
for (int i = 0; i < candidates.length; i++) {
|
|
||||||
lists[i] = candidates[i].getVersionListById(value);
|
|
||||||
}
|
|
||||||
return new MultipleSourceVersionList(lists);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getConcurrency() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,64 +20,55 @@ package org.jackhuang.hmcl.download;
|
|||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/// The service provider that provides Minecraft online file downloads.
|
||||||
* The service provider that provides Minecraft online file downloads.
|
///
|
||||||
*
|
/// @author huangyuhui
|
||||||
* @author huangyuhui
|
|
||||||
*/
|
|
||||||
public interface DownloadProvider {
|
public interface DownloadProvider {
|
||||||
|
|
||||||
String getVersionListURL();
|
List<URI> getVersionListURLs();
|
||||||
|
|
||||||
String getAssetBaseURL();
|
List<URI> getAssetObjectCandidates(String assetObjectLocation);
|
||||||
|
|
||||||
default List<URI> getAssetObjectCandidates(String assetObjectLocation) {
|
/// Inject into original URL provided by Mojang and Forge.
|
||||||
return List.of(NetworkUtils.toURI(getAssetBaseURL() + assetObjectLocation));
|
///
|
||||||
}
|
/// Since there are many provided URLs that are written in JSONs and are unmodifiable,
|
||||||
|
/// this method provides a way to change them.
|
||||||
/**
|
///
|
||||||
* Inject into original URL provided by Mojang and Forge.
|
/// @param baseURL original URL provided by Mojang and Forge.
|
||||||
*
|
/// @return the URL that is equivalent to `baseURL``, but belongs to your own service provider.
|
||||||
* Since there are many provided URLs that are written in JSONs and are unmodifiable,
|
|
||||||
* this method provides a way to change them.
|
|
||||||
*
|
|
||||||
* @param baseURL original URL provided by Mojang and Forge.
|
|
||||||
* @return the URL that is equivalent to [baseURL], but belongs to your own service provider.
|
|
||||||
*/
|
|
||||||
String injectURL(String baseURL);
|
String injectURL(String baseURL);
|
||||||
|
|
||||||
/**
|
/// Inject into original URL provided by Mojang and Forge.
|
||||||
* Inject into original URL provided by Mojang and Forge.
|
///
|
||||||
*
|
/// Since there are many provided URLs that are written in JSONs and are unmodifiable,
|
||||||
* Since there are many provided URLs that are written in JSONs and are unmodifiable,
|
/// this method provides a way to change them.
|
||||||
* this method provides a way to change them.
|
///
|
||||||
*
|
/// @param baseURL original URL provided by Mojang and Forge.
|
||||||
* @param baseURL original URL provided by Mojang and Forge.
|
/// @return the URL that is equivalent to `baseURL`, but belongs to your own service provider.
|
||||||
* @return the URL that is equivalent to [baseURL], but belongs to your own service provider.
|
|
||||||
*/
|
|
||||||
default List<URI> injectURLWithCandidates(String baseURL) {
|
default List<URI> injectURLWithCandidates(String baseURL) {
|
||||||
return List.of(NetworkUtils.toURI(injectURL(baseURL)));
|
return List.of(NetworkUtils.toURI(injectURL(baseURL)));
|
||||||
}
|
}
|
||||||
|
|
||||||
default List<URI> injectURLsWithCandidates(List<String> urls) {
|
default List<URI> injectURLsWithCandidates(List<String> urls) {
|
||||||
return urls.stream().flatMap(url -> injectURLWithCandidates(url).stream()).collect(Collectors.toList());
|
LinkedHashSet<URI> result = new LinkedHashSet<>();
|
||||||
|
for (String url : urls) {
|
||||||
|
result.addAll(injectURLWithCandidates(url));
|
||||||
|
}
|
||||||
|
return List.copyOf(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// the specific version list that this download provider provides. i.e. "fabric", "forge", "liteloader", "game", "optifine"
|
||||||
* the specific version list that this download provider provides. i.e. "fabric", "forge", "liteloader", "game", "optifine"
|
///
|
||||||
*
|
/// @param id the id of specific version list that this download provider provides. i.e. "fabric", "forge", "liteloader", "game", "optifine"
|
||||||
* @param id the id of specific version list that this download provider provides. i.e. "fabric", "forge", "liteloader", "game", "optifine"
|
/// @return the version list
|
||||||
* @return the version list
|
/// @throws IllegalArgumentException if the version list does not exist
|
||||||
* @throws IllegalArgumentException if the version list does not exist
|
|
||||||
*/
|
|
||||||
VersionList<?> getVersionListById(String id);
|
VersionList<?> getVersionListById(String id);
|
||||||
|
|
||||||
/**
|
/// The maximum download concurrency that this download provider supports.
|
||||||
* The maximum download concurrency that this download provider supports.
|
///
|
||||||
* @return the maximum download concurrency.
|
/// @return the maximum download concurrency.
|
||||||
*/
|
|
||||||
int getConcurrency();
|
int getConcurrency();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download;
|
package org.jackhuang.hmcl.download;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -26,7 +28,7 @@ import java.util.Objects;
|
|||||||
*/
|
*/
|
||||||
public final class DownloadProviderWrapper implements DownloadProvider {
|
public final class DownloadProviderWrapper implements DownloadProvider {
|
||||||
|
|
||||||
private DownloadProvider provider;
|
private volatile DownloadProvider provider;
|
||||||
|
|
||||||
public DownloadProviderWrapper(DownloadProvider provider) {
|
public DownloadProviderWrapper(DownloadProvider provider) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
@@ -46,13 +48,8 @@ public final class DownloadProviderWrapper implements DownloadProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getVersionListURL() {
|
public List<URI> getVersionListURLs() {
|
||||||
return getProvider().getVersionListURL();
|
return getProvider().getVersionListURLs();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAssetBaseURL() {
|
|
||||||
return getProvider().getAssetBaseURL();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -72,11 +69,41 @@ public final class DownloadProviderWrapper implements DownloadProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VersionList<?> getVersionListById(String id) {
|
public VersionList<?> getVersionListById(String id) {
|
||||||
return getProvider().getVersionListById(id);
|
|
||||||
|
return new VersionList<>() {
|
||||||
|
@Override
|
||||||
|
public boolean hasType() {
|
||||||
|
return getProvider().getVersionListById(id).hasType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Task<?> refreshAsync() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Task<?> refreshAsync(String gameVersion) {
|
||||||
|
return getProvider().getVersionListById(id).refreshAsync(gameVersion)
|
||||||
|
.thenComposeAsync(() -> {
|
||||||
|
lock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
versions.putAll(gameVersion, getProvider().getVersionListById(id).getVersions(gameVersion));
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getConcurrency() {
|
public int getConcurrency() {
|
||||||
return getProvider().getConcurrency();
|
return getProvider().getConcurrency();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "DownloadProviderWrapper[provider=%s]".formatted(provider);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ import org.jackhuang.hmcl.download.neoforge.NeoForgeOfficialVersionList;
|
|||||||
import org.jackhuang.hmcl.download.optifine.OptiFineBMCLVersionList;
|
import org.jackhuang.hmcl.download.optifine.OptiFineBMCLVersionList;
|
||||||
import org.jackhuang.hmcl.download.quilt.QuiltAPIVersionList;
|
import org.jackhuang.hmcl.download.quilt.QuiltAPIVersionList;
|
||||||
import org.jackhuang.hmcl.download.quilt.QuiltVersionList;
|
import org.jackhuang.hmcl.download.quilt.QuiltVersionList;
|
||||||
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
@@ -61,13 +65,13 @@ public class MojangDownloadProvider implements DownloadProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getVersionListURL() {
|
public List<URI> getVersionListURLs() {
|
||||||
return "https://piston-meta.mojang.com/mc/game/version_manifest.json";
|
return List.of(URI.create("https://piston-meta.mojang.com/mc/game/version_manifest.json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAssetBaseURL() {
|
public List<URI> getAssetObjectCandidates(String assetObjectLocation) {
|
||||||
return "https://resources.download.minecraft.net/";
|
return List.of(NetworkUtils.toURI("https://resources.download.minecraft.net/" + assetObjectLocation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -40,11 +40,6 @@ public class MultipleSourceVersionList extends VersionList<RemoteVersion> {
|
|||||||
return hasType;
|
return hasType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Task<?> loadAsync() {
|
|
||||||
throw new UnsupportedOperationException("MultipleSourceVersionList does not support loading the entire remote version list.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task<?> refreshAsync() {
|
public Task<?> refreshAsync() {
|
||||||
throw new UnsupportedOperationException("MultipleSourceVersionList does not support loading the entire remote version list.");
|
throw new UnsupportedOperationException("MultipleSourceVersionList does not support loading the entire remote version list.");
|
||||||
|
|||||||
@@ -71,17 +71,6 @@ public abstract class VersionList<T extends RemoteVersion> {
|
|||||||
return refreshAsync();
|
return refreshAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<?> loadAsync() {
|
|
||||||
return Task.composeAsync(() -> {
|
|
||||||
lock.readLock().lock();
|
|
||||||
try {
|
|
||||||
return isLoaded() ? null : refreshAsync();
|
|
||||||
} finally {
|
|
||||||
lock.readLock().unlock();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<?> loadAsync(String gameVersion) {
|
public Task<?> loadAsync(String gameVersion) {
|
||||||
return Task.composeAsync(() -> {
|
return Task.composeAsync(() -> {
|
||||||
lock.readLock().lock();
|
lock.readLock().lock();
|
||||||
|
|||||||
@@ -56,11 +56,6 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Task<?> loadAsync() {
|
|
||||||
throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task<?> refreshAsync() {
|
public Task<?> 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.");
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public final class GameVersionList extends VersionList<GameRemoteVersion> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task<?> refreshAsync() {
|
public Task<?> refreshAsync() {
|
||||||
return new GetTask(downloadProvider.getVersionListURL()).thenGetJsonAsync(GameRemoteVersions.class)
|
return new GetTask(downloadProvider.getVersionListURLs()).thenGetJsonAsync(GameRemoteVersions.class)
|
||||||
.thenAcceptAsync(root -> {
|
.thenAcceptAsync(root -> {
|
||||||
GameRemoteVersions unlistedVersions = null;
|
GameRemoteVersions unlistedVersions = null;
|
||||||
|
|
||||||
@@ -91,4 +91,9 @@ public final class GameVersionList extends VersionList<GameRemoteVersion> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "GameVersionList[downloadProvider=%s]".formatted(downloadProvider);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,17 +60,19 @@ public final class LiteLoaderBMCLVersionList extends VersionList<LiteLoaderRemot
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task<?> refreshAsync(String gameVersion) {
|
public Task<?> refreshAsync(String gameVersion) {
|
||||||
return new GetTask(NetworkUtils.withQuery(downloadProvider.injectURLWithCandidates("https://bmclapi2.bangbang93.com/liteloader/list"), Map.of("mcversion", gameVersion)))
|
return new GetTask(
|
||||||
|
NetworkUtils.withQuery(downloadProvider.getApiRoot() + "/liteloader/list", Map.of(
|
||||||
|
"mcversion", gameVersion
|
||||||
|
)))
|
||||||
.thenGetJsonAsync(LiteLoaderBMCLVersion.class)
|
.thenGetJsonAsync(LiteLoaderBMCLVersion.class)
|
||||||
.thenAcceptAsync(v -> {
|
.thenAcceptAsync(v -> {
|
||||||
lock.writeLock().lock();
|
lock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
versions.clear();
|
versions.clear();
|
||||||
|
|
||||||
versions.put(gameVersion, new LiteLoaderRemoteVersion(
|
versions.put(gameVersion, new LiteLoaderRemoteVersion(
|
||||||
gameVersion, v.version, RemoteVersion.Type.UNCATEGORIZED,
|
gameVersion, v.version, RemoteVersion.Type.UNCATEGORIZED,
|
||||||
Collections.singletonList(NetworkUtils.withQuery(
|
Collections.singletonList(NetworkUtils.withQuery(
|
||||||
downloadProvider.injectURL("https://bmclapi2.bangbang93.com/liteloader/download"),
|
downloadProvider.getApiRoot() + "/liteloader/download",
|
||||||
Collections.singletonMap("version", v.version)
|
Collections.singletonMap("version", v.version)
|
||||||
)),
|
)),
|
||||||
v.build.getTweakClass(), v.build.getLibraries()
|
v.build.getTweakClass(), v.build.getLibraries()
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task<?> refreshAsync(String gameVersion) {
|
public Task<?> refreshAsync(String gameVersion) {
|
||||||
return new GetTask(downloadProvider.injectURL(LITELOADER_LIST))
|
return new GetTask(downloadProvider.injectURLWithCandidates(LITELOADER_LIST))
|
||||||
.thenGetJsonAsync(LiteLoaderVersionsRoot.class)
|
.thenGetJsonAsync(LiteLoaderVersionsRoot.class)
|
||||||
.thenAcceptAsync(root -> {
|
.thenAcceptAsync(root -> {
|
||||||
LiteLoaderGameVersions versions = root.getVersions().get(gameVersion);
|
LiteLoaderGameVersions versions = root.getVersions().get(gameVersion);
|
||||||
|
|||||||
@@ -45,11 +45,6 @@ public final class NeoForgeBMCLVersionList extends VersionList<NeoForgeRemoteVer
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Task<?> loadAsync() {
|
|
||||||
throw new UnsupportedOperationException("NeoForgeBMCLVersionList does not support loading the entire NeoForge remote version list.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task<?> refreshAsync() {
|
public Task<?> refreshAsync() {
|
||||||
throw new UnsupportedOperationException("NeoForgeBMCLVersionList does not support loading the entire NeoForge remote version list.");
|
throw new UnsupportedOperationException("NeoForgeBMCLVersionList does not support loading the entire NeoForge remote version list.");
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ public final class NeoForgeOfficialVersionList extends VersionList<NeoForgeRemot
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final String OLD_URL = "https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/forge";
|
private static final String OLD_URL = "https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/forge";
|
||||||
|
|
||||||
private static final String META_URL = "https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/neoforge";
|
private static final String META_URL = "https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/neoforge";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -38,8 +37,8 @@ public final class NeoForgeOfficialVersionList extends VersionList<NeoForgeRemot
|
|||||||
@Override
|
@Override
|
||||||
public Task<?> refreshAsync() {
|
public Task<?> refreshAsync() {
|
||||||
return Task.allOf(
|
return Task.allOf(
|
||||||
new GetTask(downloadProvider.injectURL(OLD_URL)).thenGetJsonAsync(OfficialAPIResult.class),
|
new GetTask(downloadProvider.injectURLWithCandidates(OLD_URL)).thenGetJsonAsync(OfficialAPIResult.class),
|
||||||
new GetTask(downloadProvider.injectURL(META_URL)).thenGetJsonAsync(OfficialAPIResult.class)
|
new GetTask(downloadProvider.injectURLWithCandidates(META_URL)).thenGetJsonAsync(OfficialAPIResult.class)
|
||||||
).thenAcceptAsync(results -> {
|
).thenAcceptAsync(results -> {
|
||||||
lock.writeLock().lock();
|
lock.writeLock().lock();
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ public final class OptiFineBMCLVersionList extends VersionList<OptiFineRemoteVer
|
|||||||
Set<String> duplicates = new HashSet<>();
|
Set<String> duplicates = new HashSet<>();
|
||||||
for (OptiFineVersion element : root) {
|
for (OptiFineVersion element : root) {
|
||||||
String version = element.getType() + "_" + element.getPatch();
|
String version = element.getType() + "_" + element.getPatch();
|
||||||
String mirror = "https://bmclapi2.bangbang93.com/optifine/" + toLookupVersion(element.getGameVersion()) + "/" + element.getType() + "/" + element.getPatch();
|
String mirror = apiRoot + "/optifine/" + toLookupVersion(element.getGameVersion()) + "/" + element.getType() + "/" + element.getPatch();
|
||||||
if (!duplicates.add(mirror))
|
if (!duplicates.add(mirror))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user