var express = require("express"); var qs = require("qs"); var proxyaddr = require("proxy-addr"); var {WebSocketServer} = require("ws"); var app = express(); app.set("trust proxy", "loopback"); app.use(express.static("frontend")); var server = app.listen(process.env.PORT || 924, process.env.ADDRESS); var wss = new WebSocketServer({server, clientTracking: true}); var chatlog = []; wss.on("connection", (ws, req) => { req.ip = proxyaddr(req, app.get("trust proxy")); req.query = qs.parse(req.url.substr(req.url.indexOf('?')+1)); function broadcast(msg, excludeSelf) { if (typeof msg == "object" && !(msg instanceof Buffer)) msg = JSON.stringify(msg); for (let ows of wss.clients) if (!(ows == ws && excludeSelf)) ows.send(msg); } function broadcastChat(message) { broadcast({type: "chat", message}); chatlog.push(message); if (chatlog.length >= 100) chatlog.shift(); } ws.user = { uid: req.ip, nick: req.query.nick || req.ip, color: [Math.floor(Math.random()*256),Math.floor(Math.random()*256),Math.floor(Math.random()*256)] } let t = Array.from(wss.clients).map(ws => ws.user.id); for (let i = 0; i < 256; i++) if (!t.includes(i)) { ws.user.id = i; break; } if (ws.user.id == null) return ws.close(); console.log("join", ws.user); ws.send(JSON.stringify({ type: "load", id: ws.user.id, uid: ws.user.uid, users: Array.from(wss.clients).map(x => x.user), chatlog })); broadcast({type: "join", id: ws.user.id, uid: ws.user.uid, nick: ws.user.nick, color: ws.user.color}, true); broadcastChat({type: "join", id: ws.user.id, uid: ws.user.uid, nick: ws.user.nick}); ws.on("close", () => { console.log("leave", ws.user); broadcast({type: "leave", id: ws.user.id, uid: ws.user.uid, nick: ws.user.nick, color: ws.user.color}, true); broadcastChat({type: "leave", id: ws.user.id, uid: ws.user.uid, nick: ws.user.nick}) }); ws.on("message", (msg, isBinary) => { if (isBinary) { broadcast(Buffer.concat([msg, Buffer.from([ws.user.id])]), true); } else { msg = msg.toString(); try { msg = JSON.parse(msg); } catch (error) { return } console.log(msg); switch (msg.type) { case "chat": broadcastChat({type: "message", content: msg.message, user: ws.user}); break; case "nick": ws.user.nick = msg.nick; broadcast({type: "nick", nick: ws.user.nick, id: ws.user.id, uid: ws.user.uid}); broadcastChat({type: "nick", nick: ws.user.nick, id: ws.user.id}); break; } } }); });