Files
pixelheros/assets/script/game/common/GameDataSync.ts
panw 9baddd5462 feat(数据同步): 重构云端数据同步机制,引入防抖与本地缓存
- 新增 GameDataSync 类,封装数据同步逻辑,支持防抖与时间戳冲突解决
- 重构 SingletonModuleComp 的云端同步方法,统一调用 GameDataSync
- 优化 TalentsComp 天赋升级流程,使用新的同步机制
- 添加本地缓存支持,提升离线体验与数据恢复能力
2026-04-28 16:15:48 +08:00

143 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 getCloudData() {
const localData = this.loadFromLocal();
// 未登录微信云端前,先用本地数据顶上(让玩家秒进游戏)
if (localData && !this.isWxClient()) {
smc.overrideLocalDataWithRemote({ data: localData });
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);
// 冲突解决:基于时间戳比较(云端时间戳 > 本地时间戳 则覆盖)
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}), 执行覆盖。`);
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();
}
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 });
}
});
}
}
export const gameDataSync = new GameDataSync();