Files
mini-windrose-manager/dist/ServerManager.js
T

171 lines
6.2 KiB
JavaScript

import { mkdirSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { serverRoot, serverInstallDir, serverConfigDir, serverConfigFile, worldConfigFile, serverVersionFile, serverEventsFile, } from './paths.js';
import { installOrUpdateServer, readInstalledVersion } from './SteamCmd.js';
import { ProcessSupervisor } from './ProcessSupervisor.js';
export class ServerManager {
defaultAppId;
servers = new Map();
supervisor = new ProcessSupervisor();
constructor(defaultAppId) {
this.defaultAppId = defaultAppId;
// Crash detection + event logging + conditional auto-restart
this.supervisor.onCrash((id, exitCode) => {
const cfg = this.servers.get(id);
if (!cfg)
return;
// Log crash event
this.appendEvent(id, {
type: 'crash',
exitCode,
autoRestart: cfg.autoRestart,
});
// Conditional auto-restart
if (cfg.autoRestart) {
this.restart(id).catch((err) => {
this.appendEvent(id, {
type: 'autoRestartFailed',
error: err.message,
});
});
}
});
}
list() {
return Array.from(this.servers.values()).map((cfg) => ({
...cfg,
process: this.supervisor.getInfo(cfg.id) ?? null,
version: this.readVersion(cfg.id),
}));
}
get(id) {
const cfg = this.servers.get(id);
if (!cfg)
return null;
return {
...cfg,
process: this.supervisor.getInfo(id) ?? null,
version: this.readVersion(id),
};
}
readVersion(id) {
if (!existsSync(serverVersionFile(id)))
return null;
return JSON.parse(readFileSync(serverVersionFile(id), 'utf-8'));
}
writeVersion(id, version) {
writeFileSync(serverVersionFile(id), JSON.stringify(version, null, 2));
}
appendEvent(id, event) {
const file = serverEventsFile(id);
let events = [];
if (existsSync(file)) {
events = JSON.parse(readFileSync(file, 'utf-8'));
}
events.push({
timestamp: Date.now(),
...event,
});
writeFileSync(file, JSON.stringify(events, null, 2));
}
async getEvents(id) {
const file = serverEventsFile(id);
if (!existsSync(file))
return [];
return JSON.parse(readFileSync(file, 'utf-8'));
}
async create(id, name) {
if (this.servers.has(id)) {
throw new Error(`Server ${id} already exists`);
}
mkdirSync(serverRoot(id), { recursive: true });
mkdirSync(serverInstallDir(id), { recursive: true });
mkdirSync(serverConfigDir(id), { recursive: true });
const cfg = {
id,
name: name ?? `Windrose Server ${id}`,
installDir: serverInstallDir(id),
configDir: serverConfigDir(id),
appId: this.defaultAppId,
enabled: true,
autoRestart: false, // default
};
this.servers.set(id, cfg);
if (!existsSync(serverConfigFile(id))) {
writeFileSync(serverConfigFile(id), JSON.stringify({ serverName: cfg.name }, null, 2));
}
if (!existsSync(worldConfigFile(id))) {
writeFileSync(worldConfigFile(id), JSON.stringify({ worldName: `${cfg.name} World` }, null, 2));
}
await installOrUpdateServer(id, cfg.appId);
const version = await readInstalledVersion(id);
this.writeVersion(id, version);
return this.get(id);
}
// ---------------------------------------------------------
// CONFIG READ
// ---------------------------------------------------------
readConfig(id) {
if (!this.servers.has(id)) {
throw new Error(`Server ${id} not found`);
}
const serverCfg = JSON.parse(readFileSync(serverConfigFile(id), 'utf-8'));
const worldCfg = JSON.parse(readFileSync(worldConfigFile(id), 'utf-8'));
return { serverCfg, worldCfg };
}
// ---------------------------------------------------------
// CONFIG UPDATE
// ---------------------------------------------------------
async updateConfig(id, serverCfg, worldCfg) {
if (!this.servers.has(id)) {
throw new Error(`Server ${id} not found`);
}
writeFileSync(serverConfigFile(id), JSON.stringify(serverCfg, null, 2));
writeFileSync(worldConfigFile(id), JSON.stringify(worldCfg, null, 2));
return this.get(id);
}
async setAutoRestart(id, enabled) {
const cfg = this.servers.get(id);
if (!cfg)
throw new Error(`Server ${id} not found`);
cfg.autoRestart = enabled;
this.servers.set(id, cfg);
this.appendEvent(id, {
type: 'autoRestartChanged',
enabled,
});
return this.get(id);
}
async start(id) {
const cfg = this.servers.get(id);
if (!cfg)
throw new Error(`Server ${id} not found`);
const exePath = join(cfg.installDir, 'R5', 'Binaries', 'Win64', 'WindroseServer-Win64-Shipping.exe');
await installOrUpdateServer(id, cfg.appId);
const version = await readInstalledVersion(id);
this.writeVersion(id, version);
await this.supervisor.start(id, exePath, []);
return this.get(id);
}
async stop(id) {
await this.supervisor.stop(id);
return this.get(id);
}
async restart(id) {
const cfg = this.servers.get(id);
if (!cfg)
throw new Error(`Server ${id} not found`);
const exePath = join(cfg.installDir, 'R5', 'Binaries', 'Win64', 'WindroseServer-Win64-Shipping.exe');
await installOrUpdateServer(id, cfg.appId);
const version = await readInstalledVersion(id);
this.writeVersion(id, version);
await this.supervisor.restart(id, exePath, []);
return this.get(id);
}
async delete(id) {
await this.stop(id);
this.servers.delete(id);
return true;
}
}