M3: модуль сохранения
По ARCHITECTURE.md: реестр независимых секций, JSON с ключом на секцию. Ключи — явные строковые константы, а не имена классов: релизная сборка минифицирует имена и молча потеряла бы все сохранения. - ISaveSection, SaveManager, IStorage + MemoryStorage - CocosStorage: единственный файл модуля, знающий про движок; при отказе хранилища уходит в память, а не роняет игру - StatsSection: сыграно/побед/поражений/ничьих, текущая и лучшая серия - терпимая загрузка: битый JSON, чужая версия, сломанная секция или отвалившееся хранилище не ломают остальное - проверка чистоты расширена на modules/ со списком адаптеров Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user