diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java index ae13db7c9..d0bedac76 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -54,6 +54,7 @@ import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jackhuang.hmcl.util.versioning.VersionNumber; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentLinkedQueue; @@ -216,7 +217,10 @@ public final class LauncherHelper { if (ex != null) { String message; if (ex instanceof CurseCompletionException) { - message = i18n("modpack.type.curse.error"); + if (ex.getCause() instanceof FileNotFoundException) + message = i18n("modpack.type.curse.not_found"); + else + message = i18n("modpack.type.curse.error"); } else if (ex instanceof PermissionException) { message = i18n("launch.failed.executable_permission"); } else if (ex instanceof ProcessCreationException) { diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 0e06a27f8..32ce129c2 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -222,6 +222,7 @@ modpack.task.install.will=Install the modpack: modpack.type.curse=Curse modpack.type.curse.completion=Install relative files to Curse modpack modpack.type.curse.error=Unable to complete this Curse modpack. Please retry. +modpack.type.curse.not_found=Some of required resources are deleted and cannot be downloaded. Please consider the latest version or other modpacks. modpack.type.hmcl=HMCL modpack.type.multimc=MultiMC modpack.unsupported=Unsupported modpack, only HMCL, MultiMC, Curse modpacks are supported. diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index f00a355f1..2880a98e9 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -222,6 +222,7 @@ modpack.task.install.will=將會安裝整合包: modpack.type.curse=Curse modpack.type.curse.completion=下載 Curse 整合包相關檔案 modpack.type.curse.error=無法完成 Curse 整合包的下載,請多次重試或設定代理 +modpack.type.curse.not_found=部分必需檔案已經從網路中被刪除並且再也無法下載,請嘗試該整合包的最新版本或者安裝其他整合包。 modpack.type.hmcl=HMCL modpack.type.multimc=MultiMC modpack.unsupported=不支援該整合包。僅 HMCL、MultiMC、Curse 整合包受支援。 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index f072e9f56..024548d32 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -222,6 +222,7 @@ modpack.task.install.will=将会安装整合包: modpack.type.curse=Curse modpack.type.curse.completion=下载 Curse 整合包相关文件 modpack.type.curse.error=未能完成 Curse 整合包的下载,请多次重试或设置代理 +modpack.type.curse.not_found=部分必需文件已经在网络中被删除并且再也无法下载,请尝试该整合包的最新版本或者安装其他整合包。 modpack.type.hmcl=HMCL modpack.type.multimc=MultiMC modpack.unsupported=该整合包不被支持。仅 HMCL、MultiMC、Curse 整合包受支持。 diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java index fd960275a..6048932fb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java @@ -35,9 +35,12 @@ public class LibraryDownloadTask extends Task { protected final Library library; protected final String url; protected boolean xz; + private final Library originalLibrary; private boolean cached = false; public LibraryDownloadTask(AbstractDependencyManager dependencyManager, File file, Library library) { + this.originalLibrary = library; + setSignificance(TaskSignificance.MODERATE); if (library.is("net.minecraftforge", "forge")) @@ -86,7 +89,7 @@ public class LibraryDownloadTask extends Task { @Override public void preExecute() throws Exception { - Optional libPath = cacheRepository.getLibrary(library.setClassifier(null)); + Optional libPath = cacheRepository.getLibrary(originalLibrary); if (libPath.isPresent()) { try { FileUtils.copyFile(libPath.get().toFile(), jar); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseCompletionTask.java index 987c34326..c04415c46 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/CurseCompletionTask.java @@ -27,10 +27,12 @@ import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.NetworkUtils; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.util.Collection; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; @@ -103,6 +105,7 @@ public final class CurseCompletionTask extends Task { AtomicBoolean flag = new AtomicBoolean(true); AtomicInteger finished = new AtomicInteger(0); + AtomicBoolean notFound = new AtomicBoolean(false); // Because in China, Curse is too difficult to visit, // if failed, ignore it and retry next time. @@ -113,6 +116,9 @@ public final class CurseCompletionTask extends Task { if (StringUtils.isBlank(file.getFileName())) { try { return file.withFileName(NetworkUtils.detectFileName(file.getUrl())); + } catch (FileNotFoundException e) { + notFound.set(true); + return file; } catch (IOException ioe) { Logging.LOG.log(Level.WARNING, "Unable to fetch the file name of URL: " + file.getUrl(), ioe); flag.set(false); @@ -132,9 +138,13 @@ public final class CurseCompletionTask extends Task { } // Let this task fail if the curse manifest has not been completed. - if (!flag.get()) + // But continue other downloads. + if (!flag.get() || notFound.get()) dependencies.add(Task.of(() -> { - throw new CurseCompletionException(); + if (notFound.get()) + throw new CurseCompletionException(new FileNotFoundException()); + else + throw new CurseCompletionException(); })); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java index 203ec5aff..0e0830deb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java @@ -218,9 +218,9 @@ public class FileDownloadTask extends Task { HttpURLConnection con = NetworkUtils.createConnection(url); if (checkETag) repository.injectConnection(con); - con.connect(); + con = NetworkUtils.resolveConnection(con); - if (con.getResponseCode() == 304) { + if (con.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) { // Handle cache Path cache = repository.getCachedRemoteFile(con); FileUtils.copyFile(cache.toFile(), file); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/NetworkUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/NetworkUtils.java index 1140bb2d1..c044c9c9c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/NetworkUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/NetworkUtils.java @@ -19,6 +19,7 @@ package org.jackhuang.hmcl.util.io; import java.io.*; import java.net.*; +import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -57,13 +58,30 @@ public final class NetworkUtils { public static HttpURLConnection createConnection(URL url) throws IOException { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setDoInput(true); connection.setUseCaches(false); connection.setConnectTimeout(15000); connection.setReadTimeout(15000); return connection; } + public static HttpURLConnection resolveConnection(HttpURLConnection conn) throws IOException { + conn.setUseCaches(false); + conn.setConnectTimeout(15000); + conn.setReadTimeout(15000); + conn.setInstanceFollowRedirects(false); + Map> properties = conn.getRequestProperties(); + int code = conn.getResponseCode(); + if (code >= 300 && code <= 307 && code != 306 && code != 304) { + String newURL = conn.getHeaderField("Location").replace(" ", "%20"); + conn.disconnect(); + + HttpURLConnection redirected = (HttpURLConnection) new URL(conn.getURL(), newURL).openConnection(); + properties.forEach((key, value) -> value.forEach(element -> redirected.addRequestProperty(key, element))); + return resolveConnection(redirected); + } + return conn; + } + public static String doGet(URL url) throws IOException { return IOUtils.readFullyAsString(createConnection(url).getInputStream()); } @@ -110,10 +128,12 @@ public final class NetworkUtils { } public static String detectFileName(URL url) throws IOException { - HttpURLConnection conn = createConnection(url); - conn.connect(); - if (conn.getResponseCode() / 100 != 2) - throw new IOException("Response code " + conn.getResponseCode()); + HttpURLConnection conn = resolveConnection(createConnection(url)); + int code = conn.getResponseCode(); + if (code == 404) + throw new FileNotFoundException(); + if (code / 100 != 2) + throw new IOException(url + ": response code " + conn.getResponseCode()); return detectFileName(conn); }