Фиксы проходок (нормально, в отличии от main ветки)
ОНО РАБОТАЕТ СУКАААА
This commit is contained in:
@@ -91,6 +91,24 @@
|
|||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>global</id>
|
||||||
|
<properties>
|
||||||
|
<launcher.title>ZernMC Launcher</launcher.title>
|
||||||
|
<build.profile>global</build.profile>
|
||||||
|
<server.url>http://87.120.187.36:1582</server.url>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>zernmc</id>
|
||||||
|
<properties>
|
||||||
|
<launcher.title>ZernMC Private Launcher</launcher.title>
|
||||||
|
<build.profile>zernmc</build.profile>
|
||||||
|
<server.url>http://87.120.187.36:1582</server.url>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
<mainClass>me.sashegdev.zernmc.launcher.Main</mainClass>
|
<mainClass>me.sashegdev.zernmc.launcher.Main</mainClass>
|
||||||
|
|||||||
@@ -153,4 +153,29 @@
|
|||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
<profiles>
|
||||||
|
<!-- ==================== GLOBAL ==================== -->
|
||||||
|
<profile>
|
||||||
|
<id>global</id>
|
||||||
|
<activation>
|
||||||
|
<activeByDefault>true</activeByDefault>
|
||||||
|
</activation>
|
||||||
|
<properties>
|
||||||
|
<build.profile>global</build.profile>
|
||||||
|
<launcher.title>ZernMC Launcher</launcher.title>
|
||||||
|
<server.url>http://87.120.187.36:1582</server.url>
|
||||||
|
<!-- Можно добавить флаги для отключения некоторых фич -->
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
|
||||||
|
<!-- ==================== ZERNMC ==================== -->
|
||||||
|
<profile>
|
||||||
|
<id>zernmc</id>
|
||||||
|
<properties>
|
||||||
|
<build.profile>zernmc</build.profile>
|
||||||
|
<launcher.title>ZernMC Private Launcher</launcher.title>
|
||||||
|
<server.url>http://87.120.187.36:1582</server.url>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
</project>
|
</project>
|
||||||
@@ -4,7 +4,6 @@ import me.sashegdev.zernmc.launcher.auth.AuthManager;
|
|||||||
import me.sashegdev.zernmc.launcher.menu.*;
|
import me.sashegdev.zernmc.launcher.menu.*;
|
||||||
import me.sashegdev.zernmc.launcher.ui.ArrowMenu;
|
import me.sashegdev.zernmc.launcher.ui.ArrowMenu;
|
||||||
import me.sashegdev.zernmc.launcher.utils.*;
|
import me.sashegdev.zernmc.launcher.utils.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
@@ -23,13 +22,12 @@ public class Main {
|
|||||||
System.setProperty("file.encoding", "UTF-8");
|
System.setProperty("file.encoding", "UTF-8");
|
||||||
System.setProperty("sun.err.encoding", "UTF-8");
|
System.setProperty("sun.err.encoding", "UTF-8");
|
||||||
System.setProperty("sun.stdout.encoding", "UTF-8");
|
System.setProperty("sun.stdout.encoding", "UTF-8");
|
||||||
java.nio.charset.Charset.defaultCharset();
|
|
||||||
ZAnsi.install();
|
|
||||||
|
|
||||||
|
ZAnsi.install();
|
||||||
System.out.print("\033[H\033[2J");
|
System.out.print("\033[H\033[2J");
|
||||||
System.out.println(ZAnsi.brightGreen("Добро пожаловать в ZernMC Launcher " + CURRENT_VERSION));
|
System.out.println(ZAnsi.brightGreen("Добро пожаловать в ZernMC Launcher " + CURRENT_VERSION));
|
||||||
|
|
||||||
//проверка всех сервисов при старте
|
// Проверка всех сервисов при старте
|
||||||
ZHttpClient.checkAllServicesOnStartup();
|
ZHttpClient.checkAllServicesOnStartup();
|
||||||
|
|
||||||
checkAndAutoUpdateLauncher();
|
checkAndAutoUpdateLauncher();
|
||||||
@@ -49,8 +47,8 @@ public class Main {
|
|||||||
} else {
|
} else {
|
||||||
System.out.println(ZAnsi.brightGreen("Добро пожаловать обратно, " + AuthManager.getUsername() + "!"));
|
System.out.println(ZAnsi.brightGreen("Добро пожаловать обратно, " + AuthManager.getUsername() + "!"));
|
||||||
}
|
}
|
||||||
// === КОНЕЦ АВТОРИЗАЦИИ ===
|
|
||||||
|
|
||||||
|
// === ГЛАВНЫЙ ЦИКЛ ===
|
||||||
try {
|
try {
|
||||||
mainLoop();
|
mainLoop();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -63,7 +61,6 @@ public class Main {
|
|||||||
|
|
||||||
private static void checkAndAutoUpdateLauncher() {
|
private static void checkAndAutoUpdateLauncher() {
|
||||||
System.out.println(ZAnsi.cyan("Проверка обновлений лаунчера..."));
|
System.out.println(ZAnsi.cyan("Проверка обновлений лаунчера..."));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String json = ZHttpClient.getLauncherVersionInfo();
|
String json = ZHttpClient.getLauncherVersionInfo();
|
||||||
String serverVersion = extractVersion(json);
|
String serverVersion = extractVersion(json);
|
||||||
@@ -74,13 +71,11 @@ public class Main {
|
|||||||
if (Version.isNewer(CURRENT_VERSION, serverVersion)) {
|
if (Version.isNewer(CURRENT_VERSION, serverVersion)) {
|
||||||
System.out.println(ZAnsi.brightYellow("\nДоступна новая версия лаунчера! (" + serverVersion + ")"));
|
System.out.println(ZAnsi.brightYellow("\nДоступна новая версия лаунчера! (" + serverVersion + ")"));
|
||||||
System.out.println(ZAnsi.cyan("Начинается автоматическое обновление...\n"));
|
System.out.println(ZAnsi.cyan("Начинается автоматическое обновление...\n"));
|
||||||
|
|
||||||
performAutoUpdate(serverVersion);
|
performAutoUpdate(serverVersion);
|
||||||
restartLauncher();
|
restartLauncher();
|
||||||
} else {
|
} else {
|
||||||
System.out.println(ZAnsi.brightGreen("Лаунчер актуален."));
|
System.out.println(ZAnsi.brightGreen("Лаунчер актуален."));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println(ZAnsi.yellow("Не удалось проверить обновления лаунчера."));
|
System.out.println(ZAnsi.yellow("Не удалось проверить обновления лаунчера."));
|
||||||
System.out.println(ZAnsi.white("Ошибка: ") + e.getMessage());
|
System.out.println(ZAnsi.white("Ошибка: ") + e.getMessage());
|
||||||
@@ -109,9 +104,7 @@ public class Main {
|
|||||||
long size = Files.size(tempJar);
|
long size = Files.size(tempJar);
|
||||||
System.out.println(ZAnsi.brightGreen("Скачано успешно (" + (size / 1024) + " KB)"));
|
System.out.println(ZAnsi.brightGreen("Скачано успешно (" + (size / 1024) + " KB)"));
|
||||||
|
|
||||||
// Заменяем текущий jar
|
|
||||||
Files.move(tempJar, currentJar, StandardCopyOption.REPLACE_EXISTING);
|
Files.move(tempJar, currentJar, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
|
||||||
System.out.println(ZAnsi.brightGreen("Обновление успешно установлено!"));
|
System.out.println(ZAnsi.brightGreen("Обновление успешно установлено!"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,13 +145,60 @@ public class Main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====================== ГЛАВНЫЙ ЦИКЛ ======================
|
||||||
private static void mainLoop() throws Exception {
|
private static void mainLoop() throws Exception {
|
||||||
|
if (Config.isZernMCBuild()) {
|
||||||
|
zernMCFlow();
|
||||||
|
} else {
|
||||||
|
globalFlow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================== ZERNMC FLOW ======================
|
||||||
|
private static void zernMCFlow() throws Exception {
|
||||||
|
ConsoleUtils.clearScreen();
|
||||||
|
System.out.println(ZAnsi.header("=== ZernMC Private Launcher ==="));
|
||||||
|
|
||||||
|
// 1. Проверка подключения к серверу
|
||||||
|
System.out.println(ZAnsi.cyan("Проверка подключения к ZernMC серверу..."));
|
||||||
|
try {
|
||||||
|
String response = ZHttpClient.get("/health");
|
||||||
|
System.out.println(ZAnsi.brightGreen("✓ Сервер доступен"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(ZAnsi.brightRed("✗ Не удалось подключиться к ZernMC серверу"));
|
||||||
|
System.out.println(ZAnsi.white("Ошибка: " + e.getMessage()));
|
||||||
|
ConsoleUtils.pause();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Авторизация
|
||||||
|
boolean sessionRestored = AuthManager.loadSavedSession();
|
||||||
|
if (!sessionRestored) {
|
||||||
|
LoginMenu loginMenu = new LoginMenu();
|
||||||
|
boolean loggedIn = loginMenu.show();
|
||||||
|
if (!loggedIn) {
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println(ZAnsi.brightGreen("Добро пожаловать обратно, " + AuthManager.getUsername() + "!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Запуск меню (LaunchMenu сам определит режим и вызовет нужный flow)
|
||||||
|
LaunchMenu launchMenu = new LaunchMenu();
|
||||||
|
launchMenu.show(); // ← Здесь будет вызван showZernMCOnly() внутри
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================== GLOBAL FLOW ======================
|
||||||
|
private static void globalFlow() throws Exception {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
ConsoleUtils.clearScreen();
|
||||||
|
System.out.println(ZAnsi.header("=== ZernMC Launcher ==="));
|
||||||
|
|
||||||
List<String> options = List.of(
|
List<String> options = List.of(
|
||||||
"Запустить игру",
|
"Запустить игру",
|
||||||
"Проверка обновлений",
|
"Проверка обновлений",
|
||||||
"Настройки",
|
"Настройки",
|
||||||
"Проверка подключения к серверам Zern",
|
"Проверка подключения к серверам",
|
||||||
"Выход"
|
"Выход"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -166,13 +206,12 @@ public class Main {
|
|||||||
int choice = menu.show();
|
int choice = menu.show();
|
||||||
|
|
||||||
if (choice == -1 || choice == 4) {
|
if (choice == -1 || choice == 4) {
|
||||||
System.out.print("\033[H\033[2J");
|
|
||||||
System.out.println(ZAnsi.yellow("До свидания!"));
|
System.out.println(ZAnsi.yellow("До свидания!"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (choice) {
|
switch (choice) {
|
||||||
case 0 -> new LaunchMenu().show();
|
case 0 -> new LaunchMenu().show(); // обычный LaunchMenu
|
||||||
case 1 -> new UpdateMenu().show();
|
case 1 -> new UpdateMenu().show();
|
||||||
case 2 -> new SettingsMenu().show();
|
case 2 -> new SettingsMenu().show();
|
||||||
case 3 -> new ServerCheckMenu().show();
|
case 3 -> new ServerCheckMenu().show();
|
||||||
|
|||||||
@@ -203,31 +203,23 @@ public class AuthManager {
|
|||||||
if (!isLoggedIn()) return false;
|
if (!isLoggedIn()) return false;
|
||||||
try {
|
try {
|
||||||
String response = ZHttpClient.get("/auth/pass/my");
|
String response = ZHttpClient.get("/auth/pass/my");
|
||||||
return response.contains("\"is_active\":true");
|
JsonObject json = JsonParser.parseString(response).getAsJsonObject();
|
||||||
|
return json.has("has_active") && json.get("has_active").getAsBoolean();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println("Не удалось проверить проходки: " + e.getMessage());
|
System.err.println(ZAnsi.red("Не удалось проверить проходки: ") + e.getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String activatePass(String passCode) {
|
public static String getPassStatus() {
|
||||||
|
if (!isLoggedIn()) return "Не авторизован";
|
||||||
try {
|
try {
|
||||||
String json = "{\"pass_code\":\"" + passCode.toUpperCase() + "\"}";
|
String response = ZHttpClient.get("/auth/pass/my");
|
||||||
SimpleHttpResponse resp = post("/auth/pass/activate", json);
|
JsonObject json = JsonParser.parseString(response).getAsJsonObject();
|
||||||
|
boolean hasActive = json.has("has_active") && json.get("has_active").getAsBoolean();
|
||||||
System.out.println(ZAnsi.cyan("[AUTH] Активация проходки: HTTP " + resp.statusCode()));
|
return hasActive ? "Есть активная проходка" : "Проходка отсутствует";
|
||||||
|
|
||||||
if (resp.statusCode() == 200) {
|
|
||||||
return "Проходка успешно активирована!";
|
|
||||||
} else if (resp.statusCode() == 401) {
|
|
||||||
return "Ошибка: Требуется авторизация. Перезайдите в аккаунт.";
|
|
||||||
} else {
|
|
||||||
String error = extractError(resp.body());
|
|
||||||
return "Ошибка: " + error;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
return "Ошибка проверки";
|
||||||
return "Ошибка соединения: " + e.getMessage();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,15 @@ import me.sashegdev.zernmc.launcher.minecraft.installer.VersionInstaller;
|
|||||||
import me.sashegdev.zernmc.launcher.minecraft.model.LaunchOptions;
|
import me.sashegdev.zernmc.launcher.minecraft.model.LaunchOptions;
|
||||||
import me.sashegdev.zernmc.launcher.minecraft.model.MinecraftVersion;
|
import me.sashegdev.zernmc.launcher.minecraft.model.MinecraftVersion;
|
||||||
import me.sashegdev.zernmc.launcher.ui.ArrowMenu;
|
import me.sashegdev.zernmc.launcher.ui.ArrowMenu;
|
||||||
|
import me.sashegdev.zernmc.launcher.utils.Config;
|
||||||
import me.sashegdev.zernmc.launcher.utils.ConsoleUtils;
|
import me.sashegdev.zernmc.launcher.utils.ConsoleUtils;
|
||||||
import me.sashegdev.zernmc.launcher.utils.Input;
|
import me.sashegdev.zernmc.launcher.utils.Input;
|
||||||
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;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -23,6 +26,151 @@ import java.util.stream.Collectors;
|
|||||||
public class LaunchMenu {
|
public class LaunchMenu {
|
||||||
|
|
||||||
public void show() throws Exception {
|
public void show() throws Exception {
|
||||||
|
if (Config.isZernMCBuild()) {
|
||||||
|
showZernMCOnly();
|
||||||
|
} else {
|
||||||
|
showGlobal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================== ZERNMC BUILD ======================
|
||||||
|
private void showZernMCOnly() throws Exception {
|
||||||
|
while (true) {
|
||||||
|
ConsoleUtils.clearScreen();
|
||||||
|
System.out.println(ZAnsi.header("=== ZernMC Private Launcher ==="));
|
||||||
|
System.out.println(ZAnsi.cyan("Доступны только серверные сборки"));
|
||||||
|
|
||||||
|
if (!awaitActivePass()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PackDownloader tempDownloader = new PackDownloader(null);
|
||||||
|
List<ServerPack> availablePacks = tempDownloader.getAvailablePacks();
|
||||||
|
|
||||||
|
if (availablePacks.isEmpty()) {
|
||||||
|
System.out.println(ZAnsi.yellow("На данный момент нет доступных сборок на сервере."));
|
||||||
|
ConsoleUtils.pause();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> options = availablePacks.stream()
|
||||||
|
.map(p -> String.format("%s [%s + %s v%d] — %d файлов",
|
||||||
|
p.getName(),
|
||||||
|
p.getMinecraftVersion(),
|
||||||
|
p.getLoaderType(),
|
||||||
|
p.getVersion(),
|
||||||
|
p.getFilesCount()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
options.add("Назад в главное меню");
|
||||||
|
|
||||||
|
ArrowMenu menu = new ArrowMenu("Выберите сборку", options);
|
||||||
|
int choice = menu.show();
|
||||||
|
|
||||||
|
if (choice == -1 || choice == options.size() - 1) return;
|
||||||
|
|
||||||
|
ServerPack selected = availablePacks.get(choice);
|
||||||
|
installAndRunServerPack(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean awaitActivePass() throws Exception {
|
||||||
|
if (AuthManager.hasActivePass()) {
|
||||||
|
System.out.println(ZAnsi.brightGreen("✓ Активная проходка подтверждена"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleUtils.clearScreen();
|
||||||
|
System.out.println(ZAnsi.brightRed("У вас нет активной проходки!"));
|
||||||
|
System.out.println(ZAnsi.white("Для доступа к сборкам ZernMC требуется активная проходка."));
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
openActivationWebsite();
|
||||||
|
|
||||||
|
System.out.println(ZAnsi.cyan("Ожидаем активацию проходки... (проверка каждые 10 секунд)"));
|
||||||
|
System.out.println(ZAnsi.white("Нажмите Enter для отмены"));
|
||||||
|
|
||||||
|
for (int i = 0; i < 60; i++) {
|
||||||
|
try {
|
||||||
|
if (System.in.available() > 0) {
|
||||||
|
Input.readLine();
|
||||||
|
System.out.println(ZAnsi.yellow("\nОжидание отменено."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
|
||||||
|
Thread.sleep(10000);
|
||||||
|
|
||||||
|
if (AuthManager.hasActivePass()) {
|
||||||
|
System.out.println(ZAnsi.brightGreen("\n✓ Проходка успешно активирована!"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.print(ZAnsi.cyan("."));
|
||||||
|
if ((i + 1) % 6 == 0) System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(ZAnsi.brightRed("\n\nВремя ожидания истекло."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openActivationWebsite() {
|
||||||
|
//String url = "https://launcher.ru.zernmc.ru/activate-pass";
|
||||||
|
String url = ZHttpClient.getBaseUrl() + "/activate-pass";
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
|
||||||
|
Desktop.getDesktop().browse(new URI(url));
|
||||||
|
System.out.println(ZAnsi.cyan("Браузер открыт: " + url));
|
||||||
|
} else {
|
||||||
|
System.out.println(ZAnsi.yellow("Не удалось открыть браузер автоматически."));
|
||||||
|
System.out.println(ZAnsi.white("Откройте вручную: " + url));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(ZAnsi.brightRed("Ошибка открытия браузера: " + e.getMessage()));
|
||||||
|
System.out.println(ZAnsi.white("Ссылка: " + url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installAndRunServerPack(ServerPack selected) throws Exception {
|
||||||
|
ConsoleUtils.clearScreen();
|
||||||
|
System.out.println(ZAnsi.header("Установка сборки: " + selected.getName()));
|
||||||
|
|
||||||
|
System.out.println(ZAnsi.white(" Minecraft: ") + selected.getMinecraftVersion());
|
||||||
|
System.out.println(ZAnsi.white(" Лоадер: ") + selected.getLoaderType() +
|
||||||
|
(selected.getLoaderVersion() != null ? " " + selected.getLoaderVersion() : ""));
|
||||||
|
System.out.println(ZAnsi.white(" Версия: v") + selected.getVersion());
|
||||||
|
System.out.println(ZAnsi.white(" Файлов: ") + selected.getFilesCount());
|
||||||
|
|
||||||
|
String localName = askPackName();
|
||||||
|
if (localName == null) return;
|
||||||
|
|
||||||
|
if (InstanceManager.getInstance(localName) != null) {
|
||||||
|
System.out.println(ZAnsi.brightRed("Сборка с таким именем уже существует!"));
|
||||||
|
ConsoleUtils.pause();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceManager.createInstanceFolder(localName);
|
||||||
|
Instance newInstance = InstanceManager.getInstance(localName);
|
||||||
|
|
||||||
|
PackDownloader packDownloader = new PackDownloader(newInstance);
|
||||||
|
boolean success = packDownloader.installOrUpdatePack(selected.getName(), selected);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
System.out.println(ZAnsi.brightRed("\n[FAIL] Не удалось установить сборку."));
|
||||||
|
ConsoleUtils.pause();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(ZAnsi.brightGreen("\n[OK] Сборка '" + localName + "' успешно установлена!"));
|
||||||
|
ConsoleUtils.pause();
|
||||||
|
|
||||||
|
launchExistingInstance(newInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================== GLOBAL BUILD ======================
|
||||||
|
private void showGlobal() throws Exception {
|
||||||
while (true) {
|
while (true) {
|
||||||
ConsoleUtils.clearScreen();
|
ConsoleUtils.clearScreen();
|
||||||
List<Instance> instances = InstanceManager.getAllInstances();
|
List<Instance> instances = InstanceManager.getAllInstances();
|
||||||
@@ -37,11 +185,10 @@ public class LaunchMenu {
|
|||||||
ArrowMenu menu = new ArrowMenu("Управление сборками", options);
|
ArrowMenu menu = new ArrowMenu("Управление сборками", options);
|
||||||
int choice = menu.show();
|
int choice = menu.show();
|
||||||
|
|
||||||
if (choice == -1) break;
|
if (choice == -1 || choice == options.size() - 1) break;
|
||||||
if (choice == options.size() - 1) break;
|
|
||||||
|
|
||||||
if (choice == instances.size()) {
|
if (choice == instances.size()) {
|
||||||
installNewPack();
|
installNewPackGlobal();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +197,7 @@ public class LaunchMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void installNewPack() throws Exception {
|
private void installNewPackGlobal() throws Exception {
|
||||||
ConsoleUtils.clearScreen();
|
ConsoleUtils.clearScreen();
|
||||||
|
|
||||||
List<String> options = List.of(
|
List<String> options = List.of(
|
||||||
@@ -66,49 +213,17 @@ public class LaunchMenu {
|
|||||||
if (choice == -1 || choice == 3) return;
|
if (choice == -1 || choice == 3) return;
|
||||||
|
|
||||||
switch (choice) {
|
switch (choice) {
|
||||||
case 0 -> {
|
case 0 -> installServerPackGlobal();
|
||||||
try {
|
|
||||||
installServerPack();
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println(ZAnsi.brightRed("Ошибка: " + e.getMessage()));
|
|
||||||
e.printStackTrace();
|
|
||||||
ConsoleUtils.pause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 1 -> createVanillaInstance();
|
case 1 -> createVanillaInstance();
|
||||||
case 2 -> createCustomInstance();
|
case 2 -> createCustomInstance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void installServerPack() throws Exception {
|
private void installServerPackGlobal() throws Exception {
|
||||||
if (!AuthManager.hasActivePass()) {
|
if (!awaitActivePass()) return;
|
||||||
ConsoleUtils.clearScreen();
|
|
||||||
System.out.println(ZAnsi.brightRed("У вас нет активной проходки!"));
|
|
||||||
System.out.println(ZAnsi.white("Чтобы скачивать сборки с сервера ZernMC, необходимо активировать проходку."));
|
|
||||||
System.out.println();
|
|
||||||
System.out.print(ZAnsi.white("Введите код проходки (ZERN-XXXXXXX) или Enter для отмены: "));
|
|
||||||
|
|
||||||
String code = Input.readLine();
|
|
||||||
if (code.isEmpty()) return;
|
|
||||||
|
|
||||||
String result = AuthManager.activatePass(code);
|
|
||||||
System.out.println(ZAnsi.cyan(result));
|
|
||||||
|
|
||||||
if (!result.contains("успешно")) {
|
|
||||||
ConsoleUtils.pause();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Повторная проверка
|
|
||||||
if (!AuthManager.hasActivePass()) {
|
|
||||||
System.out.println(ZAnsi.brightRed("Не удалось активировать проходку."));
|
|
||||||
ConsoleUtils.pause();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConsoleUtils.clearScreen();
|
ConsoleUtils.clearScreen();
|
||||||
System.out.println(ZAnsi.cyan("Получение списка доступных сборок с сервера..."));
|
System.out.println(ZAnsi.cyan("Получение списка доступных сборок..."));
|
||||||
|
|
||||||
PackDownloader tempDownloader = new PackDownloader(null);
|
PackDownloader tempDownloader = new PackDownloader(null);
|
||||||
List<ServerPack> availablePacks = tempDownloader.getAvailablePacks();
|
List<ServerPack> availablePacks = tempDownloader.getAvailablePacks();
|
||||||
@@ -119,9 +234,8 @@ public class LaunchMenu {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Исправлено: убраны спецсимволы для Windows
|
|
||||||
List<String> options = availablePacks.stream()
|
List<String> options = availablePacks.stream()
|
||||||
.map(p -> String.format("%s [%s + %s v%d] - %d файлов",
|
.map(p -> String.format("%s [%s + %s v%d] — %d файлов",
|
||||||
p.getName(),
|
p.getName(),
|
||||||
p.getMinecraftVersion(),
|
p.getMinecraftVersion(),
|
||||||
p.getLoaderType(),
|
p.getLoaderType(),
|
||||||
@@ -137,33 +251,22 @@ public class LaunchMenu {
|
|||||||
|
|
||||||
ServerPack selected = availablePacks.get(choice);
|
ServerPack selected = availablePacks.get(choice);
|
||||||
|
|
||||||
// Запрашиваем имя для локальной сборки
|
|
||||||
ConsoleUtils.clearScreen();
|
ConsoleUtils.clearScreen();
|
||||||
System.out.println(ZAnsi.header("Установка сборки: " + selected.getName()));
|
System.out.println(ZAnsi.header("Установка сборки: " + selected.getName()));
|
||||||
System.out.println(ZAnsi.white(" Minecraft: ") + selected.getMinecraftVersion());
|
|
||||||
System.out.println(ZAnsi.white(" Лоадер: ") + selected.getLoaderType() + " " + selected.getLoaderVersion());
|
|
||||||
System.out.println(ZAnsi.white(" Версия: v") + selected.getVersion());
|
|
||||||
System.out.println(ZAnsi.white(" Файлов: ") + selected.getFilesCount());
|
|
||||||
System.out.println();
|
|
||||||
|
|
||||||
System.out.print(ZAnsi.white("Введите название локальной сборки (Enter = использовать имя пака): "));
|
System.out.print(ZAnsi.white("\nВведите название локальной сборки (Enter = имя пака): "));
|
||||||
String localName = Input.readLine();
|
String localName = Input.readLine().trim();
|
||||||
if (localName.isEmpty()) {
|
if (localName.isEmpty()) localName = selected.getName();
|
||||||
localName = selected.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Проверяем, существует ли уже такая сборка
|
|
||||||
if (InstanceManager.getInstance(localName) != null) {
|
if (InstanceManager.getInstance(localName) != null) {
|
||||||
System.out.println(ZAnsi.brightRed("Сборка с таким именем уже существует!"));
|
System.out.println(ZAnsi.brightRed("Сборка с таким именем уже существует!"));
|
||||||
ConsoleUtils.pause();
|
ConsoleUtils.pause();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создаем инстанс
|
|
||||||
InstanceManager.createInstanceFolder(localName);
|
InstanceManager.createInstanceFolder(localName);
|
||||||
Instance newInstance = InstanceManager.getInstance(localName);
|
Instance newInstance = InstanceManager.getInstance(localName);
|
||||||
|
|
||||||
// Устанавливаем сборку
|
|
||||||
PackDownloader packDownloader = new PackDownloader(newInstance);
|
PackDownloader packDownloader = new PackDownloader(newInstance);
|
||||||
boolean success = packDownloader.installOrUpdatePack(selected.getName(), selected);
|
boolean success = packDownloader.installOrUpdatePack(selected.getName(), selected);
|
||||||
|
|
||||||
@@ -176,151 +279,7 @@ public class LaunchMenu {
|
|||||||
ConsoleUtils.pause();
|
ConsoleUtils.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createVanillaInstance() throws Exception {
|
// ====================== manageInstance — полностью восстановлен ======================
|
||||||
ConsoleUtils.clearScreen();
|
|
||||||
System.out.println(ZAnsi.cyan("Получение списка версий Minecraft..."));
|
|
||||||
|
|
||||||
VersionInstaller versionInstaller = new VersionInstaller(null);
|
|
||||||
List<MinecraftVersion> allVersions = versionInstaller.getAvailableVersions();
|
|
||||||
|
|
||||||
List<String> versionOptions = allVersions.stream()
|
|
||||||
.map(v -> v.getId() + " (" + v.getType() + ")")
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
versionOptions.add("Назад");
|
|
||||||
|
|
||||||
ArrowMenu versionMenu = new ArrowMenu("Выбор версии Minecraft", versionOptions);
|
|
||||||
int versionChoice = versionMenu.show();
|
|
||||||
|
|
||||||
if (versionChoice == -1 || versionChoice == versionOptions.size() - 1) return;
|
|
||||||
|
|
||||||
MinecraftVersion selectedMc = allVersions.get(versionChoice);
|
|
||||||
String mcVersion = selectedMc.getId();
|
|
||||||
|
|
||||||
String packName = askPackName();
|
|
||||||
if (packName == null) return;
|
|
||||||
|
|
||||||
if (InstanceManager.getInstance(packName) != null) {
|
|
||||||
System.out.println(ZAnsi.brightRed("Сборка с таким именем уже существует!"));
|
|
||||||
ConsoleUtils.pause();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstanceManager.createInstanceFolder(packName);
|
|
||||||
Instance newInstance = InstanceManager.getInstance(packName);
|
|
||||||
|
|
||||||
MinecraftLib lib = new MinecraftLib(newInstance);
|
|
||||||
boolean success = lib.installMinecraft(mcVersion);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
System.out.println(ZAnsi.brightGreen("\n[OK] Vanilla сборка '" + packName + "' успешно создана!"));
|
|
||||||
} else {
|
|
||||||
System.out.println(ZAnsi.brightRed("\n[FAIL] Не удалось создать сборку."));
|
|
||||||
}
|
|
||||||
|
|
||||||
ConsoleUtils.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createCustomInstance() throws Exception {
|
|
||||||
ConsoleUtils.clearScreen();
|
|
||||||
System.out.println(ZAnsi.cyan("Получение списка версий Minecraft..."));
|
|
||||||
|
|
||||||
VersionInstaller versionInstaller = new VersionInstaller(null);
|
|
||||||
List<MinecraftVersion> allVersions = versionInstaller.getAvailableVersions();
|
|
||||||
|
|
||||||
List<String> versionOptions = allVersions.stream()
|
|
||||||
.map(v -> v.getId() + " (" + v.getType() + ")")
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
versionOptions.add("Назад");
|
|
||||||
|
|
||||||
ArrowMenu versionMenu = new ArrowMenu("Выбор версии Minecraft", versionOptions);
|
|
||||||
int versionChoice = versionMenu.show();
|
|
||||||
|
|
||||||
if (versionChoice == -1 || versionChoice == versionOptions.size() - 1) return;
|
|
||||||
|
|
||||||
MinecraftVersion selectedMc = allVersions.get(versionChoice);
|
|
||||||
String mcVersion = selectedMc.getId();
|
|
||||||
|
|
||||||
// === Выбор лоадера с правильной проверкой поддержки ===
|
|
||||||
List<String> loaderOptions = buildLoaderOptions(mcVersion);
|
|
||||||
ArrowMenu loaderMenu = new ArrowMenu("Выбор модлоадера для " + mcVersion, loaderOptions);
|
|
||||||
int loaderChoice = loaderMenu.show();
|
|
||||||
|
|
||||||
if (loaderChoice == -1 || loaderChoice == loaderOptions.size() - 1) return;
|
|
||||||
|
|
||||||
String selectedLoader = loaderOptions.get(loaderChoice);
|
|
||||||
|
|
||||||
if (selectedLoader.contains("Vanilla")) {
|
|
||||||
createVanillaInstance();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String loaderType = selectedLoader.contains("Fabric") ? "fabric" : "forge";
|
|
||||||
|
|
||||||
String loaderVersion;
|
|
||||||
if (loaderType.equals("fabric")) {
|
|
||||||
loaderVersion = askFabricLoaderVersion();
|
|
||||||
} else {
|
|
||||||
loaderVersion = askForgeVersion(mcVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loaderVersion == null) return;
|
|
||||||
|
|
||||||
String packName = askPackName();
|
|
||||||
if (packName == null) return;
|
|
||||||
|
|
||||||
if (InstanceManager.getInstance(packName) != null) {
|
|
||||||
System.out.println(ZAnsi.brightRed("Сборка с таким именем уже существует!"));
|
|
||||||
ConsoleUtils.pause();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstanceManager.createInstanceFolder(packName);
|
|
||||||
Instance newInstance = InstanceManager.getInstance(packName);
|
|
||||||
|
|
||||||
MinecraftLib lib = new MinecraftLib(newInstance);
|
|
||||||
|
|
||||||
boolean success = loaderType.equals("fabric")
|
|
||||||
? lib.installFabric(mcVersion, loaderVersion)
|
|
||||||
: lib.installForge(mcVersion, loaderVersion);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
System.out.println(ZAnsi.brightGreen("\n[OK] Сборка '" + packName + "' успешно установлена!"));
|
|
||||||
} else {
|
|
||||||
System.out.println(ZAnsi.brightRed("\n[FAIL] Не удалось установить сборку."));
|
|
||||||
}
|
|
||||||
|
|
||||||
ConsoleUtils.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====================== Вспомогательные методы ======================
|
|
||||||
|
|
||||||
private List<String> buildLoaderOptions(String mcVersion) {
|
|
||||||
List<String> options = new ArrayList<>();
|
|
||||||
|
|
||||||
if (isFabricSupported(mcVersion)) {
|
|
||||||
options.add("Fabric");
|
|
||||||
}
|
|
||||||
if (isForgeSupported(mcVersion)) {
|
|
||||||
options.add("Forge");
|
|
||||||
}
|
|
||||||
options.add("Vanilla");
|
|
||||||
options.add("Назад");
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isFabricSupported(String version) {
|
|
||||||
return version.matches("^1\\.(1[4-9]|[2-9]\\d).*");
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isForgeSupported(String version) {
|
|
||||||
if (version.matches("^1\\.2[2-9].*") || version.matches("^\\d{2}.*")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return version.matches("^1\\.(1[2-9]|[2-9]\\d).*") ||
|
|
||||||
version.matches("^1\\.20.*") || version.matches("^1\\.21.*");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void manageInstance(Instance instance) throws Exception {
|
private void manageInstance(Instance instance) throws Exception {
|
||||||
while (true) {
|
while (true) {
|
||||||
ConsoleUtils.clearScreen();
|
ConsoleUtils.clearScreen();
|
||||||
@@ -392,7 +351,6 @@ public class LaunchMenu {
|
|||||||
} else {
|
} else {
|
||||||
System.out.println(ZAnsi.yellow("Обновление отменено."));
|
System.out.println(ZAnsi.yellow("Обновление отменено."));
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleUtils.pause();
|
ConsoleUtils.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,6 +429,179 @@ public class LaunchMenu {
|
|||||||
ConsoleUtils.pause();
|
ConsoleUtils.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void launchExistingInstance(Instance instance) {
|
||||||
|
if (instance.isServerPack() && !AuthManager.hasActivePass()) {
|
||||||
|
ConsoleUtils.clearScreen();
|
||||||
|
System.out.println(ZAnsi.brightRed("Для запуска серверной сборки требуется активная проходка!"));
|
||||||
|
ConsoleUtils.pause();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleUtils.clearScreen();
|
||||||
|
System.out.println(ZAnsi.brightGreen("Запуск сборки: " + instance.getName()));
|
||||||
|
|
||||||
|
MinecraftLib lib = new MinecraftLib(instance);
|
||||||
|
LaunchOptions options = new LaunchOptions();
|
||||||
|
|
||||||
|
options.setUsername(AuthManager.getUsername());
|
||||||
|
options.setUuid(AuthManager.getUuid());
|
||||||
|
options.setAccessToken(AuthManager.getAccessToken());
|
||||||
|
|
||||||
|
try {
|
||||||
|
lib.launch(options);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(ZAnsi.brightRed("Ошибка при запуске: " + e.getMessage()));
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleUtils.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================== Остальные вспомогательные методы ======================
|
||||||
|
|
||||||
|
private String askPackName() {
|
||||||
|
System.out.print(ZAnsi.white("\nВведите название новой сборки: "));
|
||||||
|
String name = Input.readLine().trim();
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
System.out.println(ZAnsi.yellow("Отменено."));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createVanillaInstance() throws Exception {
|
||||||
|
ConsoleUtils.clearScreen();
|
||||||
|
System.out.println(ZAnsi.cyan("Получение списка версий Minecraft..."));
|
||||||
|
|
||||||
|
VersionInstaller versionInstaller = new VersionInstaller(null);
|
||||||
|
List<MinecraftVersion> allVersions = versionInstaller.getAvailableVersions();
|
||||||
|
|
||||||
|
List<String> versionOptions = allVersions.stream()
|
||||||
|
.map(v -> v.getId() + " (" + v.getType() + ")")
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
versionOptions.add("Назад");
|
||||||
|
|
||||||
|
ArrowMenu versionMenu = new ArrowMenu("Выбор версии Minecraft", versionOptions);
|
||||||
|
int versionChoice = versionMenu.show();
|
||||||
|
|
||||||
|
if (versionChoice == -1 || versionChoice == versionOptions.size() - 1) return;
|
||||||
|
|
||||||
|
MinecraftVersion selectedMc = allVersions.get(versionChoice);
|
||||||
|
String mcVersion = selectedMc.getId();
|
||||||
|
|
||||||
|
String packName = askPackName();
|
||||||
|
if (packName == null) return;
|
||||||
|
|
||||||
|
if (InstanceManager.getInstance(packName) != null) {
|
||||||
|
System.out.println(ZAnsi.brightRed("Сборка с таким именем уже существует!"));
|
||||||
|
ConsoleUtils.pause();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceManager.createInstanceFolder(packName);
|
||||||
|
Instance newInstance = InstanceManager.getInstance(packName);
|
||||||
|
|
||||||
|
MinecraftLib lib = new MinecraftLib(newInstance);
|
||||||
|
boolean success = lib.installMinecraft(mcVersion);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
System.out.println(ZAnsi.brightGreen("\n[OK] Vanilla сборка '" + packName + "' успешно создана!"));
|
||||||
|
} else {
|
||||||
|
System.out.println(ZAnsi.brightRed("\n[FAIL] Не удалось создать сборку."));
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleUtils.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createCustomInstance() throws Exception {
|
||||||
|
ConsoleUtils.clearScreen();
|
||||||
|
System.out.println(ZAnsi.cyan("Получение списка версий Minecraft..."));
|
||||||
|
|
||||||
|
VersionInstaller versionInstaller = new VersionInstaller(null);
|
||||||
|
List<MinecraftVersion> allVersions = versionInstaller.getAvailableVersions();
|
||||||
|
|
||||||
|
List<String> versionOptions = allVersions.stream()
|
||||||
|
.map(v -> v.getId() + " (" + v.getType() + ")")
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
versionOptions.add("Назад");
|
||||||
|
|
||||||
|
ArrowMenu versionMenu = new ArrowMenu("Выбор версии Minecraft", versionOptions);
|
||||||
|
int versionChoice = versionMenu.show();
|
||||||
|
|
||||||
|
if (versionChoice == -1 || versionChoice == versionOptions.size() - 1) return;
|
||||||
|
|
||||||
|
MinecraftVersion selectedMc = allVersions.get(versionChoice);
|
||||||
|
String mcVersion = selectedMc.getId();
|
||||||
|
|
||||||
|
List<String> loaderOptions = buildLoaderOptions(mcVersion);
|
||||||
|
ArrowMenu loaderMenu = new ArrowMenu("Выбор модлоадера для " + mcVersion, loaderOptions);
|
||||||
|
int loaderChoice = loaderMenu.show();
|
||||||
|
|
||||||
|
if (loaderChoice == -1 || loaderChoice == loaderOptions.size() - 1) return;
|
||||||
|
|
||||||
|
String selectedLoader = loaderOptions.get(loaderChoice);
|
||||||
|
|
||||||
|
if (selectedLoader.contains("Vanilla")) {
|
||||||
|
createVanillaInstance();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String loaderType = selectedLoader.contains("Fabric") ? "fabric" : "forge";
|
||||||
|
|
||||||
|
String loaderVersion = loaderType.equals("fabric")
|
||||||
|
? askFabricLoaderVersion()
|
||||||
|
: askForgeVersion(mcVersion);
|
||||||
|
|
||||||
|
if (loaderVersion == null) return;
|
||||||
|
|
||||||
|
String packName = askPackName();
|
||||||
|
if (packName == null) return;
|
||||||
|
|
||||||
|
if (InstanceManager.getInstance(packName) != null) {
|
||||||
|
System.out.println(ZAnsi.brightRed("Сборка с таким именем уже существует!"));
|
||||||
|
ConsoleUtils.pause();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceManager.createInstanceFolder(packName);
|
||||||
|
Instance newInstance = InstanceManager.getInstance(packName);
|
||||||
|
|
||||||
|
MinecraftLib lib = new MinecraftLib(newInstance);
|
||||||
|
|
||||||
|
boolean success = loaderType.equals("fabric")
|
||||||
|
? lib.installFabric(mcVersion, loaderVersion)
|
||||||
|
: lib.installForge(mcVersion, loaderVersion);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
System.out.println(ZAnsi.brightGreen("\n[OK] Сборка '" + packName + "' успешно установлена!"));
|
||||||
|
} else {
|
||||||
|
System.out.println(ZAnsi.brightRed("\n[FAIL] Не удалось установить сборку."));
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleUtils.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> buildLoaderOptions(String mcVersion) {
|
||||||
|
List<String> options = new ArrayList<>();
|
||||||
|
|
||||||
|
if (isFabricSupported(mcVersion)) options.add("Fabric");
|
||||||
|
if (isForgeSupported(mcVersion)) options.add("Forge");
|
||||||
|
options.add("Vanilla");
|
||||||
|
options.add("Назад");
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFabricSupported(String version) {
|
||||||
|
return version.matches("^1\\.(1[4-9]|[2-9]\\d).*");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isForgeSupported(String version) {
|
||||||
|
if (version.matches("^1\\.2[2-9].*") || version.matches("^\\d{2}.*")) return false;
|
||||||
|
return version.matches("^1\\.(1[2-9]|[2-9]\\d).*") ||
|
||||||
|
version.matches("^1\\.20.*") || version.matches("^1\\.21.*");
|
||||||
|
}
|
||||||
|
|
||||||
private String askFabricLoaderVersion() throws Exception {
|
private String askFabricLoaderVersion() throws Exception {
|
||||||
System.out.println(ZAnsi.cyan("Получение списка версий Fabric Loader..."));
|
System.out.println(ZAnsi.cyan("Получение списка версий Fabric Loader..."));
|
||||||
List<String> versions = ZHttpClient.getFabricLoaderVersions();
|
List<String> versions = ZHttpClient.getFabricLoaderVersions();
|
||||||
@@ -518,48 +649,8 @@ public class LaunchMenu {
|
|||||||
return compatibleVersions.get(choice);
|
return compatibleVersions.get(choice);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String askPackName() {
|
|
||||||
System.out.print(ZAnsi.white("\nВведите название новой сборки: "));
|
|
||||||
String name = Input.readLine();
|
|
||||||
if (name.isEmpty()) {
|
|
||||||
System.out.println(ZAnsi.yellow("Отменено."));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void launchExistingInstance(Instance instance) {
|
|
||||||
if (instance.isServerPack() && !AuthManager.hasActivePass()) {
|
|
||||||
ConsoleUtils.clearScreen();
|
|
||||||
System.out.println(ZAnsi.brightRed("Для запуска серверной сборки требуется активная проходка!"));
|
|
||||||
ConsoleUtils.pause();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ConsoleUtils.clearScreen();
|
|
||||||
System.out.println(ZAnsi.brightGreen("Запуск сборки: " + instance.getName()));
|
|
||||||
|
|
||||||
MinecraftLib lib = new MinecraftLib(instance);
|
|
||||||
LaunchOptions options = new LaunchOptions();
|
|
||||||
|
|
||||||
// Авторизация Minecraft
|
|
||||||
options.setUsername(AuthManager.getUsername());
|
|
||||||
options.setUuid(AuthManager.getUuid());
|
|
||||||
options.setAccessToken(AuthManager.getAccessToken());
|
|
||||||
|
|
||||||
try {
|
|
||||||
lib.launch(options);
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println(ZAnsi.brightRed("Ошибка при запуске: " + e.getMessage()));
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
ConsoleUtils.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getAllForgeVersions() throws Exception {
|
private List<String> getAllForgeVersions() throws Exception {
|
||||||
String metadataUrl = "https://maven.minecraftforge.net/net/minecraftforge/forge/maven-metadata.xml";
|
String xml = ZHttpClient.downloadString("https://maven.minecraftforge.net/net/minecraftforge/forge/maven-metadata.xml");
|
||||||
|
|
||||||
String xml = ZHttpClient.downloadString(metadataUrl);
|
|
||||||
|
|
||||||
List<String> versions = new ArrayList<>();
|
List<String> versions = new ArrayList<>();
|
||||||
int index = 0;
|
int index = 0;
|
||||||
@@ -575,7 +666,6 @@ public class LaunchMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
versions.sort((a, b) -> b.compareTo(a));
|
versions.sort((a, b) -> b.compareTo(a));
|
||||||
|
|
||||||
return versions;
|
return versions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,14 +148,46 @@ public class LoginMenu {
|
|||||||
* Читаем пароль — стараемся скрыть вывод через Console,
|
* Читаем пароль — стараемся скрыть вывод через Console,
|
||||||
* если недоступно (IDE/терминал без TTY) — читаем обычным способом.
|
* если недоступно (IDE/терминал без TTY) — читаем обычным способом.
|
||||||
*/
|
*/
|
||||||
private String readPassword(String prompt) {
|
private String readPassword(String prompt) throws IOException {
|
||||||
java.io.Console console = System.console();
|
// Создаём временный терминал для ввода пароля
|
||||||
if (console != null) {
|
org.jline.terminal.Terminal passTerminal = org.jline.terminal.TerminalBuilder.builder()
|
||||||
char[] chars = console.readPassword(prompt);
|
.system(true)
|
||||||
return chars != null ? new String(chars) : "";
|
.jna(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
passTerminal.enterRawMode();
|
||||||
|
passTerminal.writer().print(prompt);
|
||||||
|
passTerminal.writer().flush();
|
||||||
|
|
||||||
|
StringBuilder password = new StringBuilder();
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
int key = passTerminal.reader().read();
|
||||||
|
|
||||||
|
if (key == 13 || key == 10) { // Enter
|
||||||
|
passTerminal.writer().println();
|
||||||
|
break;
|
||||||
|
} else if (key == 127 || key == 8) { // Backspace
|
||||||
|
if (password.length() > 0) {
|
||||||
|
password.setLength(password.length() - 1);
|
||||||
|
passTerminal.writer().print("\b \b");
|
||||||
|
passTerminal.writer().flush();
|
||||||
}
|
}
|
||||||
// Fallback: в IDE пароль будет виден
|
} else if (key == 3) { // Ctrl+C
|
||||||
return Input.readLine(prompt);
|
passTerminal.writer().println();
|
||||||
|
System.exit(0);
|
||||||
|
} else if (key >= 32 && key < 127) { // Печатные символы
|
||||||
|
password.append((char) key);
|
||||||
|
passTerminal.writer().print('*');
|
||||||
|
passTerminal.writer().flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
passTerminal.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return password.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printBanner() {
|
private void printBanner() {
|
||||||
|
|||||||
@@ -16,17 +16,24 @@ import java.util.List;
|
|||||||
public class ServerCheckMenu {
|
public class ServerCheckMenu {
|
||||||
|
|
||||||
public void show() throws IOException {
|
public void show() throws IOException {
|
||||||
|
while (true) {
|
||||||
|
ConsoleUtils.clearScreen();
|
||||||
|
System.out.println(ZAnsi.header("Диагностика подключения"));
|
||||||
|
|
||||||
List<String> options = List.of(
|
List<String> options = List.of(
|
||||||
"Проверить подключение к ZernMC серверу",
|
"Проверить подключение к ZernMC серверу",
|
||||||
"Проверить доступ к Mojang (Minecraft)",
|
"Проверить доступ к Mojang (Minecraft)",
|
||||||
"Проверить доступ к Fabric Meta",
|
"Проверить доступ к Fabric Meta",
|
||||||
|
"Проверить доступ к Forge Maven",
|
||||||
"Назад в главное меню"
|
"Назад в главное меню"
|
||||||
);
|
);
|
||||||
|
|
||||||
ArrowMenu menu = new ArrowMenu("Диагностика подключения", options);
|
ArrowMenu menu = new ArrowMenu("Выберите проверку", options);
|
||||||
int choice = menu.show();
|
int choice = menu.show();
|
||||||
|
|
||||||
if (choice == -1 || choice == 4) return;
|
if (choice == -1 || choice == 4) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ConsoleUtils.clearScreen();
|
ConsoleUtils.clearScreen();
|
||||||
|
|
||||||
@@ -34,53 +41,58 @@ public class ServerCheckMenu {
|
|||||||
case 0 -> checkZernServer();
|
case 0 -> checkZernServer();
|
||||||
case 1 -> checkMojang();
|
case 1 -> checkMojang();
|
||||||
case 2 -> checkFabric();
|
case 2 -> checkFabric();
|
||||||
|
case 3 -> checkForge();
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleUtils.pause();
|
ConsoleUtils.pause();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void checkZernServer() {
|
private void checkZernServer() {
|
||||||
System.out.println(ZAnsi.cyan("Проверка подключения к ZernMC серверу..."));
|
System.out.println(ZAnsi.cyan("Проверка подключения к ZernMC серверу..."));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String response = ZHttpClient.get("/health");
|
String response = ZHttpClient.get("/health");
|
||||||
System.out.println(ZAnsi.brightGreen("Сервер успешно подключён!"));
|
System.out.println(ZAnsi.brightGreen("[OK] ZernMC сервер успешно подключён!"));
|
||||||
System.out.println("Ответ: " + response);
|
System.out.println(ZAnsi.white("Ответ сервера: ") + response);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println(ZAnsi.brightRed("Не удалось подключиться к ZernMC серверу"));
|
System.out.println(ZAnsi.brightRed("[FAIL] Не удалось подключиться к ZernMC серверу"));
|
||||||
System.out.println("Ошибка: " + e.getMessage());
|
System.out.println(ZAnsi.white("Ошибка: ") + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkMojang() {
|
private void checkMojang() {
|
||||||
System.out.println(ZAnsi.cyan("Проверка доступа к Mojang..."));
|
System.out.println(ZAnsi.cyan("Проверка доступа к Mojang..."));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
HttpClient client = HttpClient.newBuilder()
|
HttpClient client = HttpClient.newBuilder()
|
||||||
.connectTimeout(Duration.ofSeconds(8))
|
.connectTimeout(Duration.ofSeconds(10))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
.uri(URI.create("https://launchermeta.mojang.com/mc/game/version_manifest_v2.json"))
|
.uri(URI.create("https://piston-meta.mojang.com/mc/game/version_manifest_v2.json"))
|
||||||
.GET()
|
.GET()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
|
||||||
if (response.statusCode() == 200) {
|
if (response.statusCode() == 200) {
|
||||||
System.out.println(ZAnsi.brightGreen("Mojang доступен"));
|
System.out.println(ZAnsi.brightGreen("[OK] Mojang доступен"));
|
||||||
} else {
|
} else {
|
||||||
System.out.println(ZAnsi.brightRed("Mojang вернул код " + response.statusCode()));
|
System.out.println(ZAnsi.brightRed("[FAIL] Mojang вернул код " + response.statusCode()));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println(ZAnsi.brightRed("Нет доступа к Mojang"));
|
System.out.println(ZAnsi.brightRed("[FAIL] Нет доступа к Mojang"));
|
||||||
System.out.println("Ошибка: " + e.getMessage());
|
System.out.println(ZAnsi.white("Ошибка: ") + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkFabric() {
|
private void checkFabric() {
|
||||||
System.out.println(ZAnsi.cyan("Проверка доступа к Fabric Meta..."));
|
System.out.println(ZAnsi.cyan("Проверка доступа к Fabric Meta..."));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
HttpClient client = HttpClient.newBuilder()
|
HttpClient client = HttpClient.newBuilder()
|
||||||
.connectTimeout(Duration.ofSeconds(8))
|
.connectTimeout(Duration.ofSeconds(10))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
@@ -91,13 +103,39 @@ public class ServerCheckMenu {
|
|||||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
|
||||||
if (response.statusCode() == 200) {
|
if (response.statusCode() == 200) {
|
||||||
System.out.println(ZAnsi.brightGreen("Fabric Meta доступен"));
|
System.out.println(ZAnsi.brightGreen("[OK] Fabric Meta доступен"));
|
||||||
} else {
|
} else {
|
||||||
System.out.println(ZAnsi.brightRed("Fabric Meta вернул код " + response.statusCode()));
|
System.out.println(ZAnsi.brightRed("[FAIL] Fabric Meta вернул код " + response.statusCode()));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println(ZAnsi.brightRed("Нет доступа к Fabric Meta"));
|
System.out.println(ZAnsi.brightRed("[FAIL] Нет доступа к Fabric Meta"));
|
||||||
System.out.println("Ошибка: " + e.getMessage());
|
System.out.println(ZAnsi.white("Ошибка: ") + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkForge() {
|
||||||
|
System.out.println(ZAnsi.cyan("Проверка доступа к Forge Maven..."));
|
||||||
|
|
||||||
|
try {
|
||||||
|
HttpClient client = HttpClient.newBuilder()
|
||||||
|
.connectTimeout(Duration.ofSeconds(10))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create("https://maven.minecraftforge.net/net/minecraftforge/forge/maven-metadata.xml"))
|
||||||
|
.GET()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
|
||||||
|
if (response.statusCode() == 200) {
|
||||||
|
System.out.println(ZAnsi.brightGreen("[OK] Forge Maven доступен"));
|
||||||
|
} else {
|
||||||
|
System.out.println(ZAnsi.brightRed("[FAIL] Forge Maven вернул код " + response.statusCode()));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(ZAnsi.brightRed("[FAIL] Нет доступа к Forge Maven"));
|
||||||
|
System.out.println(ZAnsi.white("Ошибка: ") + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,8 @@ public class Config {
|
|||||||
private static final Path CONFIG_DIR = Path.of(System.getProperty("user.home"), ".zernmc");
|
private static final Path CONFIG_DIR = Path.of(System.getProperty("user.home"), ".zernmc");
|
||||||
private static final Path CONFIG_FILE = CONFIG_DIR.resolve("launcher.properties");
|
private static final Path CONFIG_FILE = CONFIG_DIR.resolve("launcher.properties");
|
||||||
|
|
||||||
|
private static final String BUILD_PROFILE = System.getProperty("build.profile", "global");
|
||||||
|
|
||||||
private static final Properties props = new Properties();
|
private static final Properties props = new Properties();
|
||||||
|
|
||||||
// Настройки
|
// Настройки
|
||||||
@@ -83,6 +85,14 @@ public class Config {
|
|||||||
return maxMemory;
|
return maxMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isZernMCBuild() {
|
||||||
|
return "zernmc".equalsIgnoreCase(BUILD_PROFILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isGlobalBuild() {
|
||||||
|
return !isZernMCBuild();
|
||||||
|
}
|
||||||
|
|
||||||
public static void setMaxMemory(int memory) {
|
public static void setMaxMemory(int memory) {
|
||||||
// Защита от слишком маленьких/больших значений
|
// Защита от слишком маленьких/больших значений
|
||||||
if (memory < 1024) memory = 1536;
|
if (memory < 1024) memory = 1536;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public class Input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String readLine(String prompt) {
|
public static String readLine(String prompt) {
|
||||||
|
flushInput(); // Очищаем буфер
|
||||||
System.out.print(prompt);
|
System.out.print(prompt);
|
||||||
return scanner.nextLine().trim();
|
return scanner.nextLine().trim();
|
||||||
}
|
}
|
||||||
@@ -79,4 +80,18 @@ public class Input {
|
|||||||
public static void close() {
|
public static void close() {
|
||||||
scanner.close();
|
scanner.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Очищает буфер ввода от оставшихся символов
|
||||||
|
*/
|
||||||
|
public static void flushInput() {
|
||||||
|
try {
|
||||||
|
while (System.in.available() > 0) {
|
||||||
|
System.in.read();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Игнорируем
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,8 @@ package me.sashegdev.zernmc.launcher.utils;
|
|||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import me.sashegdev.zernmc.launcher.auth.AuthManager;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
@@ -380,13 +382,19 @@ public class ZHttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
|
||||||
.uri(URI.create(BASE_URL + endpoint))
|
.uri(URI.create(BASE_URL + endpoint))
|
||||||
.timeout(Duration.ofSeconds(15))
|
.timeout(Duration.ofSeconds(15))
|
||||||
.header("User-Agent", "ZernMC-Launcher/1.0")
|
.header("User-Agent", "ZernMC-Launcher/1.0")
|
||||||
.GET()
|
.GET();
|
||||||
.build();
|
|
||||||
|
|
||||||
|
// ===== ДОБАВИТЬ ТОКЕН АВТОРИЗАЦИИ =====
|
||||||
|
String accessToken = AuthManager.getAccessToken();
|
||||||
|
if (accessToken != null && !accessToken.equals("0")) {
|
||||||
|
requestBuilder.header("Authorization", "Bearer " + accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequest request = requestBuilder.build();
|
||||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
|
||||||
if (response.statusCode() != 200) {
|
if (response.statusCode() != 200) {
|
||||||
@@ -401,13 +409,19 @@ public class ZHttpClient {
|
|||||||
|
|
||||||
private static String proxyGet(String endpoint) throws IOException {
|
private static String proxyGet(String endpoint) throws IOException {
|
||||||
try {
|
try {
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
|
||||||
.uri(URI.create(BASE_URL + "/proxy" + endpoint))
|
.uri(URI.create(BASE_URL + "/proxy" + endpoint))
|
||||||
.timeout(Duration.ofSeconds(30))
|
.timeout(Duration.ofSeconds(30))
|
||||||
.header("User-Agent", "ZernMC-Launcher/1.0")
|
.header("User-Agent", "ZernMC-Launcher/1.0")
|
||||||
.GET()
|
.GET();
|
||||||
.build();
|
|
||||||
|
|
||||||
|
// ===== ДОБАВИТЬ ТОКЕН АВТОРИЗАЦИИ =====
|
||||||
|
String accessToken = AuthManager.getAccessToken();
|
||||||
|
if (accessToken != null && !accessToken.equals("0")) {
|
||||||
|
requestBuilder.header("Authorization", "Bearer " + accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequest request = requestBuilder.build();
|
||||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
|
||||||
if (response.statusCode() != 200) {
|
if (response.statusCode() != 200) {
|
||||||
|
|||||||
+338
@@ -15,6 +15,8 @@ from middleware import LoggingMiddleware
|
|||||||
from cli import parse_args, run_test_mode, run_production_mode, run_development_mode
|
from cli import parse_args, run_test_mode, run_production_mode, run_development_mode
|
||||||
from log_manager import init_logging
|
from log_manager import init_logging
|
||||||
|
|
||||||
|
from fastapi.responses import Response
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
import base64
|
import base64
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
@@ -80,6 +82,331 @@ async def lifespan(app: FastAPI):
|
|||||||
logger.info("Server shutting down...")
|
logger.info("Server shutting down...")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ====================== ШАБЛОН СТРАНИЦЫ АКТИВАЦИИ ======================
|
||||||
|
ACTIVATE_PASS_HTML = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Активация проходки | ZernMC</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 40px;
|
||||||
|
max-width: 450px;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo h1 {
|
||||||
|
color: #00d4ff;
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-shadow: 0 0 20px rgba(0, 212, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo p {
|
||||||
|
color: #8892b0;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
color: #ccd6f6;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 14px 16px;
|
||||||
|
background: rgba(255, 255, 255, 0.07);
|
||||||
|
border: 1.5px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 16px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus {
|
||||||
|
border-color: #00d4ff;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
box-shadow: 0 0 0 4px rgba(0, 212, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
input::placeholder {
|
||||||
|
color: #8892b0;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 14px;
|
||||||
|
background: linear-gradient(135deg, #00d4ff 0%, #0099cc 100%);
|
||||||
|
border: none;
|
||||||
|
border-radius: 12px;
|
||||||
|
color: white;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 25px -5px rgba(0, 212, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#message {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 14px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: none;
|
||||||
|
animation: fadeIn 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(-10px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
background: rgba(0, 255, 100, 0.15);
|
||||||
|
border: 1px solid rgba(0, 255, 100, 0.3);
|
||||||
|
color: #00ff64;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
background: rgba(255, 50, 50, 0.15);
|
||||||
|
border: 1px solid rgba(255, 50, 50, 0.3);
|
||||||
|
color: #ff5050;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
background: rgba(0, 212, 255, 0.15);
|
||||||
|
border: 1px solid rgba(0, 212, 255, 0.3);
|
||||||
|
color: #00d4ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
display: inline-block;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top-color: white;
|
||||||
|
animation: spin 0.6s linear infinite;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 24px;
|
||||||
|
color: #8892b0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a {
|
||||||
|
color: #00d4ff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="logo">
|
||||||
|
<h1>ZernMC</h1>
|
||||||
|
<p>Активация проходки</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="activateForm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username">Ваш никнейм</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="username"
|
||||||
|
name="username"
|
||||||
|
placeholder="Введите ваш ник в игре"
|
||||||
|
required
|
||||||
|
minlength="3"
|
||||||
|
maxlength="16"
|
||||||
|
pattern="[a-zA-Z0-9_]+"
|
||||||
|
autocomplete="off"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="passCode">Код проходки</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="passCode"
|
||||||
|
name="passCode"
|
||||||
|
placeholder="XXXX-XXXX-XXXX"
|
||||||
|
required
|
||||||
|
minlength="8"
|
||||||
|
maxlength="20"
|
||||||
|
autocomplete="off"
|
||||||
|
style="text-transform: uppercase;"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" id="submitBtn">
|
||||||
|
Активировать проходку
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div id="message"></div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<p>Нет проходки? <a href="https://zernmc.ru" target="_blank">Получить на сайте</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const form = document.getElementById('activateForm');
|
||||||
|
const submitBtn = document.getElementById('submitBtn');
|
||||||
|
const messageDiv = document.getElementById('message');
|
||||||
|
const usernameInput = document.getElementById('username');
|
||||||
|
const passCodeInput = document.getElementById('passCode');
|
||||||
|
|
||||||
|
passCodeInput.addEventListener('input', (e) => {
|
||||||
|
e.target.value = e.target.value.toUpperCase().replace(/[^A-Z0-9-]/g, '');
|
||||||
|
});
|
||||||
|
|
||||||
|
function showMessage(text, type) {
|
||||||
|
messageDiv.textContent = text;
|
||||||
|
messageDiv.className = '';
|
||||||
|
messageDiv.classList.add(type);
|
||||||
|
messageDiv.style.display = 'block';
|
||||||
|
|
||||||
|
if (type === 'success' || type === 'info') {
|
||||||
|
setTimeout(() => {
|
||||||
|
messageDiv.style.display = 'none';
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
form.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const username = usernameInput.value.trim();
|
||||||
|
const passCode = passCodeInput.value.trim().toUpperCase();
|
||||||
|
const password = prompt("Введите пароль от вашего аккаунта " + username + ":");
|
||||||
|
|
||||||
|
if (!password) {
|
||||||
|
showMessage('Необходимо ввести пароль', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!username || !passCode) {
|
||||||
|
showMessage('Пожалуйста, заполните все поля', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
const originalText = submitBtn.textContent;
|
||||||
|
submitBtn.innerHTML = 'Активация... <span class="spinner"></span>';
|
||||||
|
messageDiv.style.display = 'none';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Логинимся с существующим аккаунтом
|
||||||
|
const loginResponse = await fetch('/auth/login', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ username: username, password: password })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!loginResponse.ok) {
|
||||||
|
const errorData = await loginResponse.json();
|
||||||
|
throw new Error(errorData.detail || 'Неверный логин или пароль');
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenData = await loginResponse.json();
|
||||||
|
|
||||||
|
// Активируем проходку
|
||||||
|
const activateResponse = await fetch('/auth/pass/activate', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${tokenData.access_token}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ pass_code: passCode })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!activateResponse.ok) {
|
||||||
|
const errorData = await activateResponse.json();
|
||||||
|
throw new Error(errorData.detail || 'Ошибка активации проходки');
|
||||||
|
}
|
||||||
|
|
||||||
|
const activateData = await activateResponse.json();
|
||||||
|
showMessage('✅ ' + activateData.message, 'success');
|
||||||
|
form.reset();
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
showMessage('❌ ' + error.message, 'error');
|
||||||
|
} finally {
|
||||||
|
submitBtn.disabled = false;
|
||||||
|
submitBtn.textContent = originalText;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Create app with lifespan
|
# Create app with lifespan
|
||||||
app = FastAPI(title="ZernMC Launcher Server", lifespan=lifespan)
|
app = FastAPI(title="ZernMC Launcher Server", lifespan=lifespan)
|
||||||
|
|
||||||
@@ -144,6 +471,17 @@ async def health():
|
|||||||
return {"status": "healthy", "timestamp": datetime.utcnow().isoformat()}
|
return {"status": "healthy", "timestamp": datetime.utcnow().isoformat()}
|
||||||
|
|
||||||
|
|
||||||
|
# ====================== WEB ИНТЕРФЕЙС ДЛЯ АКТИВАЦИИ ПРОХОДКИ ======================
|
||||||
|
|
||||||
|
@app.get("/activate-pass")
|
||||||
|
async def activate_pass_page():
|
||||||
|
"""Веб-интерфейс для активации проходки"""
|
||||||
|
return Response(
|
||||||
|
content=ACTIVATE_PASS_HTML,
|
||||||
|
media_type="text/html"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# ====================== ЭНДПОИНТЫ ДЛЯ ПАКОВ ======================
|
# ====================== ЭНДПОИНТЫ ДЛЯ ПАКОВ ======================
|
||||||
|
|
||||||
@app.get("/packs")
|
@app.get("/packs")
|
||||||
|
|||||||
Reference in New Issue
Block a user