Spaces:
Running
Running
File size: 2,296 Bytes
31d3580 91677d6 31d3580 91677d6 31d3580 8899818 31d3580 91677d6 31d3580 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | <script lang="ts">
import MapPreview from '$lib/components/map-preview.svelte';
import { Skeleton } from '$lib/components/ui/skeleton';
import { loadRoundWorld, snapshotAt, type RoundWorld } from '$lib/api/world';
import { playerColor } from '$lib/utils/player-colors';
import type { PreviewChunk } from '$lib/types';
type Props = {
matchId: number;
mapName: string;
round: number;
chunks: PreviewChunk[];
virtualTime: number;
activePlayer?: number | null;
availablePlayers?: Set<number> | null;
onSelect?: (player: number) => void;
};
let {
matchId,
mapName,
round,
chunks,
virtualTime,
activePlayer = null,
availablePlayers = null,
onSelect
}: Props = $props();
let world = $state<RoundWorld | null>(null);
let error = $state<string | null>(null);
// On round change, `round` updates a render before `chunks` (the parent's
// `previews` state is replaced after an async fetch). Filter here so we
// only kick off the world load once chunks for the *new* round have
// arrived — otherwise we'd cache an empty result under the new key.
const roundChunks = $derived(chunks.filter((c) => c.round === round));
$effect(() => {
const _matchId = matchId;
const _mapName = mapName;
const _round = round;
const _chunks = roundChunks;
if (!_chunks.length) {
world = null;
error = null;
return;
}
let cancelled = false;
const ctrl = new AbortController();
world = null;
error = null;
loadRoundWorld(_matchId, _mapName, _round, _chunks, { signal: ctrl.signal })
.then((w) => {
if (!cancelled) world = w;
})
.catch((e) => {
if (cancelled || (e as Error)?.name === 'AbortError') return;
error = (e as Error).message ?? 'Failed to load world data';
});
return () => {
cancelled = true;
ctrl.abort();
};
});
const players = $derived(
world
? snapshotAt(world, virtualTime).map((s) => ({
...s,
color: playerColor(s.player),
slot: s.player
}))
: []
);
</script>
{#if error}
<div class="rounded-md border bg-muted/30 p-3 text-xs text-muted-foreground">
Map data unavailable: {error}
</div>
{:else if !world}
<Skeleton class="aspect-square w-full rounded-md" />
{:else}
<MapPreview {mapName} {players} {activePlayer} {availablePlayers} {onSelect} />
{/if}
|