Launcher UI: MC/loader versions from server, split instances, console log sync, disable ZernMC for FREE
This commit is contained in:
@@ -4,7 +4,13 @@ import me.sashegdev.zernmc.launcher.api.auth.AuthService;
|
|||||||
import me.sashegdev.zernmc.launcher.api.instance.InstanceService;
|
import me.sashegdev.zernmc.launcher.api.instance.InstanceService;
|
||||||
import me.sashegdev.zernmc.launcher.api.launch.LaunchService;
|
import me.sashegdev.zernmc.launcher.api.launch.LaunchService;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Центральный фасад для внутреннего API лаунчера.
|
* Центральный фасад для внутреннего API лаунчера.
|
||||||
@@ -12,6 +18,8 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class LauncherAPI {
|
public class LauncherAPI {
|
||||||
|
|
||||||
|
private static final String LAUNCHER_SERVER = System.getProperty("launcher.server", "http://87.120.187.36:1582");
|
||||||
|
|
||||||
private final AuthService authService;
|
private final AuthService authService;
|
||||||
private final InstanceService instanceService;
|
private final InstanceService instanceService;
|
||||||
private final LaunchService launchService;
|
private final LaunchService launchService;
|
||||||
@@ -74,14 +82,52 @@ public class LauncherAPI {
|
|||||||
|
|
||||||
public ApiResponse<List<String>> getMCVersions() {
|
public ApiResponse<List<String>> getMCVersions() {
|
||||||
try {
|
try {
|
||||||
return ApiResponse.success(java.util.List.of("1.21", "1.20.6", "1.20.4", "1.20.1", "1.19.4", "1.18.2", "1.17.1", "1.16.5"));
|
URL url = new URL(LAUNCHER_SERVER + "/launcher/minecraft-versions");
|
||||||
} catch (Exception e) {
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
return ApiResponse.error(e.getMessage());
|
conn.setConnectTimeout(5000);
|
||||||
|
conn.setReadTimeout(10000);
|
||||||
|
if (conn.getResponseCode() == 200) {
|
||||||
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) sb.append(line);
|
||||||
|
org.json.JSONArray arr = new org.json.JSONArray(sb.toString());
|
||||||
|
List<String> versions = new java.util.ArrayList<>();
|
||||||
|
for (int i = 0; i < arr.length(); i++) {
|
||||||
|
versions.add(arr.getString(i));
|
||||||
}
|
}
|
||||||
|
return ApiResponse.success(versions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("[API] MC versions fetch failed, using fallback: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return ApiResponse.success(java.util.List.of("1.21", "1.20.6", "1.20.4", "1.20.1", "1.19.4", "1.18.2", "1.17.1", "1.16.5"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ApiResponse<List<String>> getLoaderVersions(String mcVersion, String loader) {
|
public ApiResponse<List<String>> getLoaderVersions(String mcVersion, String loader) {
|
||||||
try {
|
try {
|
||||||
|
URL url = new URL(LAUNCHER_SERVER + "/launcher/loader-versions?mc=" + mcVersion + "&loader=" + loader.toLowerCase());
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setConnectTimeout(5000);
|
||||||
|
conn.setReadTimeout(10000);
|
||||||
|
if (conn.getResponseCode() == 200) {
|
||||||
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) sb.append(line);
|
||||||
|
org.json.JSONArray arr = new org.json.JSONArray(sb.toString());
|
||||||
|
List<String> versions = new java.util.ArrayList<>();
|
||||||
|
for (int i = 0; i < arr.length(); i++) {
|
||||||
|
versions.add(arr.getString(i));
|
||||||
|
}
|
||||||
|
return ApiResponse.success(versions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("[API] Loader versions fetch failed, using fallback: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
java.util.List<String> versions = new java.util.ArrayList<>();
|
java.util.List<String> versions = new java.util.ArrayList<>();
|
||||||
switch (loader.toLowerCase()) {
|
switch (loader.toLowerCase()) {
|
||||||
case "fabric":
|
case "fabric":
|
||||||
@@ -103,8 +149,48 @@ public class LauncherAPI {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return ApiResponse.success(versions);
|
return ApiResponse.success(versions);
|
||||||
} catch (Exception e) {
|
|
||||||
return ApiResponse.error(e.getMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ApiResponse<List<java.util.Map<String, String>>> getZernMCPacks() {
|
||||||
|
try {
|
||||||
|
String token = authService.getCurrentToken();
|
||||||
|
if (token == null) {
|
||||||
|
return ApiResponse.error("Не авторизован");
|
||||||
|
}
|
||||||
|
|
||||||
|
URL url = new URL(LAUNCHER_SERVER + "/packs");
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setConnectTimeout(5000);
|
||||||
|
conn.setReadTimeout(10000);
|
||||||
|
conn.setRequestProperty("Authorization", "Bearer " + token);
|
||||||
|
|
||||||
|
if (conn.getResponseCode() == 200) {
|
||||||
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) sb.append(line);
|
||||||
|
org.json.JSONArray arr = new org.json.JSONArray(sb.toString());
|
||||||
|
List<java.util.Map<String, String>> packs = new java.util.ArrayList<>();
|
||||||
|
for (int i = 0; i < arr.length(); i++) {
|
||||||
|
org.json.JSONObject pack = arr.getJSONObject(i);
|
||||||
|
java.util.Map<String, String> packInfo = new java.util.HashMap<>();
|
||||||
|
packInfo.put("name", pack.optString("name", ""));
|
||||||
|
packInfo.put("displayName", pack.optString("displayName", pack.optString("name", "")));
|
||||||
|
packInfo.put("version", pack.optString("version", ""));
|
||||||
|
packInfo.put("mcVersion", pack.optString("mcVersion", ""));
|
||||||
|
packInfo.put("loader", pack.optString("loader", "vanilla"));
|
||||||
|
packInfo.put("description", pack.optString("description", ""));
|
||||||
|
packs.add(packInfo);
|
||||||
|
}
|
||||||
|
return ApiResponse.success(packs);
|
||||||
|
}
|
||||||
|
} else if (conn.getResponseCode() == 403) {
|
||||||
|
return ApiResponse.error("Требуется проходка");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("[API] Packs fetch failed: " + e.getMessage());
|
||||||
|
return ApiResponse.error("Ошибка загрузки сборок: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return ApiResponse.success(java.util.List.of());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,6 +105,10 @@ public class AuthService {
|
|||||||
return AuthManager.getUsername();
|
return AuthManager.getUsername();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getCurrentToken() {
|
||||||
|
return AuthManager.getAccessToken();
|
||||||
|
}
|
||||||
|
|
||||||
public static class LoginResult {
|
public static class LoginResult {
|
||||||
private String username;
|
private String username;
|
||||||
private String token;
|
private String token;
|
||||||
|
|||||||
@@ -261,6 +261,7 @@ public class JFXLauncher extends Application {
|
|||||||
server.createContext("/api/game-logs", this::handleGameLogs);
|
server.createContext("/api/game-logs", this::handleGameLogs);
|
||||||
server.createContext("/api/mc-versions", this::handleMCVersions);
|
server.createContext("/api/mc-versions", this::handleMCVersions);
|
||||||
server.createContext("/api/loader-versions", this::handleLoaderVersions);
|
server.createContext("/api/loader-versions", this::handleLoaderVersions);
|
||||||
|
server.createContext("/api/packs", this::handlePacks);
|
||||||
server.createContext("/api/exit", this::handleExit);
|
server.createContext("/api/exit", this::handleExit);
|
||||||
server.createContext("/assets/", this::handleStatic);
|
server.createContext("/assets/", this::handleStatic);
|
||||||
|
|
||||||
@@ -429,6 +430,19 @@ public class JFXLauncher extends Application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handlePacks(HttpExchange exchange) {
|
||||||
|
try {
|
||||||
|
var result = api.getZernMCPacks();
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
sendJson(exchange, Map.of("success", true, "data", result.getData()));
|
||||||
|
} else {
|
||||||
|
sendJson(exchange, Map.of("success", false, "error", result.getError()));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
sendJson(exchange, Map.of("success", false, "error", e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, String> parseQuery(String query) {
|
private Map<String, String> parseQuery(String query) {
|
||||||
Map<String, String> params = new HashMap<>();
|
Map<String, String> params = new HashMap<>();
|
||||||
if (query != null && !query.isEmpty()) {
|
if (query != null && !query.isEmpty()) {
|
||||||
|
|||||||
@@ -62,20 +62,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
<div class="current-instance-section">
|
<div class="instances-section">
|
||||||
<h3 class="section-label">Текущая сборка</h3>
|
<h3 class="section-label">ZernMC сборки</h3>
|
||||||
<div id="current-instance" class="current-instance">
|
<div id="zernmc-instances-list" class="instances-list">
|
||||||
<div class="instance-card-mini">
|
<!-- ZernMC instances -->
|
||||||
<span class="instance-name">Загрузка...</span>
|
|
||||||
<span class="instance-version">-</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="instances-section">
|
<div class="instances-section">
|
||||||
<h3 class="section-label">Все сборки</h3>
|
<h3 class="section-label">Локальные сборки</h3>
|
||||||
<div id="instances-list" class="instances-list">
|
<div id="local-instances-list" class="instances-list">
|
||||||
<!-- Dynamic content -->
|
<!-- Local instances -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const API_BASE = '/api';
|
const API_BASE = '/api';
|
||||||
|
let consoleLogPollingInterval = null;
|
||||||
|
|
||||||
class LauncherApp {
|
class LauncherApp {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -10,15 +11,79 @@ class LauncherApp {
|
|||||||
this.selectedInstance = null;
|
this.selectedInstance = null;
|
||||||
this.hasUpdate = false;
|
this.hasUpdate = false;
|
||||||
this.hasMismatches = false;
|
this.hasMismatches = false;
|
||||||
|
this.consoleLogBuffer = '';
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
this.initGridAnimation();
|
this.initGridAnimation();
|
||||||
|
this.startConsoleLogPolling();
|
||||||
await this.checkAuth();
|
await this.checkAuth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startConsoleLogPolling() {
|
||||||
|
this.pollConsoleLogs();
|
||||||
|
consoleLogPollingInterval = setInterval(() => this.pollConsoleLogs(), 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopConsoleLogPolling() {
|
||||||
|
if (consoleLogPollingInterval) {
|
||||||
|
clearInterval(consoleLogPollingInterval);
|
||||||
|
consoleLogPollingInterval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async pollConsoleLogs() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE}/logs`);
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.success && result.data) {
|
||||||
|
const newLogs = result.data;
|
||||||
|
if (newLogs !== this.consoleLogBuffer) {
|
||||||
|
this.consoleLogBuffer = newLogs;
|
||||||
|
this.syncConsoleLogs(newLogs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Silent fail for polling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syncConsoleLogs(fullLogs) {
|
||||||
|
const lines = fullLogs.split('\n').filter(l => l.trim());
|
||||||
|
const container = document.getElementById('logs-container');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
const existingLines = Array.from(container.querySelectorAll('.log-entry')).length;
|
||||||
|
const newLines = lines.slice(existingLines);
|
||||||
|
|
||||||
|
newLines.forEach(line => {
|
||||||
|
if (!line.trim()) return;
|
||||||
|
const entry = document.createElement('div');
|
||||||
|
entry.className = 'log-entry ' + this.getLogType(line);
|
||||||
|
|
||||||
|
const timestampMatch = line.match(/^\[(\d{2}:\d{2}:\d{2})\]/);
|
||||||
|
if (timestampMatch) {
|
||||||
|
entry.textContent = line;
|
||||||
|
} else {
|
||||||
|
const time = new Date().toLocaleTimeString();
|
||||||
|
entry.textContent = '[' + time + '] ' + line;
|
||||||
|
}
|
||||||
|
container.appendChild(entry);
|
||||||
|
});
|
||||||
|
|
||||||
|
container.scrollTop = container.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLogType(line) {
|
||||||
|
if (line.includes('[STDOUT]') || line.includes('Render thread/ERROR') || line.includes('/ERROR]:')) return 'error';
|
||||||
|
if (line.includes('[STDOUT]') || line.includes('Render thread/WARN') || line.includes('/WARN]:')) return 'warning';
|
||||||
|
if (line.includes('[STDOUT]') || line.includes('Render thread/INFO') || line.includes('/INFO]:')) return 'info';
|
||||||
|
if (line.includes(' успешно') || line.includes('Started') || line.includes(' запущен') || line.includes('done')) return 'success';
|
||||||
|
return 'info';
|
||||||
|
}
|
||||||
|
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
document.getElementById('login-form').addEventListener('submit', (e) => {
|
document.getElementById('login-form').addEventListener('submit', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -133,12 +198,11 @@ class LauncherApp {
|
|||||||
this.showLoading(true);
|
this.showLoading(true);
|
||||||
const result = await this.request('/account');
|
const result = await this.request('/account');
|
||||||
|
|
||||||
if (result.success && result.data) {
|
if (result.success) {
|
||||||
this.account = result.data;
|
this.account = result.data;
|
||||||
this.username = result.data.username;
|
this.username = result.data.username;
|
||||||
this.showMainScreen();
|
this.showMainScreen();
|
||||||
await this.loadInstances();
|
await this.loadInstances();
|
||||||
await this.loadCurrentInstance();
|
|
||||||
} else {
|
} else {
|
||||||
this.showLoginScreen();
|
this.showLoginScreen();
|
||||||
}
|
}
|
||||||
@@ -178,7 +242,6 @@ class LauncherApp {
|
|||||||
this.username = result.data.username;
|
this.username = result.data.username;
|
||||||
this.showMainScreen();
|
this.showMainScreen();
|
||||||
await this.loadInstances();
|
await this.loadInstances();
|
||||||
await this.loadCurrentInstance();
|
|
||||||
} else {
|
} else {
|
||||||
this.showError(result.error || 'Ошибка входа');
|
this.showError(result.error || 'Ошибка входа');
|
||||||
}
|
}
|
||||||
@@ -206,16 +269,43 @@ class LauncherApp {
|
|||||||
this.instances = result.data;
|
this.instances = result.data;
|
||||||
this.renderInstances();
|
this.renderInstances();
|
||||||
this.addLog('Загружено ' + result.data.length + ' сборок', 'success');
|
this.addLog('Загружено ' + result.data.length + ' сборок', 'success');
|
||||||
|
|
||||||
|
if (this.instances.length > 0 && !this.selectedInstance) {
|
||||||
|
this.currentInstance = this.instances[0];
|
||||||
|
this.selectedInstance = this.currentInstance;
|
||||||
|
this.addLog('Сборка загружена: ' + this.currentInstance.name, 'success');
|
||||||
|
}
|
||||||
|
this.updatePlayButton();
|
||||||
} else {
|
} else {
|
||||||
this.addLog('Ошибка загрузки: ' + (result.error || 'неизвестная ошибка'), 'error');
|
this.addLog('Ошибка загрузки: ' + (result.error || 'неизвестная ошибка'), 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInstances() {
|
renderInstances() {
|
||||||
const container = document.getElementById('instances-list');
|
const zernmcContainer = document.getElementById('zernmc-instances-list');
|
||||||
container.innerHTML = '';
|
const localContainer = document.getElementById('local-instances-list');
|
||||||
|
zernmcContainer.innerHTML = '';
|
||||||
|
localContainer.innerHTML = '';
|
||||||
|
|
||||||
this.instances.forEach(inst => {
|
const zernmcInstances = this.instances.filter(i => i.category === 'zernmc');
|
||||||
|
const localInstances = this.instances.filter(i => i.category === 'local');
|
||||||
|
|
||||||
|
zernmcInstances.forEach(inst => {
|
||||||
|
zernmcContainer.appendChild(this.createInstanceCard(inst));
|
||||||
|
});
|
||||||
|
localInstances.forEach(inst => {
|
||||||
|
localContainer.appendChild(this.createInstanceCard(inst));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (zernmcInstances.length === 0) {
|
||||||
|
zernmcContainer.innerHTML = '<div class="log-entry info" style="padding:8px;font-size:12px">Нет сборок</div>';
|
||||||
|
}
|
||||||
|
if (localInstances.length === 0) {
|
||||||
|
localContainer.innerHTML = '<div class="log-entry info" style="padding:8px;font-size:12px">Нет сборок</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createInstanceCard(inst) {
|
||||||
const card = document.createElement('div');
|
const card = document.createElement('div');
|
||||||
card.className = 'instance-card';
|
card.className = 'instance-card';
|
||||||
if (this.selectedInstance && this.selectedInstance.name === inst.name) {
|
if (this.selectedInstance && this.selectedInstance.name === inst.name) {
|
||||||
@@ -236,9 +326,7 @@ class LauncherApp {
|
|||||||
<div class="instance-card-name">${this.escapeHtml(inst.name)}</div>
|
<div class="instance-card-name">${this.escapeHtml(inst.name)}</div>
|
||||||
<div class="instance-card-details">${details}</div>
|
<div class="instance-card-details">${details}</div>
|
||||||
`;
|
`;
|
||||||
|
return card;
|
||||||
container.appendChild(card);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selectInstance(name) {
|
selectInstance(name) {
|
||||||
@@ -247,18 +335,6 @@ class LauncherApp {
|
|||||||
this.updatePlayButton();
|
this.updatePlayButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadCurrentInstance() {
|
|
||||||
if (this.instances.length > 0) {
|
|
||||||
this.currentInstance = this.instances[0];
|
|
||||||
this.selectedInstance = this.currentInstance;
|
|
||||||
this.renderCurrentInstance(this.currentInstance);
|
|
||||||
this.addLog('Сборка загружена: ' + this.currentInstance.name, 'success');
|
|
||||||
} else {
|
|
||||||
this.renderNoInstance();
|
|
||||||
}
|
|
||||||
this.updatePlayButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePlayButton() {
|
updatePlayButton() {
|
||||||
const btn = document.getElementById('play-btn');
|
const btn = document.getElementById('play-btn');
|
||||||
const instance = this.selectedInstance || this.currentInstance;
|
const instance = this.selectedInstance || this.currentInstance;
|
||||||
@@ -341,6 +417,53 @@ class LauncherApp {
|
|||||||
|
|
||||||
async showDownloadModal() {
|
async showDownloadModal() {
|
||||||
document.getElementById('download-modal').classList.remove('hidden');
|
document.getElementById('download-modal').classList.remove('hidden');
|
||||||
|
await this.loadDownloadModalData();
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadDownloadModalData() {
|
||||||
|
const mcSelect = document.getElementById('mc-version-select');
|
||||||
|
mcSelect.innerHTML = '<option value="">Загрузка...</option>';
|
||||||
|
|
||||||
|
const mcResult = await this.request('/mc-versions');
|
||||||
|
if (mcResult.success && mcResult.data) {
|
||||||
|
mcSelect.innerHTML = '<option value="">Выберите версию</option>';
|
||||||
|
mcResult.data.forEach(v => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = v;
|
||||||
|
opt.textContent = v;
|
||||||
|
mcSelect.appendChild(opt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const packSelect = document.getElementById('zernmc-pack-select');
|
||||||
|
packSelect.innerHTML = '<option value="">Загрузка...</option>';
|
||||||
|
|
||||||
|
const packResult = await this.request('/packs');
|
||||||
|
if (packResult.success && packResult.data && packResult.data.length > 0) {
|
||||||
|
packSelect.innerHTML = '<option value="">Выберите сборку</option>';
|
||||||
|
packResult.data.forEach(p => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = p.name;
|
||||||
|
opt.textContent = p.displayName + ' (' + p.version + ')';
|
||||||
|
packSelect.appendChild(opt);
|
||||||
|
});
|
||||||
|
} else if (packResult.error && packResult.error.includes('проходка')) {
|
||||||
|
packSelect.innerHTML = '<option value="">Требуется проходка</option>';
|
||||||
|
} else {
|
||||||
|
packSelect.innerHTML = '<option value="">Сборки недоступны</option>';
|
||||||
|
}
|
||||||
|
|
||||||
|
const zernmcTab = document.querySelector('[data-tab="zernmc"]');
|
||||||
|
if (this.account && !this.account.passActive) {
|
||||||
|
zernmcTab.disabled = true;
|
||||||
|
zernmcTab.style.opacity = '0.5';
|
||||||
|
zernmcTab.style.cursor = 'not-allowed';
|
||||||
|
this.switchTab('vanilla');
|
||||||
|
} else {
|
||||||
|
zernmcTab.disabled = false;
|
||||||
|
zernmcTab.style.opacity = '1';
|
||||||
|
zernmcTab.style.cursor = 'pointer';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hideDownloadModal() {
|
hideDownloadModal() {
|
||||||
@@ -438,12 +561,28 @@ class LauncherApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoaderChange(loader) {
|
async onLoaderChange(loader) {
|
||||||
const loaderVersionGroup = document.getElementById('loader-version-group');
|
const loaderVersionGroup = document.getElementById('loader-version-group');
|
||||||
|
const loaderVersionSelect = document.getElementById('loader-version-select');
|
||||||
|
const mcVersion = document.getElementById('mc-version-select').value;
|
||||||
|
|
||||||
if (loader === 'vanilla') {
|
if (loader === 'vanilla') {
|
||||||
loaderVersionGroup.classList.add('hidden');
|
loaderVersionGroup.classList.add('hidden');
|
||||||
} else {
|
} else {
|
||||||
loaderVersionGroup.classList.remove('hidden');
|
loaderVersionGroup.classList.remove('hidden');
|
||||||
|
if (mcVersion) {
|
||||||
|
loaderVersionSelect.innerHTML = '<option value="">Загрузка...</option>';
|
||||||
|
const result = await this.request('/loader-versions?mc=' + mcVersion + '&loader=' + loader);
|
||||||
|
if (result.success && result.data) {
|
||||||
|
loaderVersionSelect.innerHTML = '<option value="">Выберите версию</option>';
|
||||||
|
result.data.forEach(v => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = v;
|
||||||
|
opt.textContent = v;
|
||||||
|
loaderVersionSelect.appendChild(opt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,6 +610,7 @@ class LauncherApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clearLogs() {
|
clearLogs() {
|
||||||
|
this.consoleLogBuffer = '';
|
||||||
const container = document.getElementById('logs-container');
|
const container = document.getElementById('logs-container');
|
||||||
container.innerHTML = '<div class="log-entry info">Логи очищены</div>';
|
container.innerHTML = '<div class="log-entry info">Логи очищены</div>';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user