Server 2.10.3: browse the MSCO mod list in the web panel
Mods -> MSCO mod list: search the approved mods from msco.mbm.wtf/mods and add or update one with a click. The server lists and downloads only from the official keymaster (never from a URL in the answer), checks the SHA-256 and stores it in data/mods like an uploaded mod. Admins only. 6 new tests (154 pass). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+44
-1
@@ -761,6 +761,10 @@ function renderWorld(page) {
|
||||
|
||||
function renderMods(page) {
|
||||
const rows = h("tbody");
|
||||
const catalogRows = h("tbody", {}, h("tr", {}, h("td", { colspan: 4, class: "empty" }, "Loading …")));
|
||||
const search = h("input", { type: "search", placeholder: "Search mods …", maxlength: 60 });
|
||||
let catalog = null;
|
||||
search.addEventListener("input", () => drawCatalog());
|
||||
const fileInput = h("input", { type: "file", accept: ".dll" });
|
||||
const kb = (n) => (n / 1024).toFixed(n < 102400 ? 1 : 0) + " KB";
|
||||
const upload = async () => {
|
||||
@@ -798,7 +802,45 @@ function renderMods(page) {
|
||||
h("div", { class: "section" },
|
||||
h("div", { class: "section-head" }, h("h2", {}, "Server mods"), h("span", { class: "muted" }, "players get these when they join")),
|
||||
h("div", { class: "table-wrap" }, h("table", {},
|
||||
h("thead", {}, h("tr", {}, h("th", {}, "Mod"), h("th", {}, "Version"), h("th", {}, "Size"), h("th", {}, "SHA-256"), h("th", {}, ""))), rows))));
|
||||
h("thead", {}, h("tr", {}, h("th", {}, "Mod"), h("th", {}, "Version"), h("th", {}, "Size"), h("th", {}, "SHA-256"), h("th", {}, ""))), rows))),
|
||||
h("div", { class: "section" },
|
||||
h("div", { class: "section-head" }, h("h2", {}, "MSCO mod list"), h("span", { class: "muted" }, "mods by players, checked by the MSCO admins")),
|
||||
h("div", { class: "section-body" }, h("div", { class: "row" }, search,
|
||||
h("button", { class: "btn ghost small", type: "button", onclick: openList }, "msco.mbm.wtf/mods"))),
|
||||
h("div", { class: "table-wrap" }, h("table", {},
|
||||
h("thead", {}, h("tr", {}, h("th", {}, "Mod"), h("th", {}, "Version"), h("th", {}, "Downloads"), h("th", {}, ""))), catalogRows))));
|
||||
|
||||
function openList() { window.open("https://msco.mbm.wtf/mods", "_blank", "noopener"); }
|
||||
|
||||
async function loadCatalog() {
|
||||
try { catalog = await api("GET", "mods/catalog"); }
|
||||
catch (e) { catalogRows.replaceChildren(h("tr", {}, h("td", { colspan: 4, class: "empty" }, e.message))); return; }
|
||||
drawCatalog();
|
||||
}
|
||||
function drawCatalog() {
|
||||
if (!catalog) return;
|
||||
const q = search.value.trim().toLowerCase();
|
||||
const list = catalog.filter((m) => !q || (m.name + " " + m.description + " " + m.author).toLowerCase().includes(q));
|
||||
if (!catalog.length) { catalogRows.replaceChildren(h("tr", {}, h("td", { colspan: 4, class: "empty" }, "The MSCO mod list is empty."))); return; }
|
||||
if (!list.length) { catalogRows.replaceChildren(h("tr", {}, h("td", { colspan: 4, class: "empty" }, "No mod matches the search."))); return; }
|
||||
catalogRows.replaceChildren(...list.map((m) => h("tr", {},
|
||||
h("td", {}, h("b", {}, m.name), h("span", { class: "muted" }, " · " + m.author),
|
||||
m.description ? h("div", { class: "muted" }, m.description) : null),
|
||||
h("td", {}, m.version, h("div", { class: "muted" }, kb(m.size))),
|
||||
h("td", {}, String(m.downloads)),
|
||||
h("td", {}, h("div", { class: "actions" },
|
||||
m.upToDate ? h("span", { class: "muted" }, "Added")
|
||||
: h("button", { class: "btn small primary", type: "button", onclick: (e) => install(m, e.target) }, m.installed ? "Update" : "Add to server"))))));
|
||||
}
|
||||
async function install(m, button) {
|
||||
const ok = await modal({ title: (m.installed ? "Update " : "Add ") + m.name + "?", confirm: m.installed ? "Update" : "Add",
|
||||
text: "Every player gets this mod offered when they join and decides whether to install it. It was checked by the MSCO admins - still only add mods you want on your server." });
|
||||
if (!ok) return;
|
||||
button.disabled = true;
|
||||
await act(api("POST", "mods/catalog/" + encodeURIComponent(m.id) + "/install"));
|
||||
load();
|
||||
loadCatalog();
|
||||
}
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
@@ -812,6 +854,7 @@ function renderMods(page) {
|
||||
} catch (e) { toast(e.message, true); }
|
||||
}
|
||||
load();
|
||||
loadCatalog();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ Benutzer
|
||||
|
||||
Reference in New Issue
Block a user