mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-03-07 17:36:52 +08:00
Fix forge installer 404
This commit is contained in:
parent
e69d149c34
commit
5eba896e6d
@ -17,6 +17,9 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* The service provider that provides Minecraft online file downloads.
|
||||
*
|
||||
@ -39,6 +42,12 @@ public interface DownloadProvider {
|
||||
*/
|
||||
String injectURL(String baseURL);
|
||||
|
||||
default Stream<String> injectURLs(String[] baseURLs) {
|
||||
Stream<String> urls = Arrays.stream(baseURLs);
|
||||
Stream<String> jsonURLs = Arrays.stream(baseURLs).map(this::injectURL);
|
||||
return Stream.concat(jsonURLs, urls);
|
||||
}
|
||||
|
||||
/**
|
||||
* the specific version list that this download provider provides. i.e. "forge", "liteloader", "game", "optifine"
|
||||
*
|
||||
|
@ -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.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@ -34,7 +35,7 @@ public class RemoteVersion implements Comparable<RemoteVersion> {
|
||||
private final String libraryId;
|
||||
private final String gameVersion;
|
||||
private final String selfVersion;
|
||||
private final String url;
|
||||
private final String[] url;
|
||||
private final Type type;
|
||||
|
||||
/**
|
||||
@ -44,8 +45,8 @@ public class RemoteVersion implements Comparable<RemoteVersion> {
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param url the installer or universal jar URL.
|
||||
*/
|
||||
public RemoteVersion(String libraryId, String gameVersion, String selfVersion, String url) {
|
||||
this(libraryId, gameVersion, selfVersion, url, Type.UNCATEGORIZED);
|
||||
public RemoteVersion(String libraryId, String gameVersion, String selfVersion, String... url) {
|
||||
this(libraryId, gameVersion, selfVersion, Type.UNCATEGORIZED, url);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,7 +56,7 @@ public class RemoteVersion implements Comparable<RemoteVersion> {
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param url the installer or universal jar URL.
|
||||
*/
|
||||
public RemoteVersion(String libraryId, String gameVersion, String selfVersion, String url, Type type) {
|
||||
public RemoteVersion(String libraryId, String gameVersion, String selfVersion, Type type, String... url) {
|
||||
this.libraryId = Objects.requireNonNull(libraryId);
|
||||
this.gameVersion = Objects.requireNonNull(gameVersion);
|
||||
this.selfVersion = Objects.requireNonNull(selfVersion);
|
||||
@ -75,7 +76,7 @@ public class RemoteVersion implements Comparable<RemoteVersion> {
|
||||
return selfVersion;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
public String[] getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@ -81,22 +82,28 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
|
||||
for (ForgeVersion version : forgeVersions) {
|
||||
if (version == null)
|
||||
continue;
|
||||
String jar = null;
|
||||
List<String> urls = new ArrayList<>();
|
||||
for (ForgeVersion.File file : version.getFiles())
|
||||
if ("installer".equals(file.getCategory()) && "jar".equals(file.getFormat())) {
|
||||
jar = NetworkUtils.withQuery("https://bmclapi2.bangbang93.com/forge/download", mapOf(
|
||||
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(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())
|
||||
));
|
||||
)));
|
||||
urls.add("https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/" + classifier + "/" + fileName1);
|
||||
urls.add("https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/" + classifier + "-" + gameVersion + "/" + fileName2);
|
||||
}
|
||||
|
||||
if (jar == null)
|
||||
if (urls.isEmpty())
|
||||
continue;
|
||||
versions.put(gameVersion, new ForgeRemoteVersion(
|
||||
version.getGameVersion(), version.getVersion(), jar
|
||||
version.getGameVersion(), version.getVersion(), urls.toArray(new String[0])
|
||||
));
|
||||
}
|
||||
} finally {
|
||||
|
@ -33,10 +33,8 @@ import java.io.IOException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.util.StringUtils.removePrefix;
|
||||
import static org.jackhuang.hmcl.util.StringUtils.removeSuffix;
|
||||
@ -69,7 +67,11 @@ public final class ForgeInstallTask extends Task<Version> {
|
||||
public void preExecute() throws Exception {
|
||||
installer = Files.createTempFile("forge-installer", ".jar");
|
||||
|
||||
dependent = new FileDownloadTask(NetworkUtils.toURL(remote.getUrl()), installer.toFile())
|
||||
dependent = new FileDownloadTask(
|
||||
Arrays.stream(remote.getUrl())
|
||||
.map(NetworkUtils::toURL)
|
||||
.collect(Collectors.toList()),
|
||||
installer.toFile(), null)
|
||||
.setCacheRepository(dependencyManager.getCacheRepository())
|
||||
.setCaching(true);
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ public class ForgeRemoteVersion extends RemoteVersion {
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param url the installer or universal jar URL.
|
||||
*/
|
||||
public ForgeRemoteVersion(String gameVersion, String selfVersion, String url) {
|
||||
public ForgeRemoteVersion(String gameVersion, String selfVersion, String... url) {
|
||||
super(LibraryAnalyzer.LibraryType.FORGE.getPatchId(), gameVersion, selfVersion, url);
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ public final class GameRemoteVersion extends RemoteVersion {
|
||||
private final Date time;
|
||||
|
||||
public GameRemoteVersion(String gameVersion, String selfVersion, String url, ReleaseType type, Date time) {
|
||||
super(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId(), gameVersion, selfVersion, url, getReleaseType(type));
|
||||
super(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId(), gameVersion, selfVersion, getReleaseType(type), url);
|
||||
this.type = type;
|
||||
this.time = time;
|
||||
}
|
||||
|
@ -25,9 +25,14 @@ import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -65,7 +70,9 @@ public final class VersionJsonDownloadTask extends Task<String> {
|
||||
public void execute() throws IOException {
|
||||
RemoteVersion remoteVersion = gameVersionList.getVersion(gameVersion, gameVersion)
|
||||
.orElseThrow(() -> new IOException("Cannot find specific version " + gameVersion + " in remote repository"));
|
||||
String jsonURL = dependencyManager.getDownloadProvider().injectURL(remoteVersion.getUrl());
|
||||
dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL)).storeTo(this::setResult));
|
||||
dependencies.add(new GetTask(
|
||||
dependencyManager.getDownloadProvider().injectURLs(remoteVersion.getUrl())
|
||||
.map(NetworkUtils::toURL).collect(Collectors.toList()),
|
||||
UTF_8).storeTo(this::setResult));
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ public final class LiteLoaderInstallTask extends Task<Version> {
|
||||
Library library = new Library(
|
||||
new Artifact("com.mumfrey", "liteloader", remote.getSelfVersion()),
|
||||
"http://dl.liteloader.com/versions/",
|
||||
new LibrariesDownloadInfo(new LibraryDownloadInfo(null, remote.getUrl()))
|
||||
new LibrariesDownloadInfo(new LibraryDownloadInfo(null, remote.getUrl()[0]))
|
||||
);
|
||||
|
||||
setResult(new Version(LibraryAnalyzer.LibraryType.LITELOADER.getPatchId(),
|
||||
|
@ -72,7 +72,7 @@ public final class OptiFineBMCLVersionList extends VersionList<OptiFineRemoteVer
|
||||
continue;
|
||||
|
||||
String gameVersion = VersionNumber.normalize(element.getGameVersion());
|
||||
versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, () -> mirror, isPre));
|
||||
versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, mirror, isPre));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -45,11 +45,8 @@ import java.io.IOException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.getOrDefault;
|
||||
|
||||
@ -91,7 +88,7 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
new Artifact("optifine", "OptiFine", mavenVersion, "installer"), null,
|
||||
new LibrariesDownloadInfo(new LibraryDownloadInfo(
|
||||
"optifine/OptiFine/" + mavenVersion + "/OptiFine-" + mavenVersion + "-installer.jar",
|
||||
remote.getUrl()))
|
||||
remote.getUrl()[0]))
|
||||
);
|
||||
}
|
||||
|
||||
@ -105,7 +102,9 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
dest = Files.createTempFile("optifine-installer", ".jar");
|
||||
|
||||
if (installer == null) {
|
||||
dependents.add(new FileDownloadTask(NetworkUtils.toURL(remote.getUrl()), dest.toFile())
|
||||
dependents.add(new FileDownloadTask(
|
||||
Arrays.stream(remote.getUrl()).map(NetworkUtils::toURL).collect(Collectors.toList()),
|
||||
dest.toFile(), null)
|
||||
.setCacheRepository(dependencyManager.getCacheRepository())
|
||||
.setCaching(true));
|
||||
} else {
|
||||
@ -223,7 +222,7 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
throw new VersionMismatchException(mcVersion, gameVersion.get());
|
||||
|
||||
return new OptiFineInstallTask(dependencyManager, version,
|
||||
new OptiFineRemoteVersion(mcVersion, ofEdition + "_" + ofRelease, () -> null, false), installer);
|
||||
new OptiFineRemoteVersion(mcVersion, ofEdition + "_" + ofRelease, "", false), installer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,17 +26,9 @@ import org.jackhuang.hmcl.task.Task;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class OptiFineRemoteVersion extends RemoteVersion {
|
||||
private final Supplier<String> url;
|
||||
|
||||
public OptiFineRemoteVersion(String gameVersion, String selfVersion, Supplier<String> url, boolean snapshot) {
|
||||
super(LibraryAnalyzer.LibraryType.OPTIFINE.getPatchId(), gameVersion, selfVersion, "", snapshot ? Type.SNAPSHOT : Type.RELEASE);
|
||||
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return url.get();
|
||||
public OptiFineRemoteVersion(String gameVersion, String selfVersion, String url, boolean snapshot) {
|
||||
super(LibraryAnalyzer.LibraryType.OPTIFINE.getPatchId(), gameVersion, selfVersion, snapshot ? Type.SNAPSHOT : Type.RELEASE, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -33,6 +33,9 @@ import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@ -80,11 +83,10 @@ public class FileDownloadTask extends Task<Void> {
|
||||
}
|
||||
}
|
||||
|
||||
private final URL url;
|
||||
private final List<URL> urls;
|
||||
private final File file;
|
||||
private final IntegrityCheck integrityCheck;
|
||||
private final int retry;
|
||||
private final EventManager<FailedEvent<URL>> onFailed = new EventManager<>();
|
||||
private Path candidate;
|
||||
private boolean caching;
|
||||
private CacheRepository repository = CacheRepository.getInstance();
|
||||
@ -115,7 +117,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.url = url;
|
||||
this.urls = Collections.singletonList(url);
|
||||
this.file = file;
|
||||
this.integrityCheck = integrityCheck;
|
||||
this.retry = retry;
|
||||
@ -124,6 +126,25 @@ public class FileDownloadTask extends Task<Void> {
|
||||
setExecutor(Schedulers.io());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public FileDownloadTask(List<URL> urls, File file, IntegrityCheck integrityCheck) {
|
||||
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();
|
||||
|
||||
setName(file.getName());
|
||||
setExecutor(Schedulers.io());
|
||||
}
|
||||
|
||||
private void closeFiles() {
|
||||
if (rFile != null)
|
||||
try {
|
||||
@ -143,14 +164,6 @@ public class FileDownloadTask extends Task<Void> {
|
||||
stream = null;
|
||||
}
|
||||
|
||||
public EventManager<FailedEvent<URL>> getOnFailed() {
|
||||
return onFailed;
|
||||
}
|
||||
|
||||
public URL getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
@ -172,8 +185,6 @@ public class FileDownloadTask extends Task<Void> {
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
URL currentURL = url;
|
||||
|
||||
boolean checkETag;
|
||||
// Check cache
|
||||
if (integrityCheck != null && caching) {
|
||||
@ -182,7 +193,7 @@ public class FileDownloadTask extends Task<Void> {
|
||||
if (cache.isPresent()) {
|
||||
try {
|
||||
FileUtils.copyFile(cache.get().toFile(), file);
|
||||
Logging.LOG.log(Level.FINER, "Successfully verified file " + file + " from " + currentURL);
|
||||
Logging.LOG.log(Level.FINER, "Successfully verified file " + file + " from " + urls.get(0));
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
Logging.LOG.log(Level.WARNING, "Failed to copy cache files", e);
|
||||
@ -192,15 +203,11 @@ public class FileDownloadTask extends Task<Void> {
|
||||
checkETag = true;
|
||||
}
|
||||
|
||||
Logging.LOG.log(Level.FINER, "Downloading " + currentURL + " to " + file);
|
||||
Logging.LOG.log(Level.FINER, "Downloading " + urls.get(0) + " to " + file);
|
||||
Exception exception = null;
|
||||
|
||||
for (int repeat = 0; repeat < retry; repeat++) {
|
||||
if (repeat > 0) {
|
||||
FailedEvent<URL> event = new FailedEvent<>(this, repeat, currentURL);
|
||||
onFailed.fireEvent(event);
|
||||
currentURL = event.getNewResult();
|
||||
}
|
||||
URL url = urls.get(repeat % urls.size());
|
||||
if (Thread.interrupted()) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
@ -226,7 +233,7 @@ public class FileDownloadTask extends Task<Void> {
|
||||
repository.removeRemoteEntry(con);
|
||||
}
|
||||
} else if (con.getResponseCode() / 100 != 2) {
|
||||
throw new ResponseCodeException(currentURL, con.getResponseCode());
|
||||
throw new ResponseCodeException(url, con.getResponseCode());
|
||||
}
|
||||
|
||||
int contentLength = con.getContentLength();
|
||||
@ -316,16 +323,14 @@ public class FileDownloadTask extends Task<Void> {
|
||||
if (temp != null)
|
||||
temp.toFile().delete();
|
||||
exception = e;
|
||||
|
||||
if (e instanceof ResponseCodeException && ((ResponseCodeException) e).getResponseCode() == 404)
|
||||
break;
|
||||
Logging.LOG.log(Level.WARNING, "Failed to download " + url + ", repeat times: " + repeat + 1, e);
|
||||
} finally {
|
||||
closeFiles();
|
||||
}
|
||||
}
|
||||
|
||||
if (exception != null)
|
||||
throw new DownloadException(currentURL, exception);
|
||||
throw new DownloadException(urls.get(0), exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,9 @@ import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
@ -40,7 +43,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
*/
|
||||
public final class GetTask extends Task<String> {
|
||||
|
||||
private final URL url;
|
||||
private final List<URL> urls;
|
||||
private final Charset charset;
|
||||
private final int retry;
|
||||
private CacheRepository repository = CacheRepository.getInstance();
|
||||
@ -54,7 +57,7 @@ public final class GetTask extends Task<String> {
|
||||
}
|
||||
|
||||
public GetTask(URL url, Charset charset, int retry) {
|
||||
this.url = url;
|
||||
this.urls = Collections.singletonList(url);
|
||||
this.charset = charset;
|
||||
this.retry = retry;
|
||||
|
||||
@ -62,6 +65,15 @@ public final class GetTask extends Task<String> {
|
||||
setExecutor(Schedulers.io());
|
||||
}
|
||||
|
||||
public GetTask(List<URL> urls, Charset charset) {
|
||||
this.urls = new ArrayList<>(urls);
|
||||
this.charset = charset;
|
||||
this.retry = urls.size();
|
||||
|
||||
setName(urls.get(0).toString());
|
||||
setExecutor(Schedulers.io());
|
||||
}
|
||||
|
||||
public GetTask setCacheRepository(CacheRepository repository) {
|
||||
this.repository = repository;
|
||||
return this;
|
||||
@ -72,8 +84,7 @@ public final class GetTask extends Task<String> {
|
||||
Exception exception = null;
|
||||
boolean checkETag = true;
|
||||
for (int time = 0; time < retry; ++time) {
|
||||
if (time > 0)
|
||||
Logging.LOG.log(Level.WARNING, "Failed to download, repeat times: " + time);
|
||||
URL url = urls.get(time % urls.size());
|
||||
try {
|
||||
updateProgress(0);
|
||||
HttpURLConnection conn = NetworkUtils.createConnection(url);
|
||||
@ -122,10 +133,11 @@ public final class GetTask extends Task<String> {
|
||||
return;
|
||||
} catch (IOException ex) {
|
||||
exception = ex;
|
||||
Logging.LOG.log(Level.WARNING, "Failed to download " + url + ", repeat times: " + time + 1, ex);
|
||||
}
|
||||
}
|
||||
if (exception != null)
|
||||
throw new DownloadException(url, exception);
|
||||
throw new DownloadException(urls.get(0), exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user