From 526a24a16aa4d8a8c02364d4d04678896549fe5b Mon Sep 17 00:00:00 2001 From: SashegDev Date: Tue, 5 May 2026 08:20:24 +0000 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D1=8F=D0=B5=D0=BC=20=D0=B2=D0=B5=D1=80=D0=B8=D1=84=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0=D1=86=D0=B8=D1=8E=20=D1=84=D0=B0=D0=B9=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=B8=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D1=83=20=D0=9E?= =?UTF-8?q?=D0=91=D0=9D=D0=9E=D0=92=D0=98=D0=A2=D0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - спиздили апи файлы из alpha (ApiResponse, AuthService, InstanceService, LaunchService) - добавили в InstanceInfo поля isServerPack и serverPackName - CSS: добавили оранжевую .btn-update кнопку - JS: при загрузке инстанса проверяем целостность (verify) и обновления (updates) - Кнопка ИГРАТЬ теперь меняется на ОБНОВИТЬ если есть косяки - ОБНОВИТЬ докачивает/обновляет файлы через повторный install всё как ты хотел, красава --- .../api/instance/InstanceService.java | 14 ++- .../src/main/resources/webapp/css/styles.css | 33 ++++++ launcher/src/main/resources/webapp/js/app.js | 102 +++++++++++++++++- 3 files changed, 145 insertions(+), 4 deletions(-) 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 index 7ce0647..223fdf3 100644 --- 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 @@ -68,12 +68,14 @@ public class InstanceService { } } - private InstanceInfo toInstanceInfo(Instance instance) { +private InstanceInfo toInstanceInfo(Instance instance) { return new InstanceInfo( instance.getName(), instance.getPath().toString(), instance.getMinecraftVersion(), - instance.getLoaderType() + instance.getLoaderType(), + instance.isServerPack(), + instance.getServerPackName() ); } @@ -82,17 +84,23 @@ public class InstanceService { private String path; private String version; private String loaderType; + private boolean isServerPack; + private String serverPackName; - public InstanceInfo(String name, String path, String version, String loaderType) { + public InstanceInfo(String name, String path, String version, String loaderType, boolean isServerPack, String serverPackName) { this.name = name; this.path = path; this.version = version; this.loaderType = loaderType; + this.isServerPack = isServerPack; + this.serverPackName = serverPackName; } public String getName() { return name; } public String getPath() { return path; } public String getVersion() { return version; } public String getLoaderType() { return loaderType; } + public boolean isServerPack() { return isServerPack; } + public String getServerPackName() { return serverPackName; } } } diff --git a/launcher/src/main/resources/webapp/css/styles.css b/launcher/src/main/resources/webapp/css/styles.css index 17d1159..5adcf42 100644 --- a/launcher/src/main/resources/webapp/css/styles.css +++ b/launcher/src/main/resources/webapp/css/styles.css @@ -484,6 +484,39 @@ body { transform: none; } +.btn-update { + width: 100%; + padding: 20px 30px; + background: linear-gradient(135deg, var(--warning), #f59e0b); + border: none; + border-radius: var(--radius-md); + color: #1a1a24; + font-size: 18px; + font-weight: 700; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + transition: var(--transition-normal); + box-shadow: 0 4px 20px rgba(251, 191, 36, 0.4); +} + +.btn-update:hover { + transform: translateY(-4px) scale(1.02); + box-shadow: 0 8px 40px rgba(251, 191, 36, 0.5); +} + +.btn-update:active { + transform: translateY(0); +} + +.btn-update:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; +} + /* ==================== MODAL ==================== */ .modal { position: fixed; diff --git a/launcher/src/main/resources/webapp/js/app.js b/launcher/src/main/resources/webapp/js/app.js index a119c17..a3142dd 100644 --- a/launcher/src/main/resources/webapp/js/app.js +++ b/launcher/src/main/resources/webapp/js/app.js @@ -8,6 +8,9 @@ class App { this.instances = []; this.zernmcPacks = []; this.mcVersions = []; + this.hasUpdate = false; + this.hasMismatches = false; + this.isServerPack = false; this.init(); } @@ -210,7 +213,32 @@ class App { if (result.success && result.data && result.data.length > 0) { this.currentInstance = result.data[0]; this.renderCurrentInstance(this.currentInstance); - this.enablePlayButton(true); + + this.isServerPack = this.currentInstance.isServerPack || false; + + if (this.isServerPack) { + this.addLog('Проверка целостности файлов...', 'info'); + + const verifyResult = await this.request(`/instances/${this.currentInstance.name}/verify`); + if (verifyResult.success && verifyResult.data) { + this.hasMismatches = verifyResult.data.hasMismatches; + if (this.hasMismatches) { + this.addLog('Обнаружены изменённые файлы!', 'warning'); + } else { + this.addLog('Файлы целы', 'success'); + } + } + + const updateResult = await this.request(`/instances/${this.currentInstance.name}/updates`); + if (updateResult.success && updateResult.data) { + this.hasUpdate = updateResult.data.hasUpdate; + if (this.hasUpdate) { + this.addLog('Доступно обновление: v' + updateResult.data.currentVersion + ' → v' + updateResult.data.latestVersion, 'warning'); + } + } + } + + this.updatePlayButton(); this.addLog('Сборка загружена: ' + this.currentInstance.name, 'success'); } else { this.renderNoInstance(); @@ -219,6 +247,26 @@ class App { } } + updatePlayButton() { + const btn = document.getElementById('play-btn'); + if (!this.currentInstance) { + btn.disabled = true; + btn.className = 'btn-play'; + btn.innerHTML = 'ИГРАТЬ'; + return; + } + + if (this.hasUpdate || this.hasMismatches) { + btn.disabled = false; + btn.className = 'btn-update'; + btn.innerHTML = 'ОБНОВИТЬ'; + } else { + btn.disabled = false; + btn.className = 'btn-play'; + btn.innerHTML = 'ИГРАТЬ'; + } + } + renderCurrentInstance(instance) { const container = document.getElementById('current-instance'); container.innerHTML = ` @@ -247,6 +295,11 @@ class App { async launchInstance() { if (!this.currentInstance) return; + if (this.hasUpdate || this.hasMismatches) { + await this.updateInstance(); + return; + } + this.addLog('Проверка целостности файлов...', 'info'); this.enablePlayButton(false); @@ -262,6 +315,53 @@ class App { } } + async updateInstance() { + if (!this.currentInstance || !this.isServerPack) return; + + const packName = this.currentInstance.serverPackName; + if (!packName) { + this.addLog('Ошибка: неизвестная сборка', 'error'); + return; + } + + this.addLog('Обновление сборки...', 'info'); + this.showProgress('Обновление сборки...'); + + const result = await this.request('/instances/zernmc/install', { + method: 'POST', + body: JSON.stringify({ + packName: packName, + instanceName: this.currentInstance.name + }) + }); + + this.hideProgress(); + + if (result.success) { + this.addLog('Сборка обновлена!', 'success'); + + this.addLog('Проверка после обновления...', 'info'); + const verifyResult = await this.request(`/instances/${this.currentInstance.name}/verify`); + if (verifyResult.success && verifyResult.data) { + this.hasMismatches = verifyResult.data.hasMismatches; + } + + const updateResult = await this.request(`/instances/${this.currentInstance.name}/updates`); + if (updateResult.success && updateResult.data) { + this.hasUpdate = updateResult.data.hasUpdate; + } + + this.updatePlayButton(); + + if (!this.hasUpdate && !this.hasMismatches) { + this.addLog('Готово к игре!', 'success'); + } + } else { + this.addLog('Ошибка обновления: ' + result.error, 'error'); + this.updatePlayButton(); + } + } + // ==================== DOWNLOAD MODAL ==================== async showDownloadModal() { document.getElementById('download-modal').classList.remove('hidden');