// ── The logic half ──────────────────────────────────────────────────────── // // Shapes what the database returned into what a client should see. It is a // separate file from the SQL so that it is testable without a database, and the // suite next door tests it that way. // // The one decision worth pointing at: **a module answers when the game is // unreachable rather than failing.** The website is the internet-facing process // and your game is not; the game being down, or the sidecar being mid-restart, // is an ordinary Tuesday and not an error condition for the site. A page that // renders "offline, last seen 20 minutes ago" is right; a page that 500s because // a socket is closed is a module that has made the site's availability depend on // the game's. const db = require('./worldStatus.db') // Past this, the last thing the game said stops being news and starts being // history. Presentation, so the number lives with the code that shapes the // response rather than in the client. const STALE_AFTER_MS = 5 * 60 * 1000 /** * The public view of the world's status. * * Never throws for an absent or stale row: both are answers, not failures. */ async function getPublicStatus(now = Date.now()) { const row = await db.getStatus() if (!row) { // No row at all means the schema fragment has not been replayed — a fresh // install whose first boot has not finished. Report it as offline rather // than as an error; the next boot fixes it. return { online: false, players: 0, worldName: null, updatedAt: null, stale: true } } const updatedAt = row.updatedAt ? new Date(row.updatedAt) : null const stale = !updatedAt || now - updatedAt.getTime() > STALE_AFTER_MS return { // A stale row cannot claim the world is up. The row says what was true when // it was written, and nothing has written it since. online: Boolean(row.online) && !stale, players: stale ? 0 : Number(row.players) || 0, worldName: row.worldName || null, updatedAt: updatedAt ? updatedAt.toISOString() : null, stale, } } module.exports = { getPublicStatus, STALE_AFTER_MS }