Files
pixelheros/assets/script/game/common/GameDataSync.ts
pan ac2de96bd0 refactor(game): 重构游戏云端数据同步逻辑
1. 新增微信云函数双向同步接口,将时间戳对比逻辑移至云端完成
2. 简化初始化流程,合并云端登录与数据同步步骤
3. 重构GameDataSync类,统一同步流程并改为异步调用
4. 替换旧的getCloudData接口为新的syncFromCloud方法
2026-08-07 10:04:55 +08:00

159 lines
6.0 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);
});
}
/**
* 登录阶段与云端双向同步数据sync 模式)。
* 时间戳对比在云端完成:
* - 本地较新:云端已被本地数据覆盖,无需额外处理
* - 云端较新:用云端数据覆盖本地,并写入 localStorage
* @returns true=本次以云端数据为准覆盖了本地false=以本地数据为准或同步失败
*/
public async syncFromCloud(): Promise<boolean> {
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();