Admin: Pausenanzeige, Live-Stand und passender Umschalter
Pausierte Matches zeigen ein Badge und den Knopf Fortsetzen. Laufende Matches zeigen Map, Stand und Runde. Der Pausenzustand wird gespeichert und bei Mapwechsel oder Serverstopp zurückgesetzt.
This commit is contained in:
1 parent
da986d44c8
commit
90996d1b0a
5 files changed
+25
-5
No files matched your search
@@ -271,6 +271,9 @@ function MatchAdminCard({ m, run, busy, token, reload, setNotice }) {
|
|||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
const hasNext = m.series.some((s) => s.number === m.currentMapNumber + 1);
|
const hasNext = m.series.some((s) => s.number === m.currentMapNumber + 1);
|
||||||
|
const current = m.series.find((s) => s.number === m.currentMapNumber);
|
||||||
|
const configured = m.server?.status === 'configured';
|
||||||
|
const paused = !!m.server?.paused;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`panel admin-card status-${m.status}`}>
|
<section className={`panel admin-card status-${m.status}`}>
|
||||||
@@ -283,6 +286,14 @@ function MatchAdminCard({ m, run, busy, token, reload, setNotice }) {
|
|||||||
{m.modeLabel}, {FORMAT_LABEL[m.format]} · {m.statusLabel} · <span className={`tone-${server.tone}`}>{server.text}</span>
|
{m.modeLabel}, {FORMAT_LABEL[m.format]} · {m.statusLabel} · <span className={`tone-${server.tone}`}>{server.text}</span>
|
||||||
{m.format !== 'bo1' && <> · Serie {m.seriesScore.A}:{m.seriesScore.B}</>}
|
{m.format !== 'bo1' && <> · Serie {m.seriesScore.A}:{m.seriesScore.B}</>}
|
||||||
</div>
|
</div>
|
||||||
|
{m.status === 'live' && current && (
|
||||||
|
<div className="admin-live">
|
||||||
|
<span>Map {current.number}: {current.name}</span>
|
||||||
|
<strong>{current.score.A} : {current.score.B}</strong>
|
||||||
|
{current.live?.roundsPlayed != null && <span className="muted">Runde {current.live.roundsPlayed + 1}</span>}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{paused && <span className="admin-badge is-paused">Pausiert</span>}
|
||||||
</div>
|
</div>
|
||||||
<button className="link-btn" onClick={() => setOpen(!open)}>{open ? 'Weniger' : 'Details'}</button>
|
<button className="link-btn" onClick={() => setOpen(!open)}>{open ? 'Weniger' : 'Details'}</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -291,8 +302,8 @@ function MatchAdminCard({ m, run, busy, token, reload, setNotice }) {
|
|||||||
<Link className="btn btn-sm" to={`/m/${id}`}>Ansehen</Link>
|
<Link className="btn btn-sm" to={`/m/${id}`}>Ansehen</Link>
|
||||||
{m.status === 'lobby' && btn('Veto starten', { path: '/veto/start' }, 'btn-primary')}
|
{m.status === 'lobby' && btn('Veto starten', { path: '/veto/start' }, 'btn-primary')}
|
||||||
{(m.server?.status === 'configured') && btn('Aufwärmphase beenden', { path: '/server/warmup-end' })}
|
{(m.server?.status === 'configured') && btn('Aufwärmphase beenden', { path: '/server/warmup-end' })}
|
||||||
{(m.server?.status === 'configured') && btn('Pausieren', { path: '/server/pause', confirm: 'Match pausieren?' })}
|
{configured && !paused && btn('Pausieren', { path: '/server/pause', confirm: 'Match pausieren?' }, 'btn-warn')}
|
||||||
{(m.server?.status === 'configured') && btn('Fortsetzen', { path: '/server/resume' })}
|
{configured && paused && btn('Fortsetzen', { path: '/server/resume' }, 'btn-primary')}
|
||||||
{(m.server?.status === 'configured') && hasNext && btn('Nächste Map', { path: '/series/next' })}
|
{(m.server?.status === 'configured') && hasNext && btn('Nächste Map', { path: '/series/next' })}
|
||||||
{m.server && ['start_failed', 'failed', 'stopped', 'config_failed', 'ready'].includes(m.server.status) && !closed &&
|
{m.server && ['start_failed', 'failed', 'stopped', 'config_failed', 'ready'].includes(m.server.status) && !closed &&
|
||||||
btn('Server neu starten / wiederholen', { path: '/server/retry' })}
|
btn('Server neu starten / wiederholen', { path: '/server/retry' })}
|
||||||
|
|||||||
@@ -529,3 +529,8 @@ input:focus, select:focus { border-color: var(--link); outline: none; }
|
|||||||
.turn-seconds { min-width: 3ch; text-align: right; color: var(--muted); font-size: .875rem; font-variant-numeric: tabular-nums; }
|
.turn-seconds { min-width: 3ch; text-align: right; color: var(--muted); font-size: .875rem; font-variant-numeric: tabular-nums; }
|
||||||
.turn-timer.is-low .turn-bar span { background: var(--error); }
|
.turn-timer.is-low .turn-bar span { background: var(--error); }
|
||||||
.turn-timer.is-low .turn-seconds { color: var(--error); font-weight: 600; }
|
.turn-timer.is-low .turn-seconds { color: var(--error); font-weight: 600; }
|
||||||
|
.admin-live { display: flex; gap: 10px; align-items: baseline; margin-top: 4px; font-size: .9rem; }
|
||||||
|
.admin-live strong { font-variant-numeric: tabular-nums; }
|
||||||
|
.admin-badge { display: inline-block; margin-top: 6px; padding: 2px 10px; border-radius: 999px; font-size: .8rem; font-weight: 600; }
|
||||||
|
.admin-badge.is-paused { background: color-mix(in srgb, var(--pending) 22%, transparent); color: var(--pending); border: 1px solid var(--pending); }
|
||||||
|
.btn-warn { border-color: var(--pending); color: var(--pending); }
|
||||||
@@ -128,4 +128,6 @@ function migrate(db) {
|
|||||||
for (const [name, type] of add) {
|
for (const [name, type] of add) {
|
||||||
if (!seriesColumns.has(name)) db.exec(`ALTER TABLE series_maps ADD COLUMN ${name} ${type}`);
|
if (!seriesColumns.has(name)) db.exec(`ALTER TABLE series_maps ADD COLUMN ${name} ${type}`);
|
||||||
}
|
}
|
||||||
|
const serverColumns = columns('servers');
|
||||||
|
if (!serverColumns.has('paused')) db.exec('ALTER TABLE servers ADD COLUMN paused INTEGER NOT NULL DEFAULT 0');
|
||||||
}
|
}
|
||||||
@@ -944,6 +944,7 @@ export class MatchService extends EventEmitter {
|
|||||||
status: row.status,
|
status: row.status,
|
||||||
message: row.message,
|
message: row.message,
|
||||||
playerCount: row.player_count,
|
playerCount: row.player_count,
|
||||||
|
paused: !!row.paused,
|
||||||
attempts: row.attempts,
|
attempts: row.attempts,
|
||||||
updatedAt: row.updated_at,
|
updatedAt: row.updated_at,
|
||||||
idleTimeoutMinutes: this.config.idleTimeoutMinutes,
|
idleTimeoutMinutes: this.config.idleTimeoutMinutes,
|
||||||
|
|||||||
@@ -308,7 +308,8 @@ export class ServerManager {
|
|||||||
if (!seriesMap) throw new HttpError(409, 'Das Veto ist noch nicht abgeschlossen', 'veto_pending');
|
if (!seriesMap) throw new HttpError(409, 'Das Veto ist noch nicht abgeschlossen', 'veto_pending');
|
||||||
|
|
||||||
const mapName = pool.maps.find((m) => m.id === seriesMap.map)?.name ?? seriesMap.map;
|
const mapName = pool.maps.find((m) => m.id === seriesMap.map)?.name ?? seriesMap.map;
|
||||||
this.update(matchId, { status: 'configuring', message: `Lade Map ${seriesMap.number}: ${mapName}` });
|
// Ein Mapwechsel beendet die Pause, CS2 lädt die Map neu
|
||||||
|
this.update(matchId, { status: 'configuring', paused: 0, message: `Lade Map ${seriesMap.number}: ${mapName}` });
|
||||||
this.service.log(matchId, 'server', `Konfiguration für Map ${seriesMap.number} (${mapName}) gestartet`);
|
this.service.log(matchId, 'server', `Konfiguration für Map ${seriesMap.number} (${mapName}) gestartet`);
|
||||||
|
|
||||||
const plan = buildMapCommands({
|
const plan = buildMapCommands({
|
||||||
@@ -418,8 +419,8 @@ export class ServerManager {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new HttpError(502, `Befehl konnte nicht gesendet werden: ${err.message}`, 'server_error');
|
throw new HttpError(502, `Befehl konnte nicht gesendet werden: ${err.message}`, 'server_error');
|
||||||
}
|
}
|
||||||
|
this.update(viewer.matchId, { paused: paused ? 1 : 0 });
|
||||||
this.service.log(viewer.matchId, 'server', paused ? 'Match pausiert (Admin)' : 'Match fortgesetzt (Admin)');
|
this.service.log(viewer.matchId, 'server', paused ? 'Match pausiert (Admin)' : 'Match fortgesetzt (Admin)');
|
||||||
this.service.changed(viewer.matchId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Demo-Modus: simuliert einen Matchverlauf, indem echte CS2-Logzeilen erzeugt und ausgewertet werden. */
|
/** Demo-Modus: simuliert einen Matchverlauf, indem echte CS2-Logzeilen erzeugt und ausgewertet werden. */
|
||||||
@@ -463,7 +464,7 @@ export class ServerManager {
|
|||||||
const row = this.row(matchId);
|
const row = this.row(matchId);
|
||||||
if (!row || row.status === 'stopped') return;
|
if (!row || row.status === 'stopped') return;
|
||||||
if (row.handle) await this.safeStop(row);
|
if (row.handle) await this.safeStop(row);
|
||||||
this.update(matchId, { status: 'stopped', stopped_at: Date.now(), message: reason, player_count: null });
|
this.update(matchId, { status: 'stopped', stopped_at: Date.now(), message: reason, player_count: null, paused: 0 });
|
||||||
this.service.log(matchId, 'server', `Server gestoppt: ${reason}`);
|
this.service.log(matchId, 'server', `Server gestoppt: ${reason}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user