linx-simulator2/src/mumble.js

184 lines
5.3 KiB
JavaScript

let tls = require('tls');
var inject = require('reconnect-core');
var slice = [].slice;
var MumbleClient = require('mumble-client');
var NodeCodecs = require('mumble-client-codecs-node');
var fs = require('fs');
var execSync = require('child_process').execSync;
var currentPath = require('path').dirname(require.main.filename);
const utils = require('./utils');
const OpusEncoder = require('node-opus').OpusEncoder;
class Mumble {
constructor(id, configs, connectedCallback) {
console.log('------', configs)
this.id = id;
this.configs = configs;
let mumble = configs.mumble;
this._createCertificatesForUser(id, () => {
this._makeMumbleConnection(id, mumble.port, mumble.host, id, this.configs.murmurPassword, connectedCallback);
})
}
// Create cerificates by running createCerts.sh script.
_createCertificatesForUser(id, callback) {
var code;
var path = currentPath + '/certs/' + id + '-cert.pem';
if (!fs.existsSync(path)) {
utils.writeLog(`Creating certificates for asset ${id}`)
try {
code = execSync(currentPath + '/createCerts.sh ' + id);
} catch (e) {
return callback(false);
}
return callback(true);
} else {
utils.writeLog(`Certificates for asset ${id} already created`);
return callback(true);
}
}
_makeMumbleConnection(id, port, host, username, password, connectedCallback) {
// If ca does not exist create them.
var rootPath = currentPath + '/certs/root-cert.pem';
if (!fs.existsSync(rootPath)) {
utils.writeLog(`Creating certificates for Mumble`);
try { execSync('./createCA.sh'); }
catch(e) {
utils.writeLog(`Could not crete Mumble certificates. Error:${e}`);
utils.writeErrorLog(`ERROR_MUMBLE`);
throw 'Could not crete CA certificates. ' + e;
}
} else {
utils.writeLog(`Certificates for Mumble already created`);
}
var reconnect = inject(function(){
var args = slice.call(arguments);
var cleartextStream = tls.connect.apply(tls, args);
cleartextStream.on('secureConnect', function(){
// if(cleartextStream.authorized){
// cleartextStream.emit('connect');
// }else{
// cleartextStream.emit('error', cleartextStream.authorizationError);
// }
});
return cleartextStream;
});
var secureContext = tls.createSecureContext({
ca: [
fs.readFileSync(currentPath + '/certs/root-cert.pem'),
fs.readFileSync(currentPath + '/certs/ca1-cert.pem'),
],
key: fs.readFileSync(currentPath + '/certs/' + this.id + '-key.pem'),
cert: fs.readFileSync(currentPath + '/certs/' + this.id + '-cert.pem'),
});
// Create connection socket with Murmur server.
reconnect((socket) => {
this.socket = socket;
// Connect to mumble server.
this.client = new MumbleClient({
username: '' + username,
password,
codecs: NodeCodecs
});
this.client.connectDataStream(this.socket);
this._prepareClient()
})
.once('connect', function (con) {
// con = underlying connection
connectedCallback();
})
.on('connect', function (con) {
// con = underlying connection
})
.on('reconnect', function (n, delay) {
// n = current number of reconnect
// delay = delay used before reconnect
console.log('------ Server reconnect...');
// utils.writeErrorLog(`ERROR_MUMBLE_RECONNECT`);
// utils.exitWriteErrorLog(`ERROR_MUMBLE_RECONNECT`);
})
.on('disconnect', function (err) {
// err = possible error
console.log('err', err)
utils.writeErrorLog(`ERROR_MUMBLE_DISCONNECT`);
})
.on('error', function (err) {
// never forget
// console.log(err)
// connectedCallback(err);
utils.writeLog('Mumble _makeMumbleConnection ERROR', err);
utils.writeErrorLog(`ERROR_MUMBLE`);
})
.connect({ port: port, host: host, rejectUnauthorized: false, secureContext: secureContext });
}
_prepareClient() {
// Register all users
this.client.users.forEach(user => this._newUser(user));
// Register future users
this.client.on('newUser', user => this._newUser(user))
this.client.on('newChannel', channel => this._newChannel(channel))
}
_newUser(user) {
// @TODO: Ugly hack for the voice to work with mp3. It is the _transform function from DecoderStream class (mumble-client-codecs-node project).
user.on('voice', voice => {
voice._transform = function (chunk, encoding, callback) {
if (chunk.codec === 'Opus' && chunk.frame) {
if (!this._opus) {
this._opus = new OpusEncoder(48000, 1);
}
callback(null, {
target: chunk.target,
pcm: chunk.frame ? this._opus.decode(chunk.frame) : {buffer: Buffer.alloc(3840)},
numberOfChannels: 1,
position: chunk.position
});
} else {
callback();
}
}
});
user.on('update', (actor, properties) => {
}).on('remove', () => {
}).on('voice', stream => {
// Leave all commented and empty. We currently dont need to receive voice.
// console.log(`User ${user.username} started takling`);
// Leave it empty. Its used for 'end' event to be trigered.
stream.on('data', (data) => {
});
stream.on('end', () => {
// console.log(`User ${user.username} stopped takling`)
});
});
}
_newChannel(channel) {
}
}
module.exports = Mumble