> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Game Saves 离线模式

> 使用 PlayFab Game Saves 离线模式，让玩家在无网络连接时继续保留进度，并在网络恢复后同步云存档。

Game Saves 的设计支持在线与离线两种情况，即便网络连接不可用，玩家也能继续游玩。系统会根据网络可用性和用户选择在两种不同的模式下工作。

## 连接模式

### 已连接云端模式

当网络可用且 `PFGameSaveFilesAddUserWithUiAsync()` 成功完成时，系统运行于已连接云端模式。在此模式下：

* 所有 API 均正常工作，支持完整的云端同步
* 存档数据可按预期上传
* 存储配额信息可用
* 活动设备监控可正常工作

### 离线模式（未连接云端）

当网络不可用或用户选择离线游玩时，系统进入离线模式，功能受限。你可以使用 `PFGameSaveFilesIsConnectedToCloud()` 检查当前连接状态，以判断系统所处的模式。

## 处理初次同步时的网络故障

当在无网络连接时调用 `PFGameSaveFilesAddUserWithUiAsync()`，会触发 `PFGameSaveFilesUiSyncFailedCallback`。系统在等待用户响应期间不会触发 `XAsyncBlock` 回调。异步操作会一直暂停，直到用户选择了一个最终动作。

```cpp theme={null}
// Handle sync failure callback
void MyPFGameSaveFilesUiSyncFailedCallback(PFLocalUserHandle localUserHandle, PFGameSaveFilesSyncState syncState, HRESULT error, void* context)
{
    // Tell the user something like this:
    std::cout << "We couldn't sync your data with the cloud just now" << std::endl;
    std::cout << "Try syncing again or use this game offline" << std::endl;
    std::cout << "[Try Again]" << std::endl;
    std::cout << "[Use Offline]" << std::endl;

    // if user chooses [Try Again], call PFGameSaveFilesSetUiSyncFailedResponse(localUserHandle, PFGameSaveFilesUiSyncFailedUserAction::Retry);
    // if user chooses [Use Offline], call PFGameSaveFilesSetUiSyncFailedResponse(localUserHandle, PFGameSaveFilesUiSyncFailedUserAction::UseOffline);

    // These API calls can happen inside or outside of this callback
}
```

**用户响应选项：**

* **再试一次（`Retry`）**：重新发起网络调用。如果重试仍失败，此回调会再次触发，用户须再次响应。在每次重试循环中 `XAsyncBlock` 回调都会保持延迟。
* **使用离线模式（`UseOffline`）**：`XAsyncBlock` 回调以 `S_OK` 触发，但系统进入离线模式（可通过 `PFGameSaveFilesIsConnectedToCloud()` 检测）。
* **取消**：`XAsyncBlock` 回调以 `E_PF_GAMESAVE_USER_CANCELLED` 触发。

## 离线模式下的 API 行为

在离线模式下，各 API 的行为有所不同：

### 正常工作的 API

* `PFGameSaveFilesGetFolder()` —— 返回本地存档文件夹路径

### 功能受限的 API

* `PFGameSaveFilesUploadWithUiAsync()` —— 立即返回 `S_OK`，但异步完成时返回 `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD`
* `PFGameSaveFilesGetRemainingQuota()` —— 返回 `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD`
* `PFGameSaveFilesSetActiveDeviceChangedCallback()` —— 可以设置，但永远不会触发

### 恢复在线模式

* 再次调用 `PFGameSaveFilesAddUserWithUiAsync()` 以尝试重新连接
* 无需完全重新初始化 Game Saves 系统
* 如果网络仍不可用，会再次显示失败 UI
* 重试后使用 `PFGameSaveFilesIsConnectedToCloud()` 验证连接状态

## Game Saves 何时进入离线模式或返回错误

