Add Space Engineers server support
All checks were successful
Deploy GSM / deploy (push) Successful in 34s

Integrates Space Engineers dedicated server (docker+wine on 192.168.2.78) into GSM:
- config.json entry with docker runtime and log-based player detection
- getSpaceEngineersPlayers() parses docker logs for connect/disconnect events
- Extends type-check sites in rcon, autoshutdown, discordBot, servers routes
- Frontend ServerCard/ServerDetail logo + address wiring

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-19 20:57:14 +02:00
parent 99ca25c9e3
commit 807446920f
8 changed files with 73 additions and 8 deletions

View File

@@ -47,7 +47,7 @@ async function checkServers() {
// Get player count
// Some servers use RCON, others use log parsing (like Hytale)
let playerCount = 0;
if (server.rconPassword || server.type === 'hytale' || server.type === 'terraria') {
if (server.rconPassword || server.type === 'hytale' || server.type === 'terraria' || server.type === 'spaceengineers') {
const players = await getPlayers(server);
playerCount = players.online || 0;
}

View File

@@ -23,7 +23,8 @@ const serverDisplay = {
palworld: { name: 'Palworld', icon: '🦎', color: 0x00D4AA, address: 'palworld.zeasy.dev:8211' },
terraria: { name: 'Terraria', icon: '⚔️', color: 0x05C46B, address: 'terraria.zeasy.dev:7777' },
openttd: { name: 'OpenTTD', icon: '🚂', color: 0x1E90FF, address: 'openttd.zeasy.dev:3979' },
hytale: { name: 'Hytale', icon: '🏰', color: 0x00BFFF, address: 'hytale.zeasy.dev:5520' }
hytale: { name: 'Hytale', icon: '🏰', color: 0x00BFFF, address: 'hytale.zeasy.dev:5520' },
spaceengineers: { name: 'Space Engineers', icon: '🚀', color: 0xFFA500, address: 'spaceengineers.zeasy.dev:27016' }
};
function loadConfig() {
@@ -376,7 +377,7 @@ async function fetchServerStatuses() {
let players = { online: 0, max: null };
let playerList = { players: [] };
if (running && (server.rconPassword || server.type === 'hytale' || server.type === 'terraria')) {
if (running && (server.rconPassword || server.type === 'hytale' || server.type === 'terraria' || server.type === 'spaceengineers')) {
try {
players = await getPlayers(server);
playerList = await getPlayerList(server);

View File

@@ -1,5 +1,5 @@
import { Rcon } from 'rcon-client';
import { getHytalePlayers, getTerrariaPlayers } from './ssh.js';
import { getHytalePlayers, getTerrariaPlayers, getSpaceEngineersPlayers } from './ssh.js';
const rconConnections = new Map();
const playerCache = new Map();
@@ -118,6 +118,10 @@ export async function getPlayers(server) {
// Use log parsing for Terraria (no RCON support)
const data = await getTerrariaPlayers(server);
result = { online: data.online, max: 8 };
} else if (server.type === 'spaceengineers') {
// Use docker log parsing for Space Engineers (no RCON support)
const data = await getSpaceEngineersPlayers(server);
result = { online: data.online, max: null };
}
playerCache.set(cacheKey, { data: result, time: Date.now() });
@@ -188,6 +192,10 @@ export async function getPlayerList(server) {
// Use log parsing for Terraria (no RCON support)
const data = await getTerrariaPlayers(server);
players = data.players || [];
} else if (server.type === 'spaceengineers') {
// Use docker log parsing for Space Engineers (no RCON support)
const data = await getSpaceEngineersPlayers(server);
players = data.players || [];
}
const result = { players };

View File

@@ -694,6 +694,42 @@ export async function getTerrariaPlayers(server) {
}
}
// ============ SPACE ENGINEERS FUNCTIONS ============
// Get Space Engineers players by parsing docker logs
export async function getSpaceEngineersPlayers(server) {
try {
const ssh = await getConnection(server.host, server.sshUser);
// SE logs connect/disconnect lines through the wine-wrapped dedicated server.
// Typical patterns emitted by SpaceEngineersDedicated:
// "User <name> joined" / "User <name> connected"
// "User <name> left" / "User <name> disconnected"
// "Player '<name>' connected" / "Player '<name>' disconnected"
const result = await ssh.execCommand(
`docker logs --tail 2000 ${server.containerName} 2>&1 | ` +
`grep -iE "user .* (joined|connected|left|disconnected)|player '.*' (connected|disconnected)" | tail -200`
);
const players = new Map();
const lines = result.stdout.split('\n').filter(l => l.trim());
for (const line of lines) {
let m;
if ((m = line.match(/Player '([^']+)' connected/i))) { players.set(m[1], true); continue; }
if ((m = line.match(/Player '([^']+)' disconnected/i))) { players.delete(m[1]); continue; }
if ((m = line.match(/User\s+(.+?)\s+(?:joined|connected)/i))) { players.set(m[1].trim(), true); continue; }
if ((m = line.match(/User\s+(.+?)\s+(?:left|disconnected)/i))) { players.delete(m[1].trim()); continue; }
}
const playerList = Array.from(players.keys());
return { online: playerList.length, players: playerList };
} catch (err) {
console.error(`[SpaceEngineers] Error getting players:`, err.message);
return { online: 0, players: [] };
}
}
// ============ HYTALE FUNCTIONS ============
const HYTALE_CONFIG_PATH = "/opt/hytale/Server/config.json";
const HYTALE_LOGS_PATH = "/opt/hytale/Server/logs";