Fix #3147: 修复 LiteLoader 下载相关问题 (#3776)

* Fix #3147

* Fix: Use 'release' classifier on snapshot versions. Speed up loading time.

* Use https.
This commit is contained in:
Burning_TNT 2025-04-12 12:07:26 +08:00 committed by GitHub
parent 6c986921d6
commit 139fb41149
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 93 additions and 109 deletions

View File

@ -18,21 +18,16 @@
package org.jackhuang.hmcl.download.liteloader;
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.io.HttpRequest;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.jackhuang.hmcl.util.io.NetworkUtils;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
*
* @author huangyuhui
*/
public final class LiteLoaderBMCLVersionList extends VersionList<LiteLoaderRemoteVersion> {
@ -47,66 +42,44 @@ public final class LiteLoaderBMCLVersionList extends VersionList<LiteLoaderRemot
return false;
}
private void doBranch(String key, String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch, boolean snapshot) {
if (branch == null || repository == null)
return;
private static final class LiteLoaderBMCLVersion {
for (Map.Entry<String, LiteLoaderVersion> entry : branch.getLiteLoader().entrySet()) {
String branchName = entry.getKey();
LiteLoaderVersion v = entry.getValue();
if ("latest".equals(branchName))
continue;
private final LiteLoaderVersion build;
private final String version;
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) {
}
}
versions.put(key, new LiteLoaderRemoteVersion(gameVersion,
version, Collections.singletonList(url),
v.getTweakClass(), v.getLibraries()
));
public LiteLoaderBMCLVersion(LiteLoaderVersion build, String version) {
this.build = build;
this.version = version;
}
}
@Override
public CompletableFuture<?> refreshAsync() {
return HttpRequest.GET(downloadProvider.injectURL(LITELOADER_LIST)).getJsonAsync(LiteLoaderVersionsRoot.class)
.thenAcceptAsync(root -> {
lock.writeLock().lock();
throw new UnsupportedOperationException();
}
@Override
public CompletableFuture<?> refreshAsync(String gameVersion) {
return HttpRequest.GET(
downloadProvider.injectURL("https://bmclapi2.bangbang93.com/liteloader/list"), Pair.pair("mcversion", gameVersion)
)
.getJsonAsync(LiteLoaderBMCLVersion.class)
.thenAccept(v -> {
lock.writeLock().lock();
try {
versions.clear();
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
String gameVersion = entry.getKey();
LiteLoaderGameVersions liteLoader = entry.getValue();
String gg = VersionNumber.normalize(gameVersion);
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
}
versions.put(gameVersion, new LiteLoaderRemoteVersion(
gameVersion, v.version, RemoteVersion.Type.UNCATEGORIZED,
Collections.singletonList(NetworkUtils.withQuery(
downloadProvider.injectURL("https://bmclapi2.bangbang93.com/liteloader/download"),
Collections.singletonMap("version", v.version)
)),
v.build.getTweakClass(), v.build.getLibraries()
));
} finally {
lock.writeLock().unlock();
}
});
}
public static final String LITELOADER_LIST = "http://dl.liteloader.com/versions/versions.json";
private static String getLatestSnapshotVersion(String repo) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(repo + "maven-metadata.xml");
Element r = doc.getDocumentElement();
Element snapshot = (Element) r.getElementsByTagName("snapshot").item(0);
Node timestamp = snapshot.getElementsByTagName("timestamp").item(0);
Node buildNumber = snapshot.getElementsByTagName("buildNumber").item(0);
return timestamp.getTextContent() + "-" + buildNumber.getTextContent();
}
}

View File

