Skip to main content

Game Save 冲突与原子单元

概述

当你在多台设备上修改同一份游戏数据时,就会发生存档冲突。系统需要决定保留哪个版本。要设计存档数据结构,你需要理解系统如何检测和解决冲突。

冲突发生的时机

只有在以下条件全部满足时,同步操作(PFGameSaveFilesAddUserWithUiAsync)期间才会发生冲突:
  1. 存在本地更改:自上次同步以来,你在本地修改了文件。
  2. 存在云端更改:自上次同步以来,另一台设备上传了更新的数据。
  3. 同一原子单元:两项更改都位于同一个根级文件夹内。

冲突检测矩阵

什么是原子单元?

一些文件同步系统按文件为单位处理冲突。如果同一个文件需要同时上传和下载,就会产生冲突。在 Game Saves 中,每个根级子文件夹被视为一个原子单元
每个根级子文件夹都是一个原子单元。
如果某个根级子文件夹内的任何文件或子文件夹需要下载,同时该根级子文件夹内的任何文件或文件夹又需要上传,则整个原子单元处于冲突状态。 这种方式让你能够:
  • 保持数据完整性:相互依赖的文件保持一致
  • 提供隔离性:位于不同文件夹的独立数据同步时不会互相冲突
  • 最小化冲突:不同设备上对不同原子单元的更改会自动合并

存档结构示例

冲突场景

根级文件:特殊情况

存档根目录下的所有文件共享一个原子单元。 系统会将你直接放在存档根目录(未放入任何子文件夹)下的所有文件归为一个原子单元。如果你在本地修改了一个根级文件,而另一台设备修改了另一个根级文件,这种差异会触发冲突。

用户选项

发生冲突时,玩家在以下选项中做出选择:
  • 使用本地数据(保留本地):保留设备当前的存档数据。
  • 使用云端数据(保留云端):下载并使用云端存档数据。

关键:解决方式为全有或全无

⚠️ 重要提示:虽然原子单元决定了何时检测到冲突,但用户对冲突的解决选择会应用于整个存档,而不是按原子单元分别应用。

示例:混合冲突场景

用户看到冲突提示(由于 SlotA)。

为什么这很重要

  1. 保留本地会丢失仅存在于云端的更改:如果因为 SlotA 中的冲突而选择”保留本地”,你将不会获得另一台设备对 SlotC 所做的云端更新。
  2. 保留云端会丢失仅存在于本地的更改:如果选择”保留云端”,云端状态将覆盖你对 SlotB 和 SlotD 的本地更改。
  3. 可用于回滚:两种选择都会保留被舍弃的分支,以备将来回滚。

设计小结

这种全有或全无的方式简化了玩家体验。尽管从技术上讲按原子单元解决冲突可以保持数据一致性(因为原子单元定义了一致性边界),但会带来用户体验方面的问题:
  • 玩家需要理解原子单元与文件夹边界的概念。
  • 混合结果(有些文件夹来自本地,有些来自云端)可能让玩家对最终状态感到困惑。
  • 分别对每个冲突的原子单元进行提示,会让冲突解决过程令人不知所措。
通过给出单一的保留本地保留云端选项,玩家只需做一次清晰的决定,而无需理解底层的存档结构。

不发生冲突的情况

双端均删除

如果两台设备都删除了同一个文件(或同一原子单元中的文件),不会发生冲突。系统识别到两台设备都同意应该移除该文件。

不同原子单元中的更改

如果设备 A 修改 SlotA/ 中的文件、设备 B 修改 SlotB/ 中的文件,就不会发生冲突。同步会自动合并两项更改。

最佳实践

1. 谨慎设计文件夹结构

为独立的存档单元使用子文件夹:
将每个槽位作为一个根级子文件夹的基于槽位的游戏存档系统,是有效利用原子单元的一个简单示例。 其他示例包括:
  • 共享参考数据:将任何存档槽位都可能使用的数据(如已解锁内容或成就)存储在其自己的子文件夹中,使其独立于各存档槽位进行同步。
  • 大型资源集合:如果你的游戏存储的大量文件是独立更新的(例如已下载的内容包或用户创建的关卡),可以考虑将它们拆分到多个根级子文件夹,这样一个集合的更新不会与另一个集合的更新冲突。

2. 对频繁修改的数据避免使用根级文件

所有根级文件共享一个原子单元。如果你希望它们独立同步,请避免将频繁修改的文件放在根目录。 不建议:
建议:

3. 将相关数据分组

将必须保持一致的文件放入同一文件夹。

4. 尽量减少冲突

频繁上传以降低发生冲突的可能。你同步得越频繁,两台设备产生分歧更改的可能性就越小。

5. 监控冲突率

使用 PlayStream 事件 跟踪冲突发生频率以及玩家如何解决冲突。冲突率上升,或”保留本地”与”保留远程”选择明显偏向某一端,都可能预示着需要研究的用户体验或存档节奏问题。
最后修改于 2026年8月25日