/** * OpenSpace WebSocket related communication method * @constructor socket_server * @param {library} io socket.io library * @param {object} setting The configuration object * @param {library} openspace lib/openspace library * @property {library} io socket.io library * @property {object} config The configuration object stored from the parameter * @property {array} userList The list of connected clients * @property {array} cameraAccess The list of clients who can access camera * @property {array} planetAccess The list of clients who can access planet change * @property {array} adminID The socket id of admin */ class socket_server { constructor(io,config,openspace) { this.io = io; this.config = config; this.userList = ['undefined']; this.cameraAccess = [config.superUser]; this.planetAccess = [config.superUser]; var socketServer = this; var time = new Date(); var altitudeEvent; var frame = 100 io.on('connection', function(socket){ var address = socket.id; socket.emit('reconnect user') socketServer.config.SocketLog['connection'] && console.log('New connection from ' + address); /** * Function to create user * @param {string} msg The user name */ socket.on('create user', function(msg){ if(socketServer.userList.includes(msg)) { socket.emit('alert', "Sorry, this username is already registered!!"); } else { socketServer.userList.push(msg) socket.username = msg; if(msg==config.superUser) { socketServer.adminID = socket.id socketServer.config.SocketLog['account'] && console.log('new admin is created!! - ' + msg); } else { socketServer.config.SocketLog['account'] && console.log('new user is created!! - ' + msg); } // https://stackoverflow.com/questions/37690419/not-allowed-to-load-local-resources socket.emit('redirect', "/user.html"); } // socketServer.initAdmin('refresh',socket) socketServer.initAdmin('add',socket) }); /** * Function to change a user's ability to access OpenSpace control * @param {array} msg * @param {array} msg.type The event type (add gamepad/remove gamepad) * @param {array} msg.id The user socket id */ socket.on('access user', function(msg){ if (!socketServer.checkAccess(socket,' change access')) return var username = io.sockets.sockets[msg.id].username socketServer.config.SocketLog['account'] && console.log('access user '+username+', msg: ') socketServer.config.SocketLog['account'] && console.log(msg) if(msg.type=='add gamepad') { socketServer.cameraAccess.push(username) // console.log(socketServer.cameraAccess) socketServer.initUser(socket,io,msg.id,'#mainContent','<a href="control.html" class="ui green label">You can access the control</a>') } else if(msg.type=='remove gamepad') { socketServer.cameraAccess = socketServer.cameraAccess.filter(v => v !== username); socketServer.initUser(socket,io,msg.id,'#mainContent','<a class="ui red label">You CANNOT access the control</a>') io.to(msg.id).emit('redirect', '/user.html'); } // if(msg.type=='add plane') { // socketServer.planetAccess.push(username) // console.log(socketServer.planetAccess) // socketServer.initUser(socket,io,msg.id,'#secContent','<a class="ui green label">You can access planet navigation</a>') // } else if(msg.type=='remove plane') { // console.log('removing...') // socketServer.planetAccess = socketServer.planetAccess.filter(v => v !== username); // socketServer.initUser(socket,io,msg.id,'#secContent','<a class="ui red label">You CANNOT access planet navigation</a>') // } // socketServer.initAdmin('remove',io.sockets.sockets[msg.id]) // socketServer.initAdmin('add',io.sockets.sockets[msg.id]) }); /** * Function to logout a user * @param {string} msg The user name */ socket.on('logout user', function(msg){ // https://stackoverflow.com/questions/9792927/javascript-array-search-and-remove-string if(socketServer.userList.includes(msg)) { socketServer.userList = socketServer.userList.filter(v => v !== msg); if(socket.username==config.superUser) { socketServer.adminID = false; } socket.username = ''; } }); /** * Function to check if a user can access OpenSpace control frontend * @param {string} msg The user name */ socket.on('check user', function(msg){ if(!socketServer.cameraAccess.includes(msg)) socket.emit('redirect', "/user.html"); }); /** * Function to reconnect a user * @param {string} msg The user name */ socket.on('reconnect user', function(msg){ if(!socketServer.userList.includes(msg)) { socketServer.userList.push(msg) socket.username = msg; socketServer.config.SocketLog['reconnect'] && console.log(socket.username + " is reconnected!") } if(socket.username==config.superUser) { socketServer.adminID = socket.id } if(socketServer.cameraAccess.includes(msg) && socket.username!=config.superUser) { socket.emit('content setup', {target:'#mainContent',content:'<a href="control.html" class="ui green label">You can access the control</a>'}) } else if (!socketServer.cameraAccess.includes(msg) && socket.username!=config.superUser) { socket.emit('content setup', {target:'#mainContent',content:'<a class="ui red label">You CANNOT access the control</a>'}) } // if(socketServer.planetAccess.includes(msg) && socket.username!=config.superUser) { // socket.emit('admin setup', {target:'#secContent',content:'<a class="ui green label">You can access planet naviation</a>'}) // } else if (!socketServer.planetAccess.includes(msg) && socket.username!=config.superUser) { // socket.emit('admin setup', {target:'#secContent',content:'<a class="ui red label">You CANNOT access planet naviation</a>'}) // } if(socket.username==config.superUser) socketServer.initAdmin('init') socketServer.initAdmin('add',socket) }); /** * Function to redirect a user * @param {string} msg The user name */ socket.on('redirect', function(msg){ socket.emit('redirect', msg); }); /** * Function to handle OpenSpace alitude start event * @param {string} msg 'up' or 'down' */ socket.on('altitude start', function(msg){ if (!socketServer.checkAccess(socket,'altitude')) return var pressTime = new Date(); socketServer.config.OpenSpaceLog['Altitude'] && console.log('message: altitude start - ' + msg); socketServer.config.OpenSpaceLog['Altitude'] && console.log('message: moving altitude... - ' + msg); var type = (msg=='up') ? true:false var value = (msg=='up') ? -1: 1; openspace.moveAltitude(type,value) altitudeEvent = setInterval(function(){ var passingTime = new Date(); var type = (msg=='up') ? true:false var value = (msg=='up') ? -1: 1; value = value+value*(passingTime-pressTime)/500; openspace.moveAltitude(type,value) socketServer.config.OpenSpaceLog['Altitude'] && console.log('message: moving altitude... - ' + msg + ' since last time' + (passingTime-pressTime)); }, frame); }); /** * Function to handle OpenSpace alitude end event * @param {string} msg 'up' or 'down' */ socket.on('altitude end', function(msg){ socketServer.config.OpenSpaceLog['Altitude'] && console.log('message: altitude end - ' + msg); clearInterval(altitudeEvent); openspace.clear('alt'); }); /** * Function to handle OpenSpace latitude start event */ socket.on('latitude start', function(msg){ socketServer.config.OpenSpaceLog['Geography'] && console.log('latitude start') }); /** * Function to handle OpenSpace latitude end event */ socket.on('latitude end', function(msg){ socketServer.config.OpenSpaceLog['Geography'] && console.log('latitude end') openspace.clear('lat'); }); /** * Function to handle OpenSpace latitude move event @function * @param {array-number} msg A array includes {angle,distance} * @property {object} msg.angle What is angle triggered on joystick. * @property {library} msg.distance What is distance triggered on joystick. */ socket.on('latitude move', function(msg){ if (!socketServer.checkAccess(socket,'latitude')) return let curTime = new Date(); if((curTime-time)>frame/2) { time = curTime openspace.moveGeo(msg) } }); /** * Function to handle OpenSpace OpenSpace change planet event * @param {string} msg The number of planet */ socket.on('change planet', function(msg){ if (!socketServer.checkAccess(socket,'change planet')) return socketServer.config.OpenSpaceLog['MovePlanet'] && console.log('Move to: ' + msg); openspace.changePlanet(msg) for (var socketId in io.sockets.sockets) { var username = io.sockets.sockets[socketId].username; if(username != config.superUser) { io.to(socketId).emit('wiki', msg ); } } }); /** * Function to handle OpenSpace screenshot event */ socket.on('get img', function(){ // var address = this.id socketServer.config.SocketLog['account'] && console.log(address+' request snapshot'); var setting = { OpenSpacePath: config.OpenSpaceScreenshotPath, socketObj: socket } openspace.screenshot(setting) }); /** * Function to receive log from frontend */ socket.on('console log', function(msg){ socketServer.config.SocketLog['log'] && console.log(msg); }); // console.log(Object.keys(io.sockets.sockets)) socket.on('disconnect', function () { socketServer.initAdmin('remove',socket) // var address = this.id if (socket.username) { socketServer.userList = socketServer.userList.filter(v => v !== socket.username); socketServer.config.SocketLog['disconnect'] && console.log( socket.username + " disconnected"); } else { socketServer.config.SocketLog['disconnect'] && console.log('socket disconnected before username set'); } }); }); } /** * Function to initialize admin frontend content * @param {string} type 'add' / 'remove' / 'init' / 'refresh' * @param {object} socket The socket object */ initAdmin(type,socket) { var config = this.config; // var idList = []; // for (var socketId in io.sockets.sockets) { // var username = io.sockets.sockets[socketId].username; // if(username == config.superUser) { // // var admin = socketId; // // } else { // idList.push(socketId) // } // } if(type=='add') { var content = ''; if(!this.adminID || socket.id==this.adminID || socket.username == undefined) return // this.userList = this.userList.filter(v => v !== config.superUser); // for(let i =0; i<idList.length;i++) { var event = (this.cameraAccess.includes(socket.username)) ? "active" : '' // var event1 = (this.planetAccess.includes(this.userList[i])) ? "active" : '' content+= `<div id="-----`+socket.id+`" class="ui card"> <div class="content">`+ // <i id="+-+_+`+idList[i]+`" onclick="access(this)" class="right floated paper plane outline icon `+event1+`"></i>+ // <i id="+_+_+`+socket.id+`" onclick="access(this)" class="right floated gamepad icon `+event+`"></i>+ `<div class="header">`+socket.username+`</div> <div class="description"> `+socket.id+` </div> </div> <div class="extra content"> <span class="left floated gamepad"> <i id="_+_+_`+socket.id+`" onclick="access(this)" class="gamepad icon `+event+`"></i> Joystick </span>`+ // <span class="right floated paper plane outline"> // <i id="_+-+_`+idList[i]+`" onclick="access(this)" class="paper plane outline icon `+event1+`"></i> // Navigation // </span> `</div> </div> ` // } this.io.to(this.adminID).emit('content setup',{target:'#cardDeck',content: content,id:'#-----'+socket.id} ); } else if (type=='remove') { this.io.to(this.adminID).emit('content remove',{target:'#-----'+socket.id}); } else if (type=='init') { this.io.to(this.adminID).emit('content replace',{target:'#cardDeck',content:''}); for (var socketId in this.io.sockets.sockets) { var username = this.io.sockets.sockets[socketId].username; if(username != config.superUser) { this.initAdmin('add',this.io.sockets.sockets[socketId]) } } } else if (type=='refresh') { this.io.to(this.adminID).emit('content refresh',{target:'.content .header',target2:'.content .description',username:socket.username,id:socket.id}); } } /** * Function to initialize user frontend content * @param {object} socket The socket object * @param {library} io socket.io library * @param {int} id The user id * @param {string} target The frontend DOM id * @param {string} content The frontend content */ initUser(socket,io,id,target,content) { var config = this.config if(socket.username!=config.superUser) return; this.io.to(id).emit('content replace', {target:target,content:content} ); } /** * Function to check if a user can access certain control * @param {object} socket The socket object * @param {string} type The type of control */ checkAccess(socket,type) { if(!this.cameraAccess.includes(socket.username)) { socket.emit('alert', "Sorry, you are not admin!!"); this.config.SocketLog['access'] && console.log(socket.username+ " is try to "+type) return false; } else {return true} } } module.exports = function(io,config,openspace) { return new socket_server(io,config,openspace); }