Upload via GUI (39 Dateien)
This commit is contained in:
+145
-19
@@ -1214,7 +1214,7 @@ function applyIcons(root = document) {
|
||||
|
||||
// ---------- Instanz-Kacheln ----------
|
||||
const SINGLE_SELECTION_BUTTON_IDS = [
|
||||
'p-play', 'p-offline', 'p-edit', 'p-notes', 'p-mods', 'p-worlds', 'p-screens', 'p-logs', 'p-servers',
|
||||
'p-play', 'p-offline', 'p-edit', 'p-notes', 'p-mods', 'p-worlds', 'p-screens', 'p-logs', 'p-servers', 'p-modpack',
|
||||
'p-mcfolder', 'p-configfolder', 'p-instfolder', 'p-shortcut', 'p-export', 'p-copy',
|
||||
];
|
||||
|
||||
@@ -1292,6 +1292,9 @@ async function refreshSelectionPanel() {
|
||||
panelE.classList.add('hidden');
|
||||
panelC.classList.remove('hidden');
|
||||
|
||||
// "Modpack aktualisieren" nur bei Instanzen, die aus einem Modrinth-Paket stammen
|
||||
el('p-modpack').classList.toggle('hidden', !(inst.modpack && inst.modpack.projectId));
|
||||
|
||||
const iconData = inst.customImage ? await window.api.instanceIcon(id) : null;
|
||||
if (iconData) el('p-icon').innerHTML = `<img src="${iconData}" alt="" />`;
|
||||
else { el('p-icon').innerHTML = iconOf(inst.icon, 60); }
|
||||
@@ -1611,6 +1614,7 @@ async function openEdit(inst) {
|
||||
el('f-min-mem').value = inst.java.minMemMb || '';
|
||||
el('f-max-mem').value = inst.java.maxMemMb || '';
|
||||
zeigeSpeicherHinweis();
|
||||
fuelleInstanzJava(inst);
|
||||
el('f-extra-args').value = inst.java.extraArgs || '';
|
||||
el('f-prelaunch').value = inst.preLaunchCommand || '';
|
||||
el('f-postexit').value = inst.postExitCommand || '';
|
||||
@@ -1653,6 +1657,7 @@ async function saveInstance() {
|
||||
let targetId = editingId;
|
||||
if (editingId) {
|
||||
payload.java = {
|
||||
path: el('f-java').value.trim(),
|
||||
minMemMb: minMem,
|
||||
maxMemMb: maxMem,
|
||||
extraArgs: el('f-extra-args').value.trim(),
|
||||
@@ -1711,6 +1716,36 @@ async function launch(id, accountId) {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Java je Instanz ----------
|
||||
|
||||
async function fuelleInstanzJava(inst) {
|
||||
const feld = el('f-java');
|
||||
if (!feld) return;
|
||||
feld.value = (inst && inst.java && inst.java.path) || '';
|
||||
|
||||
const sel = el('f-java-detected');
|
||||
sel.innerHTML = '<option value="">– gefundene Java-Installationen –</option>';
|
||||
try {
|
||||
for (const j of await window.api.listJava()) {
|
||||
const o = document.createElement('option');
|
||||
o.value = j.path;
|
||||
o.textContent = `Java ${j.major} (${j.arch}) – ${j.path}`;
|
||||
sel.appendChild(o);
|
||||
}
|
||||
} catch { /* Liste bleibt leer */ }
|
||||
sel.value = feld.value && [...sel.options].some((o) => o.value === feld.value) ? feld.value : '';
|
||||
|
||||
// Der häufigste Stolperstein: global ist eine feste Fassung eingetragen,
|
||||
// die dann auch für alte Minecraft-Versionen genommen wird.
|
||||
try {
|
||||
const s = await window.api.getSettings();
|
||||
el('f-java-hint').textContent = s.javaPath
|
||||
? `Achtung: In den Einstellungen ist ${s.javaPath} fest vorgegeben und gilt für alle Instanzen. `
|
||||
+ 'Trage hier etwas ein, wenn diese Instanz eine andere Fassung braucht – ältere Minecraft-Versionen verlangen oft Java 8.'
|
||||
: 'Leer lassen: Der Launcher nimmt die Fassung, die diese Minecraft-Version verlangt, und lädt sie bei Bedarf selbst.';
|
||||
} catch { /* egal */ }
|
||||
}
|
||||
|
||||
// ---------- Arbeitsspeicher ----------
|
||||
|
||||
let systemSpeicher = null;
|
||||
@@ -1728,6 +1763,52 @@ async function zeigeSpeicherHinweis() {
|
||||
+ `(Vorschlag: ${empfohlen} MB).`;
|
||||
}
|
||||
|
||||
// ---------- Modpack aktualisieren ----------
|
||||
|
||||
let modpackNeueVersion = null;
|
||||
|
||||
async function openModpackUpdate() {
|
||||
if (!currentDetailId) return;
|
||||
modpackNeueVersion = null;
|
||||
el('mp-status').textContent = 'Suche nach einer neueren Fassung …';
|
||||
el('mp-kern').classList.add('hidden');
|
||||
el('mp-do').classList.add('hidden');
|
||||
show('modal-modpack');
|
||||
|
||||
const res = await window.api.modpackCheck(currentDetailId);
|
||||
if (!res.ok) { el('mp-status').textContent = '✖ ' + (res.message || 'Suche fehlgeschlagen.'); return; }
|
||||
if (!res.verfuegbar) {
|
||||
el('mp-status').textContent = `${res.name || 'Das Modpack'} ist auf dem neuesten Stand (${res.aktuelleNummer}).`;
|
||||
return;
|
||||
}
|
||||
|
||||
modpackNeueVersion = res.neueVersionId;
|
||||
el('mp-status').textContent = `Für ${res.name || 'dieses Modpack'} gibt es eine neuere Fassung.`;
|
||||
const zeile = (t, w) => (w ? `<div class="crash-zeile"><div class="crash-titel">${t}</div><div class="crash-wert">${escapeHtml(w)}</div></div>` : '');
|
||||
el('mp-kern').innerHTML = zeile('Installiert', res.aktuelleNummer) + zeile('Neu', res.neueNummer)
|
||||
+ zeile('Minecraft', res.spielVersion)
|
||||
+ (res.hinweise ? zeile('Änderungen', res.hinweise.slice(0, 400)) : '');
|
||||
el('mp-kern').classList.remove('hidden');
|
||||
el('mp-do').classList.remove('hidden');
|
||||
}
|
||||
|
||||
async function doModpackUpdate() {
|
||||
if (!modpackNeueVersion) return;
|
||||
const btn = el('mp-do');
|
||||
btn.disabled = true;
|
||||
el('mp-status').textContent = 'Wird aktualisiert – das kann einige Minuten dauern …';
|
||||
const res = await window.api.modpackUpdate(currentDetailId, modpackNeueVersion);
|
||||
btn.disabled = false;
|
||||
if (!res.ok) { el('mp-status').textContent = '✖ ' + (res.message || 'Aktualisierung fehlgeschlagen.'); return; }
|
||||
|
||||
el('mp-status').textContent = `✔ Auf ${res.neueNummer} aktualisiert: ${res.eingespielt} Dateien eingespielt`
|
||||
+ (res.entfernt ? `, ${res.entfernt} entfernt` : '') + '.';
|
||||
el('mp-do').classList.add('hidden');
|
||||
toast('Modpack aktualisiert.');
|
||||
await reload();
|
||||
await selectInstance(currentDetailId);
|
||||
}
|
||||
|
||||
// ---------- Serverliste ----------
|
||||
|
||||
let zuUebertragenderServer = null;
|
||||
@@ -2166,27 +2247,57 @@ function renderContentList(boxId, items, sub, emoji) {
|
||||
}
|
||||
|
||||
// ---------- Mod-Browser ----------
|
||||
|
||||
// Welche Inhaltsart wird gerade gezeigt: mod | resourcepack | shader
|
||||
let modArt = 'mod';
|
||||
const ART_LABEL = { mod: 'Mods', resourcepack: 'Ressourcenpakete', shader: 'Shader' };
|
||||
const ART_SUCHE = {
|
||||
mod: 'Mods durchsuchen … (z. B. Sodium, JEI, Create)',
|
||||
resourcepack: 'Ressourcenpakete durchsuchen … (z. B. Faithful, Bare Bones)',
|
||||
shader: 'Shader durchsuchen … (z. B. Complementary, BSL)',
|
||||
};
|
||||
// Nur Mods brauchen einen Loader – Ressourcenpakete und Shader laufen auch in Vanilla
|
||||
const ART_BRAUCHT_LOADER = { mod: true, resourcepack: false, shader: false };
|
||||
|
||||
async function openMods() {
|
||||
const inst = await window.api.getInstance(currentDetailId);
|
||||
if (!inst) return;
|
||||
el('m-title').textContent = 'Mods – ' + inst.name;
|
||||
modArt = 'mod';
|
||||
el('m-art').value = 'mod';
|
||||
await zeigeModAnsicht(inst);
|
||||
show('modal-mods');
|
||||
}
|
||||
|
||||
// Wechsel der Inhaltsart: alles neu aufbauen
|
||||
async function modArtGewechselt() {
|
||||
modArt = el('m-art').value || 'mod';
|
||||
const inst = await window.api.getInstance(currentDetailId);
|
||||
if (inst) await zeigeModAnsicht(inst);
|
||||
}
|
||||
|
||||
async function zeigeModAnsicht(inst) {
|
||||
el('m-title').textContent = ART_LABEL[modArt] + ' – ' + inst.name;
|
||||
const loaderTxt = LOADER_LABEL[inst.minecraft.loader] || inst.minecraft.loader;
|
||||
el('m-sub').textContent = (inst.minecraft.version || 'keine Version') + ' · ' + loaderTxt;
|
||||
el('m-query').placeholder = ART_SUCHE[modArt];
|
||||
|
||||
const warn = el('m-warn');
|
||||
const blocked = !inst.minecraft.version || inst.minecraft.loader === 'vanilla';
|
||||
const brauchtLoader = ART_BRAUCHT_LOADER[modArt];
|
||||
const blocked = !inst.minecraft.version || (brauchtLoader && inst.minecraft.loader === 'vanilla');
|
||||
if (blocked) {
|
||||
warn.classList.remove('hidden');
|
||||
warn.textContent = !inst.minecraft.version
|
||||
? '⚠ Diese Instanz hat keine Minecraft-Version. Bitte zuerst im Bearbeiten-Dialog eine Version wählen.'
|
||||
: '⚠ Vanilla-Instanzen unterstützen keine Mods. Wähle beim Bearbeiten einen Mod-Loader (Fabric/Forge/NeoForge/Quilt).';
|
||||
: '⚠ Vanilla-Instanzen unterstützen keine Mods. Wähle beim Bearbeiten einen Mod-Loader (Fabric/Forge/NeoForge/Quilt). Ressourcenpakete und Shader gehen auch ohne.';
|
||||
} else {
|
||||
warn.classList.add('hidden');
|
||||
}
|
||||
['m-query', 'm-search-btn', 'm-sort', 'm-category'].forEach((id) => { el(id).disabled = blocked; });
|
||||
// Kategorien gelten nur für Mods
|
||||
el('m-category').disabled = blocked || modArt !== 'mod';
|
||||
el('m-query').value = '';
|
||||
el('m-more').classList.add('hidden');
|
||||
// Ergebnisse der vorigen Instanz nicht mitschleppen
|
||||
// Ergebnisse der vorigen Ansicht nicht mitschleppen
|
||||
modUpdates = new Map();
|
||||
setUpdateStatus('');
|
||||
el('m-apply-updates').classList.add('hidden');
|
||||
@@ -2194,15 +2305,16 @@ async function openMods() {
|
||||
switchModTab('catalog');
|
||||
|
||||
await refreshInstalled();
|
||||
show('modal-mods');
|
||||
if (blocked) {
|
||||
const box = el('m-results');
|
||||
box.innerHTML = '<div class="mods-empty">Für Mods braucht diese Instanz einen Mod-Loader.</div>';
|
||||
box.innerHTML = `<div class="mods-empty">${!inst.minecraft.version
|
||||
? 'Ohne Minecraft-Version geht hier nichts.'
|
||||
: 'Für Mods braucht diese Instanz einen Mod-Loader.'}</div>`;
|
||||
const b = document.createElement('button');
|
||||
b.className = 'btn btn-primary';
|
||||
b.style.margin = '0 auto';
|
||||
b.style.display = 'block';
|
||||
b.textContent = 'Mod-Loader hinzufügen';
|
||||
b.textContent = inst.minecraft.version ? 'Mod-Loader hinzufügen' : 'Version wählen';
|
||||
b.addEventListener('click', async () => {
|
||||
hide('modal-mods');
|
||||
const i = await window.api.getInstance(currentDetailId);
|
||||
@@ -2210,7 +2322,7 @@ async function openMods() {
|
||||
});
|
||||
box.appendChild(b);
|
||||
} else {
|
||||
await loadCatalog(false); // beliebte Mods sofort anzeigen
|
||||
await loadCatalog(false); // beliebte Einträge sofort anzeigen
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2226,7 +2338,7 @@ function switchModTab(which) {
|
||||
let modUpdates = new Map();
|
||||
|
||||
async function refreshInstalled() {
|
||||
const list = await window.api.modList(currentDetailId);
|
||||
const list = await window.api.modList(currentDetailId, modArt);
|
||||
currentModsInstalled = new Set(list.map((m) => m.projectId));
|
||||
el('m-installed-count').textContent = String(list.length);
|
||||
const box = el('m-installed');
|
||||
@@ -2265,7 +2377,7 @@ async function refreshInstalled() {
|
||||
: 'Datei auf .jar.disabled umbenennen – Minecraft lädt sie dann nicht';
|
||||
schalter.addEventListener('click', async () => {
|
||||
schalter.disabled = true;
|
||||
const res = await window.api.modSetEnabled(currentDetailId, m.filename, aus);
|
||||
const res = await window.api.modSetEnabled(currentDetailId, m.filename, aus, modArt);
|
||||
if (res.ok) toast((aus ? 'Eingeschaltet: ' : 'Ausgeschaltet: ') + m.title);
|
||||
else toast(res.message || 'Umschalten fehlgeschlagen.', true);
|
||||
await refreshInstalled();
|
||||
@@ -2277,7 +2389,7 @@ async function refreshInstalled() {
|
||||
btn.className = 'btn btn-danger btn-mini';
|
||||
btn.textContent = 'Entfernen';
|
||||
btn.addEventListener('click', async () => {
|
||||
await window.api.modRemove(currentDetailId, m.filename);
|
||||
await window.api.modRemove(currentDetailId, m.filename, modArt);
|
||||
toast('Entfernt: ' + m.title);
|
||||
await refreshInstalled();
|
||||
});
|
||||
@@ -2296,7 +2408,7 @@ async function checkModUpdates() {
|
||||
el('m-apply-updates').classList.add('hidden');
|
||||
setUpdateStatus('Suche …');
|
||||
try {
|
||||
const res = await window.api.modCheckUpdates(currentDetailId);
|
||||
const res = await window.api.modCheckUpdates(currentDetailId, modArt);
|
||||
if (!res.ok) { setUpdateStatus('✖ ' + (res.message || 'Suche fehlgeschlagen.')); return; }
|
||||
|
||||
modUpdates = new Map((res.treffer || []).map((t) => [t.projectId, t]));
|
||||
@@ -2321,14 +2433,15 @@ async function applyModUpdates() {
|
||||
btn.disabled = true;
|
||||
setUpdateStatus('Wird aktualisiert …');
|
||||
try {
|
||||
const res = await window.api.modApplyUpdates(currentDetailId, [...modUpdates.keys()]);
|
||||
const res = await window.api.modApplyUpdates(currentDetailId, [...modUpdates.keys()], modArt);
|
||||
if (!res.ok) { setUpdateStatus('✖ ' + (res.message || 'Aktualisierung fehlgeschlagen.')); return; }
|
||||
modUpdates = new Map();
|
||||
await refreshInstalled();
|
||||
const n = res.erneuert.length;
|
||||
setUpdateStatus(n + (n === 1 ? ' Mod aktualisiert' : ' Mods aktualisiert')
|
||||
const wort = n === 1 ? 'Eintrag' : 'Einträge';
|
||||
setUpdateStatus(`${n} ${wort} aktualisiert`
|
||||
+ (res.fehler.length ? ' · ' + res.fehler.length + ' fehlgeschlagen' : ''));
|
||||
if (n) toast(n + (n === 1 ? ' Mod aktualisiert.' : ' Mods aktualisiert.'));
|
||||
if (n) toast(`${n} ${wort} aktualisiert.`);
|
||||
for (const f of res.fehler) toast(f.title + ': ' + f.message, true);
|
||||
btn.classList.add('hidden');
|
||||
} finally {
|
||||
@@ -2339,7 +2452,9 @@ async function applyModUpdates() {
|
||||
let modOffset = 0;
|
||||
async function loadCatalog(append) {
|
||||
const inst = await window.api.getInstance(currentDetailId);
|
||||
if (!inst || !inst.minecraft.version || inst.minecraft.loader === 'vanilla') return;
|
||||
if (!inst || !inst.minecraft.version) return;
|
||||
// Loader nur für Mods verlangen
|
||||
if (ART_BRAUCHT_LOADER[modArt] && inst.minecraft.loader === 'vanilla') return;
|
||||
const box = el('m-results');
|
||||
if (!append) { modOffset = 0; box.innerHTML = '<div class="mods-empty">Lade …</div>'; el('m-more').classList.add('hidden'); }
|
||||
try {
|
||||
@@ -2347,10 +2462,11 @@ async function loadCatalog(append) {
|
||||
query: el('m-query').value.trim(),
|
||||
gameVersion: inst.minecraft.version,
|
||||
loader: inst.minecraft.loader,
|
||||
category: el('m-category').value,
|
||||
category: modArt === 'mod' ? el('m-category').value : '',
|
||||
sort: el('m-sort').value,
|
||||
limit: 30,
|
||||
offset: modOffset,
|
||||
art: modArt,
|
||||
});
|
||||
if (!append) box.innerHTML = '';
|
||||
if (!res.hits.length && !append) {
|
||||
@@ -2412,7 +2528,7 @@ function buildResultItem(mod) {
|
||||
async function installMod(mod, btn, item) {
|
||||
btn.disabled = true; btn.textContent = 'Lädt …';
|
||||
if (item) { item.classList.add('installing'); item.classList.remove('clickable'); }
|
||||
const res = await window.api.modInstall(currentDetailId, { projectId: mod.projectId, title: mod.title });
|
||||
const res = await window.api.modInstall(currentDetailId, { projectId: mod.projectId, title: mod.title }, modArt);
|
||||
if (item) item.classList.remove('installing');
|
||||
if (res.ok) {
|
||||
const dep = res.depCount ? ` (+${res.depCount} Abhängigkeit${res.depCount > 1 ? 'en' : ''})` : '';
|
||||
@@ -3187,6 +3303,8 @@ function wire() {
|
||||
el('p-screens').addEventListener('click', openScreens);
|
||||
el('p-logs').addEventListener('click', openLogs);
|
||||
el('p-servers').addEventListener('click', openServers);
|
||||
el('p-modpack').addEventListener('click', openModpackUpdate);
|
||||
el('mp-do').addEventListener('click', doModpackUpdate);
|
||||
el('srv-add').addEventListener('click', addServer);
|
||||
el('srv-ip').addEventListener('keydown', (e) => { if (e.key === 'Enter') addServer(); });
|
||||
el('srv-copy-do').addEventListener('click', doServerCopy);
|
||||
@@ -3266,6 +3384,7 @@ function wire() {
|
||||
el('m-more').addEventListener('click', () => loadCatalog(true));
|
||||
el('m-tab-catalog').addEventListener('click', () => switchModTab('catalog'));
|
||||
el('m-tab-installed').addEventListener('click', () => switchModTab('installed'));
|
||||
el('m-art').addEventListener('change', modArtGewechselt);
|
||||
el('m-open-folder').addEventListener('click', () => window.api.openGameDir(currentDetailId));
|
||||
// Automatische Sicherung: unaufdringlich melden, Fehler deutlich
|
||||
window.api.onAutoBackup((d) => {
|
||||
@@ -3314,6 +3433,13 @@ function wire() {
|
||||
el('s-bgskin').addEventListener('change', previewSkin);
|
||||
el('s-bgskin-pose').addEventListener('change', previewSkin);
|
||||
el('s-scan-java').addEventListener('click', scanJava);
|
||||
el('f-java-detected').addEventListener('change', (e) => {
|
||||
if (e.target.value) el('f-java').value = e.target.value;
|
||||
});
|
||||
el('f-java-pick').addEventListener('click', async () => {
|
||||
const p = await window.api.pickJava();
|
||||
if (p) { el('f-java').value = p; el('f-java-detected').value = ''; }
|
||||
});
|
||||
el('s-java-detected').addEventListener('change', async (e) => {
|
||||
if (e.target.value) { el('s-java').value = e.target.value; await refreshJavaStatus(e.target.value); }
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user