import { sys } from "cc"; import { WxCloudApi } from "../wx_clound_client_api/WxCloudApi"; import { mLogger } from "./Logger"; import { smc, GameDate, CloudData } from "./SingletonModuleComp"; export class GameDataSync { private debugMode: boolean = false; private _localDataDirty: boolean = false; private _lastSyncTime: number = 0; private _syncTimerId: any = null; private readonly LOCAL_STORAGE_KEY = "Heros_GameData_Local"; /** 标记数据为脏,并更新时间戳,然后保存到本地 */ public markDataDirty() { this._localDataDirty = true; this.saveToLocal(); // 尝试触发异步同步 this.tryAsyncCloudSync(); } /** 同步数据到本地 localStorage */ private saveToLocal() { try { const data = smc.getGameDate(); data.timestamp = Date.now(); // 更新时间戳 sys.localStorage.setItem(this.LOCAL_STORAGE_KEY, JSON.stringify(data)); } catch (error) { mLogger.error(this.debugMode, 'GameDataSync', '保存本地数据失败:', error); } } /** 从本地 localStorage 读取数据 */ private loadFromLocal(): GameDate | null { try { const str = sys.localStorage.getItem(this.LOCAL_STORAGE_KEY); if (str) { return JSON.parse(str) as GameDate; } } catch (error) { mLogger.error(this.debugMode, 'GameDataSync', '读取本地数据失败:', error); } return null; } /** * 判断是否为微信客户端 */ public isWxClient(): boolean { return sys.platform === sys.Platform.WECHAT_GAME; } public updateCloudData() { this.markDataDirty(); return true; } /** 尝试异步同步云端数据,带有防抖(Debounce)保护 */ private tryAsyncCloudSync() { if (!this.isWxClient()) return; // 如果当前有同步在等待,清除之前的定时器 if (this._syncTimerId !== null) { clearTimeout(this._syncTimerId); } // 防抖:延迟 3 秒同步,期间多次操作合并为一次同步请求 this._syncTimerId = setTimeout(() => { this._syncTimerId = null; this.executeCloudSync(); }, 3000); } /** 实际执行云端同步 */ private executeCloudSync() { if (!this._localDataDirty) return; let gameData = smc.getGameDate(); // 保证云端存一份时间戳,供下次登录对比 gameData.timestamp = Date.now(); WxCloudApi.save(gameData).then((result) => { if (result.result.code === 200) { mLogger.log(this.debugMode, 'GameDataSync', "静默云端保存成功", result.result); // 同步成功,清除脏标记 this._localDataDirty = false; this._lastSyncTime = Date.now(); } else { mLogger.warn(this.debugMode, 'GameDataSync', `[GameDataSync]: 静默同步失败(等待下次重试): ${result.result.msg}`); // 失败了不清除脏标记,下次有变化或定时器检查时会再次重试 } }).catch((error) => { mLogger.error(this.debugMode, 'GameDataSync', `[GameDataSync]: 静默同步异常(等待下次重试):`, error); }); } /** * 登录阶段与云端双向同步数据(sync 模式)。 * 时间戳对比在云端完成: * - 本地较新:云端已被本地数据覆盖,无需额外处理 * - 云端较新:用云端数据覆盖本地,并写入 localStorage * @returns true=本次以云端数据为准覆盖了本地;false=以本地数据为准或同步失败 */ public async syncFromCloud(): Promise { const localData = this.loadFromLocal(); // 非微信客户端:仅使用本地缓存 if (!this.isWxClient()) { if (localData) { smc.overrideLocalDataWithRemote({ data: localData }); } return false; } try { // 本地无缓存时用空对象参与对比,云端会直接下发 const gameData = localData || ({} as GameDate); gameData.timestamp = gameData.timestamp || 0; const result = await WxCloudApi.sync(gameData); const response = result.result; if (response && response.code === 200 && response.data) { const cloudData: CloudData = { openid: response.data.openid || '', data: (response.data.game_data || {}) as GameDate, }; if (response.updated) { // 本地数据较新,云端已被覆盖 mLogger.log(this.debugMode, 'GameDataSync', `[GameDataSync]: 本地数据较新,已覆盖云端。`); if (localData) { smc.overrideLocalDataWithRemote({ data: localData }); } return false; } else { // 云端数据较新,覆盖本地 mLogger.log(this.debugMode, 'GameDataSync', `[GameDataSync]: 云端数据较新,覆盖本地。`); smc.overrideLocalDataWithRemote(cloudData); this.saveToLocal(); return true; } } mLogger.warn(this.debugMode, 'GameDataSync', `[GameDataSync]: 云端同步失败,使用本地缓存兜底。`, response?.msg); if (localData) { smc.overrideLocalDataWithRemote({ data: localData }); } return false; } catch (error) { mLogger.error(this.debugMode, 'GameDataSync', `[GameDataSync]: 云端同步异常,使用本地缓存兜底。`, error); if (localData) { smc.overrideLocalDataWithRemote({ data: localData }); } return false; } } } export const gameDataSync = new GameDataSync();