mirror of
https://github.com/AIR-EISTI/hades.git
synced 2025-01-09 19:21:29 +01:00
129 lines
3.4 KiB
JavaScript
129 lines
3.4 KiB
JavaScript
const os = require('os')
|
|
const pty = require('node-pty')
|
|
const pidusageTree = require('pidusage-tree')
|
|
|
|
const SocketService = require('./SocketService')
|
|
|
|
const NB_CPU = os.cpus().length
|
|
const TOT_MEM = os.totalmem()
|
|
|
|
class Game {
|
|
constructor (config, nickname, command, args) {
|
|
this.nickname = nickname
|
|
this.name = config.name
|
|
this.status = 'RUNNING'
|
|
this.command = command
|
|
this.args = args
|
|
this.exitCode = null
|
|
this.stdout = ''
|
|
this.statsInterval = null
|
|
this.totalMemory = TOT_MEM
|
|
this.statsHist = []
|
|
this.mapSizes = new Map()
|
|
|
|
this.proc = pty.spawn(command, args, {
|
|
uid: config.uid,
|
|
gid: config.gid,
|
|
cwd: config.cwd,
|
|
cols: 150,
|
|
rows: 30
|
|
})
|
|
SocketService.emitStatus(this.getRepr())
|
|
this.initEventGame()
|
|
this.initStats()
|
|
}
|
|
|
|
initEventGame () {
|
|
this.proc.on('exit', this.processOnClose.bind(this))
|
|
this.proc.on('error', this.processOnError.bind(this))
|
|
this.proc.on('data', this.processOnData.bind(this))
|
|
SocketService.on(`term-data@${this.proc.pid}`, this.onTermData.bind(this))
|
|
SocketService.on(`enter-server@${this.proc.pid}`, this.onEnterServer.bind(this))
|
|
SocketService.on(`term-resize@${this.proc.pid}`, this.onTermResize.bind(this))
|
|
SocketService.on(`leave-server@${this.proc.pid}`, this.onLeaveServer.bind(this))
|
|
}
|
|
|
|
initStats () {
|
|
this.statsInterval = setInterval(this.sendStats.bind(this), 5000)
|
|
}
|
|
|
|
async sendStats () {
|
|
let stats = Object.values(await pidusageTree(this.proc.pid))
|
|
let percentCpu = stats.map(s => s.cpu).reduce((a, b) => a + b, 0) / NB_CPU
|
|
let procMem = stats.map(s => s.memory).reduce((a, b) => a + b, 0)
|
|
let finalStats = {
|
|
cpu: percentCpu,
|
|
memory: procMem
|
|
}
|
|
this.statsHist.unshift(finalStats)
|
|
this.statsHist.splice(61, 1)
|
|
SocketService.emitGameStats(this.proc.pid, finalStats)
|
|
}
|
|
|
|
onTermData (ws, msg) {
|
|
this.proc.write(msg)
|
|
}
|
|
|
|
onTermResize (ws, newSize) {
|
|
if (this.status !== 'RUNNING')
|
|
return
|
|
this.mapSizes.set(ws, newSize)
|
|
this.updateTermSize()
|
|
}
|
|
|
|
onEnterServer (ws, msg) {
|
|
ws.send(JSON.stringify({event: 'term-data', data: this.stdout}))
|
|
}
|
|
|
|
onLeaveServer (ws) {
|
|
this.mapSizes.delete(ws)
|
|
this.updateTermSize()
|
|
}
|
|
|
|
processOnClose (code, signal) {
|
|
this.exitCode = code
|
|
this.status = 'CLOSED'
|
|
console.log('[' + this.proc.pid + '-' + this.name + '] Process endend with code '+ this.exitCode)
|
|
SocketService.emitStatus(this.getRepr())
|
|
clearInterval(this.statsInterval)
|
|
}
|
|
|
|
processOnData (data) {
|
|
this.stdout += data
|
|
SocketService.to(this.proc.pid, 'term-data', data.toString())
|
|
}
|
|
|
|
processOnError (err) {
|
|
console.error('[' + this.proc.pid + '-' + this.name + '] [ERROR] ' + err)
|
|
this.status = 'ERROR'
|
|
//SocketService.emitUpdateStatus(this.proc.pid, this)
|
|
}
|
|
|
|
updateTermSize() {
|
|
let genSize = this.mapSizes.values()
|
|
let min = genSize.next().value
|
|
if (!min)
|
|
return
|
|
for (let {rows, cols} of genSize) {
|
|
min.rows = Math.min(min.rows, rows)
|
|
min.cols = Math.min(min.cols, cols)
|
|
}
|
|
this.proc.resize(min.cols, min.rows)
|
|
SocketService.emitTermResize(this.proc.pid, min)
|
|
}
|
|
|
|
kill () {
|
|
this.proc.kill('SIGTERM')
|
|
}
|
|
|
|
getRepr () {
|
|
let returnObject = {...this, pid: this.proc.pid}
|
|
delete returnObject['proc']
|
|
delete returnObject['stdout']
|
|
delete returnObject['statsInterval']
|
|
delete returnObject['mapSizes']
|
|
return returnObject
|
|
}
|
|
}
|
|
|
|
module.exports = Game
|