99 lines
2.4 KiB
JavaScript
99 lines
2.4 KiB
JavaScript
const { WebSocketServer, WebSocket } = require('ws');
|
|
const http = require('http');
|
|
|
|
const PORT = process.env.PORT || 8080;
|
|
|
|
const server = http.createServer((req, res) => {
|
|
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
res.end('SyncWatch server running\n');
|
|
});
|
|
|
|
const wss = new WebSocketServer({ server });
|
|
|
|
// Map<roomCode, Set<WebSocket>>
|
|
const rooms = new Map();
|
|
|
|
function generateCode() {
|
|
let code;
|
|
do {
|
|
code = Math.floor(100000 + Math.random() * 900000).toString();
|
|
} while (rooms.has(code));
|
|
return code;
|
|
}
|
|
|
|
function broadcast(room, message, exclude) {
|
|
const data = JSON.stringify(message);
|
|
for (const client of room) {
|
|
if (client !== exclude && client.readyState === WebSocket.OPEN) {
|
|
client.send(data);
|
|
}
|
|
}
|
|
}
|
|
|
|
wss.on('connection', (ws) => {
|
|
ws.roomCode = null;
|
|
|
|
ws.on('message', (raw) => {
|
|
let msg;
|
|
try { msg = JSON.parse(raw); } catch { return; }
|
|
|
|
switch (msg.type) {
|
|
case 'create': {
|
|
if (ws.roomCode) return;
|
|
const code = generateCode();
|
|
rooms.set(code, new Set([ws]));
|
|
ws.roomCode = code;
|
|
ws.send(JSON.stringify({ type: 'created', code }));
|
|
break;
|
|
}
|
|
|
|
case 'join': {
|
|
if (ws.roomCode) return;
|
|
const code = String(msg.code || '');
|
|
const room = rooms.get(code);
|
|
if (!room) {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'Room not found' }));
|
|
return;
|
|
}
|
|
if (room.size >= 2) {
|
|
ws.send(JSON.stringify({ type: 'error', message: 'Room is full' }));
|
|
return;
|
|
}
|
|
room.add(ws);
|
|
ws.roomCode = code;
|
|
ws.send(JSON.stringify({ type: 'joined', code, memberCount: room.size }));
|
|
broadcast(room, { type: 'peer_joined' }, ws);
|
|
break;
|
|
}
|
|
|
|
case 'sync': {
|
|
if (!ws.roomCode) return;
|
|
const room = rooms.get(ws.roomCode);
|
|
if (!room) return;
|
|
broadcast(room, { type: 'sync', action: msg.action, time: msg.time }, ws);
|
|
break;
|
|
}
|
|
|
|
case 'ping':
|
|
ws.send(JSON.stringify({ type: 'pong' }));
|
|
break;
|
|
}
|
|
});
|
|
|
|
ws.on('close', () => {
|
|
if (!ws.roomCode) return;
|
|
const room = rooms.get(ws.roomCode);
|
|
if (!room) return;
|
|
room.delete(ws);
|
|
if (room.size === 0) {
|
|
rooms.delete(ws.roomCode);
|
|
} else {
|
|
broadcast(room, { type: 'peer_left' }, null);
|
|
}
|
|
});
|
|
});
|
|
|
|
server.listen(PORT, () => {
|
|
console.log(`SyncWatch server listening on port ${PORT}`);
|
|
});
|