Upload via GUI (39 Dateien)
This commit is contained in:
+146
@@ -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 };
|
||||
Reference in New Issue
Block a user