Auto-Update wie beim MSCO Launcher

Beim Start installiert sich ein neuer Launcher sofort selbst (Fortschrittsfenster, Neustart), neue
BSO-Versionen kommen nach dem Anmelden - beides abschaltbar. Einstellungen: Nach Updates suchen /
Installieren und neu starten. Updates wieder nur von der Website; dotnet publish laedt hoch (release.ps1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-28 21:19:48 +01:00
co-authored by Claude Opus 5.5
parent 9cd5f2b153
commit c7a30e7fa3
11 changed files with 284 additions and 225 deletions
+4 -3
View File
@@ -178,14 +178,15 @@ nav button.on { background: var(--panel-2); color: var(--text); }
background: var(--panel-2); color: var(--accent-2); border: 1px solid var(--border); }
.release pre { margin: 0; white-space: pre-wrap; font: inherit; color: #c4ccd8; }
/* Update-Abfrage beim Start */
/* Launcher-Update beim Start (wie beim MSCO Launcher) */
.modal-back { position: fixed; inset: 0; z-index: 90; display: grid; place-items: center; padding: 24px; background: rgba(5, 7, 10, .72); }
.modal { width: min(520px, 100%); max-height: calc(100vh - 48px); overflow: auto; display: grid; gap: 14px; background: var(--panel);
border: 1px solid var(--border); border-radius: 18px; padding: 24px; box-shadow: 0 24px 64px rgba(0, 0, 0, .5); }
.modal h2 { margin: 0; }
.modal p { margin: 0; }
.update-item { display: grid; gap: 6px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px; padding: 12px 14px; }
.update-item pre { margin: 0; max-height: 160px; overflow: auto; white-space: pre-wrap; font: inherit; font-size: 13px; color: #c4ccd8; }
.update-box { text-align: center; justify-items: center; width: min(420px, 100%); }
.update-box img { width: 72px; height: 72px; }
.update-box .progress { width: 100%; text-align: left; }
/* ---------------------------------------------------------------- Einstellungen */
.settings { display: grid; gap: 14px; }
+66 -40
View File
@@ -141,6 +141,7 @@ function showApp(username) {
$("meName").textContent = username;
$("meAvatar").textContent = (username || "?").slice(0, 1).toUpperCase();
showPage("home");
autoUpdateMod();
}
// ---------------------------------------------------------------- Seiten
@@ -419,9 +420,28 @@ async function renderSettings(root) {
uninstall.addEventListener("click", () => busy(uninstall, () => call("uninstall")));
const langBox = h("div", { class: "lang-switch", "data-lang-switch": true });
// Launcher-Update von Hand (wie beim MSCO Launcher: "Nach Updates suchen" -> "Installieren und neu starten")
const launcherBox = h("div", {});
const check = h("button", { class: "btn small", type: "button" }, t("Nach Updates suchen"));
const install = h("button", { class: "btn small primary", type: "button" }, t("Installieren und neu starten"));
install.addEventListener("click", () => busy(install, async () => {
progressBox = h("div", { class: "progress" }, h("span", { class: "small muted" }), h("div", { class: "bar" }, h("div")));
launcherBox.append(progressBox);
try { await call("installLauncherUpdate"); install.textContent = t("Neustart …"); install.disabled = true; }
catch (e) { progressBox.remove(); throw e; }
}));
check.addEventListener("click", () => busy(check, async () => {
const u = await call("checkLauncherUpdate");
launcherBox.replaceChildren(u.available
? h("div", { class: "row" }, h("b", {}, t("Launcher {0} ist da", u.latest)), install)
: h("span", { class: "muted small" }, u.latest ? t("Der Launcher ist aktuell") : t("Keine Launcher-Version auf dem Server")));
}));
root.replaceChildren(h("h1", {}, t("Einstellungen")), h("div", { class: "settings" },
h("div", { class: "card" },
toggle(t("BSO automatisch aktualisieren"), t("Vor jedem Start die neueste Mod-Version installieren"), "autoUpdateMod", s?.settings.autoUpdateMod ?? true),
toggle(t("Launcher automatisch aktualisieren"), t("Beim Start wird eine neue Launcher-Version gleich installiert (er startet von selbst neu)"),
"autoUpdateLauncher", init.autoUpdateLauncher ?? true),
toggle(t("BSO automatisch aktualisieren"), t("Neue BSO-Versionen beim Start des Launchers und vor jedem Spielstart installieren"), "autoUpdateMod", s?.settings.autoUpdateMod ?? true),
toggle(t("Launcher offen lassen"), t("Sonst schliesst er sich nach dem Spielstart"), "keepOpen", s?.settings.keepOpen ?? false),
h("div", { class: "switch" }, h("span", {}, t("Sprache")), langBox)),
h("div", { class: "card" },
@@ -431,50 +451,56 @@ async function renderSettings(root) {
h("div", { class: "card" },
h("h3", {}, "Launcher"),
h("span", { class: "muted small" }, t("Version {0} · Server {1}", init.version, init.master)),
h("div", { class: "row" }, log, uninstall))));
launcherBox,
h("div", { class: "row" }, check, log, uninstall))));
renderLangSwitch(setLang);
}
// ---------------------------------------------------------------- Update-Abfrage
// ---------------------------------------------------------------- Updates (wie beim MSCO Launcher)
/** Bei jedem Start: gibt es einen neuen Launcher (oder eine neue Mod)? Dann fragen - "Später" fragt beim naechsten Start wieder. */
async function askForUpdates() {
/**
* Beim Start: gibt es einen neueren Launcher, gleich installieren und neu starten (Einstellung "Launcher automatisch
* aktualisieren"). Hoechstens 8 s auf die Pruefung warten - ohne Netz startet der Launcher normal.
*/
async function startupUpdate(init) {
let u;
try {
u = await Promise.race([call("checkLauncherUpdate"), new Promise((_, no) => setTimeout(() => no(new Error("timeout")), 8000))]);
} catch { return false; }
if (!u || !u.available) return false;
if (init.autoUpdateLauncher === false) { toast(t("Launcher {0} ist da – unter Einstellungen installieren", u.latest)); return false; }
const progress = h("div", { class: "progress" }, h("span", { class: "small muted" }), h("div", { class: "bar" }, h("div")));
const text = h("p", { class: "muted" }, t("Launcher wird auf {0} aktualisiert …", u.latest));
const overlay = h("div", { class: "modal-back" }, h("div", { class: "modal update-box" },
h("img", { src: "logo.svg", alt: "" }),
h("h2", {}, t("Launcher {0} ist da", u.latest)),
text, progress,
h("small", { class: "muted" }, t("Der Launcher startet danach von selbst neu."))));
document.body.append(overlay);
progressBox = progress;
try {
await call("installLauncherUpdate");
text.textContent = t("Neustart …");
return true;
} catch (e) {
overlay.remove();
progressBox = null;
toast(t("Launcher-Update fehlgeschlagen: {0}", e.message), true);
return false;
}
}
/** Nach dem Anmelden: eine neue BSO-Version gleich installieren (Einstellung "BSO automatisch aktualisieren"). */
async function autoUpdateMod() {
let s;
try { s = await call("status"); } catch { return; }
const mod = s.mod.installed && s.mod.updateAvailable && !s.game.running ? s.mod : null;
if (!s.launcherUpdate && !mod) return;
const item = (name, from, to, changelog) => h("div", { class: "update-item" },
h("div", {}, h("b", {}, name + " " + to), h("span", { class: "muted small" }, " " + t("installiert: {0}", from))),
h("pre", {}, changelog || t("Keine Beschreibung")));
const progress = h("div", { class: "progress hidden" }, h("span", { class: "small muted" }), h("div", { class: "bar" }, h("div")));
const later = h("button", { class: "btn", type: "button" }, t("Später"));
const now = h("button", { class: "btn primary", type: "button" }, t("Jetzt aktualisieren"));
const box = h("div", { class: "modal-back" }, h("div", { class: "modal", role: "dialog" },
h("h2", {}, t("Update verfügbar")),
h("p", { class: "muted" }, t("Beim Start gefunden – jetzt aktualisieren?")),
s.launcherUpdate ? item("Launcher", s.launcherVersion, s.launcherUpdate, s.launcherChangelog) : null,
mod ? item(t("BSO-Mod"), mod.installed, mod.latest, mod.changelog) : null,
progress,
h("div", { class: "row", style: "justify-content:flex-end" }, later, now)));
document.body.append(box);
now.focus();
later.addEventListener("click", () => box.remove());
now.addEventListener("click", async () => {
now.disabled = later.disabled = true;
progressBox = progress;
try {
if (mod) await call("installMod");
if (s.launcherUpdate) { await call("installLauncherUpdate"); toast(t("Launcher startet neu …")); return; } // beendet sich gleich
box.remove();
toast(t("BSO {0} installiert", mod.latest));
if (!$("app").classList.contains("hidden")) showPage(page);
} catch (e) {
toast(e.message, true);
now.disabled = later.disabled = false;
}
});
if (!s.settings.autoUpdateMod || !s.game.found || s.game.running || !s.mod.updateAvailable || !s.mod.latest) return;
try {
const r = await call("installMod");
toast(t("BSO {0} installiert", r.version));
if (page === "home") showPage("home");
} catch (e) { toast(e.message, true); }
}
// ---------------------------------------------------------------- Start
@@ -494,7 +520,7 @@ async function askForUpdates() {
$("meVersion").textContent += " · DEV";
}
$("authServer").textContent = t("Server: {0}", init.master.replace(/^https?:\/\//, ""));
askForUpdates(); // auch vor dem Anmelden
if (await startupUpdate(init)) return; // neue Version startet gleich
if (!init.loggedIn) { showAuth(); return; }
const me = await call("me");
if (me.loggedIn) showApp(me.username);
+11 -2
View File
@@ -45,8 +45,17 @@ const EN = {
"Freund entfernen": "Remove friend", "{0} als Freund entfernen?": "Remove {0} as a friend?", "eingeladen": "invited", "Einladen": "Invite",
"in der Lobby": "in the lobby", "Gesendet": "Sent", "wartet …": "pending …", "Zurückziehen": "Withdraw",
// Neuigkeiten
"Update verfügbar": "Update available", "Beim Start gefunden – jetzt aktualisieren?": "Found at startup – update now?",
"installiert: {0}": "installed: {0}", "Später": "Later", "BSO-Mod": "BSO mod",
"Launcher {0} ist da – unter Einstellungen installieren": "Launcher {0} is available – install it in the settings",
"Launcher wird auf {0} aktualisiert …": "Updating the launcher to {0} …", "Launcher {0} ist da": "Launcher {0} is available",
"Der Launcher startet danach von selbst neu.": "The launcher restarts by itself.", "Neustart …": "Restarting …",
"Launcher-Update fehlgeschlagen: {0}": "Launcher update failed: {0}", "Nach Updates suchen": "Check for updates",
"Installieren und neu starten": "Install and restart", "Der Launcher ist aktuell": "The launcher is up to date",
"Keine Launcher-Version auf dem Server": "No launcher version on the server",
"Launcher automatisch aktualisieren": "Update the launcher automatically",
"Beim Start wird eine neue Launcher-Version gleich installiert (er startet von selbst neu)":
"When the launcher starts, a new launcher version is installed right away (it restarts by itself)",
"Neue BSO-Versionen beim Start des Launchers und vor jedem Spielstart installieren":
"Installs new BSO versions when the launcher starts and before every game start",
"Keine Beschreibung": "No description", "Noch keine Versionen veroeffentlicht.": "No versions published yet.",
// Einstellungen
"Spielordner waehlen": "Choose game folder", "Spielordner gespeichert": "Game folder saved", "Protokoll-Ordner oeffnen": "Open log folder",