Reuse heartbeat container state and batch host RCON status
This commit is contained in:
@@ -367,6 +367,30 @@ export function formatRawReply(buf: Buffer): string {
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
export interface AgentRconReply { ok?: boolean; reason?: string; text: string }
|
||||
|
||||
export function agentRconStatusResult(reply: AgentRconReply, endpoint: { host: string; port: number }): RconStatusResult {
|
||||
if (reply.ok === false) {
|
||||
const failures: Record<string, { reason: RconFailure; detail: string }> = {
|
||||
unconfigured: { reason: 'no-config', detail: 'Agent RCON password is not configured.' },
|
||||
unreachable: { reason: 'send-error', detail: 'Agent could not reach the local RCON endpoint.' },
|
||||
timeout: { reason: 'timeout', detail: 'Agent received no reply from the local RCON endpoint.' },
|
||||
authentication: { reason: 'bad-reply', detail: 'wrong RCON password' },
|
||||
'invalid-reply': { reason: 'bad-reply', detail: 'Agent RCON reply is missing the 0xff prefix.' }
|
||||
};
|
||||
return { ok: false, endpoint, ...(failures[reply.reason ?? ''] ?? {
|
||||
reason: 'bad-reply' as const, detail: 'Agent RCON query failed.'
|
||||
}) };
|
||||
}
|
||||
const players = parsePlayerCount(reply.text);
|
||||
if (players === null) return { ok: false, endpoint, reason: 'bad-reply', detail: 'Agent returned an unrecognized RCON status.', rawReply: reply.text.slice(0, 4000) };
|
||||
const info = parseInfoLine(reply.text); const header = parseStatusHeader(reply.text);
|
||||
return { ok: true, endpoint, status: { players,
|
||||
capacity: positiveCapacity(info.sv_maxclients ?? header.sv_maxclients),
|
||||
map: info.mapname ?? info.sv_mapname ?? header.map ?? header.mapname ?? header.sv_mapname ?? null,
|
||||
gametype: info.g_gametype ?? header.g_gametype ?? header.gametype ?? null, roster: parsePlayerRoster(reply.text) }, rawReply: reply.text.slice(0, 4000) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Full RCON status probe with structured diagnostics. Callers get either
|
||||
* (ok, endpoint, status) or (ok:false, endpoint?, reason, detail). This is
|
||||
@@ -383,25 +407,7 @@ export async function rconStatusResult(server: string): Promise<RconStatusResult
|
||||
const request = (async (): Promise<RconStatusResult> => {
|
||||
try {
|
||||
const reply = await nativeRequest<{ ok?: boolean; reason?: string; text: string }>(native, 'rcon.execute', { command: 'status' });
|
||||
if (reply.ok === false) {
|
||||
const failures: Record<string, { reason: RconFailure; detail: string }> = {
|
||||
unconfigured: { reason: 'no-config', detail: 'Agent RCON password is not configured.' },
|
||||
unreachable: { reason: 'send-error', detail: 'Agent could not reach the local RCON endpoint.' },
|
||||
timeout: { reason: 'timeout', detail: 'Agent received no reply from the local RCON endpoint.' },
|
||||
authentication: { reason: 'bad-reply', detail: 'wrong RCON password' },
|
||||
'invalid-reply': { reason: 'bad-reply', detail: 'Agent RCON reply is missing the 0xff prefix.' }
|
||||
};
|
||||
return { ok: false, endpoint, ...(failures[reply.reason ?? ''] ?? {
|
||||
reason: 'bad-reply' as const, detail: 'Agent RCON query failed.'
|
||||
}) };
|
||||
}
|
||||
const players = parsePlayerCount(reply.text);
|
||||
if (players === null) return { ok: false, endpoint, reason: 'bad-reply', detail: 'Agent returned an unrecognized RCON status.', rawReply: reply.text.slice(0, 4000) };
|
||||
const info = parseInfoLine(reply.text); const header = parseStatusHeader(reply.text);
|
||||
return { ok: true, endpoint, status: { players,
|
||||
capacity: positiveCapacity(info.sv_maxclients ?? header.sv_maxclients),
|
||||
map: info.mapname ?? info.sv_mapname ?? header.map ?? header.mapname ?? header.sv_mapname ?? null,
|
||||
gametype: info.g_gametype ?? header.g_gametype ?? header.gametype ?? null, roster: parsePlayerRoster(reply.text) }, rawReply: reply.text.slice(0, 4000) };
|
||||
return agentRconStatusResult(reply, endpoint);
|
||||
} catch (error) { return { ok: false, endpoint, reason: 'timeout', detail: (error as Error).message }; }
|
||||
})().finally(() => statusInFlight.delete(key));
|
||||
statusInFlight.set(key, request); return request;
|
||||
|
||||
Reference in New Issue
Block a user