Add Hytale server support with tmux runtime
All checks were successful
Deploy GSM / deploy (push) Successful in 29s

- Add tmux runtime support to ssh.js (status, start, stop, logs, uptime)
- Add Hytale server configuration to config.json
- Add Hytale server info and logo to frontend (ServerCard, ServerDetail)
- Add Hytale emoji to Discord notification mapping
- Update documentation with Hytale server details

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-15 13:39:58 +01:00
parent 8af57aa81a
commit b2dde62476
7 changed files with 74 additions and 2 deletions

View File

@@ -94,6 +94,15 @@ export async function getServerStatus(server) {
if (proc.pm2_env.status === "stopping") return "stopping";
return "offline";
} catch { return "offline"; }
} else if (server.runtime === 'tmux') {
const result = await ssh.execCommand(`tmux has-session -t ${server.tmuxName} 2>/dev/null && echo running || echo stopped`);
if (result.stdout.trim() === 'running') {
const uptimeResult = await ssh.execCommand(`ps -o etimes= -p $(tmux list-panes -t ${server.tmuxName} -F '#{pane_pid}' 2>/dev/null | head -1) 2>/dev/null | head -1`);
const uptime = parseInt(uptimeResult.stdout.trim()) || 999;
if (uptime < 60) return 'starting';
return 'online';
}
return 'offline';
} else {
const result = await ssh.execCommand(`screen -ls | grep -E "\\.${server.screenName}[[:space:]]"`);
if (result.code === 0) {
@@ -142,6 +151,10 @@ export async function startServer(server, options = {}) {
} else if (server.runtime === 'pm2') {
const nvmPrefix = "source ~/.nvm/nvm.sh && ";
await ssh.execCommand(nvmPrefix + "pm2 start " + server.serviceName);
} else if (server.runtime === 'tmux') {
await ssh.execCommand(`tmux kill-session -t ${server.tmuxName} 2>/dev/null || true`);
await new Promise(resolve => setTimeout(resolve, 1000));
await ssh.execCommand(`cd ${server.workDir} && tmux new-session -d -s ${server.tmuxName} '${server.startCmd}'`);
} else {
await ssh.execCommand(`screen -S ${server.screenName} -X quit 2>/dev/null`);
await new Promise(resolve => setTimeout(resolve, 1000));
@@ -159,6 +172,24 @@ export async function stopServer(server) {
} else if (server.runtime === 'pm2') {
const nvmPrefix = "source ~/.nvm/nvm.sh && ";
await ssh.execCommand(nvmPrefix + "pm2 stop " + server.serviceName);
} else if (server.runtime === 'tmux') {
// Send stop command via tmux
const stopCmd = server.stopCmd || 'stop';
await ssh.execCommand(`tmux send-keys -t ${server.tmuxName} '${stopCmd}' Enter`);
for (let i = 0; i < 30; i++) {
await new Promise(resolve => setTimeout(resolve, 2000));
const check = await ssh.execCommand(`tmux has-session -t ${server.tmuxName} 2>/dev/null && echo running || echo stopped`);
if (check.stdout.trim() === 'stopped') {
console.log(`Server ${server.id} stopped after ${(i + 1) * 2} seconds`);
return;
}
}
console.log(`Force killing ${server.id} after timeout`);
await ssh.execCommand(`tmux kill-session -t ${server.tmuxName} 2>/dev/null || true`);
await new Promise(resolve => setTimeout(resolve, 2000));
return;
} else {
// Different stop commands per server type
const stopCmd = server.type === 'zomboid' ? 'quit' : 'stop';
@@ -203,6 +234,19 @@ export async function getConsoleLog(server, lines = 50) {
const nvmPrefix = "source ~/.nvm/nvm.sh && ";
const result = await ssh.execCommand(nvmPrefix + "pm2 logs " + server.serviceName + " --lines " + lines + " --nostream");
return result.stdout || result.stderr;
} else if (server.runtime === 'tmux') {
// For tmux, use logCmd, logFile, or capture pane content
if (server.logCmd) {
const result = await ssh.execCommand(`${server.logCmd} ${lines} 2>/dev/null || echo No log file found`);
return result.stdout;
} else if (server.logFile) {
const result = await ssh.execCommand(`tail -n ${lines} ${server.logFile} 2>/dev/null || echo No log file found`);
return result.stdout;
} else {
// Capture tmux pane content (last N lines)
const result = await ssh.execCommand(`tmux capture-pane -t ${server.tmuxName} -p -S -${lines} 2>/dev/null || echo Session not found`);
return result.stdout || 'No logs available';
}
} else if (server.logFile) {
const result = await ssh.execCommand(`tail -n ${lines} ${server.logFile} 2>/dev/null || echo No log file found`);
return result.stdout;
@@ -241,6 +285,11 @@ export async function getProcessUptime(server) {
}
} catch {}
return 0;
} else if (server.runtime === "tmux") {
const result = await ssh.execCommand(`ps -o etimes= -p $(tmux list-panes -t ${server.tmuxName} -F '#{pane_pid}' 2>/dev/null | head -1) 2>/dev/null | head -1`);
const uptime = parseInt(result.stdout.trim());
if (!isNaN(uptime)) return uptime;
return 0;
} else {
const result = await ssh.execCommand(`ps -o etimes= -p $(pgrep -f "SCREEN.*${server.screenName}") 2>/dev/null | head -1`);
const uptime = parseInt(result.stdout.trim());