// LifeTown Online — entry point: static file server + WebSocket upgrade. import http from 'node:http'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { WebSocketServer } from 'ws'; import { GameServer } from './game.mjs'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const DIST = path.join(__dirname, '..', 'client', 'dist'); const PORT = Number(process.env.GAME_PORT || 7788); const MIME = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript', '.mjs': 'text/javascript', '.css': 'text/css', '.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg', '.svg': 'image/svg+xml', '.ico': 'image/x-icon', '.wasm': 'application/wasm', '.woff2': 'font/woff2', '.mp3': 'audio/mpeg', '.ogg': 'audio/ogg', '.webp': 'image/webp', }; function serveStatic(req, res) { let urlPath = decodeURIComponent(new URL(req.url, 'http://x').pathname); if (urlPath === '/') urlPath = '/index.html'; let filePath = path.normalize(path.join(DIST, urlPath)); if (!filePath.startsWith(DIST)) { res.writeHead(403); res.end('Forbidden'); return; } if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) { // SPA fallback filePath = path.join(DIST, 'index.html'); if (!fs.existsSync(filePath)) { res.writeHead(503); res.end('LifeTown client is not built yet. Run: npm run build'); return; } } const ext = path.extname(filePath).toLowerCase(); res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream', 'Cache-Control': ext === '.html' ? 'no-cache' : 'public, max-age=3600', }); fs.createReadStream(filePath).pipe(res); } const server = http.createServer(serveStatic); // Single WebSocket endpoint: /ws const wss = new WebSocketServer({ noServer: true }); server.on('upgrade', (req, socket, head) => { const { pathname } = new URL(req.url, 'http://x'); if (pathname === '/ws') { wss.handleUpgrade(req, socket, head, (ws) => game.onConnection(ws)); } else { socket.destroy(); } }); const game = new GameServer(wss); server.listen(PORT, () => { console.log(`[LifeTown] server listening on http://127.0.0.1:${PORT}`); });