// Node Server Script // Version 1.3 (8:14 PM Fri October 27, 2017) // Written by: James D. Miller var app = require('express')(); var http = require('http').Server(app); var io = require('socket.io')(http); const port = process.env.PORT || 3000; app.get('/', function(req, res) { // In a browser, if you set the URL to localhost:3000, you'll get this page: res.sendfile('links.html'); }); // Put various client data and maps in a global. var cD = {}; cD.userIndex = 0; // Map: userName[ socket.id] cD.userName = {}; // Map: id[ userName] cD.id = {}; // Map: room[ socket.id] cD.room = {}; // Map: hostID[ roomName] cD.hostID = {}; io.on('connection', function(socket) { cD.userIndex += 1; console.log(''); console.log('Their count=' + io.engine.clientsCount + ', my index=' + cD.userIndex + '.'); // Two maps // First, name this user. cD.userName[socket.id] = 'u' + cD.userIndex; cD.id['u' + cD.userIndex] = socket.id; console.log('New client: '+ cD.userName[socket.id] +', '+ socket.id + '.'); // Tell the new user their network name. io.to(socket.id).emit('your name is', cD.userName[socket.id]); // Example of how to parse out the query string if it is sent in the connection attempt from the client. //console.log('socket.handshake.query.par1=' + socket.handshake.query['par1'] + ", par2=" + socket.handshake.query['par2']); // Now set up the various listeners. I know this seems a little odd, but these listeners // need to be defined each time this connection event fires, i.e. for each socket. // Echo test... socket.on('echo-from-Client-to-Server', function(msg) { if (msg == 'server') { // This bounces off the SERVER and goes right back to the client. io.to(socket.id).emit('echo-from-Server-to-Client', 'server'); } else if (msg == 'host') { // Send this first to the host (the scenic route). Include the id of the client so that we know where to send it // when it bounces off the host. io.to( cD.hostID[ cD.room[ socket.id]]).emit('echo-from-Server-to-Host', socket.id); } }); socket.on('echo-from-Host-to-Server', function(msg) { var socket_id = msg; // Now that this has come back from the HOST, complete the trip and send this to the originating client. io.to(socket_id).emit('echo-from-Server-to-Client', 'host'); }); // Broadcast the incoming chat messages that come in. socket.on('chat message', function(msg) { //console.log('message: ' + JSON.parse(msg).text); //console.log('message: ' + msg); //console.log('Room:' + cD.room[ socket.id] + ', id:' + socket.id + ", name:" + cD.userName[socket.id] + ", msg:" + msg); // General emit to the room. io.to( cD.room[ socket.id]).emit('chat message', msg + " (" + cD.userName[socket.id] + ")"); // Special emit, only to Host //io.to(hD.id).emit('chat message', 'OnlyToHost: ' + msg); }); // Signaling in support of WebRTC. socket.on('signaling message', function(msg) { var signal_message = JSON.parse(msg); if (signal_message.to == 'host') { var target = cD.hostID[ cD.room[ socket.id]]; } else { var target = cD.id[ signal_message.to]; } //console.log('sm=' + JSON.stringify(signal_message)); // Relay the message (emit) to the target user client. io.to( target).emit("signaling message", msg); }); // Send mouse and keyboard states to the host client. socket.on('client-mK-event', function(msg) { //console.log('Client Mouse: ' + JSON.parse(msg).mouseX_px + "," + JSON.parse(msg).mouseY_px); // Determine the id of the room-host for this client. Then send data to the host for that room. // socket.id --> room --> room host. var hostID = cD.hostID[ cD.room[ socket.id]]; //console.log('room='+cD.room[ socket.id]+", hID="+cD.hostID[ cD.room[ socket.id]]); // StH: Server to Host io.to( hostID).emit('client-mK-StH-event', msg); }); socket.on('roomJoin', function(msg) { var msgParsed = JSON.parse( msg); var roomName = msgParsed.roomName; var requestStream = msgParsed.requestStream; //console.log('requestStream='+requestStream+', roomName='+roomName); // Check to make sure the room has a host. if (cD.hostID[ roomName]) { socket.join(roomName); cD.room[ socket.id] = roomName; console.log('Room ' + roomName + ' joined by ' + cD.userName[ socket.id] + '.'); io.to(socket.id).emit('chat message', 'You have joined room ' + cD.room[socket.id] + ' and your client name is '+cD.userName[socket.id]+'.'); // Give the host the name of the new user so a new game client can be created. io.to( cD.hostID[ roomName]).emit('new-game-client', JSON.stringify({'clientName':cD.userName[socket.id], 'requestStream':requestStream})); // Chat this to the host. io.to( cD.hostID[ roomName]).emit('chat message', cD.userName[socket.id]+ ' is a new client in '+roomName+'.'); } else { io.to(socket.id).emit('chat message', 'Sorry, there is no host yet for room ' + roomName + '.'); } }); socket.on('roomJoinAsHost', function(msg) { var roomName = msg; // Should check if the room already has a host. if (cD.hostID[ roomName]) { io.to(socket.id).emit('chat message', 'Sorry, there is already a host for room ' + roomName + '.'); } else { socket.join(roomName); cD.room[ socket.id] = roomName; console.log('Room ' + roomName + ' joined by ' + cD.userName[ socket.id] + '.'); io.to(socket.id).emit('chat message', 'You have joined room ' + cD.room[socket.id] + ' and your client name is '+cD.userName[socket.id]+'.'); // Set this user as the host for this room. cD.hostID[ cD.room[ socket.id]] = socket.id; console.log('User '+ cD.userName[ socket.id] +' identified as host for room ' + cD.room[ socket.id] + '.'); io.to(socket.id).emit('chat message', 'You are the host of room ' + cD.room[ socket.id] + '.'); } }); // This "disconnect" event is fired by the server. socket.on('disconnect', function() { if (cD.userName[ socket.id]) { // Report at the server. console.log(' '); var message = cD.userName[ socket.id] + ' has disconnected'; console.log( message + ' (by self, ' + socket.id + ').'); // Report to the room host. var hostID = cD.hostID[ cD.room[ socket.id]]; io.to( hostID).emit('chat message', message+'.'); io.to( hostID).emit('client-disconnected', cD.userName[ socket.id]); // Remove this user from the maps. delete cD.id[ cD.userName[ socket.id]]; delete cD.userName[ socket.id]; // If this is the host disconnecting... if (hostID == socket.id) { delete cD.hostID[ cD.room[ socket.id]]; } delete cD.room[ socket.id]; } }); socket.on('clientDisconnectByHost', function(msg) { var clientName = msg; var clientID = cD.id[ clientName]; // Send disconnect message to the client. //console.log('in clientDisconnectByHost, msg=' + msg + ', clientID=' + clientID); io.to( clientID).emit('disconnectByServer', clientName); // Don't do the following. It will disconnect the host socket. Not what we want here! //socket.disconnect(); }); socket.on('okDisconnectMe', function(msg) { // This event indicated the non-host client got the clientDisconnectByHost message (see above) and // agrees to go peacefully. var clientName = msg; var clientID = cD.id[ clientName]; // Report this at the server. console.log(' '); var message = clientName + ' has disconnected'; //if (clientName) console.log( message + ' (by host, '+clientID+').'); // Report to the room host. var hostID = cD.hostID[ cD.room[ clientID]]; io.to( hostID).emit('chat message', message); io.to( hostID).emit('client-disconnected', clientName); // Remove this user from the maps. delete cD.id[ cD.userName[ clientID]]; delete cD.userName[ clientID]; delete cD.room[ clientID]; //Finally, go ahead and disconnect this client's socket. //console.log('just before socket.disconnect()'); socket.disconnect(); }); socket.on('shutDown-p2p-deleteClient', function( msg) { //console.log('in server, shutDown-p2p-deleteClient, msg='+msg); var clientName = msg; var clientID = cD.id[ clientName]; var hostID = cD.hostID[ cD.room[ clientID]]; io.to( hostID).emit('shutDown-p2p-deleteClient', clientName); }); socket.on('command-from-host-to-all-clients', function( msg) { // General emit to the room. io.to( cD.room[ socket.id]).emit('command-from-host-to-all-clients', msg); }); }); http.listen(port, function() { console.log('listening on *:' + port); });