
【OpenHarmony/HarmonyOS】从本地排行到 AGC 云数据玩家、匹配、房间与战绩模型设计当前项目的正式数据链路以 Preferences 本地存储为主同时已经准备了 PlayerStats、MatchRequest、GameRoom 和 BattleRecord 模型。本文给出一条从离线优先走向云同步与匹配服务的演进方案。☁️⚠️ 说明本文是基于工程预留模型的架构设计不代表项目当前已经完成 AGC 云数据库和云排行榜接入。一、为什么先做本地优先游戏的基本体验不应被网络阻断。本地优先具有明显优势首次进入不等待远端请求无网也能玩 PvE、解谜和限时模式结算结果立即可见云服务故障不会导致当局数据全部丢失在账号和服务端未完成前也能独立迭代核心玩法。但本地存储无法提供跨设备同步、全服排行榜、在线匹配和可信战绩因此需要在稳定本地模型之上逐步加云层。二、四个核心云对象1. PlayerStatsclassPlayerStats { uid:string; level:number; exp:number; winRate:number; bestScore:number; updatedAt:number; userName:string; avatar:string; }它代表玩家聚合数据。uid应来自正式认证服务不应直接使用本地时间戳 ID。2. MatchRequestclassMatchRequest{ requestId:string; uid:string; status:string; roomId:string; createdAt: number; }玩家点击匹配时创建。状态可定义为matching、matched、cancelled、expired并使用服务端时间判断超时。3. GameRoomclassGameRoom{ roomId:string; playerA:string; playerB:string; state:string; lastFrameData:string; }房间保存参与者与当前状态。把完整实时帧持续写云数据库并不适合高频动作游戏lastFrameData更适合作为断线恢复摘要、准备状态或低频快照。4. BattleRecordclassBattleRecord{ recordId:string; winnerId:string; loserId:string; duration:string; timestamp:string; }战绩用于历史、排行审计和统计。工程当前字段把时长与时间戳定义为字符串后续建议改为明确数值/日期类型避免排序和区间查询困难。三、本地和云端不应互相直接覆盖推荐在本地数据外加同步元数据interface SyncEnvelopeT {schemaVersion: number;revision: number;updatedAt: number;dirty: boolean;deviceId: string;data: T; }本地每次结算先写入 Preferences并标记dirty。网络可用且已登录时后台同步队列再提交云端成功后清除 dirty 标记。flowchartTDA[游戏结算]--B[事务性写本地]B--C[生成SyncJob]C--D{登录且联网}D--否--E[保留队列]D--是--F[提交云端]F--G{成功}G--是--H[清除dirty]G--否--I[指数退避重试]四、冲突解决不能只比较分数不同字段需要不同策略bestScore取最大值totalGames、wins不能简单取最大最好上传不可重复事件或增量昵称、头像最后修改优先或让用户选择晶石余额必须有服务端账本不能客户端随意覆盖升级等级服务端校验购买交易后更新设置项通常设备本地不一定需要上云。一个统一“云端新就全覆盖本地”的策略会丢失离线期间的增量。五、排行榜数据应该由谁可信 本地ScoreManager接收客户端给出的任意分数。用于个人历史没有问题用于全服排行榜就容易被修改。云排行榜至少需要已认证 UID服务端或权威主机确认的比赛结果合理的分数范围和时长校验幂等的recordId防止重复提交可追踪的模式、难度、客户端版本异常分数审查或风控。提交接口不应只有uid score而应围绕一场不可重复的比赛记录设计。六、匹配不能由两个客户端抢同一条记录最简单的客户端匹配逻辑是查询另一个matching请求然后双方更新为matched。如果三个客户端同时读取同一个玩家可能被重复匹配。更可靠的方式是由云函数或服务端事务完成按createdAt获取等待队列在事务/锁中选择两个仍为 matching 的请求创建唯一roomId原子更新两条请求客户端监听自己的请求变化超时或取消时使用条件更新。客户端只提交和订阅不参与权威配对。七、云数据库不适合直接做 120Hz 帧同步实时动作游戏每秒可能产生几十到上百次输入/状态。把每帧 JSON 写数据库会带来写入延迟与抖动成本和限流数据库监听不是实时游戏协议状态覆盖、乱序和冲突难以处理大量无意义历史帧。更合理的分工云数据库账号、匹配、房间元数据、战绩、排行榜 云函数匹配、结算校验、异步任务 SoftBus/UDP/专用实时通道当局输入和快照 本地 Preferences离线进度、缓存、待同步队列八、模型字段需要加强建议为所有云对象增加interfaceCloudMeta{schemaVersion:number;createdAt:number;updatedAt:number;revision:number;}房间还应包含hostUid、玩家列表与队伍地图 seed 与规则版本模式、难度、目标比分state枚举而非任意字符串最后心跳和过期时间结算 ID 与签名。地图 seed 很重要双方用相同算法和 seed 生成地图避免传输完整二维数组但必须同时锁定生成器版本。九、认证与本地游客迁移 玩家可能先以本地游客玩了很多局之后才登录账号。此时要决定把本地进度合并到新账号保留云端已有进度对晶石等敏感经济只允许有限迁移一个本地档案是否只能绑定一次退出账号后保留哪些缓存。推荐把本地userId视为设备档案 ID登录后另存认证 UID并记录已完成迁移的标志保证过程幂等。十、同步队列设计interface SyncJob { jobId: string; type:SAVE_STATS|SUBMIT_RECORD|UPDATE_PROFILE; payload: string; attempts: number; nextRetryAt: number; createdAt: number; }失败后采用指数退避网络恢复时唤醒达到最大次数后保留并记录可诊断错误。不要在游戏结束弹窗中阻塞等待上传。十一、迁移路径建议 阶段 1整理本地模型修复字段类型加入 schemaVersion统一 Manager 接口和写入串行化。阶段 2只读云排行保留本地个人记录登录用户可以查看云榜失败回退空态。阶段 3异步提交战绩本地先成功后台队列提交服务端做幂等和范围校验。阶段 4用户资料同步处理游客绑定、冲突和多设备。阶段 5云端匹配云函数负责原子配对房间只保存元数据实时战斗走独立通道。十二、测试重点 ✅断网结算后重启应用任务是否仍在同一 recordId 重试是否只记一次两台设备同时修改资料如何解决匹配取消与成功同时发生时的最终状态房间过期是否清理云数据 schema 升级服务端时间与客户端时间不一致登录过期、权限拒绝和账号切换排行榜异常分数拦截网络恢复时是否集中重试造成请求风暴。十三、总结 ✨从本地游戏演进到云端不是把Preferences.put()换成一个远程 API。需要重新定义哪些数据本地优先哪些必须服务端权威每个字段如何合并提交如何幂等匹配如何原子化实时战斗与数据库如何分工游客和正式账号如何迁移失败任务如何持久重试。先做稳健的离线层再逐步叠加云能力通常比一开始让每个页面直接访问云数据库更可靠。☁️推荐标签HarmonyOSOpenHarmonyAGC云数据库离线优先游戏匹配