diff --git a/launcher/src/main/java/me/sashegdev/zernmc/launcher/Main.java b/launcher/src/main/java/me/sashegdev/zernmc/launcher/Main.java index adca0ec..5c81d1d 100644 --- a/launcher/src/main/java/me/sashegdev/zernmc/launcher/Main.java +++ b/launcher/src/main/java/me/sashegdev/zernmc/launcher/Main.java @@ -1,5 +1,6 @@ package me.sashegdev.zernmc.launcher; +import me.sashegdev.zernmc.launcher.api.LauncherAPI; import me.sashegdev.zernmc.launcher.auth.AuthManager; import me.sashegdev.zernmc.launcher.menu.*; import me.sashegdev.zernmc.launcher.ui.ArrowMenu; @@ -21,6 +22,7 @@ import java.util.List; public class Main { private static final String CURRENT_VERSION = Version.getCurrentVersion(); + private static final LauncherAPI api = new LauncherAPI(); public static void main(String[] args) throws Exception { boolean cliMode = Arrays.asList(args).contains("--cli") || Arrays.asList(args).contains("-c"); @@ -95,11 +97,11 @@ public class Main { checkAndAutoUpdateLauncher(); - // === АВТОРИЗАЦИЯ === + // === АВТОРИЗАЦИЯ (используем новый API) === System.out.println(ZAnsi.cyan("Проверка авторизации...")); - boolean sessionRestored = AuthManager.loadSavedSession(); + var sessionResponse = api.checkSession(); - if (!sessionRestored) { + if (!sessionResponse.isSuccess()) { LoginMenu loginMenu = new LoginMenu(); boolean loggedIn = loginMenu.show(); if (!loggedIn) { @@ -108,7 +110,8 @@ public class Main { System.exit(0); } } else { - System.out.println(ZAnsi.brightGreen("Добро пожаловать обратно, " + AuthManager.getUsername() + "!")); + var sessionInfo = sessionResponse.getData(); + System.out.println(ZAnsi.brightGreen("Добро пожаловать обратно, " + sessionInfo.getUsername() + "!")); } // === ГЛАВНЫЙ ЦИКЛ === diff --git a/launcher/src/main/java/me/sashegdev/zernmc/launcher/api/ApiResponse.java b/launcher/src/main/java/me/sashegdev/zernmc/launcher/api/ApiResponse.java new file mode 100644 index 0000000..c2337ae --- /dev/null +++ b/launcher/src/main/java/me/sashegdev/zernmc/launcher/api/ApiResponse.java @@ -0,0 +1,33 @@ +package me.sashegdev.zernmc.launcher.api; + +public class ApiResponse { + private boolean success; + private T data; + private String error; + + public ApiResponse(boolean success, T data, String error) { + this.success = success; + this.data = data; + this.error = error; + } + + public static ApiResponse success(T data) { + return new ApiResponse<>(true, data, null); + } + + public static ApiResponse error(String error) { + return new ApiResponse<>(false, null, error); + } + + public boolean isSuccess() { + return success; + } + + public T getData() { + return data; + } + + public String getError() { + return error; + } +} diff --git a/launcher/src/main/java/me/sashegdev/zernmc/launcher/api/auth/AuthService.java b/launcher/src/main/java/me/sashegdev/zernmc/launcher/api/auth/AuthService.java new file mode 100644 index 0000000..48097c0 --- /dev/null +++ b/launcher/src/main/java/me/sashegdev/zernmc/launcher/api/auth/AuthService.java @@ -0,0 +1,134 @@ +package me.sashegdev.zernmc.launcher.api.auth; + +import me.sashegdev.zernmc.launcher.api.ApiResponse; +import me.sashegdev.zernmc.launcher.auth.AuthManager; +import me.sashegdev.zernmc.launcher.utils.ZHttpClient; + +import java.io.IOException; + +public class AuthService { + + public ApiResponse login(String username, String password) { + try { + AuthManager.AuthResult result = AuthManager.login(username, password); + if (result.success) { + LoginResult loginResult = new LoginResult(AuthManager.getUsername(), AuthManager.getAccessToken()); + return ApiResponse.success(loginResult); + } + return ApiResponse.error(result.error != null ? result.error : "Неверный логин или пароль"); + } catch (Exception e) { + return ApiResponse.error("Ошибка авторизации: " + e.getMessage()); + } + } + + public ApiResponse logout() { + try { + AuthManager.logout(); + return ApiResponse.success(true); + } catch (Exception e) { + return ApiResponse.error("Ошибка при выходе: " + e.getMessage()); + } + } + + public ApiResponse checkSession() { + try { + boolean restored = AuthManager.loadSavedSession(); + if (restored) { + SessionInfo info = new SessionInfo( + AuthManager.getUsername(), + AuthManager.getAccessToken(), + AuthManager.hasActivePass() + ); + return ApiResponse.success(info); + } + return ApiResponse.error("Сессия не найдена"); + } catch (Exception e) { + return ApiResponse.error("Ошибка проверки сессии: " + e.getMessage()); + } + } + + public ApiResponse activatePass(String passCode) { + try { + String response = post("/auth/pass/activate", + "{\"code\":\"" + passCode + "\"}"); + return ApiResponse.success(true); + } catch (Exception e) { + return ApiResponse.error("Ошибка активации проходки: " + e.getMessage()); + } + } + + private String post(String endpoint, String jsonBody) throws Exception { + String fullUrl = ZHttpClient.getBaseUrl() + endpoint; + java.net.URL url = new java.net.URL(fullUrl); + java.net.HttpURLConnection conn = (java.net.HttpURLConnection) url.openConnection(); + + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json; charset=utf-8"); + conn.setRequestProperty("Accept", "application/json"); + conn.setRequestProperty("User-Agent", "ZernMC-Launcher/1.0"); + + if (AuthManager.getAccessToken() != null && !AuthManager.getAccessToken().equals("0")) { + conn.setRequestProperty("Authorization", "Bearer " + AuthManager.getAccessToken()); + } + + conn.setDoOutput(true); + + try (var os = conn.getOutputStream()) { + byte[] input = jsonBody.getBytes(java.nio.charset.StandardCharsets.UTF_8); + os.write(input); + } + + int statusCode = conn.getResponseCode(); + var is = (statusCode >= 200 && statusCode < 300) ? conn.getInputStream() : conn.getErrorStream(); + + String responseBody; + try (var scanner = new java.util.Scanner(is, java.nio.charset.StandardCharsets.UTF_8.name())) { + responseBody = scanner.useDelimiter("\\A").hasNext() ? scanner.next() : ""; + } + + conn.disconnect(); + + if (statusCode != 200) { + throw new IOException("HTTP " + statusCode + ": " + responseBody); + } + + return responseBody; + } + + public boolean isLoggedIn() { + return AuthManager.isLoggedIn(); + } + + public String getCurrentUsername() { + return AuthManager.getUsername(); + } + + public static class LoginResult { + private String username; + private String token; + + public LoginResult(String username, String token) { + this.username = username; + this.token = token; + } + + public String getUsername() { return username; } + public String getToken() { return token; } + } + + public static class SessionInfo { + private String username; + private String token; + private boolean passActive; + + public SessionInfo(String username, String token, boolean passActive) { + this.username = username; + this.token = token; + this.passActive = passActive; + } + + public String getUsername() { return username; } + public String getToken() { return token; } + public boolean isPassActive() { return passActive; } + } +} diff --git a/launcher/src/main/java/me/sashegdev/zernmc/launcher/api/instance/InstanceService.java b/launcher/src/main/java/me/sashegdev/zernmc/launcher/api/instance/InstanceService.java new file mode 100644 index 0000000..7ce0647 --- /dev/null +++ b/launcher/src/main/java/me/sashegdev/zernmc/launcher/api/instance/InstanceService.java @@ -0,0 +1,98 @@ +package me.sashegdev.zernmc.launcher.api.instance; + +import me.sashegdev.zernmc.launcher.api.ApiResponse; +import me.sashegdev.zernmc.launcher.minecraft.Instance; +import me.sashegdev.zernmc.launcher.minecraft.InstanceManager; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +public class InstanceService { + + public ApiResponse> getAllInstances() { + try { + List instances = InstanceManager.getAllInstances(); + List infoList = instances.stream() + .map(this::toInstanceInfo) + .collect(Collectors.toList()); + return ApiResponse.success(infoList); + } catch (IOException e) { + return ApiResponse.error("Ошибка получения списка сборок: " + e.getMessage()); + } + } + + public ApiResponse getInstance(String name) { + try { + Instance instance = InstanceManager.getInstance(name); + if (instance == null) { + return ApiResponse.error("Сборка не найдена: " + name); + } + return ApiResponse.success(toInstanceInfo(instance)); + } catch (Exception e) { + return ApiResponse.error("Ошибка получения сборки: " + e.getMessage()); + } + } + + public ApiResponse createInstance(String name) { + try { + boolean created = InstanceManager.createInstanceFolder(name); + if (!created) { + return ApiResponse.error("Сборка с таким именем уже существует: " + name); + } + Instance instance = InstanceManager.getInstance(name); + return ApiResponse.success(toInstanceInfo(instance)); + } catch (IOException e) { + return ApiResponse.error("Ошибка создания сборки: " + e.getMessage()); + } + } + + public ApiResponse deleteInstance(String name) { + try { + boolean deleted = InstanceManager.deleteInstance(name); + if (!deleted) { + return ApiResponse.error("Не удалось удалить сборку: " + name); + } + return ApiResponse.success(true); + } catch (Exception e) { + return ApiResponse.error("Ошибка удаления сборки: " + e.getMessage()); + } + } + + public ApiResponse isInstanceExists(String name) { + try { + Instance instance = InstanceManager.getInstance(name); + return ApiResponse.success(instance != null); + } catch (Exception e) { + return ApiResponse.error("Ошибка проверки сборки: " + e.getMessage()); + } + } + + private InstanceInfo toInstanceInfo(Instance instance) { + return new InstanceInfo( + instance.getName(), + instance.getPath().toString(), + instance.getMinecraftVersion(), + instance.getLoaderType() + ); + } + + public static class InstanceInfo { + private String name; + private String path; + private String version; + private String loaderType; + + public InstanceInfo(String name, String path, String version, String loaderType) { + this.name = name; + this.path = path; + this.version = version; + this.loaderType = loaderType; + } + + public String getName() { return name; } + public String getPath() { return path; } + public String getVersion() { return version; } + public String getLoaderType() { return loaderType; } + } +} diff --git a/launcher/src/main/java/me/sashegdev/zernmc/launcher/api/launch/LaunchService.java b/launcher/src/main/java/me/sashegdev/zernmc/launcher/api/launch/LaunchService.java new file mode 100644 index 0000000..c203138 --- /dev/null +++ b/launcher/src/main/java/me/sashegdev/zernmc/launcher/api/launch/LaunchService.java @@ -0,0 +1,157 @@ +package me.sashegdev.zernmc.launcher.api.launch; + +import me.sashegdev.zernmc.launcher.api.ApiResponse; +import me.sashegdev.zernmc.launcher.minecraft.Instance; +import me.sashegdev.zernmc.launcher.minecraft.InstanceManager; +import me.sashegdev.zernmc.launcher.minecraft.launch.LaunchCommandBuilder; +import me.sashegdev.zernmc.launcher.minecraft.model.LaunchOptions; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +public class LaunchService { + + public ApiResponse prepareLaunch(String instanceName) { + try { + Instance instance = InstanceManager.getInstance(instanceName); + if (instance == null) { + return ApiResponse.error("Сборка не найдена: " + instanceName); + } + + LaunchCommandBuilder builder = new LaunchCommandBuilder(instance); + LaunchOptions options = new LaunchOptions(); + + List command = builder.build(options); + + LaunchInfo info = new LaunchInfo( + instanceName, + command, + instance.getPath().toString() + ); + return ApiResponse.success(info); + } catch (Exception e) { + return ApiResponse.error("Ошибка подготовки запуска: " + e.getMessage()); + } + } + + public ApiResponse launch(String instanceName) { + try { + Instance instance = InstanceManager.getInstance(instanceName); + if (instance == null) { + return ApiResponse.error("Сборка не найдена: " + instanceName); + } + + LaunchCommandBuilder builder = new LaunchCommandBuilder(instance); + LaunchOptions options = new LaunchOptions(); + + List command = builder.build(options); + + ProcessBuilder processBuilder = new ProcessBuilder(command); + processBuilder.directory(instance.getPath().toFile()); + processBuilder.inheritIO(); + + Process process = processBuilder.start(); + + ProcessInfo info = new ProcessInfo( + instanceName, + process.pid(), + "RUNNING" + ); + return ApiResponse.success(info); + } catch (Exception e) { + return ApiResponse.error("Ошибка запуска: " + e.getMessage()); + } + } + + public ApiResponse isReady(String instanceName) { + try { + Instance instance = InstanceManager.getInstance(instanceName); + if (instance == null) { + return ApiResponse.error("Сборка не найдена: " + instanceName); + } + + Path versionJson = instance.getPath().resolve("version.json"); + boolean hasVersionJson = versionJson.toFile().exists(); + + return ApiResponse.success(hasVersionJson); + } catch (Exception e) { + return ApiResponse.error("Ошибка проверки готовности: " + e.getMessage()); + } + } + + public ApiResponse getLaunchInfo(String instanceName) { + try { + Instance instance = InstanceManager.getInstance(instanceName); + if (instance == null) { + return ApiResponse.error("Сборка не найдена: " + instanceName); + } + + InstanceInfo info = new InstanceInfo( + instance.getName(), + instance.getMinecraftVersion(), + instance.getLoaderType(), + instance.getLoaderVersion(), + instance.getAssetIndex() + ); + return ApiResponse.success(info); + } catch (Exception e) { + return ApiResponse.error("Ошибка получения информации: " + e.getMessage()); + } + } + + public static class LaunchInfo { + private String instanceName; + private List command; + private String workingDirectory; + + public LaunchInfo(String instanceName, List command, String workingDirectory) { + this.instanceName = instanceName; + this.command = command; + this.workingDirectory = workingDirectory; + } + + public String getInstanceName() { return instanceName; } + public List getCommand() { return command; } + public String getWorkingDirectory() { return workingDirectory; } + } + + public static class ProcessInfo { + private String instanceName; + private long pid; + private String status; + + public ProcessInfo(String instanceName, long pid, String status) { + this.instanceName = instanceName; + this.pid = pid; + this.status = status; + } + + public String getInstanceName() { return instanceName; } + public long getPid() { return pid; } + public String getStatus() { return status; } + } + + public static class InstanceInfo { + private String name; + private String minecraftVersion; + private String loaderType; + private String loaderVersion; + private String assetIndex; + + public InstanceInfo(String name, String minecraftVersion, String loaderType, + String loaderVersion, String assetIndex) { + this.name = name; + this.minecraftVersion = minecraftVersion; + this.loaderType = loaderType; + this.loaderVersion = loaderVersion; + this.assetIndex = assetIndex; + } + + public String getName() { return name; } + public String getMinecraftVersion() { return minecraftVersion; } + public String getLoaderType() { return loaderType; } + public String getLoaderVersion() { return loaderVersion; } + public String getAssetIndex() { return assetIndex; } + } +}