Server-Neustart im laufenden Match: Map wieder laden, volle Leerlauf-Frist ab Start

This commit is contained in:
mregosar committed 2026-10-29 16:09:45 +01:00
1 parent 1b3536452c
commit dfcca1be82
2 files changed
+37 -4

No files matched your search

+6 -4
View File
@@ -392,7 +392,8 @@ export class ServerManager {
maybeConfigure(matchId) {
const match = this.service.getMatch(matchId);
const row = this.row(matchId);
if (match.status === 'server_setup' && row?.status === 'ready') {
// Auch im laufenden Match: nach einem Neustart des Servers die aktuelle Map samt Passwort wieder laden
if (['server_setup', 'live'].includes(match.status) && row?.status === 'ready') {
this.configure(matchId).catch((err) => this.logger.error(err));
}
}
@@ -467,7 +468,7 @@ export class ServerManager {
await this.provision(matchId);
return;
}
if (row.status === 'config_failed' || (row.status === 'ready' && match.status === 'server_setup')) {
if (row.status === 'config_failed' || (row.status === 'ready' && ['server_setup', 'live'].includes(match.status))) {
this.service.log(matchId, 'server', 'Organisator wiederholt die Konfiguration');
this.configure(matchId).catch((err) => this.logger.error(err));
return;
@@ -591,11 +592,12 @@ export class ServerManager {
continue;
}
const webActive = this.service.presence(row.match_id).size > 0;
let lastUse = match.last_activity_at;
// Ein (neu) gestarteter Server zählt als Nutzung, sonst stoppt ein Neustart nach langer Pause sofort wieder
let lastUse = Math.max(match.last_activity_at, row.started_at ?? 0);
if (row.status === 'configured' && row.player_count !== null) {
// Echter Server mit bekannter Spielerzahl: entscheidend ist, ob jemand auf dem Server ist.
if (row.player_count > 0) continue;
lastUse = Math.max(row.configured_at ?? 0, row.last_player_seen_at ?? 0);
lastUse = Math.max(row.configured_at ?? 0, row.last_player_seen_at ?? 0, row.started_at ?? 0);
} else if (webActive) {
continue;
}
+31
View File
@@ -477,3 +477,34 @@ test('Spieler verlassen die Lobby, Captain geht über', async () => {
await close();
}
});
test('Neustart im laufenden Match: Map wird wieder geladen, Server läuft die volle Frist', async () => {
const { app, call, close } = await setup({ idleTimeoutMinutes: 30 });
try {
const { id, org, tokens } = await readyWingmanMatch(call, 'bo1');
await call('POST', `/matches/${id}/veto/start`, { token: org });
await playVeto(call, id, { A: tokens.a1, B: tokens.b1 });
await app.manager.poll();
await waitFor(() => app.service.getMatch(id).status === 'live', { label: 'live' });
// Lange nichts los, dann stoppt der Server
const hourAgo = Date.now() - 60 * 60_000;
app.db.prepare('UPDATE matches SET last_activity_at = ? WHERE id = ?').run(hourAgo, id);
await call('POST', `/matches/${id}/server/stop`, { token: org });
await waitFor(() => app.service.serverRow(id).status === 'stopped', { label: 'gestoppt' });
// Neustart durch den Organisator: Server fährt hoch und lädt die Map wieder (Match ist weiter "live")
const res = await call('POST', `/matches/${id}/server/retry`, { token: org });
assert.equal(res.status, 200, JSON.stringify(res.body));
await waitFor(async () => { await app.manager.poll(); return app.service.serverRow(id).status === 'configured'; }, { label: 'neu konfiguriert' });
assert.equal(app.service.getMatch(id).status, 'live');
// Die Abschaltung darf den frisch gestarteten Server nicht sofort wieder stoppen
await app.manager.reap();
assert.equal(app.service.serverRow(id).status, 'configured');
// Nach Ablauf der Frist ab dem Neustart dagegen schon
await app.manager.reap(Date.now() + 31 * 60_000);
assert.equal(app.service.serverRow(id).status, 'stopped');
} finally {
await close();
}
});