const API_BASE = 'https://api.mcstatus.io/v2/status/java/'; const REFRESH_MS = 60000; // 60s async function loadServerList() { try { const res = await fetch('./server-list.json', { cache: 'no-store' }); if (!res.ok) throw new Error('Could not load server-list.json'); const data = await res.json(); // Only active servers under "minecraft" key return (data.minecraft || []).filter(s => s.active); } catch (err) { console.error(err); document.getElementById('servers').innerHTML = '
Failed to load server-list.json
'; return []; } } function esc(s) { return s ? String(s).replace(//g, '>') : ""; } async function fetchStatus(host, port) { const address = `${host}:${port}`; const url = `${API_BASE}${encodeURIComponent(address)}?query=true&timeout=5`; try { const resp = await fetch(url, { cache: 'no-store' }); if (!resp.ok) return { online: false, rawError: resp.status }; return await resp.json(); } catch (err) { return { online: false, error: err.message }; } } function updateCard(server, card, data) { const idSafe = server.host.replace(/[:.\s]/g, '_'); const statusEl = card.querySelector(`#status-${idSafe}`); const motdEl = card.querySelector(`#motd-${idSafe}`); const modsListEl = card.querySelector(`#modslist-${idSafe}`); const metaEl = card.querySelector(`#meta-${idSafe}`); const iconEl = card.querySelector(`#icon-${idSafe}`); if (data && data.online) { const players = (data.players && data.players.online != null) ? `${data.players.online}/${data.players.max ?? '?'}` : 'N/A'; const modsText = server.mods ? 'enabled' : 'disabled'; const whitelistText = server.whitelist ? 'enabled' : 'disabled'; let motd = Array.isArray(data.motd?.clean) ? data.motd.clean.join(' ') : (data.motd?.clean || data.motd?.stripped || data.motd?.raw || '(no motd)'); const ipLine = server.host; const iconUrl = `https://api.mcstatus.io/v2/icon/${encodeURIComponent(server.host + ':' + server.port)}?timeout=2`; statusEl.innerHTML = `online   |   players ${esc(players)}   |   whitelist ${esc(whitelistText)}   |   mods ${esc(modsText)}`; motdEl.innerHTML = `MOTD: ${motd}`; if (server.mods && server.modlist) { modsListEl.innerHTML = `
`; } else { modsListEl.innerHTML = ''; } metaEl.innerHTML = `server ip: ${esc(ipLine)}`; iconEl.src = iconUrl; iconEl.style.visibility = ''; iconEl.onerror = () => iconEl.style.visibility = 'hidden'; } else { statusEl.innerHTML = `offline` + (data?.rawError || data?.error ? `
${esc(data.rawError || data.error)}
` : ''); motdEl.innerHTML = ``; modsListEl.innerHTML = ''; metaEl.innerHTML = `server ip: ${esc(server.host)}`; iconEl.style.visibility = 'hidden'; } } async function updateSingle(server, card) { const idSafe = server.host.replace(/[:.\s]/g, '_'); card.querySelector(`#status-${idSafe}`).innerHTML = 'Loading…'; const data = await fetchStatus(server.host, server.port); updateCard(server, card, data); } async function init() { const list = await loadServerList(); const container = document.getElementById('servers'); container.innerHTML = ''; const cards = {}; list.forEach(s => { const idSafe = s.host.replace(/[:.\s]/g, '_'); const card = document.createElement('div'); card.className = 'server'; card.innerHTML = `
${esc(s.name)}
${esc(s.name)} server icon
Loading…
`; container.appendChild(card); cards[s.host] = { server: s, card }; }); for (const { server, card } of Object.values(cards)) { updateSingle(server, card); } setInterval(() => { for (const { server, card } of Object.values(cards)) { updateSingle(server, card); } }, REFRESH_MS); } init();