Add Terraria player detection via log parsing
All checks were successful
Deploy GSM / deploy (push) Successful in 27s

- Add getTerrariaPlayers function in ssh.js for PM2 log parsing
- Support German and English join/leave messages
- Update rcon.js to use Terraria log parsing
- Add Terraria to player fetch conditions in servers.js
- Update autoshutdown.js and discordBot.js for Terraria support
- Update config path to tModLoader directory
- Add global error handlers in server.js
- Update CLAUDE.md with deployment rules and Terraria info

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-16 21:38:32 +01:00
parent c010065963
commit df390e63e4
7 changed files with 90 additions and 11 deletions

View File

@@ -580,7 +580,7 @@ export async function writePalworldConfig(server, filename, content) {
}
// ============ TERRARIA CONFIG ============
const TERRARIA_CONFIG_PATH = "/home/terraria/serverconfig.txt";
const TERRARIA_CONFIG_PATH = "/home/terraria/tModLoader/serverconfig.txt";
export async function readTerrariaConfig(server) {
const ssh = await getConnection(server.host, server.sshUser);
@@ -598,7 +598,7 @@ export async function writeTerrariaConfig(server, content) {
// Create backup
const backupName = `serverconfig.txt.backup.${Date.now()}`;
await ssh.execCommand(`cp ${TERRARIA_CONFIG_PATH} /home/terraria/${backupName} 2>/dev/null || true`);
await ssh.execCommand(`cp ${TERRARIA_CONFIG_PATH} /home/terraria/tModLoader/${backupName} 2>/dev/null || true`);
// Write file using sftp
const sftp = await ssh.requestSFTP();
@@ -611,7 +611,7 @@ export async function writeTerrariaConfig(server, content) {
});
// Clean up old backups (keep last 5)
await ssh.execCommand(`ls -t /home/terraria/serverconfig.txt.backup.* 2>/dev/null | tail -n +6 | xargs rm -f 2>/dev/null || true`);
await ssh.execCommand(`ls -t /home/terraria/tModLoader/serverconfig.txt.backup.* 2>/dev/null | tail -n +6 | xargs rm -f 2>/dev/null || true`);
return true;
}
@@ -643,6 +643,57 @@ export async function writeOpenTTDConfig(server, content) {
return true;
}
// ============ TERRARIA FUNCTIONS ============
// Get Terraria players by parsing PM2 logs
export async function getTerrariaPlayers(server) {
try {
const ssh = await getConnection(server.host, server.sshUser);
// Get last 500 lines of PM2 logs
const nvmPrefix = "source ~/.nvm/nvm.sh && ";
const result = await ssh.execCommand(nvmPrefix + `pm2 logs ${server.serviceName} --lines 500 --nostream 2>/dev/null | grep -E "ist beigetreten|hat das Spiel verlassen|has joined|has left" | tail -100`);
const players = new Map(); // PlayerName -> true
const lines = result.stdout.split('\n').filter(l => l.trim());
for (const line of lines) {
// German: "Lokführer ist beigetreten." / "Lokführer hat das Spiel verlassen."
// English: "PlayerName has joined." / "PlayerName has left."
// Join patterns
const joinMatchDE = line.match(/^\d+\|[^\|]+\s*\|\s*(.+?)\s+ist beigetreten\.?$/i);
const joinMatchEN = line.match(/^\d+\|[^\|]+\s*\|\s*(.+?)\s+has joined\.?$/i);
if (joinMatchDE) {
players.set(joinMatchDE[1].trim(), true);
continue;
}
if (joinMatchEN) {
players.set(joinMatchEN[1].trim(), true);
continue;
}
// Leave patterns
const leaveMatchDE = line.match(/^\d+\|[^\|]+\s*\|\s*(.+?)\s+hat das Spiel verlassen\.?$/i);
const leaveMatchEN = line.match(/^\d+\|[^\|]+\s*\|\s*(.+?)\s+has left\.?$/i);
if (leaveMatchDE) {
players.delete(leaveMatchDE[1].trim());
continue;
}
if (leaveMatchEN) {
players.delete(leaveMatchEN[1].trim());
}
}
const playerList = Array.from(players.keys());
return { online: playerList.length, players: playerList };
} catch (err) {
console.error(`[Terraria] 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";