Fix: Bootstrap update and meta parsing
- Rewrite getLauncherMeta() to properly parse server meta response - Change downloadUpdate() fallback to JAR-only (not ZIP) to avoid JRE lock issues - Simplify downloadUpdateLegacy() to skip ZIP (which locks JRE files) - Add handling for AccessDeniedException when updating locked files - Improve error logging for meta parsing failures
This commit is contained in:
@@ -161,9 +161,9 @@ public class Bootstrap {
|
|||||||
List<Map<String, Object>> files = (List<Map<String, Object>>) meta.get("files");
|
List<Map<String, Object>> files = (List<Map<String, Object>>) meta.get("files");
|
||||||
|
|
||||||
if (files == null || files.isEmpty()) {
|
if (files == null || files.isEmpty()) {
|
||||||
// Мета пустая - fallback на старый метод
|
// Мета пустая - fallback на JAR обновление
|
||||||
log("Мета недоступна, используем старый метод");
|
log("Мета пустая, используем JAR обновление");
|
||||||
downloadUpdateLegacy(newVersion);
|
downloadUpdateJar(newVersion);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,21 +182,29 @@ public class Bootstrap {
|
|||||||
|
|
||||||
log("К скачиванию: " + toDownload.size() + ", к удалению: " + toDelete.size());
|
log("К скачиванию: " + toDownload.size() + ", к удалению: " + toDelete.size());
|
||||||
|
|
||||||
// 4. Удаляем лишние файлы
|
// 4. Удаляем лишние файлы (пропускаем заблокированные)
|
||||||
for (String filePath : toDelete) {
|
for (String filePath : toDelete) {
|
||||||
Path f = baseDir.resolve(filePath);
|
Path f = baseDir.resolve(filePath);
|
||||||
if (Files.exists(f)) {
|
if (Files.exists(f)) {
|
||||||
Files.delete(f);
|
try {
|
||||||
log("Удален: " + filePath);
|
Files.delete(f);
|
||||||
|
log("Удален: " + filePath);
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
log("Пропущен (заблокирован): " + filePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Скачиваем новые/измененные файлы
|
// 5. Скачиваем новые/измененные файлы (пропускаем заблокированные)
|
||||||
String serverVersion = (String) diff.get("version");
|
String serverVersion = (String) diff.get("version");
|
||||||
for (Map<String, Object> file : toDownload) {
|
for (Map<String, Object> file : toDownload) {
|
||||||
String path = (String) file.get("path");
|
String path = (String) file.get("path");
|
||||||
downloadLauncherFile(serverVersion, path);
|
try {
|
||||||
log("Скачан: " + path);
|
downloadLauncherFile(serverVersion, path);
|
||||||
|
log("Скачан: " + path);
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
log("Пропущен (заблокирован): " + path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Записываем новую версию
|
// 6. Записываем новую версию
|
||||||
@@ -205,9 +213,9 @@ public class Bootstrap {
|
|||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log("Ошибка инкрементного обновления: " + e.getMessage());
|
log("Ошибка инкрементного обновления: " + e.getMessage());
|
||||||
log("Fallback на старый метод...");
|
log("Fallback на JAR обновление...");
|
||||||
// Fallback на старый метод
|
// Fallback на JAR обновление (не ZIP!)
|
||||||
downloadUpdateLegacy(newVersion);
|
downloadUpdateJar(newVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,23 +234,61 @@ public class Bootstrap {
|
|||||||
while ((line = br.readLine()) != null) sb.append(line);
|
while ((line = br.readLine()) != null) sb.append(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Парсим JSON вручную
|
// Парсим JSON вручную - находим первую версию и её meta
|
||||||
String json = sb.toString();
|
String json = sb.toString();
|
||||||
int versionsStart = json.indexOf("\"versions\"");
|
|
||||||
if (versionsStart == -1) throw new IOException("Нет versions в ответе");
|
|
||||||
|
|
||||||
int arrayStart = json.indexOf("[", versionsStart);
|
// Находим первый объект в массиве versions
|
||||||
int arrayEnd = json.indexOf("]", arrayStart);
|
int versionsIdx = json.indexOf("\"versions\"");
|
||||||
String versionsArray = json.substring(arrayStart + 1, arrayEnd);
|
if (versionsIdx == -1) throw new IOException("Нет versions в ответе");
|
||||||
|
|
||||||
// Ищем первую версию
|
// Находим начало массива
|
||||||
int metaStart = versionsArray.indexOf("\"meta\"");
|
int arrStart = json.indexOf("[", versionsIdx);
|
||||||
if (metaStart == -1) throw new IOException("Нет meta в версии");
|
if (arrStart == -1) throw new IOException("Нет массива versions");
|
||||||
|
|
||||||
int objStart = versionsArray.indexOf("{", metaStart);
|
// Находим первый объект версии
|
||||||
int objEnd = versionsArray.lastIndexOf("}");
|
int verObjStart = json.indexOf("{", arrStart);
|
||||||
|
if (verObjStart == -1) throw new IOException("Нет объектов версий");
|
||||||
|
|
||||||
String metaJson = versionsArray.substring(objStart, objEnd + 1);
|
// Находим конец этого объекта (с учётом вложенности)
|
||||||
|
int braceCount = 0;
|
||||||
|
int verObjEnd = verObjStart;
|
||||||
|
for (int i = verObjStart; i < json.length(); i++) {
|
||||||
|
char c = json.charAt(i);
|
||||||
|
if (c == '{') braceCount++;
|
||||||
|
else if (c == '}') {
|
||||||
|
braceCount--;
|
||||||
|
if (braceCount == 0) {
|
||||||
|
verObjEnd = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String firstVersionJson = json.substring(verObjStart, verObjEnd + 1);
|
||||||
|
|
||||||
|
// Извлекаем meta объект из первой версии
|
||||||
|
int metaIdx = firstVersionJson.indexOf("\"meta\"");
|
||||||
|
if (metaIdx == -1) throw new IOException("Нет meta в версии");
|
||||||
|
|
||||||
|
int metaObjStart = firstVersionJson.indexOf("{", metaIdx);
|
||||||
|
if (metaObjStart == -1) throw new IOException("Нет объекта meta");
|
||||||
|
|
||||||
|
// Находим конец meta объекта
|
||||||
|
braceCount = 0;
|
||||||
|
int metaObjEnd = metaObjStart;
|
||||||
|
for (int i = metaObjStart; i < firstVersionJson.length(); i++) {
|
||||||
|
char c = firstVersionJson.charAt(i);
|
||||||
|
if (c == '{') braceCount++;
|
||||||
|
else if (c == '}') {
|
||||||
|
braceCount--;
|
||||||
|
if (braceCount == 0) {
|
||||||
|
metaObjEnd = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String metaJson = firstVersionJson.substring(metaObjStart, metaObjEnd + 1);
|
||||||
return parseMetaJson(metaJson);
|
return parseMetaJson(metaJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,54 +468,11 @@ public class Bootstrap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback на старый метод (скачивание ZIP)
|
// Fallback на JAR обновление (без ZIP, чтобы избежать блокировки JRE)
|
||||||
private static void downloadUpdateLegacy(String newVersion) throws Exception {
|
private static void downloadUpdateLegacy(String newVersion) throws Exception {
|
||||||
// Пробуем скачать ZIP
|
log("Пропускаем ZIP обновление (может заблокировать JRE)...");
|
||||||
String zipUrl = BASE_URL + "/launcher/download/zip/ZernMC-win-" + newVersion + ".zip";
|
log("Используем JAR обновление...");
|
||||||
URL url = new URL(zipUrl);
|
downloadUpdateJar(newVersion);
|
||||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
|
||||||
conn.setRequestMethod("GET");
|
|
||||||
|
|
||||||
if (conn.getResponseCode() == 200) {
|
|
||||||
Path tempZip = baseDir.resolve("update.zip");
|
|
||||||
|
|
||||||
try (InputStream in = conn.getInputStream();
|
|
||||||
OutputStream out = new FileOutputStream(tempZip.toFile())) {
|
|
||||||
byte[] buf = new byte[BUFFER_SIZE];
|
|
||||||
int len;
|
|
||||||
long total = 0;
|
|
||||||
while ((len = in.read(buf)) > 0) {
|
|
||||||
out.write(buf, 0, len);
|
|
||||||
total += len;
|
|
||||||
System.out.print("\rСкачано: " + (total/1024/1024) + " MB");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log("ZIP скачан, распаковка...");
|
|
||||||
|
|
||||||
// Распаковываем ZIP
|
|
||||||
java.util.zip.ZipFile zip = new java.util.zip.ZipFile(tempZip.toFile());
|
|
||||||
java.util.Enumeration<? extends java.util.zip.ZipEntry> entries = zip.entries();
|
|
||||||
while (entries.hasMoreElements()) {
|
|
||||||
java.util.zip.ZipEntry entry = entries.nextElement();
|
|
||||||
Path outPath = baseDir.resolve(entry.getName());
|
|
||||||
if (entry.isDirectory()) {
|
|
||||||
Files.createDirectories(outPath);
|
|
||||||
} else {
|
|
||||||
Files.createDirectories(outPath.getParent());
|
|
||||||
try (InputStream is = zip.getInputStream(entry)) {
|
|
||||||
Files.copy(is, outPath, StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
zip.close();
|
|
||||||
Files.delete(tempZip);
|
|
||||||
|
|
||||||
Files.writeString(baseDir.resolve(VERSION_FILE), newVersion);
|
|
||||||
log("Обновлено до v" + newVersion + " (ZIP метод)");
|
|
||||||
} else {
|
|
||||||
// Последний fallback - JAR
|
|
||||||
downloadUpdateJar(newVersion);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void downloadUpdateJar(String newVersion) throws Exception {
|
private static void downloadUpdateJar(String newVersion) throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user