You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

813 lines
22 KiB

const mysql = require('mysql')
const Spawner = require('child_process');
const crypto = require('crypto');
const request = require('request');
const cheerio = require('cheerio');
class Child
{
/**
*
* @param {String} userID -User's Hash
*/
constructor(userID)
{
this.ID = userID;
this.child = Spawner.fork('./worker.js',[this.ID]);
this.birth = process.uptime();
this.timeOut = setTimeout(() => {
this.child.kill('SIGINT');
},2*60*60*1000);
}
get EOL()
{
const EOL = Math.trunc((this.timeOut._idleTimeout - ((process.uptime()*1000)-this.birth))/1000);
return EOL>0?EOL:0;
}
getMapData()
{
return new Promise((resolve, reject)=>
{
this.child.send('MapData');
this.child.on('message', m=>
{
if(typeof m == Object)
{
resolve(m.name);
}
})
})
}
waitStart()
{
return new Promise((resolve, reject)=>
{
var status = setInterval(() => {
if(this.isOpen)
{
clearInterval(status);
resolve(true)
}
}, 100);
})
}
createMapImage()
{
return new Promise((resolve, reject)=>
{
this.child.send('MapImage')
this.child.on('message', m=>
{
if(typeof m == String && m === 'Done')
{
resolve(m);
}
})
})
}
getConsole()
{
return new Promise((resolve, reject)=>
{
this.child.send('Console')
this.child.on('message', m=>
{
resolve(m);
})
})
}
}module.exports.Child = Child;
class playerCache
{
/**
*
* @param {String} Name
* @param {String} SteamID
* @param {Number} Ping
* @param {String} Address IP
* @param {Number} ConnectedSeconds
* @param {Number} Health
* @param {Object} Teammates ??'Not in a team'
* @param {String} Position ??'Not spawned yet'
*/
constructor(Name, SteamID, Ping, Address,ConnectedSeconds, Health, Teammates, Position)
{
this.Displayname = Name;
this.SteamID = SteamID;
this.Address = Address;
this.Health = Health;
this.Teammates = Teammates??'NOT IN A TEAM';
this.Position = Position??'NOT SPAWNED YET';
this.Ping = Ping;
this.ConnectedSeconds = ConnectedSeconds;
}
}module.exports.playerCache = playerCache;
class ServerCache
{
constructor(ID,Hostname,MaxPlayers, GameTime, Framerate, NetworkIn, NetworkOut, Seed, WorldSize)
{
this.ID = ID;
this.Hostname=Hostname;
this.MaxPlayers =MaxPlayers;
this.GameTime = GameTime;
this.Framerate = Framerate;
this.NetworkIn = NetworkIn;
this.NetworkOut = NetworkOut;
this.Seed = Seed;
this.WorldSize = WorldSize;
this.Console = [];
}
get WorldData ()
{
return {
Seed:this.Seed,
Size:this.WorldSize,
Time:this.GameTime
}
}
get NetWorkData ()
{
return {
Framerate:this.Framerate,
In:this.NetworkIn,
Out:this.NetworkIn
}
}
get ConsoleData ()
{
return this.Console;
}
get BasicData ()
{
return {
HostName:this.Hostname,
MaxPlayers:this.MaxPlayers,
GameTime:this.GameTime
}
}
}module.exports.ServerCache = ServerCache;
class pChild
{
constructor(ip, port, pw)
{
this.ip = ip;
this.port = port;
this.pw = pw;
this.child = Spawner.fork('./pWorker.js');
}
/**
* @argument {*} - Anything
*/
sendData()
{
for (var i = 0; i < arguments.length; i++)
{
}
}
/**
* To be used with programmed commands
* @param {String} command - Command to be sent
*/
send(command)
{
return new Promise((resolve, reject)=>
{
this.child.send({'command':command, 'ip':this.ip, 'port':this.port, 'pw':this.pw});
this.child.on('message', (m)=>
{
if(typeof m=='string')
{
if(m ==='end')
{
resolve();
}
}
else
{
reject("Worker didn't respond to querry.")
}
})
})
}
close ()
{
return new Promise((resolve, reject)=>
{
resolve(this.child.disconnect());
})
}
}module.exports.pChild = pChild;
class rustChild
{
constructor(size, seed)
{
this.child = Spawner.exec(`cmd /c updateServer.bat ${size} ${seed}`)
}
}module.exports.rustChild = rustChild;
class DBConnection
{
constructor()
{
this.connection = mysql.createConnection(
{
host:'localhost',
user:'root',
password:'root',
database:'legendary'
})
this.connection.connect();
}
close()
{
setTimeout(() =>
{
this.connection.destroy();
}, 2000);
}
//User
get Users()
{
return new Promise((resolve, reject)=>
{
this.connection.query('Select * from administrator', (e, r, f)=>
{
if(!e)
{
let aux = JSON.parse(JSON.stringify(r))
var payload =[];
for( var i = 0; i<aux.length; i++)
{
payload.push(aux[i].Hash)
}
resolve(payload);
this.close();
}
else
{
reject(e)
this.close();
}
});
})
}
hasSVData(Hash)
{
return new Promise((resolve, reject)=>
{
this.UData(Hash).then(
m=>
{
if(m.Server=='No server')
{
reject(false)
}
else
{
resolve(true)
}
}
)
})
}
UData(HASH)
{
return new Promise((resolve, reject)=>
{
this.connection.query(`SELECT * FROM administrator WHERE Hash = '${HASH}' `, (e, r, f)=>
{
if(e)reject(e);
r = JSON.parse(JSON.stringify(r))[0];
resolve({
id:r.A_ID,
Hash:r.Hash,
Email:r.EHash,
Server:r.IP || 'No server',
Port: JSON.stringify(r.Port) || 'No server',
SPW:r.sv_pw,
Epoch:r.Epoch
})
this.close();
})
})
}
get_ID(hash)
{
return new Promise((resolve, reject)=>
{
//User
this.Users.then((x)=>
{
for(var i =0; i<x.length; i++)
{
if(x[i]==hash)
{
this.UData(hash).then((m)=>
{
resolve(m.id);
})
.catch(()=>reject('no ID for this hash'))
}
}
}).catch(m=>
{
console.log(m);
reject('no ID for this HASH');
});
})
}
getCommands(Hash)
{
return new Promise((resolve, reject)=>
{
this.get_ID(Hash).then(id=>
{
this.connection.query(`Select * from legendary.timed_com where administrator_A_ID = '${id}'`, (e, r, f)=>
{
if(e)reject(e);
resolve(JSON.parse(JSON.stringify(r)));
})
})
})
}
addCommand(Hash, Command, Loop, Send, tz)
{
this.get_ID(Hash).then(id=>
{
this.connection.query(`INSERT INTO legendary.timed_com (\`Command\`, \`Loop\`, \`Send\`, \`administrator_A_ID\`) VALUES ('${Command}', '${Loop}', '${Send}', '${id}')`);
this.close();
})
}
removeCommand(id)
{
return new Promise((resolve, reject)=>
{
this.connection.query(`DELETE FROM legendary.timed_com WHERE (T_ID = ${id});`, (e, r, f)=>
{
if(e)reject(e)
if(r.affectedRows>0)resolve()
})
this.close()
})
}
editCommand(id, Command, Loop, Send, tz)
{
this.connection.query(`UPDATE legendary.timed_com SET \`Command\` = '${Command}', \`Loop\` = '${Loop}',\`Send\` = '${Send}' WHERE (T_ID = '${id}')`)
this.close()
}
addUser(Hash, EHash, Epoch)
{
if(Hash||EHash||Epoch)
{
this.connection.query(`INSERT INTO legendary.administrator (Hash, EHash, Epoch) VALUES ('${Hash}', '${EHash}', '${Epoch}')`)
this.close()
}
}
updateUser(HASH, NewHASH)
{
return new Promise((resolve, reject)=>
{
this.get_ID(HASH).then((m)=>
{
this.connection.query(`UPDATE administrator SET Hash='${NewHASH}' WHERE A_ID = ${m}`);
this.close();
resolve(NewHASH);
}).catch((err)=>reject(err));
})
}
addServer(HASH, IP, PW, PORT)
{
//needs to know what user should it be connected to
this.connection.query(`INSERT INTO adminsitrator (ServerIP, ServerPW, ServerPort) VALUES ('${HASH}', '${IP}','${PW}', '${PORT}')`)
this.close();
}
updateServer(Hash, IP, PW, PORT)
{
this.get_ID(Hash).then((id)=>
{
this.connection.query(`UPDATE administrator SET IP = '${IP}', sv_pw = '${PW}', port='${PORT}' WHERE A_ID = ${id}`);
this.close();
}).catch(e=>console.log('error',e))
}
get_Players(Hash)
{
return new Promise((resolve, reject)=>
{
this.connection.query(`SELECT * FROM player INNER JOIN time ON player.p_id = time.player_p_id
where administrator_a_id = (SELECT A_ID FROM administrator WHERE Hash = '${Hash}')
order by player_p_id asc;`,(e,r,f)=>
{
if(e)reject(e);this.close;
try
{
// console.log('Classes: DBConnection: Get_Players: r: ', r);
r = JSON.parse(JSON.stringify(r));
resolve(r)
} catch (err)
{
reject(err)
}
this.close;
})
})
}
}module.exports.DBConnection = DBConnection;
class _Crypto
{
constructor(User, Password)
{
this.salt = 'H$44Q3RVCd9X8Ef63tB4';
this.secret = 'mYFUZX9NSx7K74r7Jh@O';
this.pepper = String.fromCharCode(this.getRandomInt(65, 90));
this.password = Password;
this.user = User;
this.algorithm = 'aes-192-cbc';
}
get Hash() // to use on register
{
return this.hash()
}
hash(p)
{
if(!p)
{
return crypto.createHmac('sha256', this.secret).update(this.user+this.password+this.salt+this.pepper).digest('hex');
}
else
{
return crypto.createHmac('sha256', this.secret).update(this.user+this.password+this.salt+p).digest('hex');
}
}
eHash(Email)
{
return new Buffer.from(Email).toString('base64');
}
decrypt(eHash)
{
return new Buffer.from(eHash, 'base64').toString('utf8');
}
getRandomInt(min, max)
{
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
compare() // to use on login
{
return new Promise((resolve, reject)=>
{
new DBConnection().Users
.then((Users)=>
{
for(var i=0; i<26;i++)
{
const nPepper = String.fromCharCode(65+i);
const hash = this.hash(nPepper);
for(var j = 0; j<Users.length; j++)
{
if(Users[j]==hash) resolve(hash);
}
}
reject(false);
})
.catch((m)=>
{
console.log('error'+m);
})
});
}
}module.exports._Crypto = _Crypto;
class Epoch
{
/**
*
* @param {Number} Days - Must be and integer.
*/
constructor(Days)
{
this.Days = (Days*(24*60*60));
this.epoch = this.epocher();
}
epocher()
{
return Math.floor(this.Days + Date.now()/1000)
}
/**
* @param {String} time - Must follow the format of '18:12'.
*/
sEpoch(time)
{
let hours = time.split(':')[0];
let minutes = time.split(':')[1];
return Math.floor(this.next(hours, minutes)/1000);
}
/**
* @param {Number} time - Must be and integer.
*/
toDate(time)
{
//console.log('classes:toDate:', time)
const d = new Date(time*1000);
return d.getHours()+':'+ d.getMinutes();
}
/**
* Returns the next possible time for the command to be sent. Meaning if the hours inputed by the user
* have already gone past this day then the epoch will be set for the next.
* If not the epoch will be created like usual.
*
* @param {Number} hours
* @param {Number} minutes
*/
next(hours, minutes)
{
var d= new Date();
var payload = new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), hours, minutes, 0, 0).getTime();
//console.log('Classes:Next:',hours, minutes, payload)
return payload;
}
}module.exports.Epoch = Epoch;
class IPTranslator
{
constructor(IP)
{
this.IP = IP;
}
translate()
{
return new Promise((resolve, reject)=>
{
request(`http://ip-api.com/json/${this.IP}`, (e,r,b)=>
{
var body
if(!e)
{
try {
body = JSON.parse(b);
} catch (error) {
console.log('IP Transalator: Bad Parse.');
resolve('Err')
}
resolve(body);
}
else
{
resolve( {'status':'error'});
}
})
})
}
CC()
{
return new Promise((resolve, reject)=>
{
this.translate().then(m=>resolve(m.countryCode)).catch(e=>
{
console.log('IP Translator: CountryCode:'+e);
reject(e);
})
})
}
}module.exports.IPTranslator = IPTranslator;
class VAC
{
constructor(SteamID)
{
this.SteamID = SteamID;
}
get ban()
{
return new Promise((resolve,reject)=>
{
request(`http://vacbanned.com/engine/check?qsearch=${this.SteamID}`, (e,r,b)=>
{
if(e)reject('VAC: Request:'+e)
const $ = cheerio.load(b);
// console.log('Worker: VAC: body: ', $('td').text());
$('.strong').remove()
request(`http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=DCA47FAA9F3D1485FA8C1F4FA15A5266&steamid=${this.SteamID}&format=json`, (e,r,steamb)=>
{
if(e)reject('VAC: FS: Request:'+e)
var fs;
let aux
//console.log('Vac class: SteamAPI', steamb)
try
{
aux = JSON.parse(steamb)['response'].games;
} catch (error)
{
console.log('JSON parse error: ',error);
}
if(aux)
{
for (let i = 0; i < aux.length; i++)
{
// console.log('VAC: FS: Request2: '+aux[i].appid, '252490');
if(aux[i].appid)
{
if(aux[i].appid=='252490')
{
i = aux.length;
fs = 'NO'
}
else
{
fs='Yes'
}
}
}
var vac;
try {
vac = $('td').text().replace(/ /g, '').split('Hex)')[1].replace(/\n/g, 'SPLIT').split('SPLIT')[1];
} catch (error) {
vac = 'Error';
}
let payload = {
'vac':vac,
'fs':fs
}
// console.log('VAC: Payload: ', payload)
resolve(payload);
}
else
{
var vac;
try {
vac = $('td').text().replace(/ /g, '').split('Hex)')[1].replace(/\n/g, 'SPLIT').split('SPLIT')[1];
} catch (error) {
vac = 'Error';
}
let payload = {
'vac':vac,
'fs':fs
}
resolve(payload);
}
})
});
})
}
}module.exports.VAC = VAC;
/**
* Use it to transform an hour value into miliseconds.
* @param {Number} Hours - Hours to transform
*/
function hours(Hours)
{
return Hours*60*60*1000;
}
/**
* This function writes a cookie on the client side.
* Primary use: Store users Hash for every other page.
* @argument {Response} res - Response: express response object.
* @argument {String} name - Name of the cookie.
* @argument {*} value - Value of the cookie.
*/
exports.baker = (res, name, value) =>
{
return new Promise((resolve,reject)=>
{
try
{
res.cookie(name, '',{maxAge:0});
res.cookie(name, value, {maxAge:hours(2),path:'/',secure:false,httpOnly:false});
resolve();
}
catch (e)
{
reject(e)
}
})
}
/**
* Hash used on register to check if there is already a user with the same credentials.
* @param {String} user - User's email
* @param {String} password - User's Password
* @param {String} hash - User's identifying hash
*/
exports.bouncer = (user, password, hash) =>
{
return new Promise((resolve, reject)=>
{
new _Crypto(user, password).compare().then(m=>
{
if(hash)
{
if(m==hash)
{
resolve(m)
}
else
{
reject('You are on a different account.')
}
}
else
{
resolve(m)
}
}).catch(m=>
{
reject(m)
})
})
}
/**
* Reduces the number of decimal cases to the required value.
* @param {Number} num - The number you want to cut to size.
* @param {Number} decimal - Ammount of decimal cases.
*/
exports.toFixed = (num, decimal)=>
{
decimal = decimal || 0;
decimal = Math.pow(10, decimal);
return Math.floor(num * decimal) / decimal;
}
/**
* Transforms a value into a 0-1 value given the max value it could take.
* @param {Number} val - Value to transform to percentage
* @param {Number} max - Maximum the value could be
*/
exports.toPercent = (val, max)=>
{
return val/max;
}
/**
* Removes the timezone difference to a time.
* @param {String} time - Must follow 'HH:MM' format.
* @param {Number} offSet - Number of minutes from gmt+0.
*/
exports.removeTZ = (time, offSet)=>
{
var h = parseInt(time.split(':')[0]);
var maux = parseInt(time.split(':')[1]);
var m = maux - offSet;
var d = new Date();
var payload = new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), h, m, 0, 0).toString().split(' ')[4].split(' ')[0].slice(0,-3);
return payload;
}
/**
* Adds timezone difference to a time.
* @param {String} time - Must follow 'HH:MM' format.
* @param {Number} offset - Number of minutes from gmt+0.
*/
exports.addTZ = (time, offset) =>
{
offset = parseInt(offset);
let h = parseInt(time.split(':')[0]);
let m = parseInt(time.split(':')[1])+offset;
var d= new Date();
return payload = new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDay(), h, m, 0, 0).toString().split(' ')[4].split(' ')[0].slice(0,-3);
}