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); }); } /** * 将获取到的云端数据与本地对比并合并 */ public syncWithCloudData(cloudData: CloudData | null) { const localData = this.loadFromLocal(); let cloudTs = cloudData?.data?.timestamp || 0; let localTs = localData?.timestamp || 0; if (!localData || cloudTs > localTs) { mLogger.log(this.debugMode, 'GameDataSync', `[GameDataSync]: 云端数据较新 (Cloud: ${cloudTs} > Local: ${localTs}), 执行覆盖。`); if (cloudData) { smc.overrideLocalDataWithRemote(cloudData); this.saveToLocal(); // 同步到本地 } } else { mLogger.log(this.debugMode, 'GameDataSync', `[GameDataSync]: 本地数据较新 (Local: ${localTs} >= Cloud: ${cloudTs}), 使用本地数据,触发强制云同步。`); smc.overrideLocalDataWithRemote({ data: localData }); // 本地数据较新,需要强制推送到云端以保证多端一致 this._localDataDirty = true; this.executeCloudSync(); } } public getCloudData() { const localData = this.loadFromLocal(); // 未登录微信云端前,先用本地数据顶上(让玩家秒进游戏) if (localData && !this.isWxClient()) { smc.overrideLocalDataWithRemote({ data: localData }); return Promise.resolve(false); } return WxCloudApi.get().then(async (result) => { if(result.result.code === 200) { let cloudData = result.result.data as CloudData; mLogger.log(this.debugMode, 'GameDataSync', `[GameDataSync]: 获取游戏云端数据成功:`, cloudData); this.syncWithCloudData(cloudData); return true; } else { mLogger.warn(this.debugMode, 'GameDataSync', `[GameDataSync]: 获取游戏云端数据失败,使用本地缓存兜底。`); 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();