From 57d63f5bf42aa1bfe9fe1872d0102e46318b05cc Mon Sep 17 00:00:00 2001 From: Justin Bosker Date: Wed, 22 Jul 2026 00:12:44 +0200 Subject: [PATCH] initial import server-manager --- .gitignore | 9 ++++-- manager/portManager-state.json | 10 ------- manager/serverRegistry-state.json | 20 ------------- manager/src/config.js | 23 +++++++++------ manager/src/config.ts | 25 +++++++++++----- manager/src/serverLifecycle.js | 37 +++++++++++++++++++++--- manager/src/serverLifecycle.ts | 46 +++++++++++++++++++++++++++--- manager/src/serverRegistry.js | 20 ++++++++++--- manager/src/serverRegistry.ts | 19 ++++++++++++ manager/src/serverStatusManager.js | 6 ++++ manager/src/serverStatusManager.ts | 10 +++++++ manager/src/web/api.js | 2 ++ package.json | 5 +++- 13 files changed, 172 insertions(+), 60 deletions(-) delete mode 100644 manager/portManager-state.json delete mode 100644 manager/serverRegistry-state.json diff --git a/.gitignore b/.gitignore index fde66fc..e82a327 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,12 @@ node_modules/ dist/ build/ -.env + *.log -.vscode/ \ No newline at end of file +.env + +*-state.json + +.vscode/ +.idea/ \ No newline at end of file diff --git a/manager/portManager-state.json b/manager/portManager-state.json deleted file mode 100644 index 6174c15..0000000 --- a/manager/portManager-state.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "usedPorts": [ - 7777, - 7778 - ], - "usedQueryPorts": [ - 27015, - 27016 - ] -} \ No newline at end of file diff --git a/manager/serverRegistry-state.json b/manager/serverRegistry-state.json deleted file mode 100644 index 5e8d955..0000000 --- a/manager/serverRegistry-state.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "servers": [ - { - "id": "srv-ba25028b", - "name": "[EU] druecktieR | Crimson", - "wrapperId": "wrp-8adcbbf2", - "port": 7777, - "queryPort": 27015, - "created": 1784578015578 - }, - { - "id": "srv-d70d5c3a", - "name": "[EU] druecktieR | Azure", - "wrapperId": "wrp-8adcbbf2", - "port": 7778, - "queryPort": 27016, - "created": 1784653753332 - } - ] -} \ No newline at end of file diff --git a/manager/src/config.js b/manager/src/config.js index 934a33d..321cded 100644 --- a/manager/src/config.js +++ b/manager/src/config.js @@ -1,14 +1,21 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.QUERY_PORT_START = exports.GAME_PORT_START = exports.DEFAULT_DESIRED_SERVERS = exports.LIFECYCLE_CRASH_TIMEOUT = exports.LIFECYCLE_SCALE_COOLDOWN = exports.LIFECYCLE_SCALE_DOWN_THRESHOLD = exports.LIFECYCLE_MIN_UPTIME = exports.LIFECYCLE_STARTUP_GRACE_PERIOD = exports.LIFECYCLE_EMPTY_TIMEOUT = exports.LIFECYCLE_CHECK_INTERVAL = exports.RABBITMQ_URL = void 0; +exports.QUERY_PORT_START = exports.GAME_PORT_START = exports.DEFAULT_DESIRED_SERVERS = exports.LIFECYCLE_CRASH_RETENTION = exports.LIFECYCLE_CRASH_TIMEOUT = exports.LIFECYCLE_SCALE_COOLDOWN = exports.LIFECYCLE_STARTUP_GRACE_PERIOD = exports.LIFECYCLE_SCALE_DOWN_THRESHOLD = exports.LIFECYCLE_MIN_UPTIME = exports.LIFECYCLE_EMPTY_TIMEOUT = exports.LIFECYCLE_CHECK_INTERVAL = exports.RABBITMQ_URL = void 0; exports.RABBITMQ_URL = "amqp://manager:gigi1337@192.168.1.74"; -exports.LIFECYCLE_CHECK_INTERVAL = 10000; -exports.LIFECYCLE_EMPTY_TIMEOUT = 300000; -exports.LIFECYCLE_STARTUP_GRACE_PERIOD = 60000; -exports.LIFECYCLE_MIN_UPTIME = 300000; -exports.LIFECYCLE_SCALE_DOWN_THRESHOLD = 0.30; -exports.LIFECYCLE_SCALE_COOLDOWN = 60000; -exports.LIFECYCLE_CRASH_TIMEOUT = 30000; +// Lifecycle +exports.LIFECYCLE_CHECK_INTERVAL = 10000; // Alle 10 Sekunden prüfen +// Scale Down +exports.LIFECYCLE_EMPTY_TIMEOUT = 300000; // 5 Minuten leer +exports.LIFECYCLE_MIN_UPTIME = 600000; // 10 Minuten Mindestlaufzeit +exports.LIFECYCLE_SCALE_DOWN_THRESHOLD = 0.30; // Unter 30% Gesamtauslastung darf abgebaut werden +// Scale Up +exports.LIFECYCLE_STARTUP_GRACE_PERIOD = 60000; // 1 Minute Schutz nach Start +exports.LIFECYCLE_SCALE_COOLDOWN = 120000; // 2 Minuten zwischen Scale-Up Aktionen +// Crash Detection +exports.LIFECYCLE_CRASH_TIMEOUT = 120000; // 2 Minuten ohne Status -> Crash +exports.LIFECYCLE_CRASH_RETENTION = 300000; // 5 Minuten sichtbar nach Crash +// Default exports.DEFAULT_DESIRED_SERVERS = 1; +// Ports exports.GAME_PORT_START = 7777; exports.QUERY_PORT_START = 27015; diff --git a/manager/src/config.ts b/manager/src/config.ts index 092d6f8..ee0628a 100644 --- a/manager/src/config.ts +++ b/manager/src/config.ts @@ -1,13 +1,24 @@ export const RABBITMQ_URL = "amqp://manager:gigi1337@192.168.1.74"; -export const LIFECYCLE_CHECK_INTERVAL = 10000; -export const LIFECYCLE_EMPTY_TIMEOUT = 300000; -export const LIFECYCLE_STARTUP_GRACE_PERIOD = 60000; -export const LIFECYCLE_MIN_UPTIME = 300000; -export const LIFECYCLE_SCALE_DOWN_THRESHOLD = 0.30; -export const LIFECYCLE_SCALE_COOLDOWN = 60000; -export const LIFECYCLE_CRASH_TIMEOUT = 30000; +// Lifecycle +export const LIFECYCLE_CHECK_INTERVAL = 10000; // Alle 10 Sekunden prüfen + +// Scale Down +export const LIFECYCLE_EMPTY_TIMEOUT = 300000; // 5 Minuten leer +export const LIFECYCLE_MIN_UPTIME = 600000; // 10 Minuten Mindestlaufzeit +export const LIFECYCLE_SCALE_DOWN_THRESHOLD = 0.30; // Unter 30% Gesamtauslastung darf abgebaut werden + +// Scale Up +export const LIFECYCLE_STARTUP_GRACE_PERIOD = 60000; // 1 Minute Schutz nach Start +export const LIFECYCLE_SCALE_COOLDOWN = 120000; // 2 Minuten zwischen Scale-Up Aktionen + +// Crash Detection +export const LIFECYCLE_CRASH_TIMEOUT = 120000; // 2 Minuten ohne Status -> Crash +export const LIFECYCLE_CRASH_RETENTION = 300000; // 5 Minuten sichtbar nach Crash + +// Default export const DEFAULT_DESIRED_SERVERS = 1; +// Ports export const GAME_PORT_START = 7777; export const QUERY_PORT_START = 27015; diff --git a/manager/src/serverLifecycle.js b/manager/src/serverLifecycle.js index 0efb81f..2705264 100644 --- a/manager/src/serverLifecycle.js +++ b/manager/src/serverLifecycle.js @@ -51,6 +51,19 @@ class ServerLifecycle { logger.info("Server RUNNING:", server.name, "PID:", server.pid); } break; + case "RECOVERING": + if (Date.now() - + server.lastSeen + > + config_1.LIFECYCLE_CRASH_TIMEOUT) { + server.status = + "CRASHED"; + server.crashedAt = + Date.now(); + this.serverRegistry.update(server); + logger.error("Server-Recovery fehlgeschlagen:", server.name); + } + break; case "RUNNING": /* Crash Detection @@ -61,6 +74,8 @@ class ServerLifecycle { config_1.LIFECYCLE_CRASH_TIMEOUT) { server.status = "CRASHED"; + server.crashedAt = + Date.now(); this.serverRegistry.update(server); logger.error("Server abgestürzt:", server.name); break; @@ -92,7 +107,20 @@ class ServerLifecycle { } break; case "CRASHED": - logger.error("Server abgestürzt erkannt:", server.name); + if (!server.crashedAt) { + server.crashedAt = + Date.now(); + this.serverRegistry.update(server); + logger.error("Server abgestürzt erkannt:", server.name); + break; + } + if (Date.now() - + server.crashedAt + > + config_1.LIFECYCLE_CRASH_RETENTION) { + logger.info("Entferne abgestürzten Server nach Grace-Zeit:", server.name); + this.serverRegistry.remove(server.id); + } break; } } @@ -102,7 +130,8 @@ class ServerLifecycle { const activeServers = this.serverRegistry .getAll() .filter(server => server.status === "RUNNING" || - server.status === "STARTING"); + server.status === "STARTING" || + server.status === "RECOVERING"); /* SLOT AUSLASTUNG */ @@ -166,14 +195,14 @@ class ServerLifecycle { const activeCount = this.serverRegistry .getAll() .filter(s => s.status === "RUNNING" || - s.status === "STARTING") + s.status === "STARTING" || + s.status === "RECOVERING") .length; if (activeCount <= this.desiredServers) { break; } logger.info("Stoppe leeren Server:", server.name); - logger.info("Stoppe leeren Server:", server.name); this.serverManager.stopServer(server.id); } } diff --git a/manager/src/serverLifecycle.ts b/manager/src/serverLifecycle.ts index 7cc5d3d..3322dee 100644 --- a/manager/src/serverLifecycle.ts +++ b/manager/src/serverLifecycle.ts @@ -9,6 +9,7 @@ import { LIFECYCLE_SCALE_DOWN_THRESHOLD, LIFECYCLE_SCALE_COOLDOWN, LIFECYCLE_CRASH_TIMEOUT, + LIFECYCLE_CRASH_RETENTION, DEFAULT_DESIRED_SERVERS } from "./config"; @@ -178,6 +179,9 @@ export class ServerLifecycle { server.status = "CRASHED"; + server.crashedAt = + Date.now(); + this.serverRegistry.update( server @@ -218,6 +222,9 @@ export class ServerLifecycle { server.status = "CRASHED"; + server.crashedAt = + Date.now(); + this.serverRegistry.update( server @@ -317,11 +324,42 @@ export class ServerLifecycle { case "CRASHED": + if(!server.crashedAt){ - logger.error( - "Server abgestürzt erkannt:", - server.name - ); + server.crashedAt = + Date.now(); + + this.serverRegistry.update( + server + ); + + logger.error( + "Server abgestürzt erkannt:", + server.name + ); + + break; + + } + + + if( + Date.now() - + server.crashedAt + > + LIFECYCLE_CRASH_RETENTION + ){ + + logger.info( + "Entferne abgestürzten Server nach Grace-Zeit:", + server.name + ); + + this.serverRegistry.remove( + server.id + ); + + } break; diff --git a/manager/src/serverRegistry.js b/manager/src/serverRegistry.js index 9b40cae..c380884 100644 --- a/manager/src/serverRegistry.js +++ b/manager/src/serverRegistry.js @@ -21,7 +21,7 @@ class ServerRegistry { port: serverData.port, queryPort: serverData.queryPort, created: serverData.created, - status: "STARTING", + status: "RECOVERING", players: 0, maxPlayers: 0, lastSeen: Date.now() @@ -96,9 +96,14 @@ class ServerRegistry { logger.debug("Server cleanly stopped:", server.name); this.remove(server.id); } - else if (server.status === "RUNNING") { + else if (server.status === "RUNNING" || + server.status === "RECOVERING") { server.status = "CRASHED"; + if (!server.crashedAt) { + server.crashedAt = + Date.now(); + } this.servers.set(server.id, server); logger.warn("Server crashed:", server.name); } @@ -117,7 +122,9 @@ class ServerRegistry { server.lastSeen = Date.now(); const oldStatus = server.status; - if (srv.alive) { + if (srv.alive && server.status !== "STOPPING") { + server.crashedAt = + undefined; if (!server.startedAt) { server.startedAt = srv.started; @@ -125,9 +132,14 @@ class ServerRegistry { server.status = "RUNNING"; } - else if (server.status === "RUNNING") { + else if (server.status === "RUNNING" || + server.status === "RECOVERING") { server.status = "CRASHED"; + if (!server.crashedAt) { + server.crashedAt = + Date.now(); + } } if (oldStatus !== server.status) { logger.debug("Status change:", server.name, oldStatus, "->", server.status); diff --git a/manager/src/serverRegistry.ts b/manager/src/serverRegistry.ts index e057a77..2d1bd5a 100644 --- a/manager/src/serverRegistry.ts +++ b/manager/src/serverRegistry.ts @@ -42,6 +42,8 @@ export interface ServerInfo { startedAt?:number; + crashedAt?:number; + lastSeen:number; @@ -357,6 +359,13 @@ export class ServerRegistry { server.status = "CRASHED"; + if(!server.crashedAt){ + + server.crashedAt = + Date.now(); + + } + this.servers.set( server.id, @@ -416,6 +425,9 @@ export class ServerRegistry { if(srv.alive && server.status !== "STOPPING"){ + server.crashedAt = + undefined; + if(!server.startedAt){ server.startedAt = @@ -435,6 +447,13 @@ export class ServerRegistry { server.status = "CRASHED"; + if(!server.crashedAt){ + + server.crashedAt = + Date.now(); + + } + } diff --git a/manager/src/serverStatusManager.js b/manager/src/serverStatusManager.js index c1bb81a..22961da 100644 --- a/manager/src/serverStatusManager.js +++ b/manager/src/serverStatusManager.js @@ -35,6 +35,8 @@ class ServerStatusManager { return; server.status = "RUNNING"; + server.crashedAt = + undefined; if (!server.startedAt) { server.startedAt = Date.now(); @@ -48,6 +50,10 @@ class ServerStatusManager { return; server.status = "CRASHED"; + if (!server.crashedAt) { + server.crashedAt = + Date.now(); + } this.registry.update(server); logger.info("Server stopped:", data.serverId); } diff --git a/manager/src/serverStatusManager.ts b/manager/src/serverStatusManager.ts index 7e9393a..3297df2 100644 --- a/manager/src/serverStatusManager.ts +++ b/manager/src/serverStatusManager.ts @@ -160,6 +160,9 @@ export class ServerStatusManager { server.status = "RUNNING"; + server.crashedAt = + undefined; + if(!server.startedAt){ @@ -221,6 +224,13 @@ export class ServerStatusManager { server.status = "CRASHED"; + if(!server.crashedAt){ + + server.crashedAt = + Date.now(); + + } + diff --git a/manager/src/web/api.js b/manager/src/web/api.js index 0af309b..1e8843c 100644 --- a/manager/src/web/api.js +++ b/manager/src/web/api.js @@ -76,6 +76,7 @@ class WebApi { name: server.name, status: server.status, wrapperId: server.wrapperId, + pid: server.pid, port: server.port, queryPort: server.queryPort, players: server.players, @@ -95,6 +96,7 @@ class WebApi { name: server.name, status: server.status, wrapperId: server.wrapperId, + pid: server.pid, port: server.port, queryPort: server.queryPort, players: server.players, diff --git a/package.json b/package.json index 59ea356..bc72b05 100644 --- a/package.json +++ b/package.json @@ -6,5 +6,8 @@ "wrapper", "shared", "dashboard" - ] + ], + "allowScripts": { + "esbuild@0.21.5": true + } }