一个常见的集成问题是：*初次同步（`PFGameSaveFilesAddUserWithUiAsync`）何时会让玩家进入离线模式，何时会以错误代码失败？* 共有三种最终结果（已连接、离线或已取消），而取消既可能来自玩家，也可能来自游戏：

| 结果 | 如何达到 | 异步结果 | `IsConnectedToCloud()` | 存档文件夹是否可用？ | 建议的处理方式 |
| - | - | - | - | - | - |
| **已连接** | 同步成功完成 | `S_OK` | `true` | 是 | 允许游玩；存档会同步到云端。 |
| **离线** | 用户在同步失败提示中选择了 **Use Offline**（或另一台设备成为活动设备） | `S_OK` | `false` | 是 | 允许使用本地存档游玩；告知玩家存档不会同步。 |
| **被玩家取消** | 用户在任意提示中选择了 **Cancel** | `E_PF_GAMESAVE_USER_CANCELLED`（`0x800704C7`） | 不适用 | **否** | **阻止游玩**或返回到安全菜单——没有可用于保存或加载的文件夹。 |
| **被游戏取消** | 游戏对 `XAsyncBlock` 调用了 `XAsyncCancel` | `E_ABORT`（`0x80004004`） | 不适用 | **否** | 与玩家取消相同——没有可用于保存或加载的文件夹。 |

要点：

* **离线模式是成功结果，而不是错误。** 只有在明确选择 **Use Offline** 时才会进入离线模式，并且异步操作以 `S_OK` 完成。使用 `PFGameSaveFilesIsConnectedToCloud()` 检测它。
* **暂时性/技术性故障通过同步失败 UI 呈现**，而不是直接从 `PFGameSaveFilesAddUserWithUiAsync` 返回。随后由用户（或你的自动响应）选择 `Retry`、`UseOffline` 或 `Cancel`，最终得到上述三种结果之一。
* **取消是最终结果，且不会提供存档文件夹。** 将“已取消”（或任何未留下文件夹的失败）视为*不允许游玩*。
* **玩家取消和游戏取消返回不同的代码。** 只有玩家在提示中选择 **Cancel** 才会产生 `E_PF_GAMESAVE_USER_CANCELLED`。如果你的游戏自行使用 `XAsyncCancel` 取消操作，则操作以 `E_ABORT` 完成。如果希望以相同方式处理两者，请同时检查这两个代码。

> [!NOTE]
>
> 取消**冲突**提示同样是最终结果。它以 `E_PF_GAMESAVE_USER_CANCELLED` 完成，并且不会自动转入同步失败的“Use Offline”提示。如果你希望提供“拒绝冲突但继续离线游玩”的体验，请明确地进行设计（例如，重新运行 `PFGameSaveFilesAddUserWithUiAsync`，并在随后的失败中选择 Use Offline）。请参阅 [Game Saves 冲突](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/conflicts.md)。

## Game Saves 错误代码

以下 HRESULT 由 Game Saves SDK 定义。`0x8923xxxx` 范围内的代码是 Game Saves 专用的；`0x800704C7` 是 `HRESULT_FROM_WIN32(ERROR_CANCELLED)`。

