Simplify telemetry to three game events
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import Button from '$ui/components/primitives/Button.svelte';
|
||||
import { builtInPlans, requirement, requirementKinds, requirementLabels, parseBetaPlan, type BetaPlan, type RequirementKind } from '$lib/beta-plan.js';
|
||||
import { coverageSummary, needsSessionTelemetry, presetDescriptions, requirementCategory, requirementSummary } from '$lib/beta-plan-summary.js';
|
||||
import { coverageSummary, needsGameTelemetry, presetDescriptions, requirementCategory, requirementSummary } from '$lib/beta-plan-summary.js';
|
||||
let { plan = $bindable<BetaPlan>() }: { plan: BetaPlan } = $props();
|
||||
let presets = $state(builtInPlans);
|
||||
let presetName = $state('');
|
||||
@@ -102,8 +102,8 @@
|
||||
<div><dt>If a check fails</dt><dd>{failureSummary}</dd></div>
|
||||
</dl>
|
||||
<p class="plan-caption">Every requirement below must pass on each qualifying server. You can edit any check.</p>
|
||||
{#if plan.requirements.some(needsSessionTelemetry)}
|
||||
<p class="telemetry-note"><span aria-hidden="true">ⓘ</span> This plan needs session telemetry from the game reporter.</p>
|
||||
{#if plan.requirements.some(needsGameTelemetry)}
|
||||
<p class="telemetry-note"><span aria-hidden="true">ⓘ</span> Gameplay progress comes from game start, round change, and game end reports.</p>
|
||||
{/if}
|
||||
|
||||
<div class="requirements">
|
||||
@@ -120,31 +120,26 @@
|
||||
<div class="fields">
|
||||
<label>Description<input bind:value={r.label} maxlength="200" required /></label>
|
||||
{#if r.kind !== 'manual'}
|
||||
<label>{['failure-budget', 'restart-budget', 'clean-logs'].includes(r.kind) ? 'Maximum allowed' : 'Target'}
|
||||
<input type="number" min={['failure-budget', 'restart-budget', 'clean-logs'].includes(r.kind) ? 0 : ['healthy-minutes', 'populated-minutes', 'player-hours', 'sustained-population'].includes(r.kind) ? 0.01 : 1} step={['healthy-minutes', 'populated-minutes', 'player-hours', 'sustained-population'].includes(r.kind) ? 'any' : 1} bind:value={r.target} required />
|
||||
<label>{r.kind === 'failure-budget' ? 'Maximum allowed' : 'Target'}
|
||||
<input type="number" min={r.kind === 'failure-budget' ? 0 : ['healthy-minutes', 'populated-minutes', 'player-hours', 'sustained-population'].includes(r.kind) ? 0.01 : 1} step={['healthy-minutes', 'populated-minutes', 'player-hours', 'sustained-population'].includes(r.kind) ? 'any' : 1} bind:value={r.target} required />
|
||||
</label>
|
||||
{/if}
|
||||
{#if ['round', 'matches', 'populated-minutes', 'player-hours', 'sustained-population', 'feature'].includes(r.kind)}
|
||||
{#if ['round', 'matches', 'populated-minutes', 'player-hours', 'sustained-population'].includes(r.kind)}
|
||||
<label>Minimum players<input type="number" min="1" step="1" bind:value={r.minPlayers} required /></label>
|
||||
{/if}
|
||||
{#if r.kind === 'matches' || r.kind === 'feature'}
|
||||
<label>Minimum round in the same session<input type="number" min="0" step="1" bind:value={r.minRound} required /></label>
|
||||
{#if r.kind === 'matches'}
|
||||
<label>Minimum round in the same game<input type="number" min="0" step="1" bind:value={r.minRound} required /></label>
|
||||
{/if}
|
||||
{#if r.kind === 'matches'}
|
||||
<label>Minimum match duration (minutes)<input type="number" min="0" step="any" bind:value={r.minMinutes} required /></label>
|
||||
{/if}
|
||||
{#if r.kind === 'feature'}
|
||||
<label>Reported event name<input bind:value={r.event} placeholder="perk.purchased" maxlength="120" required /></label>
|
||||
{/if}
|
||||
</div>
|
||||
{#if r.kind === 'round' || r.kind === 'feature' || r.kind === 'restart-budget' || (r.kind === 'matches' && (r.minRound > 0 || r.minPlayers > 1))}
|
||||
<p>Requires session telemetry from the game reporter. Progress waits for events from this beta’s current run.</p>
|
||||
{#if needsGameTelemetry(r)}
|
||||
<p>Uses game events reported by this server automatically.</p>
|
||||
{:else if r.kind === 'recovery'}
|
||||
<p>After the other requirements pass, restart when empty and repeat them. Every cycle requires fresh evidence and checklist sign-off.</p>
|
||||
{:else if r.kind === 'failure-budget'}
|
||||
<p>Counts transitions from healthy to unhealthy. Missing or stale monitoring resets continuous health and does not earn playtime.</p>
|
||||
{:else if r.kind === 'clean-logs'}
|
||||
<p>Checks new console lines for script errors, script exceptions, unhandled exceptions and fatal errors. An unreadable log cannot pass.</p>
|
||||
<p>Counts transitions from healthy to unhealthy. Missing or stale monitoring resets continuous healthy time.</p>
|
||||
{:else if r.kind === 'manual'}
|
||||
<p>A tester records their name and observation on the beta detail page.</p>
|
||||
{/if}
|
||||
|
||||
@@ -44,10 +44,10 @@
|
||||
{@const requirement = test.plan.requirements.find(r => r.id === progress.id)!}
|
||||
<div class="requirement" class:failed={progress.failed}>
|
||||
<div><strong>{progress.passed ? '✓' : progress.failed ? '✕' : '○'} {progress.label}</strong><span>{progress.detail}</span></div>
|
||||
{#if ['round', 'matches', 'feature', 'populated-minutes', 'player-hours', 'sustained-population'].includes(requirement.kind)}
|
||||
<span>Minimum {requirement.minPlayers} player(s){requirement.minRound ? `, round ${requirement.minRound} in the same session` : ''}{requirement.minMinutes ? `, ${requirement.minMinutes} minutes per match` : ''}{requirement.event ? ` · Event: ${requirement.event}` : ''}</span>
|
||||
{#if ['round', 'matches', 'populated-minutes', 'player-hours', 'sustained-population'].includes(requirement.kind)}
|
||||
<span>Minimum {requirement.minPlayers} player(s){requirement.minRound ? `, round ${requirement.minRound} in the same game` : ''}{requirement.minMinutes ? `, ${requirement.minMinutes} minutes per match` : ''}</span>
|
||||
{/if}
|
||||
{#if !['manual', 'failure-budget', 'restart-budget', 'clean-logs'].includes(requirement.kind)}
|
||||
{#if !['manual', 'failure-budget'].includes(requirement.kind)}
|
||||
<progress aria-label={progress.label} value={Math.min(progress.current, progress.target)} max={progress.target}></progress>
|
||||
{/if}
|
||||
{#if requirement.kind === 'manual' && !progress.passed && !closed}
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
&& (!since || Date.parse(report.receivedAt) >= Date.parse(since)));
|
||||
</script>
|
||||
|
||||
{#if report && fresh}
|
||||
{#if report && report.event === 'round_change' && fresh}
|
||||
<div class="round" aria-live="polite">
|
||||
<strong>Round {report.round}</strong>
|
||||
<span title={report.receivedAt}>Last reported {elapsedSince(report.receivedAt, $uiNow)} ago · {report.players} players</span>
|
||||
<span title={report.receivedAt}>Last reported {elapsedSince(report.receivedAt, $uiNow)} ago · {report.players.length} players</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { writable, readonly } from 'svelte/store';
|
||||
import { browser } from '$app/environment';
|
||||
import type { RoundTelemetry } from '$lib/telemetry.js';
|
||||
import type { GameTelemetry } from '$lib/telemetry.js';
|
||||
|
||||
export const telemetryByServer = writable<Map<string, RoundTelemetry>>(new Map());
|
||||
export const telemetryByServer = writable<Map<string, GameTelemetry>>(new Map());
|
||||
|
||||
export interface RosterPlayer {
|
||||
num: number | null;
|
||||
@@ -54,9 +54,9 @@ export function startServerStream(): (() => void) | undefined {
|
||||
if (!browser || source) return;
|
||||
source = new EventSource('/api/events/servers');
|
||||
source.addEventListener('server-telemetry', (evt) => {
|
||||
const data = JSON.parse((evt as MessageEvent).data) as { entries: RoundTelemetry[]; snapshot?: boolean };
|
||||
const data = JSON.parse((evt as MessageEvent).data) as { entries: GameTelemetry[]; snapshot?: boolean };
|
||||
telemetryByServer.update((previous) => {
|
||||
const next = data.snapshot ? new Map<string, RoundTelemetry>() : new Map(previous);
|
||||
const next = data.snapshot ? new Map<string, GameTelemetry>() : new Map(previous);
|
||||
for (const entry of data.entries) next.set(entry.server, entry);
|
||||
return next;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user