/**
* 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);
}