v1.0.13.1 — pack download stability, proxy for MC/Forge downloads, build.version priority
This commit is contained in:
@@ -280,7 +280,7 @@ How to use CLI:
|
|||||||
</echo>
|
</echo>
|
||||||
|
|
||||||
<!-- Создаём один архив со всем -->
|
<!-- Создаём один архив со всем -->
|
||||||
<zip destfile="../../server/builds/ZernMC-win-${project.version}.zip"
|
<zip destfile="../../server/builds/ZernMC-win-${revision}.${hotfix}.zip"
|
||||||
basedir="../../server/builds"
|
basedir="../../server/builds"
|
||||||
includes="zernmc.exe,zernmc-cli.exe,bin/**,assets/**,lib/**,README.txt"
|
includes="zernmc.exe,zernmc-cli.exe,bin/**,assets/**,lib/**,README.txt"
|
||||||
excludes="build.version,*.jar"/>
|
excludes="build.version,*.jar"/>
|
||||||
|
|||||||
+5
@@ -177,8 +177,10 @@ public class PackDownloader {
|
|||||||
|
|
||||||
if (needsMinecraftInstall) {
|
if (needsMinecraftInstall) {
|
||||||
LauncherLogger.info("installOrUpdatePack: needs Minecraft install. loader=" + manifest.getLoaderType() + " loaderVer=" + manifest.getLoaderVersion());
|
LauncherLogger.info("installOrUpdatePack: needs Minecraft install. loader=" + manifest.getLoaderType() + " loaderVer=" + manifest.getLoaderVersion());
|
||||||
|
reportProgress("Installing " + manifest.getMinecraftVersion() + " with " + manifest.getLoaderType() + "...", 10, "Installing pack files", 4, 5);
|
||||||
if ("fabric".equalsIgnoreCase(manifest.getLoaderType())) {
|
if ("fabric".equalsIgnoreCase(manifest.getLoaderType())) {
|
||||||
LauncherLogger.info("installOrUpdatePack: installing Fabric mc=" + manifest.getMinecraftVersion() + " loader=" + manifest.getLoaderVersion());
|
LauncherLogger.info("installOrUpdatePack: installing Fabric mc=" + manifest.getMinecraftVersion() + " loader=" + manifest.getLoaderVersion());
|
||||||
|
reportProgress("Installing Fabric " + manifest.getLoaderVersion() + "...", 15, "Installing pack files", 4, 5);
|
||||||
boolean success = lib.installFabric(manifest.getMinecraftVersion(), manifest.getLoaderVersion());
|
boolean success = lib.installFabric(manifest.getMinecraftVersion(), manifest.getLoaderVersion());
|
||||||
LauncherLogger.info("installOrUpdatePack: Fabric install result=" + success);
|
LauncherLogger.info("installOrUpdatePack: Fabric install result=" + success);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
@@ -189,6 +191,7 @@ public class PackDownloader {
|
|||||||
}
|
}
|
||||||
} else if ("neoforge".equalsIgnoreCase(manifest.getLoaderType())) {
|
} else if ("neoforge".equalsIgnoreCase(manifest.getLoaderType())) {
|
||||||
LauncherLogger.info("installOrUpdatePack: installing NeoForge mc=" + manifest.getMinecraftVersion() + " loader=" + manifest.getLoaderVersion());
|
LauncherLogger.info("installOrUpdatePack: installing NeoForge mc=" + manifest.getMinecraftVersion() + " loader=" + manifest.getLoaderVersion());
|
||||||
|
reportProgress("Installing NeoForge " + manifest.getLoaderVersion() + "...", 15, "Installing pack files", 4, 5);
|
||||||
boolean success = lib.installNeoForge(manifest.getMinecraftVersion(), manifest.getLoaderVersion());
|
boolean success = lib.installNeoForge(manifest.getMinecraftVersion(), manifest.getLoaderVersion());
|
||||||
LauncherLogger.info("installOrUpdatePack: NeoForge install result=" + success);
|
LauncherLogger.info("installOrUpdatePack: NeoForge install result=" + success);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
@@ -199,6 +202,7 @@ public class PackDownloader {
|
|||||||
}
|
}
|
||||||
} else if ("forge".equalsIgnoreCase(manifest.getLoaderType())) {
|
} else if ("forge".equalsIgnoreCase(manifest.getLoaderType())) {
|
||||||
LauncherLogger.info("installOrUpdatePack: installing Forge mc=" + manifest.getMinecraftVersion() + " loader=" + manifest.getLoaderVersion());
|
LauncherLogger.info("installOrUpdatePack: installing Forge mc=" + manifest.getMinecraftVersion() + " loader=" + manifest.getLoaderVersion());
|
||||||
|
reportProgress("Installing Forge " + manifest.getLoaderVersion() + "...", 15, "Installing pack files", 4, 5);
|
||||||
boolean success = lib.installForge(manifest.getMinecraftVersion(), manifest.getLoaderVersion());
|
boolean success = lib.installForge(manifest.getMinecraftVersion(), manifest.getLoaderVersion());
|
||||||
LauncherLogger.info("installOrUpdatePack: Forge install result=" + success);
|
LauncherLogger.info("installOrUpdatePack: Forge install result=" + success);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
@@ -209,6 +213,7 @@ public class PackDownloader {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LauncherLogger.info("installOrUpdatePack: installing Vanilla Minecraft " + manifest.getMinecraftVersion());
|
LauncherLogger.info("installOrUpdatePack: installing Vanilla Minecraft " + manifest.getMinecraftVersion());
|
||||||
|
reportProgress("Installing Vanilla Minecraft...", 15, "Installing pack files", 4, 5);
|
||||||
boolean success = lib.installMinecraft(manifest.getMinecraftVersion());
|
boolean success = lib.installMinecraft(manifest.getMinecraftVersion());
|
||||||
LauncherLogger.info("installOrUpdatePack: Vanilla install result=" + success);
|
LauncherLogger.info("installOrUpdatePack: Vanilla install result=" + success);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
|
|||||||
+22
-79
@@ -1,19 +1,15 @@
|
|||||||
package me.sashegdev.zernmc.launcher.minecraft.installer;
|
package me.sashegdev.zernmc.launcher.minecraft.installer;
|
||||||
|
|
||||||
import me.sashegdev.zernmc.launcher.minecraft.Instance;
|
import me.sashegdev.zernmc.launcher.minecraft.Instance;
|
||||||
import me.sashegdev.zernmc.launcher.utils.ProgressBar;
|
|
||||||
import me.sashegdev.zernmc.launcher.utils.ZAnsi;
|
import me.sashegdev.zernmc.launcher.utils.ZAnsi;
|
||||||
|
import me.sashegdev.zernmc.launcher.utils.ZHttpClient;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.URI;
|
|
||||||
import java.net.http.HttpClient;
|
|
||||||
import java.net.http.HttpRequest;
|
|
||||||
import java.net.http.HttpResponse;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class ModLoaderInstaller {
|
public class ModLoaderInstaller {
|
||||||
|
|
||||||
@@ -40,9 +36,6 @@ public class ModLoaderInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final Instance instance;
|
private final Instance instance;
|
||||||
private final HttpClient httpClient = HttpClient.newBuilder()
|
|
||||||
.connectTimeout(Duration.ofSeconds(30))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
public ModLoaderInstaller(Instance instance) {
|
public ModLoaderInstaller(Instance instance) {
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
@@ -68,7 +61,7 @@ public class ModLoaderInstaller {
|
|||||||
Path installerJar = instance.getPath().resolve(jarName);
|
Path installerJar = instance.getPath().resolve(jarName);
|
||||||
|
|
||||||
System.out.println(ZAnsi.cyan("Downloading " + type.loaderName + " Installer..."));
|
System.out.println(ZAnsi.cyan("Downloading " + type.loaderName + " Installer..."));
|
||||||
downloadFileWithProgress(installerUrl, installerJar, type);
|
ZHttpClient.downloadFileWithSmartProxy(installerUrl, installerJar);
|
||||||
|
|
||||||
System.out.println(ZAnsi.cyan("Running " + type.loaderName + " Installer..."));
|
System.out.println(ZAnsi.cyan("Running " + type.loaderName + " Installer..."));
|
||||||
System.out.println(ZAnsi.yellow("This may take a few minutes. Please wait...\n"));
|
System.out.println(ZAnsi.yellow("This may take a few minutes. Please wait...\n"));
|
||||||
@@ -122,50 +115,6 @@ public class ModLoaderInstaller {
|
|||||||
System.out.println(ZAnsi.yellow("Created launcher_profiles.json"));
|
System.out.println(ZAnsi.yellow("Created launcher_profiles.json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void downloadFileWithProgress(String url, Path target, LoaderType type) throws Exception {
|
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
|
||||||
.uri(URI.create(url))
|
|
||||||
.GET()
|
|
||||||
.build();
|
|
||||||
|
|
||||||
HttpResponse<InputStream> response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
|
||||||
|
|
||||||
if (response.statusCode() != 200) {
|
|
||||||
throw new IOException("HTTP " + response.statusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
long contentLength = response.headers().firstValueAsLong("Content-Length").orElse(-1);
|
|
||||||
|
|
||||||
try (InputStream in = response.body();
|
|
||||||
FileOutputStream out = new FileOutputStream(target.toFile())) {
|
|
||||||
|
|
||||||
byte[] buffer = new byte[8192];
|
|
||||||
int bytesRead;
|
|
||||||
long totalRead = 0;
|
|
||||||
int lastPercent = -1;
|
|
||||||
|
|
||||||
while ((bytesRead = in.read(buffer)) != -1) {
|
|
||||||
out.write(buffer, 0, bytesRead);
|
|
||||||
totalRead += bytesRead;
|
|
||||||
|
|
||||||
if (contentLength > 0) {
|
|
||||||
int percent = (int) ((totalRead * 100) / contentLength);
|
|
||||||
if (percent != lastPercent) {
|
|
||||||
ProgressBar.show(type.loaderName + " Installer", percent, 100,
|
|
||||||
"% (" + ProgressBar.formatBytes(totalRead) + "/" + ProgressBar.formatBytes(contentLength) + ")");
|
|
||||||
lastPercent = percent;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
char[] spinner = {'|', '/', '-', '\\'};
|
|
||||||
int idx = (int) (totalRead / 1024) % 4;
|
|
||||||
System.out.print("\rDownloading " + type.loaderName + " Installer: " + ProgressBar.formatBytes(totalRead) + " " + spinner[idx]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProgressBar.finish(type.loaderName + " Installer (" + ProgressBar.formatBytes(Files.size(target)) + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean runInstaller(Path installerJar, LoaderType type) throws IOException, InterruptedException {
|
private boolean runInstaller(Path installerJar, LoaderType type) throws IOException, InterruptedException {
|
||||||
int maxRetries = 3;
|
int maxRetries = 3;
|
||||||
int attempt = 1;
|
int attempt = 1;
|
||||||
@@ -212,9 +161,25 @@ public class ModLoaderInstaller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int exitCode = process.waitFor();
|
int exitCode;
|
||||||
|
if (hasErrors) {
|
||||||
|
process.destroyForcibly();
|
||||||
|
exitCode = 1;
|
||||||
|
} else {
|
||||||
|
if (!process.waitFor(10, TimeUnit.MINUTES)) {
|
||||||
|
process.destroyForcibly();
|
||||||
|
System.out.println(ZAnsi.brightRed(type.loaderName + " Installer timed out after 10 minutes"));
|
||||||
|
if (attempt < maxRetries) {
|
||||||
|
attempt++;
|
||||||
|
Thread.sleep(5000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
exitCode = process.exitValue();
|
||||||
|
}
|
||||||
|
|
||||||
if (exitCode == 0 && !hasErrors) {
|
if (exitCode == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,7 +245,7 @@ public class ModLoaderInstaller {
|
|||||||
for (String mirrorUrl : entry.getValue()) {
|
for (String mirrorUrl : entry.getValue()) {
|
||||||
for (int attempt = 1; attempt <= 3; attempt++) {
|
for (int attempt = 1; attempt <= 3; attempt++) {
|
||||||
try {
|
try {
|
||||||
downloadDirect(mirrorUrl, target);
|
ZHttpClient.downloadFileWithSmartProxy(mirrorUrl, target);
|
||||||
downloaded = true;
|
downloaded = true;
|
||||||
break;
|
break;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -294,26 +259,4 @@ public class ModLoaderInstaller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void downloadDirect(String url, Path target) throws Exception {
|
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
|
||||||
.uri(URI.create(url))
|
|
||||||
.GET()
|
|
||||||
.build();
|
|
||||||
|
|
||||||
HttpResponse<InputStream> response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
|
||||||
|
|
||||||
if (response.statusCode() != 200) {
|
|
||||||
throw new IOException("HTTP " + response.statusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
try (InputStream in = response.body();
|
|
||||||
FileOutputStream out = new FileOutputStream(target.toFile())) {
|
|
||||||
byte[] buffer = new byte[8192];
|
|
||||||
int bytesRead;
|
|
||||||
while ((bytesRead = in.read(buffer)) != -1) {
|
|
||||||
out.write(buffer, 0, bytesRead);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-66
@@ -3,43 +3,38 @@ package me.sashegdev.zernmc.launcher.minecraft.installer;
|
|||||||
import me.sashegdev.zernmc.launcher.minecraft.model.MinecraftVersion;
|
import me.sashegdev.zernmc.launcher.minecraft.model.MinecraftVersion;
|
||||||
import me.sashegdev.zernmc.launcher.utils.ProgressBar;
|
import me.sashegdev.zernmc.launcher.utils.ProgressBar;
|
||||||
import me.sashegdev.zernmc.launcher.utils.ZAnsi;
|
import me.sashegdev.zernmc.launcher.utils.ZAnsi;
|
||||||
|
import me.sashegdev.zernmc.launcher.utils.ZHttpClient;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URI;
|
|
||||||
import java.net.http.HttpClient;
|
|
||||||
import java.net.http.HttpRequest;
|
|
||||||
import java.net.http.HttpResponse;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
|
|
||||||
public class VersionInstaller {
|
public class VersionInstaller {
|
||||||
|
|
||||||
private final Path minecraftDir;
|
private final Path minecraftDir;
|
||||||
private final HttpClient httpClient;
|
|
||||||
private final ExecutorService executor = Executors.newFixedThreadPool(32);
|
private final ExecutorService executor = Executors.newFixedThreadPool(32);
|
||||||
|
|
||||||
public VersionInstaller(Path minecraftDir) {
|
public VersionInstaller(Path minecraftDir) {
|
||||||
this.minecraftDir = minecraftDir;
|
this.minecraftDir = minecraftDir;
|
||||||
this.httpClient = HttpClient.newBuilder()
|
|
||||||
.connectTimeout(Duration.ofSeconds(15))
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MinecraftVersion> getAvailableVersions() throws Exception {
|
public List<MinecraftVersion> getAvailableVersions() throws Exception {
|
||||||
String jsonString = downloadString("https://piston-meta.mojang.com/mc/game/version_manifest_v2.json");
|
String jsonString = ZHttpClient.getWithSmartProxy("https://piston-meta.mojang.com/mc/game/version_manifest_v2.json");
|
||||||
JSONObject root = new JSONObject(jsonString);
|
JSONObject root = new JSONObject(jsonString);
|
||||||
JSONArray versionsArray = root.getJSONArray("versions");
|
JSONArray versionsArray = root.getJSONArray("versions");
|
||||||
|
|
||||||
@@ -70,7 +65,7 @@ public class VersionInstaller {
|
|||||||
if (versionUrl == null) throw new Exception("Version " + versionId + " not found");
|
if (versionUrl == null) throw new Exception("Version " + versionId + " not found");
|
||||||
|
|
||||||
ProgressBar.show("Fetching version info", 0, 1, "files");
|
ProgressBar.show("Fetching version info", 0, 1, "files");
|
||||||
String versionJson = downloadString(versionUrl);
|
String versionJson = ZHttpClient.getWithSmartProxy(versionUrl);
|
||||||
Files.writeString(versionDir.resolve(versionId + ".json"), versionJson);
|
Files.writeString(versionDir.resolve(versionId + ".json"), versionJson);
|
||||||
ProgressBar.show("Version info", 1, 1, "files");
|
ProgressBar.show("Version info", 1, 1, "files");
|
||||||
|
|
||||||
@@ -78,8 +73,9 @@ public class VersionInstaller {
|
|||||||
|
|
||||||
// client.jar
|
// client.jar
|
||||||
ProgressBar.show("Downloading client.jar", 0, 1, "files");
|
ProgressBar.show("Downloading client.jar", 0, 1, "files");
|
||||||
downloadFile(versionData.getJSONObject("downloads").getJSONObject("client").getString("url"),
|
ZHttpClient.downloadFileWithSmartProxy(
|
||||||
versionDir.resolve(versionId + ".jar"), "client.jar");
|
versionData.getJSONObject("downloads").getJSONObject("client").getString("url"),
|
||||||
|
versionDir.resolve(versionId + ".jar"));
|
||||||
ProgressBar.show("Client.jar", 1, 1, "files");
|
ProgressBar.show("Client.jar", 1, 1, "files");
|
||||||
|
|
||||||
// Libraries
|
// Libraries
|
||||||
@@ -147,7 +143,7 @@ public class VersionInstaller {
|
|||||||
if (!Files.exists(libJar)) {
|
if (!Files.exists(libJar)) {
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(libJar.getParent());
|
Files.createDirectories(libJar.getParent());
|
||||||
downloadFile(url, libJar, "");
|
ZHttpClient.downloadFileWithSmartProxy(url, libJar);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
failed++;
|
failed++;
|
||||||
continue;
|
continue;
|
||||||
@@ -199,7 +195,7 @@ public class VersionInstaller {
|
|||||||
Files.createDirectories(target.getParent());
|
Files.createDirectories(target.getParent());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
downloadFile(url, target, "library");
|
ZHttpClient.downloadFileWithSmartProxy(url, target);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Skip problematic libraries
|
// Skip problematic libraries
|
||||||
}
|
}
|
||||||
@@ -220,7 +216,7 @@ public class VersionInstaller {
|
|||||||
Path indexPath = indexesDir.resolve(assetIndex + ".json");
|
Path indexPath = indexesDir.resolve(assetIndex + ".json");
|
||||||
|
|
||||||
System.out.println(ZAnsi.cyan("Downloading asset index (" + assetIndex + ")..."));
|
System.out.println(ZAnsi.cyan("Downloading asset index (" + assetIndex + ")..."));
|
||||||
downloadFile(indexUrl, indexPath, "asset index");
|
ZHttpClient.downloadFileWithSmartProxy(indexUrl, indexPath);
|
||||||
|
|
||||||
String jsonContent = Files.readString(indexPath);
|
String jsonContent = Files.readString(indexPath);
|
||||||
JSONObject root = new JSONObject(jsonContent);
|
JSONObject root = new JSONObject(jsonContent);
|
||||||
@@ -249,20 +245,20 @@ public class VersionInstaller {
|
|||||||
boolean downloaded = false;
|
boolean downloaded = false;
|
||||||
for (int attempt = 1; attempt <= 3; attempt++) {
|
for (int attempt = 1; attempt <= 3; attempt++) {
|
||||||
try {
|
try {
|
||||||
downloadFile(url, target, "");
|
ZHttpClient.downloadFileWithSmartProxy(url, target);
|
||||||
|
synchronized (this) {
|
||||||
|
success[0]++;
|
||||||
|
ProgressBar.show("Assets", success[0], total, "files");
|
||||||
|
}
|
||||||
|
downloaded = true;
|
||||||
|
break;
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (attempt == 3) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
success[0]++;
|
failed[0]++;
|
||||||
ProgressBar.show("Assets", success[0], total, "files");
|
|
||||||
}
|
}
|
||||||
downloaded = true;
|
System.err.println("Failed to download " + hash);
|
||||||
break;
|
} else {
|
||||||
} catch (Exception e) {
|
|
||||||
if (attempt == 3) {
|
|
||||||
synchronized (this) {
|
|
||||||
failed[0]++;
|
|
||||||
}
|
|
||||||
System.err.println("Failed to download " + hash);
|
|
||||||
} else {
|
|
||||||
try { Thread.sleep(500 * attempt); } catch (InterruptedException ignored) {}
|
try { Thread.sleep(500 * attempt); } catch (InterruptedException ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,8 +268,17 @@ public class VersionInstaller {
|
|||||||
futures.add(future);
|
futures.add(future);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
try {
|
||||||
executor.shutdown();
|
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
||||||
|
.get(10, TimeUnit.MINUTES);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
futures.forEach(f -> f.cancel(true));
|
||||||
|
System.err.println("Asset download timed out after 10 minutes");
|
||||||
|
} catch (CancellationException e) {
|
||||||
|
// one of the futures was cancelled
|
||||||
|
} catch (CompletionException e) {
|
||||||
|
// one of the futures failed exceptionally
|
||||||
|
}
|
||||||
|
|
||||||
ProgressBar.finish("Assets downloaded (" + success[0] + " ok, " + failed[0] + " skipped)");
|
ProgressBar.finish("Assets downloaded (" + success[0] + " ok, " + failed[0] + " skipped)");
|
||||||
|
|
||||||
@@ -287,13 +292,13 @@ public class VersionInstaller {
|
|||||||
String versionUrl = getVersionUrl(versionId);
|
String versionUrl = getVersionUrl(versionId);
|
||||||
if (versionUrl == null) throw new Exception("Version not found");
|
if (versionUrl == null) throw new Exception("Version not found");
|
||||||
|
|
||||||
String versionJson = downloadString(versionUrl);
|
String versionJson = ZHttpClient.getWithSmartProxy(versionUrl);
|
||||||
JSONObject versionData = new JSONObject(versionJson);
|
JSONObject versionData = new JSONObject(versionJson);
|
||||||
|
|
||||||
if (versionData.has("assetIndex") && versionData.getJSONObject("assetIndex").has("id")) {
|
if (versionData.has("assetIndex") && versionData.getJSONObject("assetIndex").has("id")) {
|
||||||
return versionData.getJSONObject("assetIndex").getString("id"); // "5" для 1.20.1
|
return versionData.getJSONObject("assetIndex").getString("id");
|
||||||
}
|
}
|
||||||
return versionData.getString("assets"); // fallback (very old versions)
|
return versionData.getString("assets");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getVersionUrl(String versionId) throws Exception {
|
private String getVersionUrl(String versionId) throws Exception {
|
||||||
@@ -302,35 +307,4 @@ public class VersionInstaller {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private String downloadString(String url) throws Exception {
|
|
||||||
HttpRequest req = HttpRequest.newBuilder().uri(URI.create(url)).GET().build();
|
|
||||||
HttpResponse<String> resp = httpClient.send(req, HttpResponse.BodyHandlers.ofString());
|
|
||||||
if (resp.statusCode() != 200) throw new IOException("HTTP " + resp.statusCode());
|
|
||||||
return resp.body();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void downloadFile(String url, Path target, String label) throws Exception {
|
|
||||||
if (!label.isEmpty()) {
|
|
||||||
ProgressBar.clearLine();
|
|
||||||
System.out.println(ZAnsi.cyan("Downloading " + label + "..."));
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
|
||||||
.uri(URI.create(url))
|
|
||||||
.GET()
|
|
||||||
.build();
|
|
||||||
|
|
||||||
HttpResponse<Path> response = httpClient.send(request, HttpResponse.BodyHandlers.ofFile(target));
|
|
||||||
|
|
||||||
if (response.statusCode() != 200) {
|
|
||||||
if (label.isEmpty()) return; // for assets silently
|
|
||||||
throw new IOException("HTTP " + response.statusCode() + " while downloading " + label);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!label.isEmpty()) {
|
|
||||||
long size = Files.size(target);
|
|
||||||
ProgressBar.finish(label + " (" + ProgressBar.formatBytes(size) + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>1.0.13</revision>
|
<revision>1.0.13</revision>
|
||||||
<hotfix>0</hotfix>
|
<hotfix>1</hotfix>
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|||||||
+7
-5
@@ -1261,15 +1261,17 @@ async def get_pack_file(pack_name: str, file_path: str, request: Request, curren
|
|||||||
# ====================== ЭНДПОИНТЫ ДЛЯ ЛАУНЧЕРА ======================
|
# ====================== ЭНДПОИНТЫ ДЛЯ ЛАУНЧЕРА ======================
|
||||||
|
|
||||||
def get_current_launcher_version() -> str:
|
def get_current_launcher_version() -> str:
|
||||||
"""Get current launcher version from meta system (new format) or build.version (legacy)"""
|
"""Get current launcher version — prefers build.version (includes hotfix), falls back to extracted versions"""
|
||||||
|
version_file = BUILDS_DIR / "build.version"
|
||||||
|
if version_file.exists():
|
||||||
|
v = version_file.read_text().strip()
|
||||||
|
if v:
|
||||||
|
return v
|
||||||
|
|
||||||
versions = get_launcher_versions()
|
versions = get_launcher_versions()
|
||||||
if versions:
|
if versions:
|
||||||
return versions[0]["meta"]["version"]
|
return versions[0]["meta"]["version"]
|
||||||
|
|
||||||
# Fallback to build.version for legacy
|
|
||||||
version_file = BUILDS_DIR / "build.version"
|
|
||||||
if version_file.exists():
|
|
||||||
return version_file.read_text().strip()
|
|
||||||
return "1.0.0"
|
return "1.0.0"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
Pack Download Stability Fix
|
||||||
|
Update
|
||||||
|
v1.0.13.1
|
||||||
|
[{"bold":1,"text":"Fixed pack downloads getting stuck on Minecraft/Forge installation","click_action":"none"}]
|
||||||
|
- All Minecraft downloads now use smart proxy system (like the rest of the launcher)
|
||||||
|
- Added read timeouts to prevent hanging on slow/unstable connections
|
||||||
|
- Asset download no longer blocks forever if Mojang CDN stalls
|
||||||
|
- Forge installer process now has a 10-minute timeout
|
||||||
|
- Progress bar now updates during Minecraft installation phase
|
||||||
|
[{"clickable":1,"text":"Download v1.0.13.1","click_action":"open_url","url":"https://zernmc.ru/download"}]
|
||||||
Reference in New Issue
Block a user