// Server settings: read/write a curated subset of server.properties. // Changes require an MC restart -- Save writes only, Save & restart bounces MC. "use strict"; import { api } from "./api.js"; const els = {}; // Map of input element ID -> server.properties key. Keeps the form ↔ file // translation in one place; new fields can be added by adding a row here + // matching elements in index.html. const FIELDS = [ { id: "ssfMotd", key: "motd", type: "string" }, { id: "ssfGamemode", key: "gamemode", type: "string" }, { id: "ssfDifficulty", key: "difficulty", type: "string" }, { id: "ssfViewDistance", key: "view-distance", type: "int" }, { id: "ssfSimulationDistance", key: "simulation-distance", type: "int" }, { id: "ssfMaxPlayers", key: "max-players", type: "int" }, { id: "ssfSpawnProtection", key: "spawn-protection", type: "int" }, { id: "ssfPvp", key: "pvp", type: "bool" }, { id: "ssfHardcore", key: "hardcore", type: "bool" }, { id: "ssfAllowFlight", key: "allow-flight", type: "bool" }, { id: "ssfWhiteList", key: "white-list", type: "bool" }, { id: "ssfEnforceWhitelist", key: "enforce-whitelist", type: "bool" }, { id: "ssfEnableCommandBlock", key: "enable-command-block", type: "bool" }, ]; function readForm() { const out = {}; for (const f of FIELDS) { const el = document.getElementById(f.id); if (!el) continue; if (f.type === "bool") out[f.key] = el.checked ? "true" : "false"; else if (f.type === "int") { const v = parseInt(el.value, 10); if (Number.isFinite(v)) out[f.key] = String(v); } else { out[f.key] = el.value; } } return out; } function writeForm(values) { for (const f of FIELDS) { const el = document.getElementById(f.id); if (!el) continue; const v = values[f.key]; if (v === undefined) continue; if (f.type === "bool") el.checked = (v === "true"); else el.value = v; } } function renderSummary(values) { document.getElementById("ssMotd").textContent = values["motd"] ?? "--"; document.getElementById("ssDifficulty").textContent = values["difficulty"] ?? "--"; document.getElementById("ssDistances").textContent = `${values["view-distance"] ?? "--"} / ${values["simulation-distance"] ?? "--"}`; document.getElementById("ssMaxPlayers").textContent = values["max-players"] ?? "--"; const wl = values["white-list"] === "true"; const enf = values["enforce-whitelist"] === "true"; document.getElementById("ssWhitelist").textContent = wl ? (enf ? "enforced" : "enabled") : "off"; } async function refresh() { try { const data = await api("/api/server/settings"); renderSummary(data.values || {}); writeForm(data.values || {}); } catch { /* ignore -- panel just shows last-known */ } } async function postSettings() { const payload = readForm(); const res = await fetch("/api/server/settings", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); return { ok: res.ok, body: await res.json().catch(() => ({})) }; } function showMsg(text, ok = false) { els.msg.className = ok ? "acct-msg ok" : "acct-msg"; els.msg.textContent = text; } export function setupSettings() { els.msg = document.getElementById("ssMsg"); els.save = document.getElementById("ssSave"); els.restart = document.getElementById("ssRestart"); if (!els.save) return; els.save.addEventListener("click", async () => { showMsg("Saving..."); els.save.disabled = true; try { const r = await postSettings(); if (!r.ok || r.body.ok === false) { showMsg(r.body.error || `Error ${r.body.status ?? ""}`); return; } showMsg(r.body.restartRequired ? "Saved. Restart for changes to take effect." : "Saved.", true); refresh(); } catch (e) { showMsg(e.message); } finally { els.save.disabled = false; } }); els.restart.addEventListener("click", async () => { if (!confirm("Save changes and restart the server now? Players will be disconnected briefly.")) return; showMsg("Saving + restarting..."); els.save.disabled = true; els.restart.disabled = true; try { const r = await postSettings(); if (!r.ok || r.body.ok === false) { showMsg(r.body.error || `Save failed: ${r.body.status ?? ""}`); return; } const rr = await fetch("/api/server/restart", { method: "POST" }); const rb = await rr.json().catch(() => ({})); if (!rr.ok || rb.ok === false) showMsg("Saved, but restart failed: " + (rb.error || rr.status)); else showMsg("Saved + restarting. New settings live in ~30s.", true); refresh(); } catch (e) { showMsg(e.message); } finally { els.save.disabled = false; els.restart.disabled = false; } }); refresh(); // Light poll: pick up out-of-band edits to server.properties. setInterval(refresh, 30000); }