add: retry other download providers when one failed.
This commit is contained in:
@@ -26,9 +26,12 @@ import org.jackhuang.hmcl.download.MojangDownloadProvider;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
@@ -57,10 +60,22 @@ public final class DownloadProviders {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current primary preferred download provider
|
||||
*/
|
||||
public static DownloadProvider getDownloadProvider() {
|
||||
return downloadProviderProperty.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Preferred download providers have the primary one first, the official one next.
|
||||
* @return the preferred download providers
|
||||
*/
|
||||
public static List<DownloadProvider> getPreferredDownloadProviders() {
|
||||
DownloadProvider provider = getDownloadProvider();
|
||||
return Stream.concat(Stream.of(provider), providersById.values().stream().filter(x -> x != provider)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static ObservableObjectValue<DownloadProvider> downloadProviderProperty() {
|
||||
return downloadProviderProperty;
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ public final class Profile implements Observable {
|
||||
}
|
||||
|
||||
public DefaultDependencyManager getDependency() {
|
||||
return new DefaultDependencyManager(repository, DownloadProviders.getDownloadProvider(), HMCLCacheRepository.REPOSITORY);
|
||||
return new DefaultDependencyManager(repository, DownloadProviders.getDownloadProvider(), DownloadProviders.getPreferredDownloadProviders(), HMCLCacheRepository.REPOSITORY);
|
||||
}
|
||||
|
||||
public VersionSetting getVersionSetting(String id) {
|
||||
|
||||
@@ -88,7 +88,7 @@ public final class InstallerWizardProvider implements WizardProvider {
|
||||
|
||||
@Override
|
||||
public Node createPage(WizardController controller, int step, Map<String, Object> settings) {
|
||||
DownloadProvider provider = profile.getDependency().getDownloadProvider();
|
||||
DownloadProvider provider = profile.getDependency().getPrimaryDownloadProvider();
|
||||
switch (step) {
|
||||
case 0:
|
||||
return new AdditionalInstallersPage(this, controller, profile.getRepository(), provider);
|
||||
|
||||
@@ -64,7 +64,7 @@ public final class UpdateInstallerWizardProvider implements WizardProvider {
|
||||
|
||||
@Override
|
||||
public Node createPage(WizardController controller, int step, Map<String, Object> settings) {
|
||||
DownloadProvider provider = profile.getDependency().getDownloadProvider();
|
||||
DownloadProvider provider = profile.getDependency().getPrimaryDownloadProvider();
|
||||
switch (step) {
|
||||
case 0:
|
||||
return new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer." + libraryId)), gameVersion, provider, libraryId, () -> {
|
||||
|
||||
@@ -68,7 +68,7 @@ public final class VanillaInstallWizardProvider implements WizardProvider {
|
||||
|
||||
@Override
|
||||
public Node createPage(WizardController controller, int step, Map<String, Object> settings) {
|
||||
DownloadProvider provider = profile.getDependency().getDownloadProvider();
|
||||
DownloadProvider provider = profile.getDependency().getPrimaryDownloadProvider();
|
||||
switch (step) {
|
||||
case 0:
|
||||
return new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer.game")), "", provider, "game", () -> controller.onNext(new InstallersPage(controller, profile.getRepository(), provider)));
|
||||
|
||||
@@ -17,19 +17,23 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public abstract class AbstractDependencyManager implements DependencyManager {
|
||||
|
||||
public abstract DownloadProvider getDownloadProvider();
|
||||
public abstract DownloadProvider getPrimaryDownloadProvider();
|
||||
|
||||
public abstract List<DownloadProvider> getPreferredDownloadProviders();
|
||||
|
||||
@Override
|
||||
public abstract DefaultCacheRepository getCacheRepository();
|
||||
|
||||
@Override
|
||||
public VersionList<?> getVersionList(String id) {
|
||||
return getDownloadProvider().getVersionListById(id);
|
||||
return getPrimaryDownloadProvider().getVersionListById(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Note: This class has no state.
|
||||
@@ -39,11 +40,13 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
|
||||
private final DefaultGameRepository repository;
|
||||
private final DownloadProvider downloadProvider;
|
||||
private final List<DownloadProvider> preferredDownloadProviders;
|
||||
private final DefaultCacheRepository cacheRepository;
|
||||
|
||||
public DefaultDependencyManager(DefaultGameRepository repository, DownloadProvider downloadProvider, DefaultCacheRepository cacheRepository) {
|
||||
public DefaultDependencyManager(DefaultGameRepository repository, DownloadProvider downloadProvider, List<DownloadProvider> preferredDownloadProviders, DefaultCacheRepository cacheRepository) {
|
||||
this.repository = repository;
|
||||
this.downloadProvider = downloadProvider;
|
||||
this.preferredDownloadProviders = preferredDownloadProviders;
|
||||
this.cacheRepository = cacheRepository;
|
||||
}
|
||||
|
||||
@@ -53,10 +56,15 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadProvider getDownloadProvider() {
|
||||
public DownloadProvider getPrimaryDownloadProvider() {
|
||||
return downloadProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DownloadProvider> getPreferredDownloadProviders() {
|
||||
return preferredDownloadProviders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultCacheRepository getCacheRepository() {
|
||||
return cacheRepository;
|
||||
|
||||
@@ -34,7 +34,7 @@ public class DefaultGameBuilder extends GameBuilder {
|
||||
|
||||
public DefaultGameBuilder(DefaultDependencyManager dependencyManager) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.downloadProvider = dependencyManager.getDownloadProvider();
|
||||
this.downloadProvider = dependencyManager.getPrimaryDownloadProvider();
|
||||
}
|
||||
|
||||
public DefaultDependencyManager getDependencyManager() {
|
||||
|
||||
@@ -50,7 +50,7 @@ public final class FabricInstallTask extends Task<Version> {
|
||||
this.version = version;
|
||||
this.remote = remoteVersion;
|
||||
|
||||
launchMetaTask = new GetTask(NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(getLaunchMetaUrl(remote.getGameVersion(), remote.getSelfVersion()))))
|
||||
launchMetaTask = new GetTask(NetworkUtils.toURL(dependencyManager.getPrimaryDownloadProvider().injectURL(getLaunchMetaUrl(remote.getGameVersion(), remote.getSelfVersion()))))
|
||||
.setCacheRepository(dependencyManager.getCacheRepository());
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,11 @@ import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -101,8 +103,12 @@ public final class GameAssetDownloadTask extends Task<Void> {
|
||||
if (file.isFile())
|
||||
dependencyManager.getCacheRepository().tryCacheFile(file.toPath(), CacheRepository.SHA1, assetObject.getHash());
|
||||
else {
|
||||
String url = dependencyManager.getDownloadProvider().getAssetBaseURL() + assetObject.getLocation();
|
||||
FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(url), file, new FileDownloadTask.IntegrityCheck("SHA-1", assetObject.getHash()));
|
||||
List<URL> urls = dependencyManager.getPreferredDownloadProviders().stream()
|
||||
.map(downloadProvider -> downloadProvider.getAssetBaseURL() + assetObject.getLocation())
|
||||
.map(NetworkUtils::toURL)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
FileDownloadTask task = new FileDownloadTask(urls, file, new FileDownloadTask.IntegrityCheck("SHA-1", assetObject.getHash()));
|
||||
task.setName(assetObject.getHash());
|
||||
dependencies.add(task
|
||||
.setCacheRepository(dependencyManager.getCacheRepository())
|
||||
|
||||
@@ -65,7 +65,7 @@ public final class GameAssetIndexDownloadTask extends Task<Void> {
|
||||
// We should not check the hash code of asset index file since this file is not consistent
|
||||
// And Mojang will modify this file anytime. So assetIndex.hash might be outdated.
|
||||
dependencies.add(new FileDownloadTask(
|
||||
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(assetIndexInfo.getUrl())),
|
||||
NetworkUtils.toURL(dependencyManager.getPrimaryDownloadProvider().injectURL(assetIndexInfo.getUrl())),
|
||||
assetIndexFile
|
||||
).setCacheRepository(dependencyManager.getCacheRepository()));
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public final class GameDownloadTask extends Task<Void> {
|
||||
File jar = dependencyManager.getGameRepository().getVersionJar(version);
|
||||
|
||||
FileDownloadTask task = new FileDownloadTask(
|
||||
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())),
|
||||
NetworkUtils.toURL(dependencyManager.getPrimaryDownloadProvider().injectURL(version.getDownloadInfo().getUrl())),
|
||||
jar,
|
||||
IntegrityCheck.of(CacheRepository.SHA1, version.getDownloadInfo().getSha1()))
|
||||
.setCaching(true)
|
||||
|
||||
@@ -68,7 +68,7 @@ public class LibraryDownloadTask extends Task<Void> {
|
||||
this.library = library;
|
||||
this.cacheRepository = dependencyManager.getCacheRepository();
|
||||
|
||||
url = dependencyManager.getDownloadProvider().injectURL(library.getDownload().getUrl());
|
||||
url = dependencyManager.getPrimaryDownloadProvider().injectURL(library.getDownload().getUrl());
|
||||
jar = file;
|
||||
|
||||
xzFile = new File(file.getAbsoluteFile().getParentFile(), file.getName() + ".pack.xz");
|
||||
|
||||
@@ -69,7 +69,7 @@ public final class VersionJsonDownloadTask extends Task<String> {
|
||||
RemoteVersion remoteVersion = gameVersionList.getVersion(gameVersion, gameVersion)
|
||||
.orElseThrow(() -> new IOException("Cannot find specific version " + gameVersion + " in remote repository"));
|
||||
dependencies.add(new GetTask(
|
||||
dependencyManager.getDownloadProvider().injectURLs(remoteVersion.getUrl())
|
||||
dependencyManager.getPrimaryDownloadProvider().injectURLs(remoteVersion.getUrl())
|
||||
.map(NetworkUtils::toURL).collect(Collectors.toList()),
|
||||
UTF_8).storeTo(this::setResult));
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ public class FileDownloadTask extends Task<Void> {
|
||||
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
|
||||
*/
|
||||
public FileDownloadTask(URL url, File file, IntegrityCheck integrityCheck) {
|
||||
this(url, file, integrityCheck, 5);
|
||||
this(Collections.singletonList(url), file, integrityCheck);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,13 +119,7 @@ public class FileDownloadTask extends Task<Void> {
|
||||
* @param retry the times for retrying if downloading fails.
|
||||
*/
|
||||
public FileDownloadTask(URL url, File file, IntegrityCheck integrityCheck, int retry) {
|
||||
this.urls = Collections.singletonList(url);
|
||||
this.file = file;
|
||||
this.integrityCheck = integrityCheck;
|
||||
this.retry = retry;
|
||||
|
||||
setName(file.getName());
|
||||
setExecutor(Schedulers.io());
|
||||
this(Collections.singletonList(url), file, integrityCheck, retry);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,13 +129,24 @@ public class FileDownloadTask extends Task<Void> {
|
||||
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
|
||||
*/
|
||||
public FileDownloadTask(List<URL> urls, File file, IntegrityCheck integrityCheck) {
|
||||
this(urls, file, integrityCheck, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param urls urls of remote file, will be attempted in order.
|
||||
* @param file the location that download to.
|
||||
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
|
||||
* @param retry the times for retrying if downloading fails.
|
||||
*/
|
||||
public FileDownloadTask(List<URL> urls, File file, IntegrityCheck integrityCheck, int retry) {
|
||||
if (urls == null || urls.isEmpty())
|
||||
throw new IllegalArgumentException("At least one URL is required");
|
||||
|
||||
this.urls = new ArrayList<>(urls);
|
||||
this.file = file;
|
||||
this.integrityCheck = integrityCheck;
|
||||
this.retry = urls.size();
|
||||
this.retry = retry;
|
||||
|
||||
setName(file.getName());
|
||||
setExecutor(Schedulers.io());
|
||||
@@ -208,8 +213,8 @@ public class FileDownloadTask extends Task<Void> {
|
||||
Logging.LOG.log(Level.FINER, "Downloading " + urls.get(0) + " to " + file);
|
||||
Exception exception = null;
|
||||
|
||||
for (int repeat = 0; repeat < retry; repeat++) {
|
||||
URL url = urls.get(repeat % urls.size());
|
||||
for (int repeat = 0; repeat < retry * urls.size(); repeat++) {
|
||||
URL url = urls.get(repeat / urls.size());
|
||||
if (isCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user