Попытка заставить работать Forge
This commit is contained in:
+187
-26
@@ -3,7 +3,7 @@ package me.sashegdev.zernmc.launcher.minecraft.installer;
|
||||
import me.sashegdev.zernmc.launcher.minecraft.Instance;
|
||||
import me.sashegdev.zernmc.launcher.utils.ProgressBar;
|
||||
import me.sashegdev.zernmc.launcher.utils.ZAnsi;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
@@ -11,6 +11,8 @@ import java.net.http.HttpResponse;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ForgeInstaller {
|
||||
|
||||
@@ -30,56 +32,55 @@ public class ForgeInstaller {
|
||||
System.out.println(ZAnsi.cyan("Установка базовой версии Minecraft " + mcVersion + "..."));
|
||||
VersionInstaller vanillaInstaller = new VersionInstaller(instance.getPath());
|
||||
|
||||
String assetIndex = vanillaInstaller.install(mcVersion); // ← теперь возвращает String
|
||||
String assetIndex = vanillaInstaller.install(mcVersion);
|
||||
|
||||
if (assetIndex == null || assetIndex.isEmpty()) {
|
||||
System.out.println(ZAnsi.brightRed("Не удалось установить базовую версию Minecraft"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Сохраняем assetIndex (очень важно!)
|
||||
instance.setAssetIndex(assetIndex);
|
||||
|
||||
// Шаг 2: Создаём launcher_profiles.json
|
||||
createLauncherProfile();
|
||||
|
||||
// Шаг 3: Скачиваем и запускаем Forge Installer
|
||||
// Шаг 3: Скачиваем Forge Installer с прогресс-баром
|
||||
String installerUrl = "https://maven.minecraftforge.net/net/minecraftforge/forge/"
|
||||
+ mcVersion + "-" + forgeVersion
|
||||
+ "/forge-" + mcVersion + "-" + forgeVersion + "-installer.jar";
|
||||
|
||||
Path installerJar = instance.getPath().resolve("forge-installer.jar");
|
||||
|
||||
ProgressBar.show("Скачивание Forge Installer", 0, 100, "%");
|
||||
downloadFile(installerUrl, installerJar);
|
||||
ProgressBar.finish("Forge Installer скачан");
|
||||
System.out.println(ZAnsi.cyan("Скачивание Forge Installer..."));
|
||||
downloadFileWithProgress(installerUrl, installerJar);
|
||||
|
||||
// Шаг 4: Запускаем Forge Installer и показываем его вывод
|
||||
System.out.println(ZAnsi.cyan("Запуск Forge Installer..."));
|
||||
ProcessBuilder pb = new ProcessBuilder(
|
||||
"java",
|
||||
"-jar",
|
||||
installerJar.toAbsolutePath().toString(),
|
||||
"--installClient"
|
||||
);
|
||||
pb.directory(instance.getPath().toFile());
|
||||
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
|
||||
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
|
||||
System.out.println(ZAnsi.yellow("Это может занять несколько минут. Пожалуйста, подождите...\n"));
|
||||
|
||||
Process process = pb.start();
|
||||
int exitCode = process.waitFor();
|
||||
boolean success = runForgeInstaller(installerJar);
|
||||
|
||||
if (exitCode != 0) {
|
||||
System.out.println(ZAnsi.brightRed("Forge Installer завершился с ошибкой (код " + exitCode + ")"));
|
||||
return false;
|
||||
// После успешной установки Forge, но перед сохранением метаданных
|
||||
if (success) {
|
||||
// Докачиваем пропущенные библиотеки
|
||||
try {
|
||||
downloadMissingLibraries(mcVersion, forgeVersion);
|
||||
} catch (Exception e) {
|
||||
System.out.println(ZAnsi.yellow("Предупреждение: не удалось докачать некоторые библиотеки: " + e.getMessage()));
|
||||
}
|
||||
|
||||
System.out.println(ZAnsi.brightGreen("Forge " + forgeVersion + " успешно установлен!"));
|
||||
|
||||
System.out.println(ZAnsi.brightGreen("\nForge " + forgeVersion + " успешно установлен!"));
|
||||
instance.setMinecraftVersion(mcVersion);
|
||||
instance.setLoaderType("forge");
|
||||
instance.setLoaderVersion(forgeVersion);
|
||||
|
||||
// Очищаем временный файл установщика
|
||||
Files.deleteIfExists(installerJar);
|
||||
return true;
|
||||
} else {
|
||||
System.out.println(ZAnsi.brightRed("\nОшибка при установке Forge!"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void createLauncherProfile() throws IOException {
|
||||
@@ -96,14 +97,174 @@ public class ForgeInstaller {
|
||||
System.out.println(ZAnsi.yellow("Создан launcher_profiles.json"));
|
||||
}
|
||||
|
||||
private void downloadFile(String url, Path target) throws Exception {
|
||||
private void downloadFileWithProgress(String url, Path target) throws Exception {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<Path> response = httpClient.send(request, HttpResponse.BodyHandlers.ofFile(target));
|
||||
|
||||
HttpResponse<InputStream> response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
||||
|
||||
if (response.statusCode() != 200) {
|
||||
throw new IOException("Не удалось скачать Forge installer (HTTP " + response.statusCode() + ")");
|
||||
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) {
|
||||
String downloaded = ProgressBar.formatBytes(totalRead);
|
||||
String total = ProgressBar.formatBytes(contentLength);
|
||||
ProgressBar.show("Forge Installer", percent, 100, "% (" + downloaded + "/" + total + ")");
|
||||
lastPercent = percent;
|
||||
}
|
||||
} else {
|
||||
// Если размер неизвестен, показываем анимацию
|
||||
char[] spinner = {'|', '/', '-', '\\'};
|
||||
int idx = (int) (totalRead / 1024) % 4;
|
||||
System.out.print("\rСкачивание Forge Installer: " + ProgressBar.formatBytes(totalRead) + " " + spinner[idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProgressBar.finish("Forge Installer (" + ProgressBar.formatBytes(Files.size(target)) + ")");
|
||||
}
|
||||
|
||||
private boolean runForgeInstaller(Path installerJar) throws IOException, InterruptedException {
|
||||
// Пробуем до 3 раз с разными опциями
|
||||
int maxRetries = 3;
|
||||
int attempt = 1;
|
||||
|
||||
while (attempt <= maxRetries) {
|
||||
System.out.println(ZAnsi.cyan("Попытка " + attempt + " из " + maxRetries));
|
||||
|
||||
ProcessBuilder pb = new ProcessBuilder(
|
||||
"java",
|
||||
"-jar",
|
||||
installerJar.toAbsolutePath().toString(),
|
||||
"--installClient"
|
||||
);
|
||||
|
||||
// Добавляем JVM аргументы для увеличения таймаутов
|
||||
pb.environment().put("JAVA_OPTS", "-Dhttp.connectionTimeout=60000 -Dhttp.socketTimeout=60000");
|
||||
|
||||
pb.directory(instance.getPath().toFile());
|
||||
pb.redirectErrorStream(true);
|
||||
|
||||
Process process = pb.start();
|
||||
|
||||
// Читаем вывод в реальном времени
|
||||
StringBuilder output = new StringBuilder();
|
||||
boolean hasErrors = false;
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
output.append(line).append("\n");
|
||||
|
||||
// Форматируем вывод Forge Installer
|
||||
if (line.contains("Downloading") || line.contains("Extracting")) {
|
||||
System.out.println(ZAnsi.blue(" -> " + line));
|
||||
} else if (line.contains("SUCCESS") || line.contains("successfully")) {
|
||||
System.out.println(ZAnsi.brightGreen(" + " + line));
|
||||
} else if (line.contains("WARNING") || line.contains("warning")) {
|
||||
System.out.println(ZAnsi.yellow(" ! " + line));
|
||||
} else if (line.contains("ERROR") || line.contains("error") || line.contains("failed") || line.contains("timed out")) {
|
||||
System.out.println(ZAnsi.brightRed(" X " + line));
|
||||
if (line.contains("timed out") || line.contains("failed to download")) {
|
||||
hasErrors = true;
|
||||
}
|
||||
} else if (!line.isBlank()) {
|
||||
System.out.println(" " + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int exitCode = process.waitFor();
|
||||
|
||||
// Если успешно или нет ошибок скачивания
|
||||
if (exitCode == 0 && !hasErrors) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Если ошибка и это не последняя попытка
|
||||
if (attempt < maxRetries) {
|
||||
System.out.println(ZAnsi.yellow("Ошибка при установке. Повторная попытка через 5 секунд..."));
|
||||
Thread.sleep(5000);
|
||||
|
||||
// Очищаем временные файлы перед повтором
|
||||
Path librariesDir = instance.getPath().resolve("libraries");
|
||||
if (Files.exists(librariesDir)) {
|
||||
// Удаляем только частично скачанные библиотеки Forge
|
||||
try (var stream = Files.walk(librariesDir)) {
|
||||
stream.filter(p -> p.toString().contains("asm") && p.toString().endsWith(".jar"))
|
||||
.forEach(p -> {
|
||||
try { Files.deleteIfExists(p); }
|
||||
catch (IOException e) { /* ignore */ }
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
System.out.println(ZAnsi.brightRed("Forge Installer завершился с кодом ошибки: " + exitCode));
|
||||
|
||||
// Показываем возможное решение
|
||||
if (output.toString().contains("timed out")) {
|
||||
System.out.println(ZAnsi.yellow("\nВозможные решения:"));
|
||||
System.out.println(ZAnsi.yellow("1. Проверьте интернет-соединение"));
|
||||
System.out.println(ZAnsi.yellow("2. Запустите лаунчер от имени администратора"));
|
||||
System.out.println(ZAnsi.yellow("3. Временно отключите антивирус/брандмауэр"));
|
||||
System.out.println(ZAnsi.yellow("4. Попробуйте установить другую версию Forge"));
|
||||
}
|
||||
}
|
||||
|
||||
attempt++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void downloadMissingLibraries(String mcVersion, String forgeVersion) throws Exception {
|
||||
System.out.println(ZAnsi.cyan("Проверка и докачка отсутствующих библиотек..."));
|
||||
|
||||
// Список проблемных библиотек и их альтернативные URL
|
||||
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()) {
|
||||
Path target = librariesDir.resolve(entry.getKey());
|
||||
if (!Files.exists(target)) {
|
||||
Files.createDirectories(target.getParent());
|
||||
System.out.println(ZAnsi.yellow("Докачка: " + 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+198
-37
@@ -9,9 +9,6 @@ import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Генерирует полную команду запуска Minecraft (Vanilla / Fabric / Forge)
|
||||
*/
|
||||
public class LaunchCommandBuilder {
|
||||
|
||||
private final Instance instance;
|
||||
@@ -34,37 +31,52 @@ public class LaunchCommandBuilder {
|
||||
|
||||
// 3. Natives
|
||||
Path nativesDir = instance.getPath().resolve("natives");
|
||||
if (!Files.exists(nativesDir)) {
|
||||
Files.createDirectories(nativesDir);
|
||||
}
|
||||
command.add("-Djava.library.path=" + nativesDir.toAbsolutePath());
|
||||
|
||||
// 4. Classpath
|
||||
String classpath = buildClasspath();
|
||||
String loaderType = instance.getLoaderType().toLowerCase();
|
||||
|
||||
if ("forge".equals(loaderType)) {
|
||||
// Forge требует особого порядка аргументов
|
||||
command.addAll(getForgeJvmArguments());
|
||||
|
||||
// Forge специфичный classpath
|
||||
command.add("-cp");
|
||||
command.add(classpath);
|
||||
command.add(buildForgeClasspath());
|
||||
|
||||
// 5. Главный класс
|
||||
String mainClass = getMainClass();
|
||||
command.add(mainClass);
|
||||
// Главный класс для Forge
|
||||
command.add("cpw.mods.modlauncher.Launcher");
|
||||
|
||||
// 6. Аргументы Minecraft
|
||||
// Аргументы Forge
|
||||
command.addAll(getForgeArguments(options));
|
||||
} else {
|
||||
// Стандартный classpath для vanilla/fabric
|
||||
command.add("-cp");
|
||||
command.add(buildClasspath());
|
||||
|
||||
// Главный класс
|
||||
command.add(getMainClass());
|
||||
|
||||
// Стандартные аргументы Minecraft
|
||||
command.addAll(getMinecraftArguments(options));
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
private String getJavaPath() {
|
||||
// Пока берём системную java. Позже можно добавить выбор из ~/.zernmc/jre/
|
||||
return "java";
|
||||
}
|
||||
|
||||
private List<String> getJvmArguments(LaunchOptions options) {
|
||||
List<String> jvmArgs = new ArrayList<>();
|
||||
|
||||
// Выделенная память
|
||||
int ramMB = options.getMaxMemory() > 0 ? options.getMaxMemory() : 2048;
|
||||
int ramMB = options.getMaxMemory() > 0 ? options.getMaxMemory() : 4096;
|
||||
jvmArgs.add("-Xmx" + ramMB + "M");
|
||||
jvmArgs.add("-Xms" + Math.max(512, ramMB / 2) + "M");
|
||||
|
||||
// Стандартные оптимизации
|
||||
jvmArgs.add("-XX:+UseG1GC");
|
||||
jvmArgs.add("-XX:+UnlockExperimentalVMOptions");
|
||||
jvmArgs.add("-XX:G1NewSizePercent=20");
|
||||
@@ -72,12 +84,8 @@ public class LaunchCommandBuilder {
|
||||
jvmArgs.add("-XX:MaxGCPauseMillis=50");
|
||||
jvmArgs.add("-XX:G1HeapRegionSize=32M");
|
||||
|
||||
// Дополнительные JVM аргументы из настроек пользователя
|
||||
if (options.getExtraJvmArgs() != null && !options.getExtraJvmArgs().isEmpty()) {
|
||||
jvmArgs.addAll(options.getExtraJvmArgs());
|
||||
}
|
||||
|
||||
String loaderType = instance.getLoaderType().toLowerCase();
|
||||
|
||||
if ("fabric".equals(loaderType)) {
|
||||
jvmArgs.add("--add-modules=ALL-MODULE-PATH");
|
||||
jvmArgs.add("--add-opens=java.base/java.io=ALL-UNNAMED");
|
||||
@@ -91,15 +99,43 @@ public class LaunchCommandBuilder {
|
||||
jvmArgs.add("--add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED");
|
||||
}
|
||||
|
||||
if (options.getExtraJvmArgs() != null && !options.getExtraJvmArgs().isEmpty()) {
|
||||
jvmArgs.addAll(options.getExtraJvmArgs());
|
||||
}
|
||||
|
||||
return jvmArgs;
|
||||
}
|
||||
|
||||
private List<String> getForgeJvmArguments() {
|
||||
List<String> jvmArgs = new ArrayList<>();
|
||||
|
||||
// Критические аргументы для Forge
|
||||
jvmArgs.add("--add-modules=ALL-MODULE-PATH");
|
||||
jvmArgs.add("--add-opens=java.base/java.util.jar=ALL-UNNAMED");
|
||||
jvmArgs.add("--add-opens=java.base/java.lang.invoke=ALL-UNNAMED");
|
||||
jvmArgs.add("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED");
|
||||
jvmArgs.add("--add-opens=java.base/java.io=ALL-UNNAMED");
|
||||
jvmArgs.add("--add-opens=java.base/java.nio=ALL-UNNAMED");
|
||||
jvmArgs.add("--add-opens=java.base/java.net=ALL-UNNAMED");
|
||||
jvmArgs.add("--add-opens=java.base/java.util=ALL-UNNAMED");
|
||||
jvmArgs.add("--add-opens=java.base/sun.nio.ch=ALL-UNNAMED");
|
||||
jvmArgs.add("--add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED");
|
||||
|
||||
// Forge специфичные свойства
|
||||
jvmArgs.add("-Dforge.logging.console.level=debug");
|
||||
jvmArgs.add("-Dforge.logging.mojang.level=info");
|
||||
jvmArgs.add("-DignoreList=bootstraplauncher,securejarhandler,asm-commons,asm-util,asm-analysis,asm-tree,asm,JarJarFileSystems,client-extra,fmlcore,javafmllanguage,lowcodelanguage,mclanguage,forge-");
|
||||
jvmArgs.add("-DmergeModules=jna-5.10.0.jar,jna-platform-5.10.0.jar");
|
||||
|
||||
return jvmArgs;
|
||||
}
|
||||
|
||||
private String buildClasspath() throws Exception {
|
||||
List<String> paths = new ArrayList<>();
|
||||
|
||||
//String loaderType = instance.getLoaderType().toLowerCase();
|
||||
String versionId = getVersionId();
|
||||
String versionId = getVersionId(); // ← используем getVersionId()
|
||||
|
||||
// Добавляем основной jar
|
||||
Path versionJar = instance.getPath()
|
||||
.resolve("versions")
|
||||
.resolve(versionId)
|
||||
@@ -108,18 +144,18 @@ public class LaunchCommandBuilder {
|
||||
if (Files.exists(versionJar)) {
|
||||
paths.add(versionJar.toAbsolutePath().toString());
|
||||
} else {
|
||||
Path altVersionJar = instance.getPath()
|
||||
// Fallback на vanilla версию
|
||||
String mcVersion = instance.getMinecraftVersion();
|
||||
Path fallbackJar = instance.getPath()
|
||||
.resolve("versions")
|
||||
.resolve(instance.getMinecraftVersion())
|
||||
.resolve(instance.getMinecraftVersion() + ".jar");
|
||||
|
||||
if (Files.exists(altVersionJar)) {
|
||||
paths.add(altVersionJar.toAbsolutePath().toString());
|
||||
} else {
|
||||
System.err.println(ZAnsi.yellow("Warning: Vanilla Minecraft jar not found at: " + versionJar));
|
||||
.resolve(mcVersion)
|
||||
.resolve(mcVersion + ".jar");
|
||||
if (Files.exists(fallbackJar)) {
|
||||
paths.add(fallbackJar.toAbsolutePath().toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Все библиотеки
|
||||
Path librariesDir = instance.getPath().resolve("libraries");
|
||||
if (Files.exists(librariesDir)) {
|
||||
try (var stream = Files.walk(librariesDir)) {
|
||||
@@ -133,16 +169,92 @@ public class LaunchCommandBuilder {
|
||||
return String.join(separator, paths);
|
||||
}
|
||||
|
||||
private String buildForgeClasspath() throws Exception {
|
||||
List<String> paths = new ArrayList<>();
|
||||
|
||||
String versionId = getVersionId(); // ← используем getVersionId()
|
||||
String mcVersion = instance.getMinecraftVersion();
|
||||
String forgeVersion = instance.getLoaderVersion();
|
||||
|
||||
// 1. Сначала добавляем все библиотеки из libraries
|
||||
Path librariesDir = instance.getPath().resolve("libraries");
|
||||
if (Files.exists(librariesDir)) {
|
||||
try (var stream = Files.walk(librariesDir)) {
|
||||
stream.filter(p -> p.toString().endsWith(".jar"))
|
||||
.map(p -> p.toAbsolutePath().toString())
|
||||
.forEach(paths::add);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Добавляем jar версии (используя versionId)
|
||||
Path versionJar = instance.getPath()
|
||||
.resolve("versions")
|
||||
.resolve(versionId)
|
||||
.resolve(versionId + ".jar");
|
||||
if (Files.exists(versionJar)) {
|
||||
paths.add(0, versionJar.toAbsolutePath().toString());
|
||||
} else {
|
||||
// Fallback на vanilla jar
|
||||
Path vanillaJar = instance.getPath()
|
||||
.resolve("versions")
|
||||
.resolve(mcVersion)
|
||||
.resolve(mcVersion + ".jar");
|
||||
if (Files.exists(vanillaJar)) {
|
||||
paths.add(0, vanillaJar.toAbsolutePath().toString());
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Добавляем Forge universal jar
|
||||
Path forgeUniversal = instance.getPath()
|
||||
.resolve("libraries")
|
||||
.resolve("net")
|
||||
.resolve("minecraftforge")
|
||||
.resolve("forge")
|
||||
.resolve(mcVersion + "-" + forgeVersion)
|
||||
.resolve("forge-" + mcVersion + "-" + forgeVersion + "-universal.jar");
|
||||
if (Files.exists(forgeUniversal)) {
|
||||
paths.add(forgeUniversal.toAbsolutePath().toString());
|
||||
}
|
||||
|
||||
// 4. Добавляем Forge client jar
|
||||
Path forgeClient = instance.getPath()
|
||||
.resolve("libraries")
|
||||
.resolve("net")
|
||||
.resolve("minecraftforge")
|
||||
.resolve("forge")
|
||||
.resolve(mcVersion + "-" + forgeVersion)
|
||||
.resolve("forge-" + mcVersion + "-" + forgeVersion + "-client.jar");
|
||||
if (Files.exists(forgeClient)) {
|
||||
paths.add(forgeClient.toAbsolutePath().toString());
|
||||
}
|
||||
|
||||
// 5. Добавляем fmlcore и другие Forge модули
|
||||
String[] forgeModules = {"fmlcore", "javafmllanguage", "lowcodelanguage", "mclanguage"};
|
||||
for (String module : forgeModules) {
|
||||
Path modulePath = instance.getPath()
|
||||
.resolve("libraries")
|
||||
.resolve("net")
|
||||
.resolve("minecraftforge")
|
||||
.resolve(module)
|
||||
.resolve(mcVersion + "-" + forgeVersion)
|
||||
.resolve(module + "-" + mcVersion + "-" + forgeVersion + ".jar");
|
||||
if (Files.exists(modulePath)) {
|
||||
paths.add(modulePath.toAbsolutePath().toString());
|
||||
}
|
||||
}
|
||||
|
||||
String separator = System.getProperty("os.name").toLowerCase().contains("win") ? ";" : ":";
|
||||
return String.join(separator, paths);
|
||||
}
|
||||
|
||||
private String getMainClass() {
|
||||
String loaderType = instance.getLoaderType().toLowerCase();
|
||||
|
||||
if ("fabric".equals(loaderType)) {
|
||||
// Fabric 0.14+ использует KnotClient
|
||||
return "net.fabricmc.loader.impl.launch.knot.KnotClient";
|
||||
}
|
||||
else if ("forge".equals(loaderType)) {
|
||||
// Forge 1.20.1 использует ClientModLoader
|
||||
return "net.minecraftforge.client.loading.ClientModLoader";
|
||||
return "cpw.mods.modlauncher.Launcher";
|
||||
}
|
||||
else {
|
||||
return "net.minecraft.client.main.Main";
|
||||
@@ -168,15 +280,64 @@ public class LaunchCommandBuilder {
|
||||
args.add(options.getUsername() != null ? options.getUsername() : "Player");
|
||||
|
||||
args.add("--accessToken");
|
||||
args.add("0"); // потом токен от блядкого сервера
|
||||
args.add(options.getAccessToken() != null ? options.getAccessToken() : "0");
|
||||
|
||||
args.add("--uuid");
|
||||
args.add("00000000-0000-0000-0000-000000000000"); // тоже потом от блядкого сервера
|
||||
args.add(options.getUuid() != null ? options.getUuid() : "00000000-0000-0000-0000-000000000000");
|
||||
|
||||
args.add("--userType");
|
||||
args.add("legacy");
|
||||
|
||||
if (options.getWidth() > 0) {
|
||||
args.add("--width");
|
||||
args.add(String.valueOf(options.getWidth()));
|
||||
}
|
||||
if (options.getHeight() > 0) {
|
||||
args.add("--height");
|
||||
args.add(String.valueOf(options.getHeight()));
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
private List<String> getForgeArguments(LaunchOptions options) {
|
||||
List<String> args = new ArrayList<>();
|
||||
|
||||
// Forge требует специфические аргументы в правильном порядке
|
||||
args.add("--launchTarget");
|
||||
args.add("forgeclient");
|
||||
|
||||
args.add("--fml.forgeVersion");
|
||||
args.add(instance.getLoaderVersion());
|
||||
|
||||
args.add("--fml.mcVersion");
|
||||
args.add(instance.getMinecraftVersion());
|
||||
|
||||
args.add("--fml.forgeGroup");
|
||||
args.add("net.minecraftforge");
|
||||
|
||||
// Добавляем стандартные аргументы Minecraft (Forge их тоже принимает)
|
||||
args.add("--gameDir");
|
||||
args.add(instance.getPath().toAbsolutePath().toString());
|
||||
|
||||
args.add("--assetsDir");
|
||||
args.add(instance.getPath().resolve("assets").toAbsolutePath().toString());
|
||||
|
||||
args.add("--assetIndex");
|
||||
args.add(instance.getAssetIndex());
|
||||
|
||||
args.add("--username");
|
||||
args.add(options.getUsername() != null ? options.getUsername() : "Player");
|
||||
|
||||
args.add("--accessToken");
|
||||
args.add(options.getAccessToken() != null ? options.getAccessToken() : "0");
|
||||
|
||||
args.add("--uuid");
|
||||
args.add(options.getUuid() != null ? options.getUuid() : "00000000-0000-0000-0000-000000000000");
|
||||
|
||||
args.add("--userType");
|
||||
args.add("legacy");
|
||||
|
||||
// Дополнительные параметры
|
||||
if (options.getWidth() > 0) {
|
||||
args.add("--width");
|
||||
args.add(String.valueOf(options.getWidth()));
|
||||
@@ -198,14 +359,14 @@ public class LaunchCommandBuilder {
|
||||
return mcVersion;
|
||||
}
|
||||
else if ("fabric".equals(loaderType)) {
|
||||
// Fabric использует vanilla версию для jar файла
|
||||
return mcVersion;
|
||||
}
|
||||
else if ("forge".equals(loaderType)) {
|
||||
// Forge создаёт свою версию в папке versions
|
||||
return mcVersion + "-forge-" + loaderVer;
|
||||
}
|
||||
|
||||
return mcVersion;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -52,6 +52,17 @@ public class ProgressBar {
|
||||
System.out.flush();
|
||||
}
|
||||
|
||||
public static void showAnimated(String label, long current, long total, String unit) {
|
||||
if (total <= 0) {
|
||||
// Анимация для неизвестного размера
|
||||
char[] spinner = {'|', '/', '-', '\\'};
|
||||
int idx = (int) (current / 1024) % 4;
|
||||
System.out.print("\r" + label + " [" + spinner[idx] + "] " + formatBytes(current));
|
||||
} else {
|
||||
show(label, (int) ((current * 100) / total), 100, unit);
|
||||
}
|
||||
}
|
||||
|
||||
public static void finish(String message) {
|
||||
System.out.println("\r" + ZAnsi.brightGreen(message + " завершено ✓"));
|
||||
System.out.flush();
|
||||
|
||||
Reference in New Issue
Block a user