| 代码 | 值 | 返回时机 |
| - | - | - |
| `E_PF_GAMESAVE_USER_CANCELLED` | `0x800704C7` | 用户在任意 UI 提示中选择了 **Cancel**。最终结果；无存档文件夹。（如果游戏使用 `XAsyncCancel` 取消 `XAsyncBlock`，则操作改为以标准的 `E_ABORT`（`0x80004004`）完成。） |
| `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD` | `0x89237004` | 会话处于离线模式时，由 `PFGameSaveFilesUploadWithUiAsync()` 和 `PFGameSaveFilesGetRemainingQuota()` 返回。 |
| `E_PF_GAMESAVE_NETWORK_FAILURE` | `0x89237005` | 在仍连接云端的情况下发生暂时性网络故障后，由 `PFGameSaveFilesUploadWithUiAsync()` 返回。可以稍后安全地重试。 |
| `E_PF_GAMESAVE_DEVICE_NO_LONGER_ACTIVE` | `0x89237007` | 当此设备不再是活动设备（另一台设备已接管）时，由 `PFGameSaveFilesUploadWithUiAsync()` 返回。 |
| `E_PF_GAMESAVE_DISK_FULL` | `0x89237008` | 本地磁盘空间不足，无法下载存档数据。提示用户释放空间后重试。 |
| `E_PF_GAMESAVE_OPERATION_IN_PROGRESS` | `0x89237009` | 此用户已有冲突的操作正在运行：在另一个上传或云端重置正在进行时调用了 `PFGameSaveFilesUploadWithUiAsync()`；在云端重置正在进行时调用了 `PFGameSaveFilesAddUserWithUiAsync()`；或者在另一个重置、上传或下载正在进行时调用了 `PFGameSaveFilesResetCloudAsync()`。未启动或修改任何内容；请等待正在进行的操作完成后再重试。从 2026 年 10 月 GDK (2610) 开始提供。 |
| `E_PF_GAMESAVE_LOCAL_FILE_UNAVAILABLE` | `0x8923700A` | 当上传期间无法读取本地存档文件时（例如游戏仍以写入方式打开该文件），由 `PFGameSaveFilesUploadWithUiAsync()` 返回。未向云端提交任何内容。暂时性：在游戏停止写入该文件后重试。在 2025 年 10 月 GDK (2510) QFE5、2026 年 4 月 GDK (2604) QFE6 和 2026 年 10 月 GDK (2610) 中提供。 |
| `E_PF_GAMESAVE_DOWNLOAD_IN_PROGRESS` | `0x89237006` | 在 `PFGameSaveFilesAddUserWithUiAsync()` 完成初次同步之前调用了 `PFGameSaveFilesUploadWithUiAsync()`。 |
| `E_PF_GAMESAVE_USER_NOT_ADDED` | `0x89237003` | 在针对该用户的 `PFGameSaveFilesAddUserWithUiAsync()` 完成之前调用了某个 API。 |
| `E_PF_GAMESAVE_USER_ALREADY_ADDED` | `0x89237002` | 针对已添加的用户再次调用了 `PFGameSaveFilesAddUserWithUiAsync()`。 |
| `E_PF_GAMESAVE_ALREADY_INITIALIZED` | `0x89237001` | 在两次调用 `PFGameSaveFilesInitialize()` 之间未调用 `PFGameSaveFilesUninitializeAsync()`。 |
| `E_PF_GAMESAVE_NOT_INITIALIZED` | `0x89237000` | 在 `PFGameSaveFilesInitialize()` 之前调用了某个 API。 |

