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');