Files
game-server-watcher/dist/game-server.js
Smith 792e44f82b 🧹
2022-08-03 16:12:42 +02:00

317 lines
11 KiB
JavaScript
Raw Permalink Blame History

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GameServer = exports.saveDb = exports.initDb = void 0;
const got_1 = __importDefault(require("got"));
const node8_gamedig_1 = require("node8-gamedig");
const lowdb_1 = require("@commonify/lowdb");
const ipregex_1 = __importDefault(require("./lib/ipregex"));
const getip_1 = __importDefault(require("./lib/getip"));
const STEAM_WEB_API_KEY = process.env.STEAM_WEB_API_KEY || '';
const DATA_PATH = process.env.DATA_PATH || './data/';
const DBG = Boolean(process.env.DBG || false);
const adapter = new lowdb_1.JSONFile(DATA_PATH + 'servers.json');
const db = new lowdb_1.Low(adapter);
async function initDb() {
await db.read();
db.data = db.data || {
population: {}
};
}
exports.initDb = initDb;
async function saveDb() {
try {
return await db.write();
}
catch (e) {
console.error('gs.saveDb', e.message || e);
}
}
exports.saveDb = saveDb;
class GameServer {
constructor(config) {
this.online = false;
console.log('game-server init', config.host, config.port, config.type, config.appId);
this.ip = '0.0.0.0';
this.config = config;
this.history = new ServerHistory(config.host + ':' + config.port, config.graphHistoryHours, config.timezoneOffset);
this._niceName = config.host + ':' + config.port;
}
async update() {
await this.getIp();
if (DBG)
console.log('gs.up', this.config.host, this.config.port);
let info = await this.gamedig();
if (DBG)
console.log('gs.gamedig', Object.assign({}, info, { players: info === null || info === void 0 ? void 0 : info.players.length }));
if (!info && STEAM_WEB_API_KEY && this.config.appId) {
info = await this.steam();
if (DBG)
console.log('gs.steam', info);
}
if (info) {
if (info.players.length > 0 && DBG) {
console.log('gs.players.0', info.players[0]);
}
this.online = true;
this.info = info;
this.history.add(info.playersNum);
}
else {
this.online = false;
console.error('game-server not available', this.config.host, this.config.port);
this.history.cleanStats();
}
}
async gamedig() {
try {
const res = await (0, node8_gamedig_1.query)({
host: this.config.host,
port: this.config.port,
type: this.config.type,
});
const raw = res.raw;
const game = raw.game || raw.folder || this.config.type;
const players = res.players.map((p) => {
return new GsPlayer(p);
});
return {
connect: res.connect,
name: res.name,
game: game,
map: res.map || '',
playersNum: res.numplayers || res.players.length,
playersMax: res.maxplayers,
players
};
}
catch (e) {
console.error(['gs.gamedig', this.config.host, this.config.port].join(':'), e.message || e);
}
return null;
}
async steam() {
const reqUrl = 'https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\\appid\\' + this.config.appId + '\\addr\\' + this.ip + '&key=' + STEAM_WEB_API_KEY;
try {
const res = await (0, got_1.default)(reqUrl, {
responseType: 'json',
headers: { 'user-agent': 'game-server-watcher/1.0' }
}).json();
if (Array.isArray(res.response.servers)) {
const matching = res.response.servers.find((s) => s.gameport === this.config.port);
if (matching) {
return {
connect: matching.addr,
name: matching.name,
game: matching.gamedir,
map: matching.map,
playersNum: matching.players,
playersMax: matching.max_players,
players: []
};
}
}
}
catch (e) {
console.error(['gs.steam', this.config.host, this.config.port].join(':'), e.message || e);
}
return null;
}
get niceName() {
var _a;
let nn = (_a = this.info) === null || _a === void 0 ? void 0 : _a.name;
if (nn) {
for (let i = 0; i < nn.length; i++) {
if (nn[i] == '^') {
nn = nn.slice(0, i) + ' ' + nn.slice(i + 2);
}
else if (nn[i] == '█') {
nn = nn.slice(0, i) + ' ' + nn.slice(i + 1);
}
else if (nn[i] == '<27>') {
nn = nn.slice(0, i) + ' ' + nn.slice(i + 2);
}
;
}
;
if (nn)
this._niceName = nn;
}
return this._niceName;
}
async getIp() {
if (this.ip === '0.0.0.0') {
if (ipregex_1.default.test(this.config.host)) {
this.ip = this.config.host;
}
else {
this.ip = await (0, getip_1.default)(this.config.host) || '0.0.0.0';
}
}
return this.ip;
}
}
exports.GameServer = GameServer;
class GsPlayer {
constructor(p) {
this._player = p;
}
get(prop) {
const p = this._player;
let re;
if (p[prop] !== undefined) {
re = String(p[prop]);
}
else if (p.raw && p.raw[prop] !== undefined) {
re = String(p.raw[prop]);
}
if (re === 'NaN') {
re = undefined;
}
return re;
}
}
class ServerHistory {
constructor(id, graphHistoryHours = 12, timezoneOffset = 0) {
this._stats = [];
this.id = id;
this.graphHistoryHours = graphHistoryHours;
this.timezoneOffset = timezoneOffset;
}
yyyymmddhh(d) {
return parseInt(d.toISOString().slice(0, 13).replace(/\D/g, ''), 10);
}
formatHour(h) {
const ampm = (h >= 12) ? 'pm' : 'am';
const hours = (h > 12) ? h - 12 : h;
return hours + ampm;
}
add(playersNum) {
var _a;
if (!((_a = db.data) === null || _a === void 0 ? void 0 : _a.population))
return;
if (!db.data.population[this.id])
db.data.population[this.id] = [];
const dh = this.yyyymmddhh(new Date());
db.data.population[this.id].push({
dateHour: dh,
playersNum
});
this.cleanStats();
}
cleanStats() {
var _a;
if (!((_a = db.data) === null || _a === void 0 ? void 0 : _a.population))
return;
if (!db.data.population[this.id])
db.data.population[this.id] = [];
const d = new Date();
d.setHours(d.getHours() - this.graphHistoryHours - 1);
const minDh = this.yyyymmddhh(d);
db.data.population[this.id] = db.data.population[this.id].filter(i => i.dateHour > minDh);
this._stats = [];
}
stats() {
var _a;
if (!((_a = db.data) === null || _a === void 0 ? void 0 : _a.population))
return [];
if (this._stats.length === 0) {
const grouped = {};
if (this.id in db.data.population) {
for (const d of db.data.population[this.id]) {
if (!grouped[d.dateHour])
grouped[d.dateHour] = [];
grouped[d.dateHour].push(d);
}
}
const stats = [];
for (const dh in grouped) {
const avg = grouped[dh].reduce((total, next) => total + next.playersNum, 0) / grouped[dh].length;
const max = grouped[dh].reduce((max, next) => next.playersNum > max ? next.playersNum : max, 0);
stats.push({
dateHour: parseInt(dh, 10),
avg,
max
});
}
this._stats = stats.sort((a, b) => a.dateHour - b.dateHour);
}
return this._stats;
}
statsChart() {
const stats = this.stats();
const e = encodeURIComponent;
const avg = [];
const max = [];
const xlabels = [];
const dh = this.yyyymmddhh(new Date());
if (stats.length === 0 || stats[stats.length - 1].dateHour < dh) {
stats.push({
dateHour: dh,
avg: -1,
max: -1
});
}
const firstDateHour = new Date();
firstDateHour.setHours(firstDateHour.getHours() - this.graphHistoryHours);
const fdh = this.yyyymmddhh(firstDateHour);
if (stats[0].dateHour > fdh) {
stats.unshift({
dateHour: fdh,
avg: -1,
max: -1
});
}
let lastH;
for (const s of stats) {
const sh = s.dateHour % 100;
const d = new Date();
d.setHours(sh);
d.setTime(d.getTime() + (this.timezoneOffset * 60 * 60 * 1000));
const h = d.getHours();
if (lastH !== undefined) {
let nextH = lastH;
do {
nextH++;
if (nextH > 23)
nextH = 0;
if (nextH !== h) {
avg.push('_');
max.push('_');
xlabels.push(this.formatHour(nextH));
}
} while (nextH !== h);
}
avg.push(s.avg > -1 ? String(Math.round(s.avg)) : '_');
max.push(s.max > -1 ? String(s.max) : '_');
xlabels.push(this.formatHour(h));
lastH = h;
}
const values = avg.concat(max);
return [
'https://image-charts.com/chart?cht=lc',
'chs=600x300',
'chf=' + e('bg,s,202225'),
'chma=' + e('0,0,10,0'),
'chls=' + e('2|2'),
// 'chm='+e('B,011040,0,0,0'), // line fill
'chg=' + e('1,1,2,2,303030'),
// 'chm='+e('d,ffffff,0,-1,3|d,ffffff,1,-1,3'), // value markers
'chdl=' + e('AVG|MAX'),
'chdlp=t',
'chdls=' + e('ffffff,8'),
'chxt=' + e('x,y'),
'chxs=' + e('0,ffffff,12,s|1,ffffff,15'),
'chds=a',
'chd=' + e('t:' + avg.join(',') + '|' + max.join(',')),
'chl=' + e(values.join('|')),
'chlps=' + e('color,ffffff|anchor,end|font.size,12|align,top'),
'chxl=' + e('0:|' + xlabels.join('|')),
// 'chxr='+e('1,0,'+playersMax), // axis range
'chco=' + e('1234ef,fd7501') // data colors
].join('&');
}
}