Initial project structure
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"wrapperId": "wrp-8adcbbf2"
|
||||
}
|
||||
Generated
+243
@@ -0,0 +1,243 @@
|
||||
{
|
||||
"name": "wrapper",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wrapper",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"amqplib": "^2.0.1",
|
||||
"steam-server-query": "^1.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/amqplib": "^0.10.8",
|
||||
"@types/node": "^26.1.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
|
||||
"integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@tsconfig/node16": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/amqplib": {
|
||||
"version": "0.10.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.10.8.tgz",
|
||||
"integrity": "sha512-vtDp8Pk1wsE/AuQ8/Rgtm6KUZYqcnTgNvEHwzCkX8rL7AGsC6zqAfKAAJhUZXFhM/Pp++tbnUHiam/8vVpPztA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "26.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz",
|
||||
"integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~8.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.17.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
|
||||
"integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.5",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz",
|
||||
"integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"acorn": "^8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/amqplib": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/amqplib/-/amqplib-2.0.1.tgz",
|
||||
"integrity": "sha512-a3P2MgfCf9nzVis12VxWEn0dS6hcqve7dlEAhXDtIWR27BlhtMkILOc+H9aeHjDi6i6r94dYKc2Kx2OFe3avvg==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
|
||||
"integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/steam-server-query": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/steam-server-query/-/steam-server-query-1.1.3.tgz",
|
||||
"integrity": "sha512-mXVYpH7HhWchbpKXKc5PvwSrHpB7vCgMfZKVBadOONNfL+KEwkaopte6nF5MkyAmRieML8bg9KC0QnMqUD4rnA=="
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
"@tsconfig/node12": "^1.0.7",
|
||||
"@tsconfig/node14": "^1.0.0",
|
||||
"@tsconfig/node16": "^1.0.2",
|
||||
"acorn": "^8.4.1",
|
||||
"acorn-walk": "^8.1.1",
|
||||
"arg": "^4.1.0",
|
||||
"create-require": "^1.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"v8-compile-cache-lib": "^3.0.1",
|
||||
"yn": "3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"ts-node": "dist/bin.js",
|
||||
"ts-node-cwd": "dist/bin-cwd.js",
|
||||
"ts-node-esm": "dist/bin-esm.js",
|
||||
"ts-node-script": "dist/bin-script.js",
|
||||
"ts-node-transpile-only": "dist/bin-transpile.js",
|
||||
"ts-script": "dist/bin-script-deprecated.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/core": ">=1.2.50",
|
||||
"@swc/wasm": ">=1.2.50",
|
||||
"@types/node": "*",
|
||||
"typescript": ">=2.7"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@swc/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@swc/wasm": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.7.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
||||
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
|
||||
"integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "wrapper",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "ts-node src/index.ts"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"amqplib": "^2.0.1",
|
||||
"steam-server-query": "^1.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/amqplib": "^0.10.8",
|
||||
"@types/node": "^26.1.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import fs from "fs";
|
||||
|
||||
|
||||
const FILE = "./config.json";
|
||||
|
||||
|
||||
export interface WrapperConfig {
|
||||
|
||||
wrapperId?: string;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function loadConfig(): WrapperConfig {
|
||||
|
||||
|
||||
if(!fs.existsSync(FILE)){
|
||||
|
||||
return {};
|
||||
|
||||
}
|
||||
|
||||
|
||||
return JSON.parse(
|
||||
fs.readFileSync(
|
||||
FILE,
|
||||
"utf-8"
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function saveConfig(
|
||||
config: WrapperConfig
|
||||
){
|
||||
|
||||
|
||||
fs.writeFileSync(
|
||||
|
||||
FILE,
|
||||
|
||||
JSON.stringify(
|
||||
config,
|
||||
null,
|
||||
4
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
import * as amqp from "amqplib";
|
||||
import os from "os";
|
||||
import { loadConfig, saveConfig } from "./config";
|
||||
import { ProcessManager } from "./processManager";
|
||||
import { StatusReporter } from "./statusReporter";
|
||||
import { QueryManager } from "./queryManager";
|
||||
|
||||
|
||||
class Wrapper {
|
||||
|
||||
processManager =
|
||||
new ProcessManager();
|
||||
|
||||
queryManager =
|
||||
new QueryManager(
|
||||
this.processManager
|
||||
);
|
||||
|
||||
connection!: amqp.ChannelModel;
|
||||
channel!: amqp.Channel;
|
||||
|
||||
|
||||
id?: string;
|
||||
|
||||
|
||||
|
||||
async start(){
|
||||
|
||||
|
||||
const config =
|
||||
loadConfig();
|
||||
|
||||
|
||||
|
||||
if(config.wrapperId){
|
||||
|
||||
|
||||
this.id =
|
||||
config.wrapperId;
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
"Starte mit vorhandener ID:",
|
||||
this.id
|
||||
);
|
||||
|
||||
|
||||
}else{
|
||||
|
||||
|
||||
console.log(
|
||||
"Keine Wrapper ID vorhanden - neue Registrierung"
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
this.connection =
|
||||
await amqp.connect(
|
||||
"amqp://manager:gigi1337@192.168.1.74"
|
||||
);
|
||||
|
||||
|
||||
|
||||
this.channel =
|
||||
await this.connection.createChannel();
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
"RabbitMQ verbunden"
|
||||
);
|
||||
|
||||
|
||||
|
||||
this.queryManager.start();
|
||||
await this.register();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async register(){
|
||||
|
||||
|
||||
|
||||
const reply =
|
||||
await this.channel.assertQueue(
|
||||
"",
|
||||
{
|
||||
exclusive:true
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
const replyQueue =
|
||||
reply.queue;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
await this.channel.consume(
|
||||
|
||||
replyQueue,
|
||||
|
||||
msg=>{
|
||||
|
||||
|
||||
if(!msg)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
const data =
|
||||
JSON.parse(
|
||||
msg.content.toString()
|
||||
);
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
"Antwort vom Manager:",
|
||||
data
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
if(data.type==="wrapper.registered"){
|
||||
|
||||
|
||||
|
||||
this.id =
|
||||
data.wrapperId;
|
||||
|
||||
|
||||
|
||||
saveConfig({
|
||||
|
||||
wrapperId:
|
||||
this.id
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
"Wrapper ID gespeichert:",
|
||||
this.id
|
||||
);
|
||||
|
||||
if(!this.id){
|
||||
|
||||
console.error(
|
||||
"Keine Wrapper ID erhalten!"
|
||||
);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
this.listenCommands(
|
||||
data.commandQueue
|
||||
);
|
||||
|
||||
const reporter =
|
||||
new StatusReporter(
|
||||
|
||||
this.channel,
|
||||
|
||||
this.processManager,
|
||||
|
||||
this.queryManager,
|
||||
|
||||
this.id
|
||||
|
||||
);
|
||||
|
||||
|
||||
reporter.start();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.channel.ack(msg);
|
||||
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const message = {
|
||||
|
||||
|
||||
|
||||
type:
|
||||
"wrapper.register",
|
||||
|
||||
|
||||
|
||||
// vorhandene ID mitsenden
|
||||
wrapperId:
|
||||
this.id,
|
||||
|
||||
|
||||
|
||||
hostname:
|
||||
os.hostname(),
|
||||
|
||||
|
||||
|
||||
cpu:
|
||||
os.cpus().length,
|
||||
|
||||
|
||||
|
||||
ram:
|
||||
Math.round(
|
||||
os.totalmem()/1024/1024
|
||||
),
|
||||
|
||||
|
||||
|
||||
replyQueue
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
await this.channel.sendToQueue(
|
||||
|
||||
"wrapper.register",
|
||||
|
||||
Buffer.from(
|
||||
JSON.stringify(message)
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
"Registrierung gesendet",
|
||||
this.id ?? "ohne ID"
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async listenCommands(queue:string){
|
||||
|
||||
|
||||
await this.channel.consume(
|
||||
|
||||
queue,
|
||||
|
||||
msg=>{
|
||||
|
||||
|
||||
if(!msg)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
const command =
|
||||
JSON.parse(
|
||||
msg.content.toString()
|
||||
);
|
||||
|
||||
|
||||
|
||||
switch(command.type){
|
||||
|
||||
|
||||
|
||||
case "test":
|
||||
|
||||
|
||||
console.log(
|
||||
"TEST COMMAND:",
|
||||
command.message
|
||||
);
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
case "server.start":
|
||||
|
||||
|
||||
this.processManager.startServer(
|
||||
command
|
||||
);
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case "server.stop":
|
||||
|
||||
|
||||
this.processManager.stopServer(
|
||||
command.serverId
|
||||
);
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
|
||||
|
||||
console.log(
|
||||
"Unbekannter Command:",
|
||||
command
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.channel.ack(msg);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
"Warte auf Commands:",
|
||||
queue
|
||||
);
|
||||
|
||||
|
||||
|
||||
this.heartbeat();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
heartbeat(){
|
||||
|
||||
console.log("Heartbeat gestartet");
|
||||
setInterval(()=>{
|
||||
|
||||
|
||||
|
||||
if(!this.id)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
const heartbeat = {
|
||||
|
||||
|
||||
type:
|
||||
"wrapper.heartbeat",
|
||||
|
||||
|
||||
|
||||
wrapperId:
|
||||
this.id
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
this.channel.sendToQueue(
|
||||
|
||||
|
||||
"wrapper.register",
|
||||
|
||||
|
||||
|
||||
Buffer.from(
|
||||
|
||||
JSON.stringify(
|
||||
heartbeat
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
"Heartbeat gesendet"
|
||||
);
|
||||
|
||||
|
||||
|
||||
},10000);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
new Wrapper().start();
|
||||
@@ -0,0 +1,352 @@
|
||||
import { spawn, ChildProcess } from "child_process";
|
||||
|
||||
|
||||
|
||||
interface ServerProcess {
|
||||
|
||||
|
||||
id:string;
|
||||
|
||||
name:string;
|
||||
|
||||
port:number;
|
||||
|
||||
queryPort:number;
|
||||
|
||||
process:ChildProcess;
|
||||
|
||||
started:number;
|
||||
|
||||
alive:boolean;
|
||||
|
||||
|
||||
players:number;
|
||||
|
||||
maxPlayers:number;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class ProcessManager {
|
||||
|
||||
|
||||
|
||||
private processes =
|
||||
new Map<string, ServerProcess>();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
startServer(data:any){
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
"Starte Game Server:",
|
||||
data.name
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
const args = [
|
||||
|
||||
`-Port=${data.port}`,
|
||||
|
||||
`-QueryPort=${data.queryPort}`,
|
||||
|
||||
`-ServerName=${data.name}`
|
||||
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const process = spawn(
|
||||
"/home/heroes/HeroesOfValorServer.sh",
|
||||
args,
|
||||
{
|
||||
cwd:"/home/heroes",
|
||||
stdio:"pipe",
|
||||
detached:true
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
process.stdout?.on(
|
||||
"data",
|
||||
data=>{
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
`[${data.name}]`,
|
||||
data.toString()
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
process.stderr?.on(
|
||||
|
||||
"data",
|
||||
|
||||
data=>{
|
||||
|
||||
|
||||
console.error(
|
||||
|
||||
"SERVER ERROR:",
|
||||
data.toString()
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
process.on("exit", code=>{
|
||||
|
||||
|
||||
const server =
|
||||
this.processes.get(data.serverId);
|
||||
|
||||
|
||||
|
||||
if(server){
|
||||
|
||||
|
||||
console.log(
|
||||
"Server beendet:",
|
||||
server.name,
|
||||
code
|
||||
);
|
||||
|
||||
|
||||
this.processes.delete(
|
||||
data.serverId
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this.processes.set(
|
||||
|
||||
data.serverId,
|
||||
|
||||
{
|
||||
|
||||
id:data.serverId,
|
||||
|
||||
name:data.name,
|
||||
|
||||
port:data.port,
|
||||
|
||||
queryPort:data.queryPort,
|
||||
|
||||
process,
|
||||
|
||||
started:Date.now(),
|
||||
|
||||
alive:true,
|
||||
|
||||
|
||||
players:0,
|
||||
|
||||
maxPlayers:0
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Server gestartet:",
|
||||
|
||||
{
|
||||
|
||||
id:data.serverId,
|
||||
|
||||
pid:process.pid
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
stopServer(id:string){
|
||||
|
||||
|
||||
|
||||
const server =
|
||||
this.processes.get(id);
|
||||
|
||||
|
||||
|
||||
|
||||
if(!server){
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Server nicht gefunden:",
|
||||
|
||||
id
|
||||
|
||||
);
|
||||
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Stoppe Server:",
|
||||
|
||||
server.name
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
process.kill(
|
||||
-server.process.pid!,
|
||||
"SIGTERM"
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
getServers(){
|
||||
|
||||
|
||||
return Array.from(
|
||||
|
||||
this.processes.values()
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
getStatus(){
|
||||
|
||||
|
||||
return Array.from(
|
||||
this.processes.values()
|
||||
).map(server => ({
|
||||
|
||||
|
||||
id:
|
||||
server.id,
|
||||
|
||||
|
||||
name:
|
||||
server.name,
|
||||
|
||||
|
||||
port:
|
||||
server.port,
|
||||
|
||||
|
||||
queryPort:
|
||||
server.queryPort,
|
||||
|
||||
|
||||
pid:
|
||||
server.process.pid,
|
||||
|
||||
|
||||
alive:
|
||||
server.alive,
|
||||
|
||||
|
||||
players:
|
||||
server.players,
|
||||
|
||||
|
||||
maxPlayers:
|
||||
server.maxPlayers,
|
||||
|
||||
|
||||
started:
|
||||
server.started
|
||||
|
||||
|
||||
}));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import { queryGameServerInfo } from "steam-server-query";
|
||||
import { ProcessManager } from "./processManager";
|
||||
|
||||
|
||||
export class QueryManager {
|
||||
|
||||
|
||||
constructor(
|
||||
private processManager: ProcessManager
|
||||
){}
|
||||
|
||||
|
||||
|
||||
|
||||
start(){
|
||||
|
||||
|
||||
console.log(
|
||||
"Query Manager gestartet"
|
||||
);
|
||||
|
||||
|
||||
|
||||
setInterval(()=>{
|
||||
|
||||
|
||||
this.updateQueries();
|
||||
|
||||
|
||||
},5000);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async updateQueries(){
|
||||
|
||||
|
||||
|
||||
const servers =
|
||||
this.processManager.getServers();
|
||||
|
||||
|
||||
|
||||
|
||||
for(const server of servers){
|
||||
|
||||
|
||||
try {
|
||||
|
||||
|
||||
const info =
|
||||
await queryGameServerInfo(`127.0.0.1:${server.queryPort}`);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
server.players =
|
||||
info.players;
|
||||
|
||||
|
||||
|
||||
server.maxPlayers =
|
||||
info.maxPlayers;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Query:",
|
||||
server.name,
|
||||
`${server.players}/${server.maxPlayers}`
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
catch(err){
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Query fehlgeschlagen:",
|
||||
server.name
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
getStatus(){
|
||||
|
||||
|
||||
return this.processManager
|
||||
.getServers()
|
||||
.map(server => ({
|
||||
|
||||
|
||||
id:
|
||||
server.id,
|
||||
|
||||
|
||||
players:
|
||||
server.players,
|
||||
|
||||
|
||||
maxPlayers:
|
||||
server.maxPlayers
|
||||
|
||||
|
||||
}));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
import * as amqp from "amqplib";
|
||||
import { ProcessManager } from "./processManager";
|
||||
import os from "os";
|
||||
import { QueryManager } from "./queryManager";
|
||||
|
||||
|
||||
|
||||
export class StatusReporter {
|
||||
|
||||
|
||||
constructor(
|
||||
|
||||
private channel: amqp.Channel,
|
||||
|
||||
private processManager: ProcessManager,
|
||||
|
||||
private queryManager: QueryManager,
|
||||
|
||||
private wrapperId: string
|
||||
|
||||
){}
|
||||
|
||||
|
||||
|
||||
start(){
|
||||
|
||||
console.log(
|
||||
"Status Reporter gestartet"
|
||||
);
|
||||
|
||||
|
||||
setInterval(()=>{
|
||||
|
||||
|
||||
this.sendStatus();
|
||||
|
||||
|
||||
},10000);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
sendStatus(){
|
||||
|
||||
|
||||
|
||||
const processes =
|
||||
this.processManager.getStatus();
|
||||
|
||||
|
||||
const queries =
|
||||
this.queryManager.getStatus();
|
||||
|
||||
|
||||
|
||||
const servers =
|
||||
processes.map(server => {
|
||||
|
||||
|
||||
const query =
|
||||
queries.find(
|
||||
(q:any) =>
|
||||
q.id === server.id
|
||||
);
|
||||
|
||||
|
||||
|
||||
return {
|
||||
|
||||
|
||||
...server,
|
||||
|
||||
|
||||
players:
|
||||
query?.players ?? 0,
|
||||
|
||||
|
||||
maxPlayers:
|
||||
query?.maxPlayers ?? 0
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const status = {
|
||||
|
||||
|
||||
type:
|
||||
"wrapper.status",
|
||||
|
||||
|
||||
wrapperId:
|
||||
this.wrapperId,
|
||||
|
||||
|
||||
servers,
|
||||
|
||||
|
||||
|
||||
ramFree:
|
||||
Math.round(
|
||||
|
||||
os.freemem()
|
||||
/
|
||||
1024
|
||||
/
|
||||
1024
|
||||
|
||||
)
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this.channel.sendToQueue(
|
||||
|
||||
|
||||
"wrapper.register",
|
||||
|
||||
|
||||
|
||||
Buffer.from(
|
||||
|
||||
JSON.stringify(status)
|
||||
|
||||
)
|
||||
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(
|
||||
|
||||
"Status gesendet:",
|
||||
|
||||
servers.length,
|
||||
|
||||
"Server"
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user