58 lines
2.5 KiB
TypeScript
58 lines
2.5 KiB
TypeScript
import type { HostLibraryCache } from '../game-files.js';
|
|
|
|
export const TRANSFER_REPORT_FRESH_MS = 30_000;
|
|
|
|
const nonnegative = (value: number | null | undefined): number | null =>
|
|
typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : null;
|
|
|
|
/** Keep byte progress factual; only the elapsed clock advances between host reports. */
|
|
export function gameFileTransferProgress(cache: HostLibraryCache, connected: boolean, now: number) {
|
|
const active = cache.status === 'downloading' || cache.status === 'verifying';
|
|
const complete = cache.status === 'ready' || cache.status === 'outdated';
|
|
const phase = active
|
|
? (cache.status as 'downloading' | 'verifying')
|
|
: (cache.transfer?.phase ?? null);
|
|
const total = nonnegative(cache.bytesTotal) ?? 0;
|
|
const downloaded = Math.min(total, nonnegative(cache.bytesDownloaded) ?? 0);
|
|
const verified = complete ? total : nonnegative(cache.bytesVerified);
|
|
const phaseBytes =
|
|
phase === 'verifying' ? (verified === null ? null : Math.min(total, verified)) : downloaded;
|
|
const transfer = cache.transfer;
|
|
const samePhase = transfer?.phase === phase;
|
|
const reportedAt = Date.parse(cache.updatedAt);
|
|
const reportAge =
|
|
Number.isFinite(reportedAt) && Number.isFinite(now) ? Math.max(0, now - reportedAt) : null;
|
|
const stale =
|
|
active && !!transfer && (reportAge === null || reportAge > TRANSFER_REPORT_FRESH_MS);
|
|
const live = active && connected && samePhase && !stale && reportAge !== null;
|
|
const elapsed = nonnegative(transfer?.elapsedSeconds);
|
|
// Stop extrapolating when reports stop. A disconnected or finished host uses its saved duration.
|
|
const elapsedSeconds =
|
|
elapsed === null
|
|
? null
|
|
: elapsed +
|
|
(active && connected && samePhase && reportAge !== null
|
|
? Math.min(reportAge, TRANSFER_REPORT_FRESH_MS) / 1000
|
|
: 0);
|
|
const rate = nonnegative(transfer?.bytesPerSecond);
|
|
const estimate = nonnegative(transfer?.etaSeconds);
|
|
const hasProgress = phaseBytes !== null && phaseBytes > 0 && phaseBytes < total;
|
|
const bytesPerSecond = live && hasProgress && rate !== null && rate > 0 ? rate : null;
|
|
const etaSeconds =
|
|
bytesPerSecond !== null && estimate !== null && estimate > 0 ? estimate : null;
|
|
return {
|
|
active,
|
|
complete,
|
|
phase,
|
|
total,
|
|
downloaded,
|
|
verified,
|
|
phaseBytes,
|
|
elapsedSeconds,
|
|
bytesPerSecond,
|
|
etaSeconds,
|
|
stale,
|
|
live
|
|
};
|
|
}
|