Initial project structure
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.env
|
||||||
|
*.log
|
||||||
|
|
||||||
|
.vscode/
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Server Manager
|
||||||
|
|
||||||
|
Projekt zur Verwaltung von Dedicated Game Servern.
|
||||||
|
|
||||||
|
## Komponenten
|
||||||
|
|
||||||
|
- Manager
|
||||||
|
- Wrapper
|
||||||
|
|
||||||
|
## Architektur
|
||||||
|
|
||||||
|
Manager steuert Wrapper über RabbitMQ.
|
||||||
Generated
+237
@@ -0,0 +1,237 @@
|
|||||||
|
{
|
||||||
|
"name": "manager",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "manager",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"amqplib": "^2.0.1"
|
||||||
|
},
|
||||||
|
"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/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,21 @@
|
|||||||
|
{
|
||||||
|
"name": "manager",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "ts-node src/index.ts"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"amqplib": "^2.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/amqplib": "^0.10.8",
|
||||||
|
"@types/node": "^26.1.1",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import * as amqp from "amqplib";
|
||||||
|
|
||||||
|
|
||||||
|
export class CommandService {
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private channel: amqp.Channel
|
||||||
|
){}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async send(
|
||||||
|
queue:string,
|
||||||
|
command:any
|
||||||
|
){
|
||||||
|
|
||||||
|
|
||||||
|
await this.channel.sendToQueue(
|
||||||
|
|
||||||
|
queue,
|
||||||
|
|
||||||
|
Buffer.from(
|
||||||
|
JSON.stringify(command)
|
||||||
|
)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Command gesendet:",
|
||||||
|
command
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import { Rabbit } from "./rabbit";
|
||||||
|
import { WrapperManager } from "./wrapperManager";
|
||||||
|
import { WrapperRegistry } from "./wrapperRegistry";
|
||||||
|
import { ServerRegistry } from "./serverRegistry";
|
||||||
|
import { ServerManager } from "./serverManager";
|
||||||
|
import { PortManager } from "./portManager";
|
||||||
|
import { ServerStatusManager } from "./serverStatusManager";
|
||||||
|
import { ServerLifecycle } from "./serverLifecycle";
|
||||||
|
|
||||||
|
|
||||||
|
const wrapperRegistry =
|
||||||
|
new WrapperRegistry();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const serverRegistry =
|
||||||
|
new ServerRegistry();
|
||||||
|
|
||||||
|
const portManager =
|
||||||
|
new PortManager();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function main(){
|
||||||
|
|
||||||
|
|
||||||
|
const rabbit =
|
||||||
|
new Rabbit();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await rabbit.connect();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const wrapperManager =
|
||||||
|
new WrapperManager(
|
||||||
|
|
||||||
|
rabbit,
|
||||||
|
|
||||||
|
wrapperRegistry,
|
||||||
|
|
||||||
|
serverRegistry
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await wrapperManager.start();
|
||||||
|
|
||||||
|
|
||||||
|
const serverManager =
|
||||||
|
new ServerManager(
|
||||||
|
|
||||||
|
rabbit,
|
||||||
|
|
||||||
|
wrapperRegistry,
|
||||||
|
|
||||||
|
serverRegistry,
|
||||||
|
|
||||||
|
portManager
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
const lifecycle =
|
||||||
|
new ServerLifecycle(
|
||||||
|
|
||||||
|
serverRegistry,
|
||||||
|
|
||||||
|
serverManager
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
lifecycle.start();
|
||||||
|
|
||||||
|
const statusManager =
|
||||||
|
new ServerStatusManager(
|
||||||
|
|
||||||
|
rabbit,
|
||||||
|
|
||||||
|
serverRegistry
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
await statusManager.start();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Manager gestartet"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
main();
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
const locations = [
|
||||||
|
|
||||||
|
"Crimson",
|
||||||
|
"Azure",
|
||||||
|
"Emerald",
|
||||||
|
"Amber",
|
||||||
|
"Sapphire",
|
||||||
|
"Ruby",
|
||||||
|
"Obsidian",
|
||||||
|
"Pearl",
|
||||||
|
"Onyx",
|
||||||
|
"Ivory",
|
||||||
|
"Copper",
|
||||||
|
"Platinum",
|
||||||
|
"Titanium",
|
||||||
|
"Neon",
|
||||||
|
"Shadow",
|
||||||
|
"Frost",
|
||||||
|
"Solar",
|
||||||
|
"Void",
|
||||||
|
"Storm",
|
||||||
|
"Ocean"
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function generateServerName(
|
||||||
|
existingNames:string[]
|
||||||
|
){
|
||||||
|
|
||||||
|
|
||||||
|
for(const location of locations){
|
||||||
|
|
||||||
|
|
||||||
|
const name =
|
||||||
|
"[EU] druecktieR | " + location;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!existingNames.includes(name)){
|
||||||
|
|
||||||
|
|
||||||
|
return name;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Falls alle Namen vergeben sind
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
"[EU] druecktieR | " +
|
||||||
|
|
||||||
|
Math.random()
|
||||||
|
.toString(36)
|
||||||
|
.substring(2,6)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
export class PortManager {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private usedPorts =
|
||||||
|
new Set<number>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private usedQueryPorts =
|
||||||
|
new Set<number>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private gamePortStart =
|
||||||
|
7777;
|
||||||
|
|
||||||
|
|
||||||
|
private queryPortStart =
|
||||||
|
27015;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
allocate(){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let port =
|
||||||
|
this.gamePortStart;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
while(
|
||||||
|
this.usedPorts.has(port)
|
||||||
|
){
|
||||||
|
|
||||||
|
port++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let queryPort =
|
||||||
|
this.queryPortStart;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
while(
|
||||||
|
this.usedQueryPorts.has(queryPort)
|
||||||
|
){
|
||||||
|
|
||||||
|
queryPort++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.usedPorts.add(
|
||||||
|
port
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
this.usedQueryPorts.add(
|
||||||
|
queryPort
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
|
||||||
|
port,
|
||||||
|
|
||||||
|
|
||||||
|
queryPort
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
release(
|
||||||
|
port:number,
|
||||||
|
queryPort:number
|
||||||
|
){
|
||||||
|
|
||||||
|
|
||||||
|
this.usedPorts.delete(
|
||||||
|
port
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
this.usedQueryPorts.delete(
|
||||||
|
queryPort
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import * as amqp from "amqplib";
|
||||||
|
|
||||||
|
|
||||||
|
export class Rabbit {
|
||||||
|
|
||||||
|
connection!: amqp.ChannelModel;
|
||||||
|
|
||||||
|
channel!: amqp.Channel;
|
||||||
|
|
||||||
|
|
||||||
|
async connect(){
|
||||||
|
|
||||||
|
this.connection =
|
||||||
|
await amqp.connect(
|
||||||
|
"amqp://manager:gigi1337@192.168.1.74"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
this.channel =
|
||||||
|
await this.connection.createChannel();
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"RabbitMQ verbunden"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,670 @@
|
|||||||
|
import { ServerRegistry } from "./serverRegistry";
|
||||||
|
import { ServerManager } from "./serverManager";
|
||||||
|
|
||||||
|
|
||||||
|
export class ServerLifecycle {
|
||||||
|
|
||||||
|
|
||||||
|
private creatingServers = 0;
|
||||||
|
|
||||||
|
|
||||||
|
private emptyTimeout = 300000; // 5 Minuten
|
||||||
|
|
||||||
|
|
||||||
|
private lastScaleUp = 0;
|
||||||
|
|
||||||
|
|
||||||
|
private scaleCooldown = 60000; // 1 Minute
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
|
||||||
|
private serverRegistry: ServerRegistry,
|
||||||
|
|
||||||
|
private serverManager: ServerManager,
|
||||||
|
|
||||||
|
private desiredServers:number = 1
|
||||||
|
|
||||||
|
){}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
start(){
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Server Lifecycle gestartet"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
setInterval(()=>{
|
||||||
|
|
||||||
|
|
||||||
|
this.checkServers();
|
||||||
|
|
||||||
|
|
||||||
|
},10000);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setDesiredServers(count:number){
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Neue Soll Server Anzahl:",
|
||||||
|
count
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
this.desiredServers =
|
||||||
|
count;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
checkServers(){
|
||||||
|
|
||||||
|
|
||||||
|
const servers =
|
||||||
|
this.serverRegistry.getAll();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
STATUS AKTUALISIEREN
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
for(const server of servers){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
switch(server.status){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
case "STARTING":
|
||||||
|
|
||||||
|
|
||||||
|
if(server.pid){
|
||||||
|
|
||||||
|
|
||||||
|
server.status =
|
||||||
|
"RUNNING";
|
||||||
|
|
||||||
|
|
||||||
|
this.serverRegistry.update(
|
||||||
|
server
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Server RUNNING:",
|
||||||
|
server.name,
|
||||||
|
"PID:",
|
||||||
|
server.pid
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
case "RUNNING":
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Crash Detection
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
if(
|
||||||
|
Date.now() -
|
||||||
|
server.lastSeen
|
||||||
|
>
|
||||||
|
30000
|
||||||
|
){
|
||||||
|
|
||||||
|
|
||||||
|
server.status =
|
||||||
|
"CRASHED";
|
||||||
|
|
||||||
|
|
||||||
|
this.serverRegistry.update(
|
||||||
|
server
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Server abgestürzt:",
|
||||||
|
server.name
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Leere Server tracken
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
if(server.players === 0){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!server.emptySince){
|
||||||
|
|
||||||
|
|
||||||
|
server.emptySince =
|
||||||
|
Date.now();
|
||||||
|
|
||||||
|
|
||||||
|
this.serverRegistry.update(
|
||||||
|
server
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Server leer:",
|
||||||
|
server.name
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
|
||||||
|
|
||||||
|
if(server.emptySince){
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Server wieder aktiv:",
|
||||||
|
server.name
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
server.emptySince =
|
||||||
|
undefined;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.serverRegistry.update(
|
||||||
|
server
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
case "CRASHED":
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Server abgestürzt erkannt:",
|
||||||
|
server.name
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
AKTIVE SERVER
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const activeServers =
|
||||||
|
|
||||||
|
this.serverRegistry
|
||||||
|
.getAll()
|
||||||
|
.filter(
|
||||||
|
|
||||||
|
server =>
|
||||||
|
server.status === "RUNNING" ||
|
||||||
|
server.status === "STARTING"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
SLOT AUSLASTUNG
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const runningServers =
|
||||||
|
|
||||||
|
activeServers.filter(
|
||||||
|
|
||||||
|
server =>
|
||||||
|
server.status === "RUNNING"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const totalPlayers =
|
||||||
|
|
||||||
|
runningServers.reduce(
|
||||||
|
|
||||||
|
(sum,server)=>
|
||||||
|
sum + server.players,
|
||||||
|
|
||||||
|
0
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const totalSlots =
|
||||||
|
|
||||||
|
runningServers.reduce(
|
||||||
|
|
||||||
|
(sum,server)=>
|
||||||
|
sum + server.maxPlayers,
|
||||||
|
|
||||||
|
0
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const usage =
|
||||||
|
|
||||||
|
totalSlots > 0
|
||||||
|
|
||||||
|
?
|
||||||
|
|
||||||
|
totalPlayers / totalSlots
|
||||||
|
|
||||||
|
:
|
||||||
|
|
||||||
|
0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
|
||||||
|
"Kapazität:",
|
||||||
|
|
||||||
|
`${totalPlayers}/${totalSlots}`,
|
||||||
|
|
||||||
|
`${(usage * 100).toFixed(1)}%`
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
MINDEST SERVER ANZAHL
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const totalActive =
|
||||||
|
|
||||||
|
activeServers.length +
|
||||||
|
this.creatingServers;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(
|
||||||
|
totalActive <
|
||||||
|
this.desiredServers
|
||||||
|
){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const missing =
|
||||||
|
|
||||||
|
this.desiredServers -
|
||||||
|
totalActive;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.startServers(
|
||||||
|
missing
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
SCALE UP BEI 90%
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const shouldScale =
|
||||||
|
|
||||||
|
|
||||||
|
runningServers.length > 0 &&
|
||||||
|
|
||||||
|
|
||||||
|
this.creatingServers === 0 &&
|
||||||
|
|
||||||
|
|
||||||
|
usage >= 0.90 &&
|
||||||
|
|
||||||
|
|
||||||
|
Date.now() -
|
||||||
|
this.lastScaleUp
|
||||||
|
>
|
||||||
|
this.scaleCooldown;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(shouldScale){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
|
||||||
|
"Auslastung hoch - starte zusätzlichen Server"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.lastScaleUp =
|
||||||
|
Date.now();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.startServers(1);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
LEERE SERVER ENTFERNEN
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const removableServers =
|
||||||
|
|
||||||
|
|
||||||
|
activeServers
|
||||||
|
|
||||||
|
.filter(server =>
|
||||||
|
|
||||||
|
|
||||||
|
server.status === "RUNNING" &&
|
||||||
|
|
||||||
|
|
||||||
|
server.players === 0 &&
|
||||||
|
|
||||||
|
|
||||||
|
server.emptySince &&
|
||||||
|
|
||||||
|
|
||||||
|
Date.now() -
|
||||||
|
server.emptySince
|
||||||
|
>
|
||||||
|
this.emptyTimeout
|
||||||
|
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
.sort(
|
||||||
|
|
||||||
|
(a,b)=>
|
||||||
|
|
||||||
|
(a.emptySince ?? 0) -
|
||||||
|
(b.emptySince ?? 0)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for(const server of removableServers){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const activeCount =
|
||||||
|
|
||||||
|
this.serverRegistry
|
||||||
|
.getAll()
|
||||||
|
.filter(
|
||||||
|
|
||||||
|
s =>
|
||||||
|
s.status === "RUNNING" ||
|
||||||
|
s.status === "STARTING"
|
||||||
|
|
||||||
|
)
|
||||||
|
.length;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(
|
||||||
|
activeCount <=
|
||||||
|
this.desiredServers
|
||||||
|
){
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
|
||||||
|
"Stoppe leeren Server:",
|
||||||
|
|
||||||
|
server.name
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.serverManager.stopServer(
|
||||||
|
|
||||||
|
server.id
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private startServers(count:number){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
|
||||||
|
"Starte Server:",
|
||||||
|
|
||||||
|
count
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for(
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
i < count;
|
||||||
|
|
||||||
|
i++
|
||||||
|
|
||||||
|
){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.creatingServers++;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.serverManager
|
||||||
|
.createServer()
|
||||||
|
|
||||||
|
.then(()=>{
|
||||||
|
|
||||||
|
|
||||||
|
this.creatingServers--;
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
.catch(err=>{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.creatingServers--;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.error(
|
||||||
|
|
||||||
|
"Server Erstellung fehlgeschlagen:",
|
||||||
|
|
||||||
|
err
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
import { ServerRegistry } from "./serverRegistry";
|
||||||
|
import { Rabbit } from "./rabbit";
|
||||||
|
|
||||||
|
|
||||||
|
export class ServerLifecycleManager {
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
|
||||||
|
private serverRegistry: ServerRegistry,
|
||||||
|
|
||||||
|
private rabbit: Rabbit
|
||||||
|
|
||||||
|
){}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
start(){
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Server Lifecycle Manager gestartet"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
setInterval(()=>{
|
||||||
|
|
||||||
|
|
||||||
|
this.checkServers();
|
||||||
|
|
||||||
|
|
||||||
|
},5000);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
checkServers(){
|
||||||
|
|
||||||
|
|
||||||
|
const servers =
|
||||||
|
this.serverRegistry.getAll();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for(const server of servers){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
switch(server.status){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
case "STARTING":
|
||||||
|
|
||||||
|
|
||||||
|
this.checkStarting(
|
||||||
|
server
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
case "RUNNING":
|
||||||
|
|
||||||
|
|
||||||
|
this.checkRunning(
|
||||||
|
server
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
case "CRASHED":
|
||||||
|
|
||||||
|
|
||||||
|
this.checkOffline(
|
||||||
|
server
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
checkStarting(server:any){
|
||||||
|
|
||||||
|
|
||||||
|
// kommt später
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
checkRunning(server:any){
|
||||||
|
|
||||||
|
|
||||||
|
// kommt später
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
checkOffline(server:any){
|
||||||
|
|
||||||
|
|
||||||
|
// kommt später
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,355 @@
|
|||||||
|
import { Rabbit } from "./rabbit";
|
||||||
|
import { WrapperRegistry } from "./wrapperRegistry";
|
||||||
|
import { ServerRegistry, ServerInfo } from "./serverRegistry";
|
||||||
|
import { randomUUID } from "crypto";
|
||||||
|
import { generateServerName } from "./nameGenerator";
|
||||||
|
import { PortManager } from "./portManager";
|
||||||
|
|
||||||
|
|
||||||
|
export class ServerManager {
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
|
||||||
|
private rabbit:Rabbit,
|
||||||
|
|
||||||
|
private wrapperRegistry:WrapperRegistry,
|
||||||
|
|
||||||
|
private serverRegistry:ServerRegistry,
|
||||||
|
|
||||||
|
private portManager:PortManager
|
||||||
|
|
||||||
|
){}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async createServer(){
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
freien Wrapper suchen
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const wrapper =
|
||||||
|
this.findBestWrapper();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!wrapper){
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Kein Wrapper verfügbar"
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const serverId =
|
||||||
|
"srv-" +
|
||||||
|
randomUUID()
|
||||||
|
.slice(0,8);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const serverName =
|
||||||
|
generateServerName(
|
||||||
|
|
||||||
|
this.serverRegistry
|
||||||
|
.getAll()
|
||||||
|
.map(server => server.name)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Ports reservieren
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const ports =
|
||||||
|
this.portManager.allocate();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const server:ServerInfo = {
|
||||||
|
|
||||||
|
|
||||||
|
id:
|
||||||
|
serverId,
|
||||||
|
|
||||||
|
|
||||||
|
name:
|
||||||
|
serverName,
|
||||||
|
|
||||||
|
|
||||||
|
wrapperId:
|
||||||
|
wrapper.id,
|
||||||
|
|
||||||
|
|
||||||
|
status:
|
||||||
|
"STARTING",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
port:
|
||||||
|
ports.port,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
queryPort:
|
||||||
|
ports.queryPort,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
players:
|
||||||
|
0,
|
||||||
|
|
||||||
|
|
||||||
|
maxPlayers:
|
||||||
|
21,
|
||||||
|
|
||||||
|
created:
|
||||||
|
Date.now(),
|
||||||
|
|
||||||
|
lastSeen:
|
||||||
|
Date.now()
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Registry Eintrag
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
this.serverRegistry.add(
|
||||||
|
server
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Command an Wrapper
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const command = {
|
||||||
|
|
||||||
|
|
||||||
|
type:
|
||||||
|
"server.start",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
serverId,
|
||||||
|
|
||||||
|
|
||||||
|
name:
|
||||||
|
serverName,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
port:
|
||||||
|
ports.port,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
queryPort:
|
||||||
|
ports.queryPort
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const sent =
|
||||||
|
this.rabbit.channel.sendToQueue(
|
||||||
|
|
||||||
|
wrapper.commandQueue,
|
||||||
|
|
||||||
|
Buffer.from(
|
||||||
|
JSON.stringify(command)
|
||||||
|
)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if(!sent){
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
"Server Start Command konnte nicht gesendet werden"
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
|
||||||
|
"Server Start gesendet:",
|
||||||
|
|
||||||
|
server
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async stopServer(serverId:string){
|
||||||
|
|
||||||
|
|
||||||
|
const server =
|
||||||
|
this.serverRegistry.get(
|
||||||
|
serverId
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if(!server){
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Server nicht gefunden:",
|
||||||
|
serverId
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const wrapper =
|
||||||
|
this.wrapperRegistry.get(
|
||||||
|
server.wrapperId
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if(!wrapper){
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Wrapper nicht gefunden:",
|
||||||
|
server.wrapperId
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const command = {
|
||||||
|
|
||||||
|
|
||||||
|
type:
|
||||||
|
"server.stop",
|
||||||
|
|
||||||
|
|
||||||
|
serverId
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.rabbit.channel.sendToQueue(
|
||||||
|
|
||||||
|
wrapper.commandQueue,
|
||||||
|
|
||||||
|
Buffer.from(
|
||||||
|
JSON.stringify(command)
|
||||||
|
)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
server.status =
|
||||||
|
"STOPPING";
|
||||||
|
|
||||||
|
|
||||||
|
this.serverRegistry.update(
|
||||||
|
server
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Server Stop gesendet:",
|
||||||
|
server.name
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
findBestWrapper(){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const wrappers =
|
||||||
|
this.wrapperRegistry.getAll()
|
||||||
|
.filter(
|
||||||
|
w=>w.online
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(wrappers.length===0)
|
||||||
|
return undefined;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Einfachste Version:
|
||||||
|
|
||||||
|
wenigste Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
wrappers.sort(
|
||||||
|
|
||||||
|
(a,b)=>
|
||||||
|
|
||||||
|
a.servers.length -
|
||||||
|
b.servers.length
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return wrappers[0];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,378 @@
|
|||||||
|
export interface ServerInfo {
|
||||||
|
|
||||||
|
|
||||||
|
id:string;
|
||||||
|
|
||||||
|
name:string;
|
||||||
|
|
||||||
|
wrapperId:string;
|
||||||
|
|
||||||
|
|
||||||
|
status:
|
||||||
|
| "STARTING"
|
||||||
|
| "RUNNING"
|
||||||
|
| "STOPPING"
|
||||||
|
| "CRASHED";
|
||||||
|
|
||||||
|
|
||||||
|
port:number;
|
||||||
|
|
||||||
|
queryPort:number;
|
||||||
|
|
||||||
|
|
||||||
|
pid?:number;
|
||||||
|
|
||||||
|
|
||||||
|
players:number;
|
||||||
|
|
||||||
|
|
||||||
|
maxPlayers:number;
|
||||||
|
|
||||||
|
|
||||||
|
emptySince?:number;
|
||||||
|
|
||||||
|
|
||||||
|
created:number;
|
||||||
|
|
||||||
|
|
||||||
|
lastSeen:number;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class ServerRegistry {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private servers =
|
||||||
|
new Map<string, ServerInfo>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
add(server:ServerInfo){
|
||||||
|
|
||||||
|
|
||||||
|
this.servers.set(
|
||||||
|
|
||||||
|
server.id,
|
||||||
|
|
||||||
|
server
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Server registriert:",
|
||||||
|
server
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
get(id:string){
|
||||||
|
|
||||||
|
|
||||||
|
return this.servers.get(
|
||||||
|
id
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
getAll(){
|
||||||
|
|
||||||
|
|
||||||
|
return Array.from(
|
||||||
|
|
||||||
|
this.servers.values()
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
update(server:ServerInfo){
|
||||||
|
|
||||||
|
|
||||||
|
this.servers.set(
|
||||||
|
|
||||||
|
server.id,
|
||||||
|
|
||||||
|
server
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
remove(id:string){
|
||||||
|
|
||||||
|
|
||||||
|
this.servers.delete(
|
||||||
|
id
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Server entfernt:",
|
||||||
|
id
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
list(){
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"\n=== SERVER ÜBERSICHT ==="
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const servers =
|
||||||
|
this.getAll();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(servers.length === 0){
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Keine Server vorhanden"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for(const server of servers){
|
||||||
|
|
||||||
|
|
||||||
|
console.log({
|
||||||
|
|
||||||
|
|
||||||
|
id:
|
||||||
|
server.id,
|
||||||
|
|
||||||
|
|
||||||
|
name:
|
||||||
|
server.name,
|
||||||
|
|
||||||
|
|
||||||
|
wrapper:
|
||||||
|
server.wrapperId,
|
||||||
|
|
||||||
|
|
||||||
|
status:
|
||||||
|
server.status,
|
||||||
|
|
||||||
|
|
||||||
|
port:
|
||||||
|
server.port,
|
||||||
|
|
||||||
|
|
||||||
|
queryPort:
|
||||||
|
server.queryPort,
|
||||||
|
|
||||||
|
|
||||||
|
players:
|
||||||
|
`${server.players}/${server.maxPlayers}`
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
syncWrapperStatus(data:any){
|
||||||
|
|
||||||
|
if(!data.servers)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const runningServerIds =
|
||||||
|
new Set(
|
||||||
|
data.servers.map(
|
||||||
|
(srv:any)=>srv.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for(const server of this.servers.values()){
|
||||||
|
|
||||||
|
|
||||||
|
if(
|
||||||
|
server.wrapperId === data.wrapperId &&
|
||||||
|
!runningServerIds.has(server.id)
|
||||||
|
){
|
||||||
|
|
||||||
|
|
||||||
|
if(server.status === "STOPPING"){
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Server sauber beendet:",
|
||||||
|
server.name
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
this.remove(
|
||||||
|
server.id
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(server.status === "RUNNING"){
|
||||||
|
|
||||||
|
|
||||||
|
server.status =
|
||||||
|
"CRASHED";
|
||||||
|
|
||||||
|
|
||||||
|
this.servers.set(
|
||||||
|
server.id,
|
||||||
|
server
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Server abgestürzt:",
|
||||||
|
server.name
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for(const srv of data.servers){
|
||||||
|
const server =
|
||||||
|
this.servers.get(
|
||||||
|
srv.id
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if(!server)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
server.pid =
|
||||||
|
srv.pid;
|
||||||
|
|
||||||
|
server.players =
|
||||||
|
srv.players ?? 0;
|
||||||
|
|
||||||
|
|
||||||
|
server.maxPlayers =
|
||||||
|
srv.maxPlayers ?? 0;
|
||||||
|
|
||||||
|
|
||||||
|
server.lastSeen =
|
||||||
|
Date.now();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const oldStatus =
|
||||||
|
server.status;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(srv.alive){
|
||||||
|
|
||||||
|
server.status =
|
||||||
|
"RUNNING";
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(
|
||||||
|
server.status === "RUNNING"
|
||||||
|
){
|
||||||
|
|
||||||
|
server.status =
|
||||||
|
"CRASHED";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(oldStatus !== server.status){
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Status Änderung:",
|
||||||
|
server.name,
|
||||||
|
oldStatus,
|
||||||
|
"->",
|
||||||
|
server.status
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.servers.set(
|
||||||
|
server.id,
|
||||||
|
server
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
import { Rabbit } from "./rabbit";
|
||||||
|
import { ServerRegistry } from "./serverRegistry";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class ServerStatusManager {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
|
||||||
|
private rabbit:Rabbit,
|
||||||
|
|
||||||
|
private registry:ServerRegistry
|
||||||
|
|
||||||
|
){}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async start(){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const queue =
|
||||||
|
"server.status";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await this.rabbit.channel.assertQueue(
|
||||||
|
queue
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Warte auf Server Status..."
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.rabbit.channel.consume(
|
||||||
|
|
||||||
|
queue,
|
||||||
|
|
||||||
|
|
||||||
|
msg=>{
|
||||||
|
|
||||||
|
|
||||||
|
if(!msg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const data =
|
||||||
|
JSON.parse(
|
||||||
|
msg.content.toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
switch(data.type){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
case "server.started":
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.serverStarted(
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
case "server.stopped":
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.serverStopped(
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Unbekannter Server Status:",
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.rabbit.channel.ack(
|
||||||
|
msg
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
serverStarted(data:any){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const server =
|
||||||
|
this.registry.get(
|
||||||
|
data.serverId
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!server)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
server.status =
|
||||||
|
"RUNNING";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.registry.update(
|
||||||
|
server
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
|
||||||
|
"Server läuft:",
|
||||||
|
|
||||||
|
server.name
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
serverStopped(data:any){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const server =
|
||||||
|
this.registry.get(
|
||||||
|
data.serverId
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!server)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
server.status =
|
||||||
|
"CRASHED";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.registry.update(
|
||||||
|
server
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
|
||||||
|
"Server crashed:",
|
||||||
|
|
||||||
|
server.name
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
export interface Wrapper {
|
||||||
|
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
hostname: string;
|
||||||
|
|
||||||
|
cpu: number;
|
||||||
|
|
||||||
|
ram: number;
|
||||||
|
|
||||||
|
lastHeartbeat: number;
|
||||||
|
|
||||||
|
commandQueue: string;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface Message {
|
||||||
|
|
||||||
|
type: string;
|
||||||
|
|
||||||
|
[key:string]: any;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,615 @@
|
|||||||
|
import { Rabbit } from "./rabbit";
|
||||||
|
import { randomUUID } from "crypto";
|
||||||
|
import { WrapperRegistry, WrapperInfo } from "./wrapperRegistry";
|
||||||
|
import { ServerRegistry } from "./serverRegistry";
|
||||||
|
|
||||||
|
|
||||||
|
export class WrapperManager {
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private rabbit: Rabbit,
|
||||||
|
private registry: WrapperRegistry,
|
||||||
|
private serverRegistry: ServerRegistry
|
||||||
|
){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async start(){
|
||||||
|
|
||||||
|
|
||||||
|
const queue =
|
||||||
|
"wrapper.register";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await this.rabbit.channel.assertQueue(
|
||||||
|
queue
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Warte auf Wrapper..."
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.rabbit.channel.consume(
|
||||||
|
|
||||||
|
queue,
|
||||||
|
|
||||||
|
async msg => {
|
||||||
|
|
||||||
|
|
||||||
|
if(!msg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const data =
|
||||||
|
JSON.parse(
|
||||||
|
msg.content.toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
switch(data.type){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
case "wrapper.register":
|
||||||
|
|
||||||
|
|
||||||
|
await this.register(
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
case "wrapper.heartbeat":
|
||||||
|
|
||||||
|
|
||||||
|
this.heartbeat(
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
case "wrapper.status":
|
||||||
|
|
||||||
|
|
||||||
|
this.registry.updateStatus(
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
this.serverRegistry.syncWrapperStatus(
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Unbekannte Nachricht:",
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.rabbit.channel.ack(
|
||||||
|
msg
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Offline Check
|
||||||
|
|
||||||
|
setInterval(()=>{
|
||||||
|
|
||||||
|
|
||||||
|
this.registry.checkOffline(
|
||||||
|
30000
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
},5000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async register(data:any){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let id =
|
||||||
|
data.wrapperId;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Neue Wrapper bekommen eine ID
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(!id){
|
||||||
|
|
||||||
|
|
||||||
|
id =
|
||||||
|
"wrp-" +
|
||||||
|
randomUUID()
|
||||||
|
.slice(0,8);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Neue Wrapper ID:",
|
||||||
|
id
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const existing =
|
||||||
|
this.registry.get(
|
||||||
|
id
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Wrapper war schon bekannt
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(existing){
|
||||||
|
|
||||||
|
|
||||||
|
existing.online = true;
|
||||||
|
|
||||||
|
|
||||||
|
existing.lastHeartbeat =
|
||||||
|
Date.now();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
existing.hostname =
|
||||||
|
data.hostname;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
existing.cpu =
|
||||||
|
data.cpu;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
existing.ram =
|
||||||
|
data.ram;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
existing.ramFree =
|
||||||
|
data.ramFree ?? data.ram;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
existing.servers =
|
||||||
|
data.servers ?? existing.servers;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.registry.update(existing);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Neuer Wrapper
|
||||||
|
*/
|
||||||
|
|
||||||
|
else {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const commandQueue =
|
||||||
|
`wrapper.${id}.commands`;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await this.rabbit.channel.assertQueue(
|
||||||
|
commandQueue
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const wrapper:WrapperInfo = {
|
||||||
|
|
||||||
|
|
||||||
|
id,
|
||||||
|
|
||||||
|
|
||||||
|
hostname:
|
||||||
|
data.hostname,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
commandQueue,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cpu:
|
||||||
|
data.cpu,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ram:
|
||||||
|
data.ram,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ramFree:
|
||||||
|
data.ram,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
servers:
|
||||||
|
[],
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
online:true,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
lastHeartbeat:
|
||||||
|
Date.now()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.registry.add(
|
||||||
|
wrapper
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Antwort zurück an Wrapper
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const wrapper =
|
||||||
|
this.registry.get(
|
||||||
|
id
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(data.replyQueue){
|
||||||
|
|
||||||
|
|
||||||
|
this.rabbit.channel.sendToQueue(
|
||||||
|
|
||||||
|
|
||||||
|
data.replyQueue,
|
||||||
|
|
||||||
|
|
||||||
|
Buffer.from(
|
||||||
|
|
||||||
|
JSON.stringify({
|
||||||
|
|
||||||
|
|
||||||
|
type:
|
||||||
|
"wrapper.registered",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wrapperId:
|
||||||
|
id,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
commandQueue:
|
||||||
|
wrapper?.commandQueue
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
heartbeat(data:any){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.registry.updateHeartbeat(
|
||||||
|
|
||||||
|
data.wrapperId
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
|
||||||
|
"Heartbeat:",
|
||||||
|
|
||||||
|
data.wrapperId
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sendCommand(
|
||||||
|
|
||||||
|
wrapperId:string,
|
||||||
|
|
||||||
|
command:any
|
||||||
|
|
||||||
|
){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const wrapper =
|
||||||
|
this.registry.get(
|
||||||
|
wrapperId
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!wrapper){
|
||||||
|
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
|
||||||
|
"Wrapper nicht gefunden"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.rabbit.channel.sendToQueue(
|
||||||
|
|
||||||
|
|
||||||
|
wrapper.commandQueue,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Buffer.from(
|
||||||
|
|
||||||
|
JSON.stringify(
|
||||||
|
command
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
getWrappers(){
|
||||||
|
|
||||||
|
|
||||||
|
return this.registry.getAll();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
listWrappers(){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const wrappers =
|
||||||
|
this.registry.getAll();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
|
||||||
|
"\n======================"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
|
||||||
|
" WRAPPER ÜBERSICHT "
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
|
||||||
|
"======================\n"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(wrappers.length===0){
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
|
||||||
|
"Keine Wrapper registriert"
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for(const wrapper of wrappers){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
console.log(`
|
||||||
|
|
||||||
|
ID:
|
||||||
|
${wrapper.id}
|
||||||
|
|
||||||
|
|
||||||
|
Host:
|
||||||
|
${wrapper.hostname}
|
||||||
|
|
||||||
|
|
||||||
|
CPU:
|
||||||
|
${wrapper.cpu} Cores
|
||||||
|
|
||||||
|
|
||||||
|
RAM:
|
||||||
|
${wrapper.ram} MB
|
||||||
|
|
||||||
|
|
||||||
|
RAM frei:
|
||||||
|
${wrapper.ramFree} MB
|
||||||
|
|
||||||
|
|
||||||
|
Server:
|
||||||
|
${wrapper.servers.length}
|
||||||
|
|
||||||
|
|
||||||
|
Online:
|
||||||
|
${wrapper.online}
|
||||||
|
|
||||||
|
|
||||||
|
Heartbeat:
|
||||||
|
${new Date(
|
||||||
|
wrapper.lastHeartbeat
|
||||||
|
).toLocaleString()}
|
||||||
|
|
||||||
|
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
`);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
export interface WrapperInfo {
|
||||||
|
|
||||||
|
id:string;
|
||||||
|
|
||||||
|
hostname:string;
|
||||||
|
|
||||||
|
commandQueue:string;
|
||||||
|
|
||||||
|
cpu:number;
|
||||||
|
|
||||||
|
ram:number;
|
||||||
|
|
||||||
|
|
||||||
|
ramFree:number;
|
||||||
|
|
||||||
|
|
||||||
|
online:boolean;
|
||||||
|
|
||||||
|
lastHeartbeat:number;
|
||||||
|
|
||||||
|
|
||||||
|
servers:string[];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class WrapperRegistry {
|
||||||
|
|
||||||
|
|
||||||
|
private wrappers =
|
||||||
|
new Map<string, WrapperInfo>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
add(wrapper:WrapperInfo){
|
||||||
|
|
||||||
|
|
||||||
|
this.wrappers.set(
|
||||||
|
wrapper.id,
|
||||||
|
wrapper
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Wrapper gespeichert:",
|
||||||
|
wrapper.id
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
get(id:string){
|
||||||
|
|
||||||
|
return this.wrappers.get(id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
getAll(){
|
||||||
|
|
||||||
|
return Array.from(
|
||||||
|
this.wrappers.values()
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
updateHeartbeat(id:string){
|
||||||
|
|
||||||
|
|
||||||
|
const wrapper =
|
||||||
|
this.wrappers.get(id);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!wrapper)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wrapper.lastHeartbeat =
|
||||||
|
Date.now();
|
||||||
|
|
||||||
|
|
||||||
|
wrapper.online =
|
||||||
|
true;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
update(wrapper:WrapperInfo){
|
||||||
|
|
||||||
|
|
||||||
|
this.wrappers.set(
|
||||||
|
wrapper.id,
|
||||||
|
wrapper
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
checkOffline(timeout:number){
|
||||||
|
|
||||||
|
|
||||||
|
const now =
|
||||||
|
Date.now();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for(const wrapper of this.wrappers.values()){
|
||||||
|
|
||||||
|
|
||||||
|
if(
|
||||||
|
wrapper.online &&
|
||||||
|
now - wrapper.lastHeartbeat > timeout
|
||||||
|
){
|
||||||
|
|
||||||
|
|
||||||
|
wrapper.online = false;
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"Wrapper offline:",
|
||||||
|
wrapper.id
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatus(data:any){
|
||||||
|
|
||||||
|
|
||||||
|
const wrapper =
|
||||||
|
this.wrappers.get(
|
||||||
|
data.wrapperId
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!wrapper)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wrapper.ramFree =
|
||||||
|
data.ramFree;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wrapper.servers =
|
||||||
|
data.servers;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wrapper.lastHeartbeat =
|
||||||
|
Date.now();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wrapper.online =
|
||||||
|
true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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