Upload via GUI (39 Dateien)
This commit is contained in:
+76
-12
@@ -50,9 +50,18 @@ function newId() {
|
||||
const DEFAULT_SETTINGS = {
|
||||
instancesDir: null, // null => Standard (userData/instances)
|
||||
javaPath: '', // leer => "java" aus PATH
|
||||
autoJava: true, // fehlende Java-Version bei Bedarf selbst herunterladen
|
||||
autoBackupEnabled: false, // Sicherungen nach Zeitplan – standardmäßig aus
|
||||
autoBackupEvery: 'daily', // 'daily' | 'weekly'
|
||||
autoBackupBeforeLaunch: false, // zusätzlich vor jedem Spielstart
|
||||
autoBackupKeep: 5, // wie viele automatische Sicherungen behalten werden
|
||||
autoBackupLast: '', // Zeitpunkt der letzten automatischen Sicherung
|
||||
defaultMinMemMb: 2048,
|
||||
defaultMaxMemMb: 4096,
|
||||
azureClientId: '', // Azure-App-ID für den Microsoft-Login (Phase 3)
|
||||
curseforgeApiKey: '', // eigener CurseForge-API-Key (hat Vorrang)
|
||||
curseforgeProxyUrl: '', // sonst: Adresse der eigenen CurseForge-Weiterleitung
|
||||
curseforgeProxyToken: '', // optionales Geheimnis für diese Weiterleitung
|
||||
globalPreLaunch: '',
|
||||
globalPostExit: '',
|
||||
lastSelectedId: '', // zuletzt ausgewählte Instanz (beim Start wieder markieren)
|
||||
@@ -125,32 +134,84 @@ function modsIndexFile(id) {
|
||||
|
||||
// ---------- installierte Mods (Metadaten je Instanz) ----------
|
||||
|
||||
/*
|
||||
* Abgeschaltete Mods heißen "<name>.jar.disabled" – so übergeht Minecraft sie,
|
||||
* ohne dass die Datei verloren geht. In mods.json steht immer der eingeschaltete
|
||||
* Name; ob eine Mod an ist, entscheidet allein die Datei auf der Platte.
|
||||
*/
|
||||
const MOD_ENDUNG = /\.(jar|zip)$/i;
|
||||
const AUS_ENDUNG = /\.(jar|zip)\.disabled$/i;
|
||||
|
||||
function scanModsDir(id) {
|
||||
const dir = modsDir(id);
|
||||
if (!fs.existsSync(dir)) return [];
|
||||
return fs.readdirSync(dir, { withFileTypes: true })
|
||||
.filter((entry) => entry.isFile() && /\.(jar|zip)$/i.test(entry.name))
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map((entry) => ({
|
||||
const gefunden = [];
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
if (!entry.isFile()) continue;
|
||||
const aus = AUS_ENDUNG.test(entry.name);
|
||||
if (!aus && !MOD_ENDUNG.test(entry.name)) continue;
|
||||
const filename = aus ? entry.name.replace(/\.disabled$/i, '') : entry.name;
|
||||
gefunden.push({
|
||||
projectId: null,
|
||||
title: entry.name.replace(/\.(jar|zip)$/i, ''),
|
||||
filename: entry.name,
|
||||
title: filename.replace(MOD_ENDUNG, ''),
|
||||
filename,
|
||||
versionNumber: 'Importiert',
|
||||
}));
|
||||
enabled: !aus,
|
||||
});
|
||||
}
|
||||
gefunden.sort((a, b) => a.filename.localeCompare(b.filename));
|
||||
return gefunden;
|
||||
}
|
||||
|
||||
function listMods(id) {
|
||||
const aufDerPlatte = new Map(scanModsDir(id).map((m) => [m.filename, m]));
|
||||
const indexed = readJson(modsIndexFile(id), []);
|
||||
const merged = new Map();
|
||||
|
||||
for (const entry of indexed) {
|
||||
if (entry && entry.filename) merged.set(entry.filename, entry);
|
||||
if (!entry || !entry.filename) continue;
|
||||
const platte = aufDerPlatte.get(entry.filename);
|
||||
merged.set(entry.filename, Object.assign({}, entry, {
|
||||
enabled: platte ? platte.enabled : true,
|
||||
fehlt: !platte, // steht im Verzeichnis, Datei ist aber weg
|
||||
}));
|
||||
}
|
||||
for (const entry of scanModsDir(id)) {
|
||||
if (!merged.has(entry.filename)) merged.set(entry.filename, entry);
|
||||
for (const [name, entry] of aufDerPlatte) {
|
||||
if (!merged.has(name)) merged.set(name, Object.assign({ fehlt: false }, entry));
|
||||
}
|
||||
return [...merged.values()];
|
||||
}
|
||||
|
||||
/*
|
||||
* Schaltet eine Mod an oder aus, indem die Datei umbenannt wird.
|
||||
* -> { ok, enabled } oder { ok: false, reason }
|
||||
*/
|
||||
function setModEnabled(id, filename, enabled) {
|
||||
const dir = modsDir(id);
|
||||
const anPfad = path.join(dir, filename);
|
||||
const ausPfad = anPfad + '.disabled';
|
||||
// Pfad darf den mods-Ordner nicht verlassen
|
||||
if (path.dirname(anPfad) !== dir) return { ok: false, reason: 'bad-name' };
|
||||
|
||||
const vonPfad = enabled ? ausPfad : anPfad;
|
||||
const nachPfad = enabled ? anPfad : ausPfad;
|
||||
|
||||
if (fs.existsSync(anPfad) && fs.existsSync(ausPfad)) {
|
||||
// beide Fassungen da – nichts anfassen, sonst geht eine verloren
|
||||
return { ok: false, reason: 'both-exist' };
|
||||
}
|
||||
if (!fs.existsSync(vonPfad)) {
|
||||
if (fs.existsSync(nachPfad)) return { ok: true, enabled, unveraendert: true };
|
||||
return { ok: false, reason: 'not-found' };
|
||||
}
|
||||
try {
|
||||
fs.renameSync(vonPfad, nachPfad);
|
||||
return { ok: true, enabled };
|
||||
} catch (err) {
|
||||
return { ok: false, reason: err.message };
|
||||
}
|
||||
}
|
||||
|
||||
function addMod(id, entry) {
|
||||
const list = listMods(id).filter((m) => m.filename !== entry.filename);
|
||||
list.push(entry);
|
||||
@@ -161,9 +222,11 @@ function addMod(id, entry) {
|
||||
function removeMod(id, filename) {
|
||||
const list = listMods(id).filter((m) => m.filename !== filename);
|
||||
writeJson(modsIndexFile(id), list);
|
||||
// zugehörige .jar löschen
|
||||
// zugehörige Datei löschen – auch, wenn sie gerade abgeschaltet ist
|
||||
const jar = path.join(modsDir(id), filename);
|
||||
try { if (fs.existsSync(jar)) fs.unlinkSync(jar); } catch { /* egal */ }
|
||||
for (const p of [jar, jar + '.disabled']) {
|
||||
try { if (fs.existsSync(p)) fs.unlinkSync(p); } catch { /* egal */ }
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -366,6 +429,7 @@ module.exports = {
|
||||
listMods,
|
||||
addMod,
|
||||
removeMod,
|
||||
setModEnabled,
|
||||
listInstances,
|
||||
getInstance,
|
||||
findInstanceByName,
|
||||
|
||||
Reference in New Issue
Block a user