v1.0.14.2 — fix JFX hang (ForkJoinPool), retry logic for connection reset, library logging
This commit is contained in:
+13
-2
@@ -65,17 +65,28 @@ public class VersionInstaller {
|
||||
if (versionUrl == null) throw new Exception("Version " + versionId + " not found");
|
||||
|
||||
ProgressBar.show("Fetching version info", 0, 1, "files");
|
||||
String versionJson = ZHttpClient.getWithSmartProxy(versionUrl);
|
||||
String versionJson;
|
||||
try {
|
||||
versionJson = ZHttpClient.getWithSmartProxy(versionUrl);
|
||||
Files.writeString(versionDir.resolve(versionId + ".json"), versionJson);
|
||||
} catch (Exception e) {
|
||||
System.err.println(ZAnsi.red("[VERSION] Failed to fetch version info: " + e.getMessage()));
|
||||
throw e;
|
||||
}
|
||||
ProgressBar.show("Version info", 1, 1, "files");
|
||||
|
||||
JSONObject versionData = new JSONObject(versionJson);
|
||||
|
||||
// client.jar
|
||||
ProgressBar.show("Downloading client.jar", 0, 1, "files");
|
||||
try {
|
||||
ZHttpClient.downloadFileWithSmartProxy(
|
||||
versionData.getJSONObject("downloads").getJSONObject("client").getString("url"),
|
||||
versionDir.resolve(versionId + ".jar"));
|
||||
} catch (Exception e) {
|
||||
System.err.println(ZAnsi.red("[VERSION] Failed to download client.jar: " + e.getMessage()));
|
||||
throw e;
|
||||
}
|
||||
ProgressBar.show("Client.jar", 1, 1, "files");
|
||||
|
||||
// Libraries
|
||||
@@ -197,7 +208,7 @@ public class VersionInstaller {
|
||||
try {
|
||||
ZHttpClient.downloadFileWithSmartProxy(url, target);
|
||||
} catch (Exception e) {
|
||||
// Skip problematic libraries
|
||||
System.err.println(ZAnsi.yellow("[LIB] Failed to download " + path + ": " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
count++;
|
||||
|
||||
@@ -802,7 +802,7 @@ public class JFXLauncher extends Application {
|
||||
setInstallProgressWithStage("Installation failed", 0, 100, "Failed", 0, 1);
|
||||
log("Install error: " + name);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (Throwable e) {
|
||||
log("Install error: " + e.getMessage());
|
||||
setInstallInProgress(false);
|
||||
setInstallProgressWithStage("Error: " + e.getMessage(), 0, 100, "Error", 0, 1);
|
||||
|
||||
@@ -18,6 +18,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class ZHttpClient {
|
||||
@@ -25,6 +26,7 @@ public class ZHttpClient {
|
||||
private static final HttpClient client = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofSeconds(15))
|
||||
.version(HttpClient.Version.HTTP_1_1)
|
||||
.executor(Executors.newCachedThreadPool())
|
||||
.build();
|
||||
|
||||
private static String BASE_URL = "https://api.zernmc.ru";
|
||||
@@ -237,6 +239,9 @@ public class ZHttpClient {
|
||||
if (url.contains("maven.neoforged.net")) return ServiceType.NEOFORGE_MAVEN;
|
||||
if (url.contains("google.com")) return ServiceType.GOOGLE;
|
||||
if (url.contains("cloudflare.com")) return ServiceType.CLOUDFLARE;
|
||||
if (url.contains("libraries.minecraft.net")) return ServiceType.MOJANG_RESOURCES;
|
||||
if (url.contains("piston-data.mojang.com")) return ServiceType.MOJANG_META;
|
||||
if (url.contains("repo1.maven.org") || url.contains("repo.maven.apache.org")) return ServiceType.MOJANG_RESOURCES;
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -256,10 +261,13 @@ public class ZHttpClient {
|
||||
return cause instanceof java.net.ConnectException ||
|
||||
cause instanceof java.net.UnknownHostException ||
|
||||
cause instanceof java.nio.channels.ClosedChannelException ||
|
||||
cause instanceof java.net.SocketException ||
|
||||
msg.contains("connection") ||
|
||||
msg.contains("timeout") ||
|
||||
msg.contains("refused") ||
|
||||
msg.contains("closed");
|
||||
msg.contains("closed") ||
|
||||
msg.contains("reset") ||
|
||||
msg.contains("abort");
|
||||
}
|
||||
|
||||
private static void markServiceAsBlocked(String url) {
|
||||
@@ -278,6 +286,8 @@ public class ZHttpClient {
|
||||
|
||||
public static String getWithSmartProxy(String url) throws IOException, InterruptedException {
|
||||
if (!shouldUseProxyForUrl(url)) {
|
||||
int directRetries = 3;
|
||||
for (int directAttempt = 1; directAttempt <= directRetries; directAttempt++) {
|
||||
try {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
@@ -299,11 +309,22 @@ public class ZHttpClient {
|
||||
} catch (Exception e) {
|
||||
if (isConnectionError(e)) {
|
||||
directFailCount++;
|
||||
markServiceAsBlocked(url);
|
||||
if (directAttempt < directRetries) {
|
||||
System.out.println(ZAnsi.yellow("[NET] Direct retry " + directAttempt + "/" + directRetries + " for " + url.substring(0, Math.min(60, url.length())) + "..."));
|
||||
try { Thread.sleep(1000 * directAttempt); } catch (InterruptedException ie) { break; }
|
||||
continue;
|
||||
}
|
||||
markServiceAsBlocked(url);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int maxRetries = 3;
|
||||
for (int attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
String encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8);
|
||||
String proxyUrl = BASE_URL + "/proxy/download?url=" + encodedUrl;
|
||||
@@ -325,12 +346,20 @@ public class ZHttpClient {
|
||||
return response.body();
|
||||
|
||||
} catch (Exception e) {
|
||||
if (attempt == maxRetries || !isConnectionError(e)) {
|
||||
throw new IOException("Failed to fetch data directly or via proxy: " + e.getMessage(), e);
|
||||
}
|
||||
try { Thread.sleep(1000 * attempt); } catch (InterruptedException ie) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
throw new IOException("Failed to fetch data: " + url);
|
||||
}
|
||||
|
||||
public static void downloadFileWithSmartProxy(String url, Path target) throws Exception {
|
||||
if (!shouldUseProxyForUrl(url)) {
|
||||
int directRetries = 3;
|
||||
for (int directAttempt = 1; directAttempt <= directRetries; directAttempt++) {
|
||||
try {
|
||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
@@ -356,11 +385,23 @@ public class ZHttpClient {
|
||||
} catch (Exception e) {
|
||||
if (isConnectionError(e)) {
|
||||
directFailCount++;
|
||||
markServiceAsBlocked(url);
|
||||
if (directAttempt < directRetries) {
|
||||
System.out.println(ZAnsi.yellow("[NET] Direct download retry " + directAttempt + "/" + directRetries + " for " + url.substring(0, Math.min(60, url.length())) + "..."));
|
||||
try { Thread.sleep(1000 * directAttempt); } catch (InterruptedException ie) { break; }
|
||||
continue;
|
||||
}
|
||||
markServiceAsBlocked(url);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int maxRetries = 3;
|
||||
for (int attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
String encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8);
|
||||
String proxyUrl = BASE_URL + "/proxy/download?url=" + encodedUrl;
|
||||
|
||||
@@ -378,6 +419,15 @@ public class ZHttpClient {
|
||||
}
|
||||
|
||||
proxySuccessCount++;
|
||||
return;
|
||||
|
||||
} catch (Exception e) {
|
||||
if (attempt == maxRetries || !isConnectionError(e)) {
|
||||
throw new IOException("Proxy download failed: " + e.getMessage(), e);
|
||||
}
|
||||
try { Thread.sleep(1000 * attempt); } catch (InterruptedException ie) { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String get(String endpoint) throws IOException, InterruptedException {
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
|
||||
<properties>
|
||||
<revision>1.0.14</revision>
|
||||
<hotfix>1</hotfix>
|
||||
<hotfix>2</hotfix>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
Reference in New Issue
Block a user