minor fixes
This commit is contained in:
@@ -69,7 +69,7 @@ public class Bootstrap {
|
||||
|
||||
private static String getServerVersion() {
|
||||
try {
|
||||
URL url = new URL(BASE_URL.replace("download?type=jar", "version"));
|
||||
URL url = new URL(BASE_URL + "/launcher/version");
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
if (conn.getResponseCode() == 200) {
|
||||
|
||||
@@ -114,7 +114,7 @@ public class LauncherAPI {
|
||||
}
|
||||
idx = end;
|
||||
}
|
||||
versions.sort((a, b) -> b.compareTo(a));
|
||||
versions.sort(LauncherAPI::compareVersions);
|
||||
break;
|
||||
case "neoforge":
|
||||
String neoforgeXml = ZHttpClient.downloadString("https://maven.neoforged.net/releases/net/neoforged/neoforge/maven-metadata.xml");
|
||||
@@ -129,7 +129,7 @@ public class LauncherAPI {
|
||||
}
|
||||
neoidx = end;
|
||||
}
|
||||
versions.sort((a, b) -> b.compareTo(a));
|
||||
versions.sort(LauncherAPI::compareVersions);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -141,6 +141,23 @@ public class LauncherAPI {
|
||||
}
|
||||
}
|
||||
|
||||
private static int compareVersions(String a, String b) {
|
||||
String[] partsA = a.split("\\.");
|
||||
String[] partsB = b.split("\\.");
|
||||
int len = Math.min(partsA.length, partsB.length);
|
||||
for (int i = 0; i < len; i++) {
|
||||
try {
|
||||
int numA = Integer.parseInt(partsA[i]);
|
||||
int numB = Integer.parseInt(partsB[i]);
|
||||
if (numA != numB) return Integer.compare(numB, numA);
|
||||
} catch (NumberFormatException e) {
|
||||
int cmp = partsA[i].compareTo(partsB[i]);
|
||||
if (cmp != 0) return cmp;
|
||||
}
|
||||
}
|
||||
return Integer.compare(partsB.length, partsA.length);
|
||||
}
|
||||
|
||||
private boolean isNeoForgeCompatible(String version, String mcVersion) {
|
||||
if (mcVersion.startsWith("1.21")) {
|
||||
return version.contains("1.21") && !version.contains("1.20");
|
||||
|
||||
@@ -12,8 +12,10 @@ public class AuthService {
|
||||
|
||||
public ApiResponse<LoginResult> register(String username, String password) {
|
||||
try {
|
||||
String response = post("/auth/register",
|
||||
"{\"username\":\"" + username + "\",\"password\":\"" + password + "\"}");
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("username", username);
|
||||
json.addProperty("password", password);
|
||||
String response = post("/auth/register", json.toString());
|
||||
|
||||
// If registration succeeds, auto-login
|
||||
AuthManager.AuthResult result = AuthManager.login(username, password);
|
||||
@@ -74,8 +76,9 @@ public class AuthService {
|
||||
|
||||
public ApiResponse<Boolean> activatePass(String passCode) {
|
||||
try {
|
||||
String response = post("/auth/pass/activate",
|
||||
"{\"pass_code\":\"" + passCode + "\"}");
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("pass_code", passCode);
|
||||
String response = post("/auth/pass/activate", json.toString());
|
||||
AuthManager.refreshUserInfo();
|
||||
return ApiResponse.success(true);
|
||||
} catch (Exception e) {
|
||||
|
||||
+1
-1
@@ -186,7 +186,7 @@ public class LaunchService {
|
||||
}
|
||||
String args = Config.getExtraJvmArgs();
|
||||
if (args != null && !args.isEmpty()) {
|
||||
for (String arg : args.split("\\s+")) {
|
||||
for (String arg : args.split("\n")) {
|
||||
arg = arg.trim();
|
||||
if (!arg.isEmpty()) extraArgs.add(arg);
|
||||
}
|
||||
|
||||
@@ -123,12 +123,18 @@ public class AuthManager {
|
||||
public static void logout() {
|
||||
if (session != null && session.refreshToken != null) {
|
||||
try {
|
||||
post("/auth/logout", "{\"refresh_token\":\"" + session.refreshToken + "\"}");
|
||||
} catch (Exception ignored) {}
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("refresh_token", session.refreshToken);
|
||||
post("/auth/logout", json.toString());
|
||||
} catch (Exception e) {
|
||||
LauncherLogger.warn("Logout error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
session = null;
|
||||
userInfo = null;
|
||||
try { Files.deleteIfExists(AUTH_FILE); } catch (Exception ignored) {}
|
||||
try { Files.deleteIfExists(AUTH_FILE); } catch (Exception e) {
|
||||
LauncherLogger.warn("Failed to delete auth.json: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isLoggedIn() {
|
||||
@@ -140,23 +146,28 @@ public class AuthManager {
|
||||
}
|
||||
|
||||
public static String getUsername() {
|
||||
return session != null ? session.username : "Player";
|
||||
AuthSession localSession = session;
|
||||
return localSession != null ? localSession.username : "Player";
|
||||
}
|
||||
|
||||
public static String getUuid() {
|
||||
return session != null ? session.uuid : "00000000-0000-0000-0000-000000000000";
|
||||
AuthSession localSession = session;
|
||||
return localSession != null ? localSession.uuid : "00000000-0000-0000-0000-000000000000";
|
||||
}
|
||||
|
||||
public static String getAccessToken() {
|
||||
if (session == null) return "0";
|
||||
AuthSession localSession = session;
|
||||
if (localSession == null) return "0";
|
||||
if (isAccessTokenExpired()) {
|
||||
boolean refreshed = tryRefresh();
|
||||
if (!refreshed) {
|
||||
if (session == null) return "0";
|
||||
return session.accessToken != null ? session.accessToken : "0";
|
||||
localSession = session;
|
||||
if (localSession == null) return "0";
|
||||
return localSession.accessToken != null ? localSession.accessToken : "0";
|
||||
}
|
||||
}
|
||||
return session != null && session.accessToken != null ? session.accessToken : "0";
|
||||
localSession = session;
|
||||
return localSession != null && localSession.accessToken != null ? localSession.accessToken : "0";
|
||||
}
|
||||
|
||||
private static boolean isAccessTokenExpired() {
|
||||
@@ -174,8 +185,9 @@ public class AuthManager {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
String body = "{\"refresh_token\":\"" + session.refreshToken + "\"}";
|
||||
SimpleHttpResponse resp = post("/auth/refresh", body);
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("refresh_token", session.refreshToken);
|
||||
SimpleHttpResponse resp = post("/auth/refresh", json.toString());
|
||||
|
||||
if (resp.statusCode() == 200) {
|
||||
AuthSession newSession = GSON.fromJson(resp.body(), AuthSession.class);
|
||||
|
||||
+10
-8
@@ -8,6 +8,7 @@ import me.sashegdev.zernmc.launcher.minecraft.launch.LaunchCommandBuilder;
|
||||
import me.sashegdev.zernmc.launcher.minecraft.model.LaunchOptions;
|
||||
import me.sashegdev.zernmc.launcher.ui.jfx.JFXLauncher;
|
||||
import me.sashegdev.zernmc.launcher.utils.ConsoleUtils;
|
||||
import me.sashegdev.zernmc.launcher.utils.LauncherLogger;
|
||||
import me.sashegdev.zernmc.launcher.utils.ZAnsi;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@@ -160,14 +161,15 @@ public class MinecraftLib {
|
||||
}
|
||||
|
||||
private void safeDeleteDirectory(Path dir) {
|
||||
try {
|
||||
Files.walk(dir)
|
||||
.sorted((a, b) -> b.compareTo(a))
|
||||
.forEach(p -> {
|
||||
try { Files.deleteIfExists(p); }
|
||||
catch (IOException ignored) {}
|
||||
});
|
||||
} catch (IOException ignored) {}
|
||||
try (var stream = Files.walk(dir)) {
|
||||
stream.sorted((a, b) -> b.compareTo(a))
|
||||
.forEach(p -> {
|
||||
try { Files.deleteIfExists(p); }
|
||||
catch (IOException e) { /* ignore */ }
|
||||
});
|
||||
} catch (IOException e) {
|
||||
LauncherLogger.warn("safeDeleteDirectory: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteOldVersionDirs(Path versionsDir, String keepVersion) throws IOException {
|
||||
|
||||
+11
-3
@@ -20,6 +20,7 @@ import java.net.http.HttpResponse;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
@@ -464,12 +465,19 @@ public class PackDownloader {
|
||||
*/
|
||||
private void downloadFile(FileInfo file, Path destination) throws Exception {
|
||||
String url = ZHttpClient.getBaseUrl() + file.getUrl();
|
||||
String accessToken = AuthManager.getAccessToken();
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
HttpRequest.Builder builder = HttpRequest.newBuilder()
|
||||
.uri(java.net.URI.create(url))
|
||||
.GET()
|
||||
.build();
|
||||
.timeout(Duration.ofSeconds(60))
|
||||
.header("User-Agent", "ZernMC-Launcher/1.0")
|
||||
.GET();
|
||||
|
||||
if (accessToken != null && !accessToken.equals("0")) {
|
||||
builder.header("Authorization", "Bearer " + accessToken);
|
||||
}
|
||||
|
||||
HttpRequest request = builder.build();
|
||||
HttpResponse<InputStream> response = httpClient.send(request,
|
||||
HttpResponse.BodyHandlers.ofInputStream());
|
||||
|
||||
|
||||
+23
-15
@@ -11,7 +11,9 @@ import java.net.http.HttpResponse;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ForgeInstaller {
|
||||
@@ -240,29 +242,35 @@ public class ForgeInstaller {
|
||||
System.out.println(ZAnsi.cyan("Checking and downloading missing libraries..."));
|
||||
|
||||
// List of problematic libraries and their alternate URLs
|
||||
Map<String, String> alternativeUrls = new HashMap<>();
|
||||
alternativeUrls.put("org/ow2/asm/asm/9.6/asm-9.6.jar",
|
||||
"https://repo1.maven.org/maven2/org/ow2/asm/asm/9.6/asm-9.6.jar");
|
||||
alternativeUrls.put("org/ow2/asm/asm/9.6/asm-9.6.jar",
|
||||
"https://mirrors.huaweicloud.com/repository/maven/org/ow2/asm/asm/9.6/asm-9.6.jar");
|
||||
|
||||
Path librariesDir = instance.getPath().resolve("libraries");
|
||||
|
||||
for (Map.Entry<String, String> entry : alternativeUrls.entrySet()) {
|
||||
// Map from maven path to list of mirror URLs (tried in order)
|
||||
Map<String, List<String>> alternativeUrls = new HashMap<>();
|
||||
alternativeUrls.put("org/ow2/asm/asm/9.6/asm-9.6.jar", Arrays.asList(
|
||||
"https://repo1.maven.org/maven2/org/ow2/asm/asm/9.6/asm-9.6.jar",
|
||||
"https://mirrors.huaweicloud.com/repository/maven/org/ow2/asm/asm/9.6/asm-9.6.jar"
|
||||
));
|
||||
|
||||
for (Map.Entry<String, List<String>> entry : alternativeUrls.entrySet()) {
|
||||
Path target = librariesDir.resolve(entry.getKey());
|
||||
if (!Files.exists(target)) {
|
||||
Files.createDirectories(target.getParent());
|
||||
System.out.println(ZAnsi.yellow("Downloading: " + target.getFileName()));
|
||||
|
||||
for (int attempt = 1; attempt <= 3; attempt++) {
|
||||
try {
|
||||
downloadFileWithProgress(entry.getValue(), target);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
if (attempt == 3) throw e;
|
||||
System.out.println(ZAnsi.yellow("Повторная попытка " + attempt + "/3..."));
|
||||
Thread.sleep(2000);
|
||||
boolean downloaded = false;
|
||||
for (String mirrorUrl : entry.getValue()) {
|
||||
for (int attempt = 1; attempt <= 3; attempt++) {
|
||||
try {
|
||||
downloadFileWithProgress(mirrorUrl, target);
|
||||
downloaded = true;
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
if (attempt == 3 && mirrorUrl.equals(entry.getValue().get(entry.getValue().size() - 1))) throw e;
|
||||
System.out.println(ZAnsi.yellow("Retry " + attempt + "/3..."));
|
||||
try { Thread.sleep(2000); } catch (InterruptedException ignored) {}
|
||||
}
|
||||
}
|
||||
if (downloaded) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -183,6 +183,7 @@ public class VersionInstaller {
|
||||
}
|
||||
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
||||
executor.shutdown();
|
||||
|
||||
ProgressBar.finish("Assets downloaded (" + success[0] + " ok, " + failed[0] + " skipped)");
|
||||
|
||||
|
||||
@@ -41,12 +41,24 @@ public class Config {
|
||||
}
|
||||
}
|
||||
|
||||
maxMemory = Integer.parseInt(props.getProperty("maxMemory", "4096"));
|
||||
try {
|
||||
maxMemory = Integer.parseInt(props.getProperty("maxMemory", "4096"));
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println(ZAnsi.yellow("Config: invalid maxMemory value, using default"));
|
||||
}
|
||||
ramManuallySet = Boolean.parseBoolean(props.getProperty("ramManuallySet", "false"));
|
||||
serverUrl = props.getProperty("serverUrl", serverUrl);
|
||||
lastUsername = props.getProperty("lastUsername", lastUsername);
|
||||
windowWidth = Integer.parseInt(props.getProperty("windowWidth", "1280"));
|
||||
windowHeight = Integer.parseInt(props.getProperty("windowHeight", "720"));
|
||||
try {
|
||||
windowWidth = Integer.parseInt(props.getProperty("windowWidth", "1280"));
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println(ZAnsi.yellow("Config: invalid windowWidth value, using default"));
|
||||
}
|
||||
try {
|
||||
windowHeight = Integer.parseInt(props.getProperty("windowHeight", "720"));
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println(ZAnsi.yellow("Config: invalid windowHeight value, using default"));
|
||||
}
|
||||
extraJvmArgs = props.getProperty("extraJvmArgs", "");
|
||||
javaPath = props.getProperty("javaPath", "java");
|
||||
locale = props.getProperty("locale", "en");
|
||||
@@ -235,7 +247,6 @@ public class Config {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("-XX:ParallelGCThreads=").append(Math.max(1, cores));
|
||||
sb.append(" -XX:ConcGCThreads=").append(Math.max(1, cores / 2));
|
||||
sb.append(" -XX:+UseContainerSupport");
|
||||
sb.append(" -XX:+AlwaysPreTouch");
|
||||
if (ramMB >= 8192) {
|
||||
sb.append(" -XX:+UseZGC");
|
||||
|
||||
Reference in New Issue
Block a user