diff --git a/gsm-backend/services/ssh.js b/gsm-backend/services/ssh.js index c74c465..c0f3e75 100644 --- a/gsm-backend/services/ssh.js +++ b/gsm-backend/services/ssh.js @@ -701,14 +701,13 @@ 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 joined" / "User connected" - // "User left" / "User disconnected" - // "Player '' connected" / "Player '' disconnected" + // SE (via SpaceEngineersDedicated.exe under wine) emits: + // Join: "OnConnectedClient attempt" + // Leave (resolved): "User left " + // Leave (unresolved): "User left [...]" (failed/kicked attempt) 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` + `docker logs --tail 3000 ${server.containerName} 2>&1 | ` + + `grep -E "OnConnectedClient .* attempt|User left " | tail -300` ); const players = new Map(); @@ -716,10 +715,20 @@ export async function getSpaceEngineersPlayers(server) { 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; } + if ((m = line.match(/OnConnectedClient\s+(\S+)\s+attempt/))) { + players.set(m[1], true); + continue; + } + if ((m = line.match(/User left\s+(.+?)\s*$/))) { + const token = m[1].trim(); + if (token.startsWith('[')) { + // unresolved leave (failed connection) — drop oldest pending entry + const firstKey = players.keys().next().value; + if (firstKey !== undefined) players.delete(firstKey); + } else { + players.delete(token); + } + } } const playerList = Array.from(players.keys());