По ARCHITECTURE.md: реестр независимых секций, JSON с ключом на секцию. Ключи — явные строковые константы, а не имена классов: релизная сборка минифицирует имена и молча потеряла бы все сохранения. - ISaveSection, SaveManager, IStorage + MemoryStorage - CocosStorage: единственный файл модуля, знающий про движок; при отказе хранилища уходит в память, а не роняет игру - StatsSection: сыграно/побед/поражений/ничьих, текущая и лучшая серия - терпимая загрузка: битый JSON, чужая версия, сломанная секция или отвалившееся хранилище не ломают остальное - проверка чистоты расширена на modules/ со списком адаптеров Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
65 lines
2.0 KiB
TypeScript
65 lines
2.0 KiB
TypeScript
import { sys } from 'cc';
|
|
import { IStorage, MemoryStorage } from './IStorage';
|
|
|
|
/**
|
|
* The one file in the save module that touches the engine.
|
|
*
|
|
* `sys.localStorage` is backed by the browser store on web and by a native file
|
|
* on Android and iOS, so one implementation covers every target. It can still
|
|
* fail — private browsing, a full quota, a locked profile — so every call is
|
|
* guarded and falls back to an in-memory store, which keeps the session
|
|
* consistent even when nothing can be persisted.
|
|
*/
|
|
export class CocosStorage implements IStorage {
|
|
private readonly _fallback = new MemoryStorage();
|
|
private _usingFallback = false;
|
|
|
|
/** True once a storage call has failed and the session went in-memory. */
|
|
get degraded(): boolean {
|
|
return this._usingFallback;
|
|
}
|
|
|
|
getItem(key: string): string | null {
|
|
if (this._usingFallback) {
|
|
return this._fallback.getItem(key);
|
|
}
|
|
try {
|
|
return sys.localStorage.getItem(key);
|
|
} catch (error) {
|
|
this._degrade(error);
|
|
return this._fallback.getItem(key);
|
|
}
|
|
}
|
|
|
|
setItem(key: string, value: string): void {
|
|
if (!this._usingFallback) {
|
|
try {
|
|
sys.localStorage.setItem(key, value);
|
|
return;
|
|
} catch (error) {
|
|
this._degrade(error);
|
|
}
|
|
}
|
|
this._fallback.setItem(key, value);
|
|
}
|
|
|
|
removeItem(key: string): void {
|
|
if (!this._usingFallback) {
|
|
try {
|
|
sys.localStorage.removeItem(key);
|
|
return;
|
|
} catch (error) {
|
|
this._degrade(error);
|
|
}
|
|
}
|
|
this._fallback.removeItem(key);
|
|
}
|
|
|
|
private _degrade(error: unknown): void {
|
|
if (this._usingFallback) return;
|
|
this._usingFallback = true;
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
console.warn(`[save] device storage is unavailable, progress will not persist: ${message}`);
|
|
}
|
|
}
|