diff --git a/src/lib/stores/game.ts b/src/lib/stores/game.ts index 4b7b6ea..b166580 100644 --- a/src/lib/stores/game.ts +++ b/src/lib/stores/game.ts @@ -1,7 +1,7 @@ import { writable } from 'svelte/store'; import { browser } from '$app/environment'; import type { GameState, QuestionRef } from '$lib/types'; -import { getQuestionSetById, getTotalQuestions } from '$lib/question-sets'; +import { findQuestion, getQuestionSetById, getTotalQuestions } from '$lib/question-sets'; /** * Игровое состояние полностью отделено от компонентов. @@ -32,21 +32,50 @@ function loadState(): GameState { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return initialState(); const parsed = JSON.parse(raw) as Partial; + // Защита от повреждённых/чужих данных: проверяем базовую структуру. if ( - parsed && - (parsed.questionSetId === null || typeof parsed.questionSetId === 'string') && - typeof parsed.score === 'number' && - Array.isArray(parsed.played) && - (parsed.current === null || typeof parsed.current === 'string') + !parsed || + (parsed.questionSetId !== null && typeof parsed.questionSetId !== 'string') || + typeof parsed.score !== 'number' || + !Array.isArray(parsed.played) || + (parsed.current !== null && typeof parsed.current !== 'string') ) { - return { - questionSetId: parsed.questionSetId ?? null, - score: parsed.score ?? 0, - played: parsed.played as QuestionRef[], - current: parsed.current ?? null - }; + return initialState(); } + + const setId = parsed.questionSetId; + // Набор не выбран — экран выбора, возвращаем чистое состояние. + if (setId === null) return initialState(); + + // Набор неизвестен (удалён/переименован) — полный сброс партии. + const set = getQuestionSetById(setId); + if (!set) return initialState(); + + // Счёт должен быть конечным неотрицательным числом, иначе сброс. + if (!Number.isFinite(parsed.score) || parsed.score < 0) return initialState(); + + // Оставляем только уникальные ссылки на реально существующие вопросы. + const played: QuestionRef[] = []; + const seen = new Set(); + for (const ref of parsed.played) { + if (typeof ref !== 'string' || seen.has(ref)) continue; + if (!findQuestion(set, ref)) continue; + seen.add(ref); + played.push(ref); + } + + // Текущий вопрос сбрасываем, если он не существует или уже сыгран. + const current = parsed.current; + const safeCurrent = + current !== null && findQuestion(set, current) && !seen.has(current) ? current : null; + + return { + questionSetId: setId, + score: parsed.score, + played, + current: safeCurrent + }; } catch { // Повреждённый JSON — просто начинаем заново. }