服务端云存档的迁移
精华03/2474 浏览开发心得
踩过的坑
1. clientCloud 与 serverCloud 是两套独立存储这是整个迁移过程中代价最大的一个坑。旧版单机架构使用 clientCloud(客户端云存储)保存玩家进度,迁移到服务端权威架构后改用 serverCloud。我们最初以为切换 API 就行了,结果发现:serverCloud 根本读不到 clientCloud 里的数据——它们是完全独立的存储空间,key 相同也不互通。上线后玩家登录直接变成新档,之前几十小时的进度"消失"了。教训:切换存储后端时,必须设计数据迁移层。我们最终的方案是双层迁移:
- 服务端 serverLoad() 读取所有历史 key 格式(sv_all、sv_core/sv_military/sv_progress、save_data)做兜底
- 客户端首次连接时从 clientCloud 读取旧存档,通过 MIGRATE_SAVE action 发送给服务端,用 _saveSeq 序列号比较决定是否覆盖
所有对 UIMain 的引用改为 getUIMain().Build(),保证实际调用时模块已完成初始化。
3. 存档格式的历史包袱——三种 key 格式并存SaveManager.lua 在迭代过程中用过三种存储 key 格式:
1. clientClou
- 最早期的 save_data(单 key 存全量数据)
- 中期的分片方案 sv_core / sv_military / sv_progress(解决单 key 超出大小限制)
- 最终的 sv_all(优化后恢复单 key)
- 最终的 sv_all(优化后恢复单 key)
- 迁移时如果只读 sv_all,那些用旧版本存档的玩家数据就丢了。必须按优先级逐个尝试所有格式,还要处理"sv_all 存在但是空白新档、旧分片有真实数据"的边界情况
4. Manager 层的耦合设计增加迁移难度旧的 Manager(如 BuildingManager、TechManager)内部直接调用 SaveManager.Save() 做持久化。迁移到服务端架构后,持久化应该由 Server 统一管控。需要把所有 Manager 改为无副作用的纯逻辑计算:只修改传入的 game state,不主动触发存档。这涉及到对 13 个 Manager 文件的逐一审查和修改。
5. 远程事件的序列化限制UrhoX 的远程事件通过 VariantMap 传递数据,value 类型有限(String、Int、Bool 等),不能直接传 Lua table。所有游戏状态和操作参数都需要手动用 cjson.encode/decode 序列化为 JSON 字符串。同时需要注意单次传输的数据量限制,全量状态同步(STATE_SYNC)在游戏后期数据量较大时需要关注。
6. LSP 对引擎全局变量的误报IsServerMode、serverCloud、clientCloud、SERVER_MAX_PLAYERS 等是引擎在运行时注入的全局变量,LSP 无法感知它们的存在,会产生大量 undefined-global 警告。需要在文件头部用 ---@diagnostic disable-next-line: undefined-global 或 ---@type 注解手动声明。



