Upload via GUI (39 Dateien)

This commit is contained in:
Git Manager GUI
2026-08-02 17:01:18 +02:00
parent a803f297ea
commit eff6403dd5
26 changed files with 3536 additions and 134 deletions
+146
View File
@@ -0,0 +1,146 @@
'use strict';
/*
* servers.js – Serverliste einer Instanz (servers.dat)
* -----------------------------------------------------
* Minecraft legt die Liste der gespeicherten Server als unkomprimiertes NBT
* unter <spielordner>/servers.dat ab. Aufbau:
*
* Compound "" Wurzel
* List "servers" Liste von Compounds
* String "name" Anzeigename
* String "ip" Adresse (optional mit :Port)
* String "icon" Base64-PNG des Serversymbols (optional)
* Byte "acceptTextures" Ressourcenpakete annehmen (optional)
*
* Beim Schreiben wird die vorhandene Datei zuerst gesichert – eine kaputte
* servers.dat würde alle gespeicherten Server kosten.
*/
const fs = require('fs');
const path = require('path');
const nbt = require('./nbt');
function datei(gameDir) {
return path.join(gameDir, 'servers.dat');
}
/*
* Liest die Serverliste.
* -> { ok, server: [{ name, ip, icon, acceptTextures }] }
*/
function lies(gameDir) {
const p = datei(gameDir);
if (!fs.existsSync(p)) return { ok: true, server: [], leer: true };
let roh;
try { roh = fs.readFileSync(p); } catch (err) { return { ok: false, message: err.message }; }
if (!roh.length) return { ok: true, server: [], leer: true };
try {
const { wert } = nbt.lese(roh);
const liste = Array.isArray(wert.servers) ? wert.servers : [];
return {
ok: true,
server: liste.map((s) => ({
name: String(s.name || ''),
ip: String(s.ip || ''),
icon: typeof s.icon === 'string' ? s.icon : '',
acceptTextures: Number(s.acceptTextures || 0),
})),
};
} catch (err) {
return { ok: false, message: 'servers.dat konnte nicht gelesen werden: ' + err.message };
}
}
/*
* Schreibt die Serverliste zurück.
* Die alte Datei wird vorher als servers.dat.bak gesichert.
*/
function schreibe(gameDir, server) {
const p = datei(gameDir);
try {
fs.mkdirSync(gameDir, { recursive: true });
const eintraege = (server || []).map((s) => {
const c = {
__typen: { name: nbt.TAG.STRING, ip: nbt.TAG.STRING },
name: String(s.name || ''),
ip: String(s.ip || ''),
};
// icon und acceptTextures nur mitschreiben, wenn vorhanden –
// so wie Minecraft es selbst tut
if (s.icon) { c.__typen.icon = nbt.TAG.STRING; c.icon = String(s.icon); }
if (s.acceptTextures) { c.__typen.acceptTextures = nbt.TAG.BYTE; c.acceptTextures = Number(s.acceptTextures); }
return c;
});
const liste = eintraege;
liste.__typ = nbt.TAG.COMPOUND;
const wurzel = { __typen: { servers: nbt.TAG.LIST }, servers: liste };
const daten = nbt.schreibe('', wurzel);
// erst sichern, dann über eine Zwischendatei ersetzen
if (fs.existsSync(p)) {
try { fs.copyFileSync(p, p + '.bak'); } catch { /* Sicherung ist Kür */ }
}
const tmp = p + '.neu';
fs.writeFileSync(tmp, daten);
fs.renameSync(tmp, p);
return { ok: true, anzahl: eintraege.length };
} catch (err) {
return { ok: false, message: 'servers.dat konnte nicht geschrieben werden: ' + err.message };
}
}
// Adresse normalisieren und grob prüfen
function pruefeAdresse(ip) {
const s = String(ip || '').trim();
if (!s) return { ok: false, message: 'Bitte eine Serveradresse angeben.' };
if (/\s/.test(s)) return { ok: false, message: 'Die Adresse darf keine Leerzeichen enthalten.' };
const m = s.match(/^([^:]+)(?::(\d+))?$/);
if (!m) return { ok: false, message: 'Die Adresse sieht nicht wie ein Server aus.' };
if (m[2] && (Number(m[2]) < 1 || Number(m[2]) > 65535)) {
return { ok: false, message: 'Der Port muss zwischen 1 und 65535 liegen.' };
}
return { ok: true, ip: s };
}
function fuegeHinzu(gameDir, eintrag) {
const geprueft = pruefeAdresse(eintrag && eintrag.ip);
if (!geprueft.ok) return geprueft;
const gelesen = lies(gameDir);
if (!gelesen.ok) return gelesen;
const liste = gelesen.server;
// dieselbe Adresse nicht doppelt aufnehmen
if (liste.some((s) => s.ip.toLowerCase() === geprueft.ip.toLowerCase())) {
return { ok: false, message: 'Dieser Server steht bereits in der Liste.' };
}
liste.push({
name: String((eintrag && eintrag.name) || geprueft.ip).trim() || geprueft.ip,
ip: geprueft.ip,
icon: '',
acceptTextures: 0,
});
return schreibe(gameDir, liste);
}
function entferne(gameDir, ip) {
const gelesen = lies(gameDir);
if (!gelesen.ok) return gelesen;
const vorher = gelesen.server.length;
const liste = gelesen.server.filter((s) => s.ip.toLowerCase() !== String(ip || '').toLowerCase());
if (liste.length === vorher) return { ok: false, message: 'Server nicht gefunden.' };
return schreibe(gameDir, liste);
}
/*
* Einen Server in eine andere Instanz übertragen – der eigentliche Zweck:
* denselben Server nicht in jeder Instanz neu eintippen zu müssen.
*/
function uebertrage(zielGameDir, eintrag) {
return fuegeHinzu(zielGameDir, eintrag);
}
module.exports = { lies, schreibe, fuegeHinzu, entferne, uebertrage, pruefeAdresse, datei };