По ARCHITECTURE.md: реестр независимых секций, JSON с ключом на секцию. Ключи — явные строковые константы, а не имена классов: релизная сборка минифицирует имена и молча потеряла бы все сохранения. - ISaveSection, SaveManager, IStorage + MemoryStorage - CocosStorage: единственный файл модуля, знающий про движок; при отказе хранилища уходит в память, а не роняет игру - StatsSection: сыграно/побед/поражений/ничьих, текущая и лучшая серия - терпимая загрузка: битый JSON, чужая версия, сломанная секция или отвалившееся хранилище не ломают остальное - проверка чистоты расширена на modules/ со списком адаптеров Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
36 lines
1.0 KiB
TypeScript
36 lines
1.0 KiB
TypeScript
/**
|
|
* The key-value store a save is written to.
|
|
*
|
|
* Kept behind an interface so the save logic itself has no engine dependency
|
|
* and can be tested headlessly. `CocosStorage` is the real implementation;
|
|
* `MemoryStorage` is the one tests use.
|
|
*/
|
|
export interface IStorage {
|
|
getItem(key: string): string | null;
|
|
setItem(key: string, value: string): void;
|
|
removeItem(key: string): void;
|
|
}
|
|
|
|
/** An in-process store. Used by tests, and as a fallback when storage is unavailable. */
|
|
export class MemoryStorage implements IStorage {
|
|
private readonly _data = new Map<string, string>();
|
|
|
|
getItem(key: string): string | null {
|
|
const value = this._data.get(key);
|
|
return value === undefined ? null : value;
|
|
}
|
|
|
|
setItem(key: string, value: string): void {
|
|
this._data.set(key, value);
|
|
}
|
|
|
|
removeItem(key: string): void {
|
|
this._data.delete(key);
|
|
}
|
|
|
|
/** Test helper: everything currently stored. */
|
|
get entries(): ReadonlyMap<string, string> {
|
|
return this._data;
|
|
}
|
|
}
|