Upload via GUI (324 Dateien)

This commit is contained in:
2026-11-09 13:17:57 +00:00
parent 12b12fb09d
commit 300af2ed39
2 changed files with 87 additions and 3 deletions
+59 -3
View File
@@ -222,6 +222,45 @@ let letzterUpdateCheck = { checking: true };
// Hauptfenster hat seine Empfänger registriert – erst dann den Update-Hinweis schicken
const hauptfensterBereit = new Promise((resolve) => ipcMain.once('renderer:ready', resolve));
/*
* Update und laufendes Spiel
* --------------------------
* Ein Update beendet den Launcher (und der Installer ersetzt dessen Dateien). Das darf nie
* passieren, solange Minecraft läuft: Das Update wird erst installiert, wenn kein Spiel mehr
* läuft. Damit es trotzdem nicht untergeht, bekommt der Spieler im Spiel eine Einblendung
* (wie bei den Freunde-Hinweisen), und nach dem letzten Spielende öffnet sich der Update-Dialog.
*/
const spielLaeuft = () => laufendeInstanzen.size > 0;
const SPIEL_LAEUFT_TEXT = 'Minecraft läuft noch – das Update wird erst installiert, wenn kein Spiel mehr läuft. Bitte zuerst das Spiel beenden.';
let updateWartetAufSpielende = false; // Hinweis kam im Spiel -> nach Spielende den Dialog zeigen
let angekuendigteVersion = null; // Dialog schon gezeigt, nicht bei jeder Prüfung wieder
const updateImSpielGemeldet = new Set(); // pid des Spiels, das den Hinweis schon bekam
// Einblendung auf dem Minecraft-Fenster, einmal je Spielstart
async function meldeUpdateImSpiel() {
const info = pendingUpdateInfo;
if (!info || !info.available || !spielLaeuft()) return;
for (const pid of [...laufendePid.values()]) {
if (updateImSpielGemeldet.has(pid)) continue;
// false, solange das Spielfenster noch nicht da ist – dann beim nächsten Durchlauf erneut
const gezeigt = await zeigeIngameOverlay('AeroMC-Update', [
`AeroMC ${info.latestVersion || 'Update'} ist verfügbar.`,
'Bitte nach dem Spiel installieren.',
], pid, 10000).catch(() => false);
// nur vermerken, wenn dieses Spiel noch läuft – sonst bliebe nach seinem Ende ein falscher Zustand zurück
if (gezeigt && spielLaeuft() && [...laufendePid.values()].includes(pid)) { updateImSpielGemeldet.add(pid); updateWartetAufSpielende = true; }
}
}
// Dialog zeigen – aber nie, während ein Spiel läuft (er würde dem Spiel den Fokus nehmen)
function kuendigeUpdateAn(info) {
if (!info || !info.available) return;
if (spielLaeuft()) { updateWartetAufSpielende = true; meldeUpdateImSpiel().catch(() => {}); return; }
if (info.latestVersion && info.latestVersion === angekuendigteVersion) return;
angekuendigteVersion = info.latestVersion || true;
broadcast('update:available', info);
}
async function pruefeAufUpdates() {
letzterUpdateCheck = { checking: true };
broadcast('update:checked', letzterUpdateCheck);
@@ -357,7 +396,15 @@ ipcMain.handle('recording:getServer', (_e, id) => letzteQuickPlayServer.get(id)
const laufendePid = new Map(); // instanceId -> pid des zuletzt gestarteten Minecraft-Prozesses
// Instanzen, deren Spiel gerade läuft (für den pulsierenden Rand der Karte)
const laufendeInstanzen = new Set();
function meldeLaufende() { broadcast('game:laufend', [...laufendeInstanzen]); }
function meldeLaufende() {
broadcast('game:laufend', [...laufendeInstanzen]);
// letztes Spiel beendet und ein Update wartet -> jetzt den Update-Dialog zeigen
if (!spielLaeuft() && updateWartetAufSpielende) {
updateWartetAufSpielende = false;
updateImSpielGemeldet.clear();
kuendigeUpdateAn(pendingUpdateInfo);
}
}
ipcMain.handle('game:laufend', () => [...laufendeInstanzen]);
const kontoPid = new Map(); // kontoId -> pid, für den Ingame-Toast: der muss auf DEM Fenster
// des prüfenden Kontos erscheinen, nicht auf irgendeinem laufenden Minecraft-Fenster
@@ -1169,8 +1216,13 @@ app.whenReady().then(() => {
pruefeAufUpdates().then(async (info) => {
if (!info.available) return;
await hauptfensterBereit;
broadcast('update:available', info);
kuendigeUpdateAn(info);
}).catch(() => {});
// Der Launcher bleibt oft tagelang offen: alle 3 Stunden erneut prüfen. Läuft ein Spiel,
// gibt es nur die Einblendung im Spiel, den Dialog erst nach Spielende.
setInterval(() => { pruefeAufUpdates().then(kuendigeUpdateAn).catch(() => {}); }, 3 * 60 * 60 * 1000);
// Hinweis im Spiel, sobald das Spielfenster da ist (auch für ein später gestartetes Spiel)
setInterval(() => { meldeUpdateImSpiel().catch(() => {}); }, 30000);
// Anmelde-Dienste automatisch überwachen (sofort + alle 2 Minuten)
setTimeout(() => checkAuthServices(), 2500);
@@ -1230,7 +1282,7 @@ ipcMain.handle('settings:save', (_e, patch) => {
// Beta-Teilnahme umgeschaltet -> gleich neu nach Updates suchen
if (patch && Object.prototype.hasOwnProperty.call(patch, 'betaTeilnahme') && !!patch.betaTeilnahme !== betaStand) {
betaStand = !!patch.betaTeilnahme;
pruefeAufUpdates().then((info) => { if (info.available) broadcast('update:available', info); }).catch(() => {});
pruefeAufUpdates().then((info) => { if (info.available) { angekuendigteVersion = null; kuendigeUpdateAn(info); } }).catch(() => {});
}
if (patch && Object.prototype.hasOwnProperty.call(patch, 'friendsEnabled')) {
aktualisiereFreundeTastenkuerzel();
@@ -5575,11 +5627,15 @@ ipcMain.handle('update:lastCheck', () => letzterUpdateCheck);
// beliebige Datei als "Update" ausführen lassen.
ipcMain.handle('update:install', async () => {
try {
// Nie während Minecraft läuft: Der Launcher beendet sich für das Update
if (spielLaeuft()) { updateWartetAufSpielende = true; return { ok: false, spielLaeuft: true, message: SPIEL_LAEUFT_TEXT }; }
const asset = pendingUpdateInfo && pendingUpdateInfo.available ? pendingUpdateInfo.asset : null;
if (!asset) throw new Error('Kein geprüftes Update vorhanden – bitte erneut nach Updates suchen.');
const file = await updater.downloadAndInstall(asset, (p) => {
broadcast('update:progress', p);
});
// Während des Downloads kann ein Spiel gestartet worden sein – die Datei bleibt liegen
if (spielLaeuft()) { updateWartetAufSpielende = true; return { ok: false, spielLaeuft: true, message: SPIEL_LAEUFT_TEXT }; }
const res = await updater.applyUpdate(file);
// Portable: der Tausch klappt erst, wenn der Launcher die EXE freigibt
setTimeout(() => app.quit(), 1200);
+28
View File
@@ -1711,7 +1711,24 @@ function showUpdateDialog(info) {
: (channel
? `Für den Kanal ${channel} liegt in diesem Release keine passende Datei bereit. Bitte im Release nachsehen und von Hand aktualisieren.`
: 'Die Paketart dieses Launchers lässt sich nicht sicher bestimmen. Bitte das Update von Hand aus dem Release laden.');
updHintNormal = el('upd-hint').textContent;
show('modal-update');
aktualisiereUpdateKnopf();
}
// Solange Minecraft läuft, wird nicht installiert: Der Launcher beendet sich für das Update
let updHintNormal = '';
let updInstalliertGerade = false;
async function aktualisiereUpdateKnopf() {
if (!pendingUpdate || !pendingUpdate.asset || updInstalliertGerade) return;
let laeuft = false;
try { laeuft = ((await window.api.laufendeInstanzen()) || []).length > 0; } catch { /* dann nicht sperren */ }
const btn = el('upd-install');
btn.disabled = laeuft;
btn.textContent = laeuft ? 'Minecraft läuft noch' : 'Installieren';
el('upd-hint').textContent = laeuft
? 'Das Update wird erst installiert, wenn kein Minecraft mehr läuft. Beende das Spiel – danach kannst du hier auf „Installieren“ klicken.'
: updHintNormal;
}
// Links in den Update-Hinweisen im Browser öffnen, nie im Launcher-Fenster
@@ -1731,10 +1748,19 @@ async function startUpdate() {
const btn = el('upd-install');
btn.disabled = true;
btn.textContent = 'Wird installiert …';
updInstalliertGerade = true;
el('upd-progress').classList.remove('hidden');
el('upd-status').textContent = 'Wird heruntergeladen …';
const res = await window.api.updateInstall();
if (!res.ok) updInstalliertGerade = false;
if (!res.ok && res.spielLaeuft) {
// Spiel lief schon oder ist während des Downloads gestartet worden
el('upd-progress').classList.add('hidden');
el('upd-status').textContent = '';
aktualisiereUpdateKnopf();
return;
}
if (res.ok) {
el('upd-status').textContent = res.kind === 'portable'
? 'Launcher schließt sich, wird ersetzt und startet automatisch neu.'
@@ -7849,6 +7875,8 @@ window.addEventListener('DOMContentLoaded', async () => {
}
el('upd-notes').addEventListener('click', updateLinkKlick);
window.api.onLaufendeInstanzen(setzeLaufende);
// Update-Dialog offen: Knopf freigeben, sobald das letzte Spiel beendet ist (bzw. sperren, wenn eins startet)
window.api.onLaufendeInstanzen(() => { if (!el('modal-update').classList.contains('hidden')) aktualisiereUpdateKnopf(); });
window.api.laufendeInstanzen().then(setzeLaufende).catch(() => {});
el('grid').addEventListener('mousemove', kippeZurMaus);
baueLichtpunkte();