@ -30,6 +30,7 @@ import java.util.List;
public class LiteLoaderRemoteVersion extends RemoteVersion {
private final String tweakClass;
private final Collection<Library> libraries;
/**
* Constructor.
*
@ -37,8 +38,8 @@ public class LiteLoaderRemoteVersion extends RemoteVersion {
* @param selfVersion the version string of the remote version.
* @param urls the installer or universal jar original URL.
*/
LiteLoaderRemoteVersion(String gameVersion, String selfVersion, List<String> urls, String tweakClass, Collection<Library> libraries) {
super(LibraryAnalyzer.LibraryType.LITELOADER.getPatchId(), gameVersion, selfVersion, null, urls);
LiteLoaderRemoteVersion(String gameVersion, String selfVersion, Type type, List<String> urls, String tweakClass, Collection<Library> libraries) {
super(LibraryAnalyzer.LibraryType.LITELOADER.getPatchId(), gameVersion, selfVersion, null, type, urls);
this.tweakClass = tweakClass;
this.libraries = libraries;

View File

@ -18,21 +18,20 @@
package org.jackhuang.hmcl.download.liteloader;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.util.io.HttpRequest;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
/**
*
* @author huangyuhui
*/
public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVersion> {
@ -45,52 +44,39 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
@Override
public boolean hasType() {
return false;
return true;
}
private void doBranch(String key, String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch, boolean snapshot) {
if (branch == null || repository == null)
return;
for (Map.Entry<String, LiteLoaderVersion> 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 {
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) {
}
}
versions.put(key, new LiteLoaderRemoteVersion(gameVersion,
version, Collections.singletonList(url),
v.getTweakClass(), v.getLibraries()
));
}
}
public static final String LITELOADER_LIST = "https://dl.liteloader.com/versions/versions.json";
@Override
public CompletableFuture<?> refreshAsync() {
public CompletableFuture<?> refreshAsync(String gameVersion) {
return HttpRequest.GET(downloadProvider.injectURL(LITELOADER_LIST)).getJsonAsync(LiteLoaderVersionsRoot.class)
.thenAcceptAsync(root -> {
lock.writeLock().lock();
LiteLoaderGameVersions versions = root.getVersions().get(gameVersion);
if (versions == null) {
return;
}
LiteLoaderRemoteVersion snapshot = null;
if (versions.getSnapshots() != null) {
try {
versions.clear();
snapshot = loadSnapshotVersion(gameVersion, versions.getSnapshots().getLiteLoader().get("latest"));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
String gameVersion = entry.getKey();
LiteLoaderGameVersions liteLoader = entry.getValue();
lock.writeLock().lock();
try {
this.versions.clear();
String gg = VersionNumber.normalize(gameVersion);
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
if (versions.getRepoitory() != null && versions.getArtifacts() != null) {
loadArtifactVersion(gameVersion, versions.getRepoitory(), versions.getArtifacts());
}
if (snapshot != null) {
this.versions.put(gameVersion, snapshot);
}
} finally {
lock.writeLock().unlock();
@ -98,16 +84,40 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
});
}
public static final String LITELOADER_LIST = "http://dl.liteloader.com/versions/versions.json";
@Override
public CompletableFuture<?> refreshAsync() {
throw new UnsupportedOperationException();
}
private static String getLatestSnapshotVersion(String repo) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(repo + "maven-metadata.xml");
Element r = doc.getDocumentElement();
Element snapshot = (Element) r.getElementsByTagName("snapshot").item(0);
Node timestamp = snapshot.getElementsByTagName("timestamp").item(0);
Node buildNumber = snapshot.getElementsByTagName("buildNumber").item(0);
return timestamp.getTextContent() + "-" + buildNumber.getTextContent();
private void loadArtifactVersion(String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch) {
for (Map.Entry<String, LiteLoaderVersion> entry : branch.getLiteLoader().entrySet()) {
String branchName = entry.getKey();
LiteLoaderVersion v = entry.getValue();
if ("latest".equals(branchName))
continue;
versions.put(gameVersion, new LiteLoaderRemoteVersion(
gameVersion, v.getVersion(), RemoteVersion.Type.RELEASE,
Collections.singletonList(repository.getUrl() + "com/mumfrey/liteloader/" + gameVersion + "/" + v.getFile()),
v.getTweakClass(), v.getLibraries()
));
}
}
// Workaround for https://github.com/HMCL-dev/HMCL/issues/3147: Some LiteLoader artifacts aren't published on http://dl.liteloader.com/repo
private static final String SNAPSHOT_METADATA = "https://repo.mumfrey.com/content/repositories/snapshots/com/mumfrey/liteloader/%s-SNAPSHOT/maven-metadata.xml";
private static final String SNAPSHOT_FILE = "https://repo.mumfrey.com/content/repositories/snapshots/com/mumfrey/liteloader/%s-SNAPSHOT/liteloader-%s-%s-%s-release.jar";
private LiteLoaderRemoteVersion loadSnapshotVersion(String gameVersion, LiteLoaderVersion v) throws IOException {
String root = HttpRequest.GET(String.format(SNAPSHOT_METADATA, gameVersion)).getString();
Document document = Jsoup.parseBodyFragment(root);
String timestamp = Objects.requireNonNull(document.select("timestamp"), "timestamp").text();
String buildNumber = Objects.requireNonNull(document.select("buildNumber"), "buildNumber").text();
return new LiteLoaderRemoteVersion(
gameVersion, timestamp + "-" + buildNumber, RemoteVersion.Type.SNAPSHOT,
Collections.singletonList(String.format(SNAPSHOT_FILE, gameVersion, gameVersion, timestamp, buildNumber)),
v.getTweakClass(), v.getLibraries()
);
}
}