有关这些结果如何由内置（进程外）系统 UI 与你自己的回调分别产生，请参阅 [Game Saves UI 回调](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/ui-callbacks.md#in-process-vs-out-of-process-system-ui)。

## 其他连接状态场景

`PFGameSaveFilesIsConnectedToCloud()` API 特别有用，因为断开连接可能以多种方式发生：

* **同步期间网络不可用**：用户明确选择离线游玩
* **活动设备变更**：另一台设备接管成为活动设备，会自动将当前设备置于离线模式

在尝试云端操作前使用此 API，以便向用户提供合适的反馈。

## 离线支持最佳实践

1. **始终实现同步失败回调**，以妥善处理网络问题
2. 在尝试云端操作前，**定期使用 `PFGameSaveFilesIsConnectedToCloud()` 检查连接状态**
3. 在离线模式下运行时**告知玩家**，让他们了解存档不会同步
4. 在网络恢复时**提供重试选项**
5. **本地存档始终可用**——不论网络状态如何，玩家都可以继续游玩
6. **监控断开事件**——请记住，当另一台设备成为活动设备时，本设备可能被断开

## 已连接模式下的上传行为

即使在已连接模式下，上传也可能因网络问题或速率限制而失败。发生此情况时，`XAsyncBlock` 回调不会立即触发。系统会在通过 [UI 回调状态机](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/ui-callbacks.md#how-the-state-machine-works) 等待用户响应期间暂停异步操作：

1. `PFGameSaveFilesUiSyncFailedCallback` 会带着错误详情触发。
2. `XAsyncBlock` 回调尚未触发。操作会在系统等待响应期间暂停。
3. 如果用户选择 `Retry`，将重试上传。如果再次失败，回调会再次触发，用户须再次响应。
4. 如果用户选择 `Cancel`，`XAsyncBlock` 回调会以 `E_PF_GAMESAVE_USER_CANCELLED` 触发。
5. 如果重试成功，`XAsyncBlock` 回调会以 `S_OK` 触发。

网络恢复后，游戏可以稍后再次调用 `PFGameSaveFilesUploadWithUiAsync()`。

> [!NOTE]
>
> **在进程内模式下，上传正在运行时不要在同步失败提示中提供 `Use Offline`。** 上传会触发与初次同步相同的 `PFGameSaveFilesUiSyncFailedCallback`，因此 SDK 不会限制可选项。但是，如果你的游戏对上传失败响应 `UseOffline`，上传将以 `E_ABORT` 失败（并且调试版本会触发断言）。此时只应提供 `Retry` 和 `Cancel`。

## 上传何时使会话进入离线状态

上传通常不会改变连接模式，但在以下情况下，上传结束时会话可能处于离线状态。在每种情况下，上传都会以 `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD` 完成，并且之后 `PFGameSaveFilesIsConnectedToCloud()` 返回 `false`。

* **会话已经处于离线状态。** 上传不会运行，并以 `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD` 完成，即使网络已经恢复也是如此。
* **云端有较新的存档。** 如果在上传最终完成时服务报告另一台设备已提交了较新的存档，Game Saves 会使会话进入离线状态，触发活动设备变更回调，并以 `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD` 完成上传。
* **玩家在重新锁定提示中选择 `Use Offline`。** 如果之前的上传已提交其数据但随后失败，则下一次上传必须先重新获取存档锁。在此步骤中出现的同步失败提示可以用 `UseOffline` 响应，这会使会话进入离线状态，并以 `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD` 完成上传。

若要重新连接，请再次调用 `PFGameSaveFilesAddUserWithUiAsync()`。

## 取消上传

上传的取消方式与初次同步相同：用户可以在上传进行期间对**进度** UI 选择 `Cancel`，或者在上传遇到错误时对**同步失败**提示选择 `Cancel`。无论哪种方式，`PFGameSaveFilesUploadWithUiAsync()` 都会以 `E_PF_GAMESAVE_USER_CANCELLED`（`0x800704C7`）完成，并且存档不会被上传。其最终结果为**成功**、**已取消**或**失败**（其中 `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD` 表示会话现在处于离线状态；请参阅[上传何时使会话进入离线状态](#when-an-upload-takes-the-session-offline)）。请将 `E_PF_GAMESAVE_USER_CANCELLED` 与其他失败区分处理，以便区分“玩家主动退出”和“上传出错”。如果你的游戏自行使用 `XAsyncCancel` 取消上传，则会以 `E_ABORT` 而不是 `E_PF_GAMESAVE_USER_CANCELLED` 完成。


## Related topics

- [Game Saves 错误处理常见问题](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/error-handling-faq.md)
- [Game Saves UI 回调](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/ui-callbacks.md)
- [游戏存档快速入门](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/quickstart.md)
- [处理离线游戏的最佳实践](https://docs.xbox.com/zh-CN/services/xbox-services/develop/best-practices/live-best-practices-offline-play.md)
- [XBOX 服务用户特权的客户端使用](https://docs.xbox.com/zh-CN/services/xbox-services/fundamentals/identity/privileges/concepts/live-user-privileges-client.md)
