v1.0.16.2 — fix NeoForge/Forge install (bundled JRE for installers, visible progress in JFX UI, skip existing vanilla files), hide zernmc-cli.exe from users
This commit is contained in:
@@ -159,6 +159,7 @@
|
|||||||
<headerType>gui</headerType>
|
<headerType>gui</headerType>
|
||||||
<dontWrapJar>false</dontWrapJar>
|
<dontWrapJar>false</dontWrapJar>
|
||||||
<mainClass>me.sashegdev.zernmc.launcher.Bootstrap</mainClass>
|
<mainClass>me.sashegdev.zernmc.launcher.Bootstrap</mainClass>
|
||||||
|
<cmdLine>--jfx</cmdLine>
|
||||||
<jre>
|
<jre>
|
||||||
<path>lib/jre21</path>
|
<path>lib/jre21</path>
|
||||||
<minVersion>21</minVersion>
|
<minVersion>21</minVersion>
|
||||||
@@ -283,20 +284,16 @@
|
|||||||
ZernMC Launcher
|
ZernMC Launcher
|
||||||
|
|
||||||
Files:
|
Files:
|
||||||
- zernmc.exe - Main launcher with GUI (no console window)
|
- zernmc.exe - Main launcher with GUI
|
||||||
- zernmc-cli.exe - CLI version for servers/advanced users (with console)
|
|
||||||
|
|
||||||
How to use GUI:
|
How to use:
|
||||||
Just run zernmc.exe
|
Just run zernmc.exe
|
||||||
|
|
||||||
How to use CLI:
|
|
||||||
Run from command line: zernmc-cli.exe --cli
|
|
||||||
</echo>
|
</echo>
|
||||||
|
|
||||||
<!-- Создаём один архив со всем -->
|
<!-- Создаём один архив со всем -->
|
||||||
<zip destfile="../../server/builds/ZernMC-win-${revision}.${hotfix}.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,bin/**,assets/**,lib/**,README.txt"
|
||||||
excludes="build.version,*.jar"/>
|
excludes="build.version,*.jar"/>
|
||||||
</target>
|
</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
+18
-17
@@ -1,8 +1,9 @@
|
|||||||
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.JavaResolver;
|
||||||
|
import me.sashegdev.zernmc.launcher.utils.LauncherLogger;
|
||||||
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.ZHttpClient;
|
import me.sashegdev.zernmc.launcher.utils.ZHttpClient;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -19,7 +20,7 @@ public class FabricInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean install(String minecraftVersion, String loaderVersion) throws Exception {
|
public boolean install(String minecraftVersion, String loaderVersion) throws Exception {
|
||||||
System.out.println(ZAnsi.cyan("Installing Fabric " + loaderVersion + " for Minecraft " + minecraftVersion));
|
LauncherLogger.info("Installing Fabric " + loaderVersion + " for Minecraft " + minecraftVersion);
|
||||||
|
|
||||||
Path instancePath = instance.getPath();
|
Path instancePath = instance.getPath();
|
||||||
cleanOldFabricLoaders();
|
cleanOldFabricLoaders();
|
||||||
@@ -27,7 +28,7 @@ public class FabricInstaller {
|
|||||||
VersionInstaller versionInstaller = new VersionInstaller(instancePath);
|
VersionInstaller versionInstaller = new VersionInstaller(instancePath);
|
||||||
String assetIndex = versionInstaller.install(minecraftVersion);
|
String assetIndex = versionInstaller.install(minecraftVersion);
|
||||||
|
|
||||||
System.out.println(ZAnsi.green("Asset index obtained: " + assetIndex));
|
LauncherLogger.info("Asset index obtained: " + assetIndex);
|
||||||
|
|
||||||
instance.setAssetIndex(assetIndex);
|
instance.setAssetIndex(assetIndex);
|
||||||
instance.setMinecraftVersion(minecraftVersion);
|
instance.setMinecraftVersion(minecraftVersion);
|
||||||
@@ -46,12 +47,12 @@ public class FabricInstaller {
|
|||||||
|
|
||||||
preDownloadFabricLibraries(minecraftVersion, loaderVersion);
|
preDownloadFabricLibraries(minecraftVersion, loaderVersion);
|
||||||
|
|
||||||
System.out.println(ZAnsi.cyan("Running Fabric Installer..."));
|
LauncherLogger.info("Running Fabric Installer...");
|
||||||
|
|
||||||
String fabricVersionId = "fabric-loader-" + loaderVersion + "-" + minecraftVersion;
|
String fabricVersionId = "fabric-loader-" + loaderVersion + "-" + minecraftVersion;
|
||||||
|
|
||||||
ProcessBuilder pb = new ProcessBuilder(
|
ProcessBuilder pb = new ProcessBuilder(
|
||||||
"java", "-jar", installerJar.toAbsolutePath().toString(),
|
JavaResolver.getJavaExecutable(), "-jar", installerJar.toAbsolutePath().toString(),
|
||||||
"client",
|
"client",
|
||||||
"-dir", instancePath.toAbsolutePath().toString(),
|
"-dir", instancePath.toAbsolutePath().toString(),
|
||||||
"-mcversion", minecraftVersion,
|
"-mcversion", minecraftVersion,
|
||||||
@@ -66,20 +67,20 @@ public class FabricInstaller {
|
|||||||
boolean finished = process.waitFor(10, TimeUnit.MINUTES);
|
boolean finished = process.waitFor(10, TimeUnit.MINUTES);
|
||||||
if (!finished) {
|
if (!finished) {
|
||||||
process.destroyForcibly();
|
process.destroyForcibly();
|
||||||
System.out.println(ZAnsi.brightRed("Fabric Installer timed out after 10 minutes"));
|
LauncherLogger.error("Fabric Installer timed out after 10 minutes");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int exitCode = process.exitValue();
|
int exitCode = process.exitValue();
|
||||||
|
|
||||||
if (exitCode != 0) {
|
if (exitCode != 0) {
|
||||||
System.out.println(ZAnsi.brightRed("Fabric Installer failed (code " + exitCode + ")"));
|
LauncherLogger.error("Fabric Installer failed (code " + exitCode + ")");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Path fabricVersionDir = instancePath.resolve("versions").resolve(fabricVersionId);
|
Path fabricVersionDir = instancePath.resolve("versions").resolve(fabricVersionId);
|
||||||
|
|
||||||
if (Files.exists(fabricVersionDir)) {
|
if (Files.exists(fabricVersionDir)) {
|
||||||
System.out.println(ZAnsi.brightGreen("Fabric installed successfully!"));
|
LauncherLogger.info("Fabric installed successfully!");
|
||||||
|
|
||||||
instance.setLoaderType("fabric");
|
instance.setLoaderType("fabric");
|
||||||
instance.setLoaderVersion(loaderVersion);
|
instance.setLoaderVersion(loaderVersion);
|
||||||
@@ -91,7 +92,7 @@ public class FabricInstaller {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
System.out.println(ZAnsi.brightRed("Fabric Installer ran, but version not found."));
|
LauncherLogger.error("Fabric Installer ran, but version not found.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,7 +101,7 @@ public class FabricInstaller {
|
|||||||
try {
|
try {
|
||||||
ZHttpClient.downloadFile(url, target);
|
ZHttpClient.downloadFile(url, target);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println(ZAnsi.yellow("Failed to download Fabric Installer: " + e.getMessage()));
|
LauncherLogger.warn("Failed to download Fabric Installer: " + e.getMessage());
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,7 +133,7 @@ public class FabricInstaller {
|
|||||||
|
|
||||||
ZHttpClient.repairLibrariesFromJson(loaderJson, instance.getPath());
|
ZHttpClient.repairLibrariesFromJson(loaderJson, instance.getPath());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println(ZAnsi.yellow("Pre-download of Fabric libraries incomplete: " + e.getMessage()));
|
LauncherLogger.warn("Pre-download of Fabric libraries incomplete: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +141,7 @@ public class FabricInstaller {
|
|||||||
Path versionJson = fabricVersionDir.resolve(fabricVersionDir.getFileName() + ".json");
|
Path versionJson = fabricVersionDir.resolve(fabricVersionDir.getFileName() + ".json");
|
||||||
|
|
||||||
if (!Files.exists(versionJson)) {
|
if (!Files.exists(versionJson)) {
|
||||||
System.out.println(ZAnsi.yellow("Version JSON file not found: " + versionJson));
|
LauncherLogger.warn("Version JSON file not found: " + versionJson);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +149,7 @@ public class FabricInstaller {
|
|||||||
|
|
||||||
// Check and fix asset index
|
// Check and fix asset index
|
||||||
if (!content.contains("\"assets\":\"" + assetIndex + "\"")) {
|
if (!content.contains("\"assets\":\"" + assetIndex + "\"")) {
|
||||||
System.out.println(ZAnsi.yellow("Fixing asset index in version JSON file..."));
|
LauncherLogger.warn("Fixing asset index in version JSON file...");
|
||||||
|
|
||||||
// Replace assets with correct value
|
// Replace assets with correct value
|
||||||
content = content.replaceAll("\"assets\":\\s*\"[^\"]*\"", "\"assets\": \"" + assetIndex + "\"");
|
content = content.replaceAll("\"assets\":\\s*\"[^\"]*\"", "\"assets\": \"" + assetIndex + "\"");
|
||||||
@@ -159,9 +160,9 @@ public class FabricInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Files.writeString(versionJson, content);
|
Files.writeString(versionJson, content);
|
||||||
System.out.println(ZAnsi.green("Asset index fixed to: " + assetIndex));
|
LauncherLogger.info("Asset index fixed to: " + assetIndex);
|
||||||
} else {
|
} else {
|
||||||
System.out.println(ZAnsi.green("Asset index in version JSON is correct: " + assetIndex));
|
LauncherLogger.info("Asset index in version JSON is correct: " + assetIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +170,7 @@ public class FabricInstaller {
|
|||||||
Path librariesDir = instance.getPath().resolve("libraries/net/fabricmc/fabric-loader");
|
Path librariesDir = instance.getPath().resolve("libraries/net/fabricmc/fabric-loader");
|
||||||
if (!Files.exists(librariesDir)) return;
|
if (!Files.exists(librariesDir)) return;
|
||||||
|
|
||||||
System.out.println(ZAnsi.yellow("Cleaning old Fabric Loader versions..."));
|
LauncherLogger.warn("Cleaning old Fabric Loader versions...");
|
||||||
|
|
||||||
try (var stream = Files.walk(librariesDir)) {
|
try (var stream = Files.walk(librariesDir)) {
|
||||||
stream.filter(Files::isDirectory)
|
stream.filter(Files::isDirectory)
|
||||||
@@ -195,7 +196,7 @@ public class FabricInstaller {
|
|||||||
int end = xml.indexOf("</latest>", start);
|
int end = xml.indexOf("</latest>", start);
|
||||||
return xml.substring(start, end).trim();
|
return xml.substring(start, end).trim();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println(ZAnsi.yellow("Error getting Fabric Installer version: " + e.getMessage()));
|
LauncherLogger.warn("Error getting Fabric Installer version: " + e.getMessage());
|
||||||
throw new Exception("Failed to get Fabric Installer version", e);
|
throw new Exception("Failed to get Fabric Installer version", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-27
@@ -1,8 +1,9 @@
|
|||||||
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.JavaResolver;
|
||||||
|
import me.sashegdev.zernmc.launcher.utils.LauncherLogger;
|
||||||
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.ZHttpClient;
|
import me.sashegdev.zernmc.launcher.utils.ZHttpClient;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@@ -19,16 +20,16 @@ public class ForgeInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean install(String mcVersion, String forgeVersion) throws Exception {
|
public boolean install(String mcVersion, String forgeVersion) throws Exception {
|
||||||
System.out.println(ZAnsi.cyan("Installing Forge " + forgeVersion + " for Minecraft " + mcVersion));
|
LauncherLogger.info("Installing Forge " + forgeVersion + " for Minecraft " + mcVersion);
|
||||||
|
|
||||||
// Step 1: Install vanilla and get real assetIndex
|
// Step 1: Install vanilla and get real assetIndex
|
||||||
System.out.println(ZAnsi.cyan("Installing base Minecraft version " + mcVersion + "..."));
|
LauncherLogger.info("Installing base Minecraft version " + mcVersion + "...");
|
||||||
VersionInstaller vanillaInstaller = new VersionInstaller(instance.getPath());
|
VersionInstaller vanillaInstaller = new VersionInstaller(instance.getPath());
|
||||||
|
|
||||||
String assetIndex = vanillaInstaller.install(mcVersion);
|
String assetIndex = vanillaInstaller.install(mcVersion);
|
||||||
|
|
||||||
if (assetIndex == null || assetIndex.isEmpty()) {
|
if (assetIndex == null || assetIndex.isEmpty()) {
|
||||||
System.out.println(ZAnsi.brightRed("Failed to install base Minecraft version"));
|
LauncherLogger.error("Failed to install base Minecraft version");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,12 +45,12 @@ public class ForgeInstaller {
|
|||||||
|
|
||||||
Path installerJar = instance.getPath().resolve("forge-installer.jar");
|
Path installerJar = instance.getPath().resolve("forge-installer.jar");
|
||||||
|
|
||||||
System.out.println(ZAnsi.cyan("Downloading Forge Installer..."));
|
LauncherLogger.info("Downloading Forge Installer...");
|
||||||
downloadFileWithProgress(installerUrl, installerJar);
|
downloadFileWithProgress(installerUrl, installerJar);
|
||||||
|
|
||||||
// Step 4: Run Forge Installer and show its output
|
// Step 4: Run Forge Installer and show its output
|
||||||
System.out.println(ZAnsi.cyan("Running Forge Installer..."));
|
LauncherLogger.info("Running Forge Installer...");
|
||||||
System.out.println(ZAnsi.yellow("This may take a few minutes. Please wait...\n"));
|
LauncherLogger.info("This may take a few minutes. Please wait...");
|
||||||
|
|
||||||
boolean success = runForgeInstaller(installerJar);
|
boolean success = runForgeInstaller(installerJar);
|
||||||
|
|
||||||
@@ -59,10 +60,10 @@ public class ForgeInstaller {
|
|||||||
try {
|
try {
|
||||||
downloadMissingLibraries(mcVersion, forgeVersion);
|
downloadMissingLibraries(mcVersion, forgeVersion);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println(ZAnsi.yellow("Warning: could not download some libraries: " + e.getMessage()));
|
LauncherLogger.warn("Warning: could not download some libraries: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println(ZAnsi.brightGreen("\nForge " + forgeVersion + " installed successfully!"));
|
LauncherLogger.info("\nForge " + forgeVersion + " installed successfully!");
|
||||||
instance.setMinecraftVersion(mcVersion);
|
instance.setMinecraftVersion(mcVersion);
|
||||||
instance.setLoaderType("forge");
|
instance.setLoaderType("forge");
|
||||||
instance.setLoaderVersion(forgeVersion);
|
instance.setLoaderVersion(forgeVersion);
|
||||||
@@ -71,7 +72,7 @@ public class ForgeInstaller {
|
|||||||
Files.deleteIfExists(installerJar);
|
Files.deleteIfExists(installerJar);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
System.out.println(ZAnsi.brightRed("\nError installing Forge!"));
|
LauncherLogger.error("\nError installing Forge!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,7 +88,7 @@ public class ForgeInstaller {
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
Files.writeString(profilePath, minimalProfile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
Files.writeString(profilePath, minimalProfile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
System.out.println(ZAnsi.yellow("Created launcher_profiles.json"));
|
LauncherLogger.info("Created launcher_profiles.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void downloadFileWithProgress(String url, Path target) throws Exception {
|
private void downloadFileWithProgress(String url, Path target) throws Exception {
|
||||||
@@ -102,10 +103,10 @@ public class ForgeInstaller {
|
|||||||
int attempt = 1;
|
int attempt = 1;
|
||||||
|
|
||||||
while (attempt <= maxRetries) {
|
while (attempt <= maxRetries) {
|
||||||
System.out.println(ZAnsi.cyan("Attempt " + attempt + " of " + maxRetries));
|
LauncherLogger.info("Attempt " + attempt + " of " + maxRetries);
|
||||||
|
|
||||||
ProcessBuilder pb = new ProcessBuilder(
|
ProcessBuilder pb = new ProcessBuilder(
|
||||||
"java",
|
JavaResolver.getJavaExecutable(),
|
||||||
"-jar",
|
"-jar",
|
||||||
installerJar.toAbsolutePath().toString(),
|
installerJar.toAbsolutePath().toString(),
|
||||||
"--installClient",
|
"--installClient",
|
||||||
@@ -131,18 +132,18 @@ public class ForgeInstaller {
|
|||||||
|
|
||||||
// Format Forge Installer output
|
// Format Forge Installer output
|
||||||
if (line.contains("Downloading") || line.contains("Extracting")) {
|
if (line.contains("Downloading") || line.contains("Extracting")) {
|
||||||
System.out.println(ZAnsi.blue(" -> " + line));
|
LauncherLogger.info(" -> " + line);
|
||||||
} else if (line.contains("SUCCESS") || line.contains("successfully")) {
|
} else if (line.contains("SUCCESS") || line.contains("successfully")) {
|
||||||
System.out.println(ZAnsi.brightGreen(" + " + line));
|
LauncherLogger.info(" + " + line);
|
||||||
} else if (line.contains("WARNING") || line.contains("warning")) {
|
} else if (line.contains("WARNING") || line.contains("warning")) {
|
||||||
System.out.println(ZAnsi.yellow(" ! " + line));
|
LauncherLogger.warn(" ! " + line);
|
||||||
} else if (line.contains("ERROR") || line.contains("error") || line.contains("failed") || line.contains("timed out")) {
|
} else if (line.contains("ERROR") || line.contains("error") || line.contains("failed") || line.contains("timed out")) {
|
||||||
System.out.println(ZAnsi.brightRed(" X " + line));
|
LauncherLogger.error(" X " + line);
|
||||||
if (line.contains("timed out") || line.contains("failed to download")) {
|
if (line.contains("timed out") || line.contains("failed to download")) {
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
}
|
}
|
||||||
} else if (!line.isBlank()) {
|
} else if (!line.isBlank()) {
|
||||||
System.out.println(" " + line);
|
LauncherLogger.info(" " + line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +155,7 @@ public class ForgeInstaller {
|
|||||||
} else {
|
} else {
|
||||||
if (!process.waitFor(10, TimeUnit.MINUTES)) {
|
if (!process.waitFor(10, TimeUnit.MINUTES)) {
|
||||||
process.destroyForcibly();
|
process.destroyForcibly();
|
||||||
System.out.println(ZAnsi.brightRed("Forge Installer timed out after 10 minutes"));
|
LauncherLogger.error("Forge Installer timed out after 10 minutes");
|
||||||
if (attempt < maxRetries) {
|
if (attempt < maxRetries) {
|
||||||
attempt++;
|
attempt++;
|
||||||
Thread.sleep(5000);
|
Thread.sleep(5000);
|
||||||
@@ -172,7 +173,7 @@ public class ForgeInstaller {
|
|||||||
|
|
||||||
// If error and not last attempt
|
// If error and not last attempt
|
||||||
if (attempt < maxRetries) {
|
if (attempt < maxRetries) {
|
||||||
System.out.println(ZAnsi.yellow("Install error. Retrying in 5 seconds..."));
|
LauncherLogger.warn("Install error. Retrying in 5 seconds...");
|
||||||
Thread.sleep(5000);
|
Thread.sleep(5000);
|
||||||
|
|
||||||
// Clean temp files before retry
|
// Clean temp files before retry
|
||||||
@@ -188,15 +189,15 @@ public class ForgeInstaller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
System.out.println(ZAnsi.brightRed("Forge Installer exited with error code: " + exitCode));
|
LauncherLogger.error("Forge Installer exited with error code: " + exitCode);
|
||||||
|
|
||||||
// Show possible solution
|
// Show possible solution
|
||||||
if (output.toString().contains("timed out")) {
|
if (output.toString().contains("timed out")) {
|
||||||
System.out.println(ZAnsi.yellow("\nPossible solutions:"));
|
LauncherLogger.warn("\nPossible solutions:");
|
||||||
System.out.println(ZAnsi.yellow("1. Check your internet connection"));
|
LauncherLogger.warn("1. Check your internet connection");
|
||||||
System.out.println(ZAnsi.yellow("2. Run the launcher as administrator"));
|
LauncherLogger.warn("2. Run the launcher as administrator");
|
||||||
System.out.println(ZAnsi.yellow("3. Temporarily disable antivirus/firewall"));
|
LauncherLogger.warn("3. Temporarily disable antivirus/firewall");
|
||||||
System.out.println(ZAnsi.yellow("4. Try installing a different Forge version"));
|
LauncherLogger.warn("4. Try installing a different Forge version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +208,7 @@ public class ForgeInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void downloadMissingLibraries(String mcVersion, String forgeVersion) throws Exception {
|
private void downloadMissingLibraries(String mcVersion, String forgeVersion) throws Exception {
|
||||||
System.out.println(ZAnsi.cyan("Checking and downloading missing libraries..."));
|
LauncherLogger.info("Checking and downloading missing libraries...");
|
||||||
ZHttpClient.repairMissingLibraries(instance.getPath());
|
ZHttpClient.repairMissingLibraries(instance.getPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+28
-27
@@ -1,7 +1,8 @@
|
|||||||
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.ZAnsi;
|
import me.sashegdev.zernmc.launcher.utils.JavaResolver;
|
||||||
|
import me.sashegdev.zernmc.launcher.utils.LauncherLogger;
|
||||||
import me.sashegdev.zernmc.launcher.utils.ZHttpClient;
|
import me.sashegdev.zernmc.launcher.utils.ZHttpClient;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@@ -42,14 +43,14 @@ public class ModLoaderInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean install(String mcVersion, String loaderVersion, LoaderType type) throws Exception {
|
public boolean install(String mcVersion, String loaderVersion, LoaderType type) throws Exception {
|
||||||
System.out.println(ZAnsi.cyan("Installing " + type.loaderName + " " + loaderVersion + " for Minecraft " + mcVersion));
|
LauncherLogger.info("Installing " + type.loaderName + " " + loaderVersion + " for Minecraft " + mcVersion);
|
||||||
|
|
||||||
System.out.println(ZAnsi.cyan("Installing base Minecraft version " + mcVersion + "..."));
|
LauncherLogger.info("Installing base Minecraft version " + mcVersion + "...");
|
||||||
VersionInstaller vanillaInstaller = new VersionInstaller(instance.getPath());
|
VersionInstaller vanillaInstaller = new VersionInstaller(instance.getPath());
|
||||||
String assetIndex = vanillaInstaller.install(mcVersion);
|
String assetIndex = vanillaInstaller.install(mcVersion);
|
||||||
|
|
||||||
if (assetIndex == null || assetIndex.isEmpty()) {
|
if (assetIndex == null || assetIndex.isEmpty()) {
|
||||||
System.out.println(ZAnsi.brightRed("Failed to install base Minecraft version"));
|
LauncherLogger.error("Failed to install base Minecraft version");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,11 +61,11 @@ public class ModLoaderInstaller {
|
|||||||
String jarName = type.loaderName + "-installer.jar";
|
String jarName = type.loaderName + "-installer.jar";
|
||||||
Path installerJar = instance.getPath().resolve(jarName);
|
Path installerJar = instance.getPath().resolve(jarName);
|
||||||
|
|
||||||
System.out.println(ZAnsi.cyan("Downloading " + type.loaderName + " Installer..."));
|
LauncherLogger.info("Downloading " + type.loaderName + " Installer...");
|
||||||
ZHttpClient.downloadFileWithSmartProxy(installerUrl, installerJar);
|
ZHttpClient.downloadFileWithSmartProxy(installerUrl, installerJar);
|
||||||
|
|
||||||
System.out.println(ZAnsi.cyan("Running " + type.loaderName + " Installer..."));
|
LauncherLogger.info("Running " + type.loaderName + " Installer...");
|
||||||
System.out.println(ZAnsi.yellow("This may take a few minutes. Please wait...\n"));
|
LauncherLogger.info("This may take a few minutes. Please wait...");
|
||||||
|
|
||||||
boolean success = runInstaller(installerJar, type);
|
boolean success = runInstaller(installerJar, type);
|
||||||
|
|
||||||
@@ -72,10 +73,10 @@ public class ModLoaderInstaller {
|
|||||||
try {
|
try {
|
||||||
downloadMissingLibraries(type);
|
downloadMissingLibraries(type);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println(ZAnsi.yellow("Warning: could not download some libraries: " + e.getMessage()));
|
LauncherLogger.warn("Warning: could not download some libraries: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println(ZAnsi.brightGreen("\n" + type.loaderName + " " + loaderVersion + " installed successfully!"));
|
LauncherLogger.info("\n" + type.loaderName + " " + loaderVersion + " installed successfully!");
|
||||||
instance.setMinecraftVersion(mcVersion);
|
instance.setMinecraftVersion(mcVersion);
|
||||||
instance.setLoaderType(type.loaderName);
|
instance.setLoaderType(type.loaderName);
|
||||||
instance.setLoaderVersion(loaderVersion);
|
instance.setLoaderVersion(loaderVersion);
|
||||||
@@ -83,7 +84,7 @@ public class ModLoaderInstaller {
|
|||||||
Files.deleteIfExists(installerJar);
|
Files.deleteIfExists(installerJar);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
System.out.println(ZAnsi.brightRed("\nError installing " + type.loaderName + "!"));
|
LauncherLogger.error("\nError installing " + type.loaderName + "!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,7 +113,7 @@ public class ModLoaderInstaller {
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
Files.writeString(profilePath, minimalProfile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
Files.writeString(profilePath, minimalProfile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
System.out.println(ZAnsi.yellow("Created launcher_profiles.json"));
|
LauncherLogger.info("Created launcher_profiles.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean runInstaller(Path installerJar, LoaderType type) throws IOException, InterruptedException {
|
private boolean runInstaller(Path installerJar, LoaderType type) throws IOException, InterruptedException {
|
||||||
@@ -120,10 +121,10 @@ public class ModLoaderInstaller {
|
|||||||
int attempt = 1;
|
int attempt = 1;
|
||||||
|
|
||||||
while (attempt <= maxRetries) {
|
while (attempt <= maxRetries) {
|
||||||
System.out.println(ZAnsi.cyan("Attempt " + attempt + " of " + maxRetries));
|
LauncherLogger.info("Attempt " + attempt + " of " + maxRetries);
|
||||||
|
|
||||||
ProcessBuilder pb = new ProcessBuilder(
|
ProcessBuilder pb = new ProcessBuilder(
|
||||||
"java",
|
JavaResolver.getJavaExecutable(),
|
||||||
"-jar",
|
"-jar",
|
||||||
installerJar.toAbsolutePath().toString(),
|
installerJar.toAbsolutePath().toString(),
|
||||||
"--installClient",
|
"--installClient",
|
||||||
@@ -145,18 +146,18 @@ public class ModLoaderInstaller {
|
|||||||
output.append(line).append("\n");
|
output.append(line).append("\n");
|
||||||
|
|
||||||
if (line.contains("Downloading") || line.contains("Extracting")) {
|
if (line.contains("Downloading") || line.contains("Extracting")) {
|
||||||
System.out.println(ZAnsi.blue(" -> " + line));
|
LauncherLogger.info(" -> " + line);
|
||||||
} else if (line.contains("SUCCESS") || line.contains("successfully")) {
|
} else if (line.contains("SUCCESS") || line.contains("successfully")) {
|
||||||
System.out.println(ZAnsi.brightGreen(" + " + line));
|
LauncherLogger.info(" + " + line);
|
||||||
} else if (line.contains("WARNING") || line.contains("warning")) {
|
} else if (line.contains("WARNING") || line.contains("warning")) {
|
||||||
System.out.println(ZAnsi.yellow(" ! " + line));
|
LauncherLogger.warn(" ! " + line);
|
||||||
} else if (line.contains("ERROR") || line.contains("error") || line.contains("failed") || line.contains("timed out")) {
|
} else if (line.contains("ERROR") || line.contains("error") || line.contains("failed") || line.contains("timed out")) {
|
||||||
System.out.println(ZAnsi.brightRed(" X " + line));
|
LauncherLogger.error(" X " + line);
|
||||||
if (line.contains("timed out") || line.contains("failed to download")) {
|
if (line.contains("timed out") || line.contains("failed to download")) {
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
}
|
}
|
||||||
} else if (!line.isBlank()) {
|
} else if (!line.isBlank()) {
|
||||||
System.out.println(" " + line);
|
LauncherLogger.info(" " + line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,7 +169,7 @@ public class ModLoaderInstaller {
|
|||||||
} else {
|
} else {
|
||||||
if (!process.waitFor(10, TimeUnit.MINUTES)) {
|
if (!process.waitFor(10, TimeUnit.MINUTES)) {
|
||||||
process.destroyForcibly();
|
process.destroyForcibly();
|
||||||
System.out.println(ZAnsi.brightRed(type.loaderName + " Installer timed out after 10 minutes"));
|
LauncherLogger.error(type.loaderName + " Installer timed out after 10 minutes");
|
||||||
if (attempt < maxRetries) {
|
if (attempt < maxRetries) {
|
||||||
attempt++;
|
attempt++;
|
||||||
Thread.sleep(5000);
|
Thread.sleep(5000);
|
||||||
@@ -184,7 +185,7 @@ public class ModLoaderInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (attempt < maxRetries) {
|
if (attempt < maxRetries) {
|
||||||
System.out.println(ZAnsi.yellow("Install error. Retrying in 5 seconds..."));
|
LauncherLogger.warn("Install error. Retrying in 5 seconds...");
|
||||||
Thread.sleep(5000);
|
Thread.sleep(5000);
|
||||||
|
|
||||||
Path librariesDir = instance.getPath().resolve("libraries");
|
Path librariesDir = instance.getPath().resolve("libraries");
|
||||||
@@ -198,14 +199,14 @@ public class ModLoaderInstaller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
System.out.println(ZAnsi.brightRed(type.loaderName + " Installer exited with error code: " + exitCode));
|
LauncherLogger.error(type.loaderName + " Installer exited with error code: " + exitCode);
|
||||||
|
|
||||||
if (output.toString().contains("timed out")) {
|
if (output.toString().contains("timed out")) {
|
||||||
System.out.println(ZAnsi.yellow("\nPossible solutions:"));
|
LauncherLogger.warn("\nPossible solutions:");
|
||||||
System.out.println(ZAnsi.yellow("1. Check your internet connection"));
|
LauncherLogger.warn("1. Check your internet connection");
|
||||||
System.out.println(ZAnsi.yellow("2. Run the launcher as administrator"));
|
LauncherLogger.warn("2. Run the launcher as administrator");
|
||||||
System.out.println(ZAnsi.yellow("3. Temporarily disable antivirus/firewall"));
|
LauncherLogger.warn("3. Temporarily disable antivirus/firewall");
|
||||||
System.out.println(ZAnsi.yellow("4. Try installing a different version"));
|
LauncherLogger.warn("4. Try installing a different version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +217,7 @@ public class ModLoaderInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void downloadMissingLibraries(LoaderType type) throws Exception {
|
private void downloadMissingLibraries(LoaderType type) throws Exception {
|
||||||
System.out.println(ZAnsi.cyan("Checking and downloading missing libraries..."));
|
LauncherLogger.info("Checking and downloading missing libraries...");
|
||||||
ZHttpClient.repairMissingLibraries(instance.getPath());
|
ZHttpClient.repairMissingLibraries(instance.getPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-27
@@ -1,8 +1,9 @@
|
|||||||
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.JavaResolver;
|
||||||
|
import me.sashegdev.zernmc.launcher.utils.LauncherLogger;
|
||||||
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.ZHttpClient;
|
import me.sashegdev.zernmc.launcher.utils.ZHttpClient;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@@ -20,14 +21,14 @@ public class NeoForgeInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean install(String mcVersion, String neoForgeVersion) throws Exception {
|
public boolean install(String mcVersion, String neoForgeVersion) throws Exception {
|
||||||
System.out.println(ZAnsi.cyan("Installing NeoForge " + neoForgeVersion + " for Minecraft " + mcVersion));
|
LauncherLogger.info("Installing NeoForge " + neoForgeVersion + " for Minecraft " + mcVersion);
|
||||||
|
|
||||||
System.out.println(ZAnsi.cyan("Installing base Minecraft version " + mcVersion + "..."));
|
LauncherLogger.info("Installing base Minecraft version " + mcVersion + "...");
|
||||||
VersionInstaller vanillaInstaller = new VersionInstaller(instance.getPath());
|
VersionInstaller vanillaInstaller = new VersionInstaller(instance.getPath());
|
||||||
String assetIndex = vanillaInstaller.install(mcVersion);
|
String assetIndex = vanillaInstaller.install(mcVersion);
|
||||||
|
|
||||||
if (assetIndex == null || assetIndex.isEmpty()) {
|
if (assetIndex == null || assetIndex.isEmpty()) {
|
||||||
System.out.println(ZAnsi.brightRed("Failed to install base Minecraft version"));
|
LauncherLogger.error("Failed to install base Minecraft version");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,11 +46,11 @@ public class NeoForgeInstaller {
|
|||||||
|
|
||||||
Path installerJar = instance.getPath().resolve("neoforge-installer.jar");
|
Path installerJar = instance.getPath().resolve("neoforge-installer.jar");
|
||||||
|
|
||||||
System.out.println(ZAnsi.cyan("Downloading NeoForge Installer..."));
|
LauncherLogger.info("Downloading NeoForge Installer...");
|
||||||
downloadFileWithProgress(installerUrl, installerJar);
|
downloadFileWithProgress(installerUrl, installerJar);
|
||||||
|
|
||||||
System.out.println(ZAnsi.cyan("Running NeoForge Installer..."));
|
LauncherLogger.info("Running NeoForge Installer...");
|
||||||
System.out.println(ZAnsi.yellow("This may take a few minutes. Please wait...\n"));
|
LauncherLogger.info("This may take a few minutes. Please wait...");
|
||||||
|
|
||||||
boolean success = runNeoForgeInstaller(installerJar);
|
boolean success = runNeoForgeInstaller(installerJar);
|
||||||
|
|
||||||
@@ -57,10 +58,10 @@ public class NeoForgeInstaller {
|
|||||||
try {
|
try {
|
||||||
downloadMissingLibraries(mcVersion, neoForgeVersion, mavenGroup, mavenArtifact);
|
downloadMissingLibraries(mcVersion, neoForgeVersion, mavenGroup, mavenArtifact);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println(ZAnsi.yellow("Warning: could not download some libraries: " + e.getMessage()));
|
LauncherLogger.warn("Warning: could not download some libraries: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println(ZAnsi.brightGreen("\nNeoForge " + neoForgeVersion + " installed successfully!"));
|
LauncherLogger.info("\nNeoForge " + neoForgeVersion + " installed successfully!");
|
||||||
instance.setMinecraftVersion(mcVersion);
|
instance.setMinecraftVersion(mcVersion);
|
||||||
instance.setLoaderType("neoforge");
|
instance.setLoaderType("neoforge");
|
||||||
instance.setLoaderVersion(neoForgeVersion);
|
instance.setLoaderVersion(neoForgeVersion);
|
||||||
@@ -68,7 +69,7 @@ public class NeoForgeInstaller {
|
|||||||
Files.deleteIfExists(installerJar);
|
Files.deleteIfExists(installerJar);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
System.out.println(ZAnsi.brightRed("\nError installing NeoForge!"));
|
LauncherLogger.error("\nError installing NeoForge!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,7 +99,7 @@ public class NeoForgeInstaller {
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
Files.writeString(profilePath, minimalProfile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
Files.writeString(profilePath, minimalProfile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
System.out.println(ZAnsi.yellow("Created launcher_profiles.json"));
|
LauncherLogger.info("Created launcher_profiles.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void downloadFileWithProgress(String url, Path target) throws Exception {
|
private void downloadFileWithProgress(String url, Path target) throws Exception {
|
||||||
@@ -112,10 +113,10 @@ public class NeoForgeInstaller {
|
|||||||
int attempt = 1;
|
int attempt = 1;
|
||||||
|
|
||||||
while (attempt <= maxRetries) {
|
while (attempt <= maxRetries) {
|
||||||
System.out.println(ZAnsi.cyan("Attempt " + attempt + " of " + maxRetries));
|
LauncherLogger.info("Attempt " + attempt + " of " + maxRetries);
|
||||||
|
|
||||||
ProcessBuilder pb = new ProcessBuilder(
|
ProcessBuilder pb = new ProcessBuilder(
|
||||||
"java",
|
JavaResolver.getJavaExecutable(),
|
||||||
"-jar",
|
"-jar",
|
||||||
installerJar.toAbsolutePath().toString(),
|
installerJar.toAbsolutePath().toString(),
|
||||||
"--installClient",
|
"--installClient",
|
||||||
@@ -137,25 +138,25 @@ public class NeoForgeInstaller {
|
|||||||
output.append(line).append("\n");
|
output.append(line).append("\n");
|
||||||
|
|
||||||
if (line.contains("Downloading") || line.contains("Extracting")) {
|
if (line.contains("Downloading") || line.contains("Extracting")) {
|
||||||
System.out.println(ZAnsi.blue(" -> " + line));
|
LauncherLogger.info(" -> " + line);
|
||||||
} else if (line.contains("SUCCESS") || line.contains("successfully")) {
|
} else if (line.contains("SUCCESS") || line.contains("successfully")) {
|
||||||
System.out.println(ZAnsi.brightGreen(" + " + line));
|
LauncherLogger.info(" + " + line);
|
||||||
} else if (line.contains("WARNING") || line.contains("warning")) {
|
} else if (line.contains("WARNING") || line.contains("warning")) {
|
||||||
System.out.println(ZAnsi.yellow(" ! " + line));
|
LauncherLogger.warn(" ! " + line);
|
||||||
} else if (line.contains("ERROR") || line.contains("error") || line.contains("failed") || line.contains("timed out")) {
|
} else if (line.contains("ERROR") || line.contains("error") || line.contains("failed") || line.contains("timed out")) {
|
||||||
System.out.println(ZAnsi.brightRed(" X " + line));
|
LauncherLogger.error(" X " + line);
|
||||||
if (line.contains("timed out") || line.contains("failed to download")) {
|
if (line.contains("timed out") || line.contains("failed to download")) {
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
}
|
}
|
||||||
} else if (!line.isBlank()) {
|
} else if (!line.isBlank()) {
|
||||||
System.out.println(" " + line);
|
LauncherLogger.info(" " + line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!process.waitFor(10, TimeUnit.MINUTES)) {
|
if (!process.waitFor(10, TimeUnit.MINUTES)) {
|
||||||
process.destroyForcibly();
|
process.destroyForcibly();
|
||||||
System.out.println(ZAnsi.brightRed("NeoForge Installer timed out after 10 minutes"));
|
LauncherLogger.error("NeoForge Installer timed out after 10 minutes");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int exitCode = process.exitValue();
|
int exitCode = process.exitValue();
|
||||||
@@ -165,7 +166,7 @@ public class NeoForgeInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (attempt < maxRetries) {
|
if (attempt < maxRetries) {
|
||||||
System.out.println(ZAnsi.yellow("Install error. Retrying in 5 seconds..."));
|
LauncherLogger.warn("Install error. Retrying in 5 seconds...");
|
||||||
Thread.sleep(5000);
|
Thread.sleep(5000);
|
||||||
|
|
||||||
Path librariesDir = instance.getPath().resolve("libraries");
|
Path librariesDir = instance.getPath().resolve("libraries");
|
||||||
@@ -179,14 +180,14 @@ public class NeoForgeInstaller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
System.out.println(ZAnsi.brightRed("NeoForge Installer exited with error code: " + exitCode));
|
LauncherLogger.error("NeoForge Installer exited with error code: " + exitCode);
|
||||||
|
|
||||||
if (output.toString().contains("timed out")) {
|
if (output.toString().contains("timed out")) {
|
||||||
System.out.println(ZAnsi.yellow("\nPossible solutions:"));
|
LauncherLogger.warn("\nPossible solutions:");
|
||||||
System.out.println(ZAnsi.yellow("1. Check your internet connection"));
|
LauncherLogger.warn("1. Check your internet connection");
|
||||||
System.out.println(ZAnsi.yellow("2. Run the launcher as administrator"));
|
LauncherLogger.warn("2. Run the launcher as administrator");
|
||||||
System.out.println(ZAnsi.yellow("3. Temporarily disable antivirus/firewall"));
|
LauncherLogger.warn("3. Temporarily disable antivirus/firewall");
|
||||||
System.out.println(ZAnsi.yellow("4. Try installing a different NeoForge version"));
|
LauncherLogger.warn("4. Try installing a different NeoForge version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +198,7 @@ public class NeoForgeInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void downloadMissingLibraries(String mcVersion, String neoForgeVersion, String mavenGroup, String mavenArtifact) throws Exception {
|
private void downloadMissingLibraries(String mcVersion, String neoForgeVersion, String mavenGroup, String mavenArtifact) throws Exception {
|
||||||
System.out.println(ZAnsi.cyan("Checking and downloading missing libraries..."));
|
LauncherLogger.info("Checking and downloading missing libraries...");
|
||||||
ZHttpClient.repairMissingLibraries(instance.getPath());
|
ZHttpClient.repairMissingLibraries(instance.getPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-19
@@ -1,6 +1,7 @@
|
|||||||
package me.sashegdev.zernmc.launcher.minecraft.installer;
|
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.LauncherLogger;
|
||||||
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 me.sashegdev.zernmc.launcher.utils.ZHttpClient;
|
||||||
@@ -8,6 +9,7 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.IOException;
|
||||||
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;
|
||||||
@@ -56,11 +58,22 @@ public class VersionInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String install(String versionId) throws Exception {
|
public String install(String versionId) throws Exception {
|
||||||
System.out.println(ZAnsi.cyan("Full install of Minecraft " + versionId + "..."));
|
LauncherLogger.info("Full install of Minecraft " + versionId + "...");
|
||||||
ProgressBar.setStage("Version", 0, 5);
|
ProgressBar.setStage("Version", 0, 5);
|
||||||
Path versionDir = minecraftDir.resolve("versions").resolve(versionId);
|
Path versionDir = minecraftDir.resolve("versions").resolve(versionId);
|
||||||
Files.createDirectories(versionDir);
|
Files.createDirectories(versionDir);
|
||||||
|
|
||||||
|
boolean alreadyInstalled = Files.exists(versionDir.resolve(versionId + ".json"))
|
||||||
|
&& Files.exists(versionDir.resolve(versionId + ".jar"));
|
||||||
|
if (alreadyInstalled) {
|
||||||
|
LauncherLogger.info("Minecraft " + versionId + " already installed, skipping");
|
||||||
|
String existing = Files.readString(versionDir.resolve(versionId + ".json"));
|
||||||
|
JSONObject existingData = new JSONObject(existing);
|
||||||
|
return existingData.has("assetIndex")
|
||||||
|
? existingData.getJSONObject("assetIndex").getString("id")
|
||||||
|
: existingData.getString("assets");
|
||||||
|
}
|
||||||
|
|
||||||
ProgressBar.show("Fetching version info", 0, 1, "files");
|
ProgressBar.show("Fetching version info", 0, 1, "files");
|
||||||
String versionJson;
|
String versionJson;
|
||||||
try {
|
try {
|
||||||
@@ -82,24 +95,27 @@ public class VersionInstaller {
|
|||||||
|
|
||||||
// client.jar
|
// client.jar
|
||||||
ProgressBar.show("Downloading client.jar", 0, 1, "files");
|
ProgressBar.show("Downloading client.jar", 0, 1, "files");
|
||||||
try {
|
Path clientJar = versionDir.resolve(versionId + ".jar");
|
||||||
ZHttpClient.downloadFileWithSmartProxy(
|
if (!Files.exists(clientJar)) {
|
||||||
versionData.getJSONObject("downloads").getJSONObject("client").getString("url"),
|
try {
|
||||||
versionDir.resolve(versionId + ".jar"));
|
ZHttpClient.downloadFileWithSmartProxy(
|
||||||
} catch (Exception e) {
|
versionData.getJSONObject("downloads").getJSONObject("client").getString("url"),
|
||||||
System.err.println(ZAnsi.red("[VERSION] Failed to download client.jar: " + e.getMessage()));
|
clientJar);
|
||||||
throw e;
|
} 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");
|
ProgressBar.show("Client.jar", 1, 1, "files");
|
||||||
|
|
||||||
// Libraries
|
// Libraries
|
||||||
ProgressBar.setStage("Libraries", 1, 5);
|
ProgressBar.setStage("Libraries", 1, 5);
|
||||||
System.out.println(ZAnsi.cyan("Downloading libraries..."));
|
LauncherLogger.info("Downloading libraries...");
|
||||||
downloadLibraries(versionData.getJSONArray("libraries"));
|
downloadLibraries(versionData.getJSONArray("libraries"));
|
||||||
|
|
||||||
// Natives
|
// Natives
|
||||||
ProgressBar.setStage("Natives", 2, 5);
|
ProgressBar.setStage("Natives", 2, 5);
|
||||||
System.out.println(ZAnsi.cyan("Extracting natives..."));
|
LauncherLogger.info("Extracting natives...");
|
||||||
extractNatives(versionData.getJSONArray("libraries"));
|
extractNatives(versionData.getJSONArray("libraries"));
|
||||||
|
|
||||||
String assetIndex;
|
String assetIndex;
|
||||||
@@ -109,15 +125,15 @@ public class VersionInstaller {
|
|||||||
assetIndex = versionData.getString("assets");
|
assetIndex = versionData.getString("assets");
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println(ZAnsi.cyan("Asset index: " + assetIndex));
|
LauncherLogger.info("Asset index: " + assetIndex);
|
||||||
|
|
||||||
// Download assets using correct index
|
// Download assets using correct index
|
||||||
ProgressBar.setStage("Assets", 3, 5);
|
ProgressBar.setStage("Assets", 3, 5);
|
||||||
System.out.println(ZAnsi.cyan("Downloading assets..."));
|
LauncherLogger.info("Downloading assets...");
|
||||||
downloadAssets(versionData, assetIndex);
|
downloadAssets(versionData, assetIndex);
|
||||||
|
|
||||||
ProgressBar.setStage("Done", 4, 5);
|
ProgressBar.setStage("Done", 4, 5);
|
||||||
System.out.println(ZAnsi.brightGreen("\nMinecraft " + versionId + " fully installed!"));
|
LauncherLogger.info("\nMinecraft " + versionId + " fully installed!");
|
||||||
return assetIndex;
|
return assetIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +206,7 @@ public class VersionInstaller {
|
|||||||
if (extracted > 0) {
|
if (extracted > 0) {
|
||||||
ProgressBar.finish("Natives extracted (" + extracted + " ok, " + failed + " failed)");
|
ProgressBar.finish("Natives extracted (" + extracted + " ok, " + failed + " failed)");
|
||||||
} else {
|
} else {
|
||||||
System.out.println(ZAnsi.yellow("No natives to extract for " + classifier));
|
LauncherLogger.warn("No natives to extract for " + classifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,6 +224,12 @@ public class VersionInstaller {
|
|||||||
Path target = minecraftDir.resolve("libraries").resolve(path);
|
Path target = minecraftDir.resolve("libraries").resolve(path);
|
||||||
Files.createDirectories(target.getParent());
|
Files.createDirectories(target.getParent());
|
||||||
|
|
||||||
|
if (Files.exists(target)) {
|
||||||
|
count++;
|
||||||
|
ProgressBar.show("Libraries", count, total, "files");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ZHttpClient.downloadFileWithSmartProxy(url, target);
|
ZHttpClient.downloadFileWithSmartProxy(url, target);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -229,14 +251,18 @@ public class VersionInstaller {
|
|||||||
Files.createDirectories(indexesDir);
|
Files.createDirectories(indexesDir);
|
||||||
Path indexPath = indexesDir.resolve(assetIndex + ".json");
|
Path indexPath = indexesDir.resolve(assetIndex + ".json");
|
||||||
|
|
||||||
System.out.println(ZAnsi.cyan("Downloading asset index (" + assetIndex + ")..."));
|
if (!Files.exists(indexPath)) {
|
||||||
ZHttpClient.downloadFileWithSmartProxy(indexUrl, indexPath);
|
LauncherLogger.info("Downloading asset index (" + assetIndex + ")...");
|
||||||
|
ZHttpClient.downloadFileWithSmartProxy(indexUrl, indexPath);
|
||||||
|
} else {
|
||||||
|
LauncherLogger.info("Asset index already present (" + assetIndex + ")");
|
||||||
|
}
|
||||||
|
|
||||||
String jsonContent = Files.readString(indexPath);
|
String jsonContent = Files.readString(indexPath);
|
||||||
JSONObject root = new JSONObject(jsonContent);
|
JSONObject root = new JSONObject(jsonContent);
|
||||||
JSONObject objects = root.getJSONObject("objects");
|
JSONObject objects = root.getJSONObject("objects");
|
||||||
|
|
||||||
System.out.println(ZAnsi.cyan("Downloading " + objects.length() + " asset objects (index: " + assetIndex + ")..."));
|
LauncherLogger.info("Downloading " + objects.length() + " asset objects (index: " + assetIndex + ")...");
|
||||||
|
|
||||||
int total = objects.length();
|
int total = objects.length();
|
||||||
int[] success = {0};
|
int[] success = {0};
|
||||||
@@ -256,6 +282,15 @@ public class VersionInstaller {
|
|||||||
Files.createDirectories(target.getParent());
|
Files.createDirectories(target.getParent());
|
||||||
|
|
||||||
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
||||||
|
try {
|
||||||
|
if (Files.exists(target) && Files.size(target) > 0) {
|
||||||
|
synchronized (this) {
|
||||||
|
success[0]++;
|
||||||
|
ProgressBar.show("Assets", success[0], total, "files");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (IOException ignored) {}
|
||||||
boolean downloaded = false;
|
boolean downloaded = false;
|
||||||
for (int attempt = 1; attempt <= 3; attempt++) {
|
for (int attempt = 1; attempt <= 3; attempt++) {
|
||||||
try {
|
try {
|
||||||
@@ -297,8 +332,8 @@ public class VersionInstaller {
|
|||||||
ProgressBar.finish("Assets downloaded (" + success[0] + " ok, " + failed[0] + " skipped)");
|
ProgressBar.finish("Assets downloaded (" + success[0] + " ok, " + failed[0] + " skipped)");
|
||||||
|
|
||||||
if (failed[0] > 0) {
|
if (failed[0] > 0) {
|
||||||
System.out.println(ZAnsi.yellow("Warning: " + failed[0] + " asset files could not be downloaded."));
|
LauncherLogger.warn("Warning: " + failed[0] + " asset files could not be downloaded.");
|
||||||
System.out.println(ZAnsi.yellow("The game will launch, but some textures/sounds may be missing."));
|
LauncherLogger.warn("The game will launch, but some textures/sounds may be missing.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ public final class JFXBridge {
|
|||||||
private static final String JFX_CLASS = "me.sashegdev.zernmc.launcher.ui.jfx.JFXLauncher";
|
private static final String JFX_CLASS = "me.sashegdev.zernmc.launcher.ui.jfx.JFXLauncher";
|
||||||
|
|
||||||
private static volatile java.lang.reflect.Method appendGameLogMethod;
|
private static volatile java.lang.reflect.Method appendGameLogMethod;
|
||||||
|
private static volatile java.lang.reflect.Method appendLauncherLogFromLoggerMethod;
|
||||||
private static volatile java.lang.reflect.Method setInstallProgressMethod;
|
private static volatile java.lang.reflect.Method setInstallProgressMethod;
|
||||||
private static volatile java.lang.reflect.Method setInstallStageMethod;
|
private static volatile java.lang.reflect.Method setInstallStageMethod;
|
||||||
|
|
||||||
@@ -49,6 +50,25 @@ public final class JFXBridge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forward a launcher log line to the JFX UI without re-logging it through
|
||||||
|
* LauncherLogger. Called from LauncherLogger.write so all logged lines
|
||||||
|
* (including installer output) become visible in the UI. Safe no-op in CLI mode.
|
||||||
|
*/
|
||||||
|
public static void appendLauncherLogFromLogger(String msg) {
|
||||||
|
if (appendLauncherLogFromLoggerMethod == null) {
|
||||||
|
appendLauncherLogFromLoggerMethod = method("appendLauncherLogFromLogger", String.class);
|
||||||
|
}
|
||||||
|
if (appendLauncherLogFromLoggerMethod == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
appendLauncherLogFromLoggerMethod.invoke(null, msg);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
appendLauncherLogFromLoggerMethod = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Forward install progress to the JFX UI. Safe no-op in CLI mode. */
|
/** Forward install progress to the JFX UI. Safe no-op in CLI mode. */
|
||||||
public static void setInstallProgress(String label, int current, int total) {
|
public static void setInstallProgress(String label, int current, int total) {
|
||||||
if (setInstallProgressMethod == null) {
|
if (setInstallProgressMethod == null) {
|
||||||
|
|||||||
@@ -134,6 +134,19 @@ public class JFXLauncher extends Application {
|
|||||||
try { consumer.onLog(msg); } catch (Exception ignored) {}
|
try { consumer.onLog(msg); } catch (Exception ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a line to the UI log buffer without re-logging it through
|
||||||
|
* LauncherLogger (avoids recursion when called from LauncherLogger.write).
|
||||||
|
*/
|
||||||
|
public static void appendLauncherLogFromLogger(String msg) {
|
||||||
|
synchronized (launcherLogBuffer) {
|
||||||
|
launcherLogBuffer.append(msg).append("\n");
|
||||||
|
}
|
||||||
|
for (LogConsumer consumer : logConsumers) {
|
||||||
|
try { consumer.onLog(msg); } catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void appendGameLog(String msg) {
|
public static void appendGameLog(String msg) {
|
||||||
synchronized (gameLogBuffer) {
|
synchronized (gameLogBuffer) {
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package me.sashegdev.zernmc.launcher.utils;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the java executable used to run the launcher itself, so child
|
||||||
|
* processes (Forge/NeoForge/Fabric installers) run on the bundled JRE
|
||||||
|
* instead of relying on a system java that may not exist or be too old.
|
||||||
|
*
|
||||||
|
* Order of preference:
|
||||||
|
* 1. The JVM currently running the launcher (bundled jre21 in lib/jre21).
|
||||||
|
* 2. A jre21 next to the working directory (dev layout).
|
||||||
|
* 3. System PATH lookup (last resort).
|
||||||
|
*/
|
||||||
|
public final class JavaResolver {
|
||||||
|
|
||||||
|
private JavaResolver() {}
|
||||||
|
|
||||||
|
public static String getJavaExecutable() {
|
||||||
|
String os = System.getProperty("os.name", "").toLowerCase();
|
||||||
|
String javaExe = os.contains("win") ? "java.exe" : "java";
|
||||||
|
|
||||||
|
Path fromHome = Paths.get(System.getProperty("java.home"), "bin", javaExe);
|
||||||
|
if (Files.exists(fromHome)) {
|
||||||
|
return fromHome.toAbsolutePath().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Path bundled = Paths.get("").toAbsolutePath().resolve("lib").resolve("jre21").resolve("bin").resolve(javaExe);
|
||||||
|
if (Files.exists(bundled)) {
|
||||||
|
return bundled.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Path devJre = Paths.get("").toAbsolutePath().resolve("jre21").resolve("bin").resolve(javaExe);
|
||||||
|
if (Files.exists(devJre)) {
|
||||||
|
return devJre.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return javaExe;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package me.sashegdev.zernmc.launcher.utils;
|
package me.sashegdev.zernmc.launcher.utils;
|
||||||
|
|
||||||
|
import me.sashegdev.zernmc.launcher.ui.jfx.JFXBridge;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
@@ -94,6 +96,11 @@ public class LauncherLogger {
|
|||||||
// full/hung stdout pipe (e.g. javaw piped to the parent exe) can never
|
// full/hung stdout pipe (e.g. javaw piped to the parent exe) can never
|
||||||
// block application threads.
|
// block application threads.
|
||||||
offerStdout(line);
|
offerStdout(line);
|
||||||
|
|
||||||
|
// Forward to the JFX UI log so installer/progress output is visible
|
||||||
|
// in the GUI. The bridge is a safe no-op when JavaFX is unavailable.
|
||||||
|
JFXBridge.appendLauncherLogFromLogger(line);
|
||||||
|
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
StringWriter sw = new StringWriter();
|
StringWriter sw = new StringWriter();
|
||||||
PrintWriter pw = new PrintWriter(sw);
|
PrintWriter pw = new PrintWriter(sw);
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>1.0.16</revision>
|
<revision>1.0.16</revision>
|
||||||
<hotfix>0</hotfix>
|
<hotfix>2</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>
|
||||||
|
|||||||
@@ -1347,6 +1347,9 @@ def generate_launcher_builds_meta():
|
|||||||
# Skip root-level JARs — only bin/*.jar is needed at runtime
|
# Skip root-level JARs — only bin/*.jar is needed at runtime
|
||||||
if rel_path in ("zernmclauncher.jar", "zernmc-bootstrap.jar"):
|
if rel_path in ("zernmclauncher.jar", "zernmc-bootstrap.jar"):
|
||||||
continue
|
continue
|
||||||
|
# CLI exe is not shipped to users
|
||||||
|
if rel_path == "zernmc-cli.exe":
|
||||||
|
continue
|
||||||
stat = file_path.stat()
|
stat = file_path.stat()
|
||||||
|
|
||||||
# Calculate hash
|
# Calculate hash
|
||||||
|
|||||||
Reference in New Issue
Block a user