> ## 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 UI 回调

> 通过注册回调、处理状态机以及为冲突和重试调用响应 API，为 PlayFab Game Saves 实现自定义同步 UI。

Game Saves 提供了一组 UI 回调，让您的游戏可以响应同步操作期间的事件。在 XBOX 和 Windows 上，平台为这些事件提供了内置 UI。在其他平台（例如 Steam Deck）上，您的游戏必须通过处理这些回调来实现自己的 UI。

在 XBOX 和 Windows 上设置回调会用您的实现覆盖平台提供的 UI。如果您的游戏需要在所有平台上为玩家提供一致的体验，这将非常有用。在所有平台上注册相同的回调，内置 UI 就不会出现。

## 状态机的工作原理

Game Saves 使用内部状态机来协调 UI 回调与异步操作生命周期。当 UI 回调触发时，异步操作会暂停。在回调被解决之前，`XAsyncBlock` 回调不会触发。在游戏调用相应的响应 API 或异步操作被取消之前，状态机不会前进。

这意味着：

* 每个回调类型都有一个相应的响应 API。调用响应 API 来告诉系统下一步做什么。
* 响应 API 可以在回调函数内部或外部调用。
* 如果响应操作是 `Retry`，操作会重试，并可能再次触发相同的回调。
* 只有当操作达到终止状态（成功、取消或离线回退）时，`XAsyncBlock` 回调才会触发。

例如，如果上传由于速率限制而失败：

1. `PFGameSaveFilesUiSyncFailedCallback` 触发并携带错误。
2. `XAsyncBlock` 回调尚未触发。状态机等待响应。
3. 如果用户选择 `Retry` 并且重试也失败，同步失败回调会再次触发。
4. 如果用户选择 `Cancel`，`XAsyncBlock` 回调将触发并携带 `E_PF_GAMESAVE_USER_CANCELLED`。
5. 如果重试成功，`XAsyncBlock` 回调将触发并携带 `S_OK`。

## 回调何时触发

UI 回调仅在两个异步操作期间触发：

| 操作 | 可能触发的回调 |
| - | - |
| `PFGameSaveFilesAddUserWithUiAsync` | 进度、同步失败、活动设备争用、冲突、存储空间不足 |
| `PFGameSaveFilesUploadWithUiAsync` | 进度、同步失败 |

## 注册回调

在调用 `PFGameSaveFilesAddUserWithUiAsync` 或 `PFGameSaveFilesUploadWithUiAsync` 之前注册所有回调：

```cpp theme={null}
PFGameSaveUICallbacks callbacks{};
callbacks.progressCallback = MyProgressCallback;
callbacks.progressContext = nullptr;
callbacks.syncFailedCallback = MySyncFailedCallback;
callbacks.syncFailedContext = nullptr;
callbacks.activeDeviceContentionCallback = MyActiveDeviceContentionCallback;
callbacks.activeDeviceContentionContext = nullptr;
callbacks.conflictCallback = MyConflictCallback;
callbacks.conflictContext = nullptr;
callbacks.outOfStorageCallback = MyOutOfStorageCallback;
callbacks.outOfStorageContext = nullptr;

HRESULT hr = PFGameSaveFilesSetUiCallbacks(&callbacks);
```

## 回调参考

### 进度

报告上传或下载进度。在回调内使用 `PFGameSaveFilesUiProgressGetProgress` 检索当前的 `PFGameSaveFilesSyncState`、已完成的字节数和总字节数。

**回调**：`PFGameSaveFilesUiProgressCallback`

**响应 API**：`PFGameSaveFilesSetUiProgressResponse`

| 操作 | 效果 |
| - | - |
| `Cancel` | 取消操作。`XAsyncBlock` 回调触发并携带 `E_PF_GAMESAVE_USER_CANCELLED`。 |

> [!NOTE]
>
> 进度回调不需要响应即可继续。操作会自行继续进行。仅在用户想要取消时才调用响应 API。

#### 同步状态

`PFGameSaveFilesSyncState` 枚举指示操作处于哪个阶段：

| 状态 | 描述 | 可安全写入存档文件夹？ |
| - | - | - |
| `NotStarted` | 操作尚未开始 | 是 |
| `PreparingForDownload` | 准备从云端下载 | 是 |
| `Downloading` | 正在从云端下载 | 否 |
| `PreparingForUpload` | 正在读取和压缩本地文件 | 否 |
| `Uploading` | 正在向云端上传（本地文件已捕获） | 是 |
| `SyncComplete` | 操作已完成 | 是 |

### 同步失败

当同步操作失败时触发，例如由于网络问题或速率限制。

**回调**：`PFGameSaveFilesUiSyncFailedCallback`

**参数**：接收 `PFGameSaveFilesSyncState`（失败的阶段）和 `HRESULT`（错误代码）。

**响应 API**：`PFGameSaveFilesSetUiSyncFailedResponse`

| 操作 | 效果 |
| - | - |
| `Cancel` | 取消操作。`XAsyncBlock` 回调触发并携带 `E_PF_GAMESAVE_USER_CANCELLED`。 |
| `Retry` | 重试失败的操作。如果重试失败，此回调会再次触发。 |
| `UseOffline` | 仅在 `PFGameSaveFilesAddUserWithUiAsync` 期间有效。`XAsyncBlock` 回调触发并携带 `S_OK`，但系统进入离线模式。使用 `PFGameSaveFilesIsConnectedToCloud()` 检测此状态。 |

有关离线模式行为的更多详细信息，请参阅[Game Saves 离线模式](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/offline.md)。

### 活动设备争用

在 `PFGameSaveFilesAddUserWithUiAsync` 期间，当另一台设备已经是该用户的活动设备时触发。回调接收本地和远程存档数据的 `PFGameSaveDescriptor` 结构，其中包括可以显示以帮助用户做出决定的设备名称、时间戳和存档大小。

**回调**：`PFGameSaveFilesUiActiveDeviceContentionCallback`

**响应 API**：`PFGameSaveFilesSetUiActiveDeviceContentionResponse`

| 操作 | 效果 |
| - | - |
| `Cancel` | 取消操作。`XAsyncBlock` 回调触发并携带 `E_PF_GAMESAVE_USER_CANCELLED`。 |
| `Retry` | 重试——如果用户预期另一台设备很快会释放，则很有用。如果另一台设备仍处于活动状态，此回调会再次触发。 |
| `SyncLastSavedData` | 将本地设备设为活动状态并同步。远程设备不能再上传，并会收到活动设备已更改的通知。 |

有关活动设备行为的更多详细信息，请参阅[Game Saves 活动设备变更](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/activedevicechanges.md)。

### 冲突

在 `PFGameSaveFilesAddUserWithUiAsync` 期间，当本地和云存档数据发生分歧时触发。回调接收本地和远程存档数据的 `PFGameSaveDescriptor` 结构。

**回调**：`PFGameSaveFilesUiConflictCallback`

**响应 API**：`PFGameSaveFilesSetUiConflictResponse`

| 操作 | 效果 |
| - | - |
| `Cancel` | 取消操作。`XAsyncBlock` 回调触发并携带 `E_PF_GAMESAVE_USER_CANCELLED`。 |
| `TakeLocal` | 保留本地存档数据并上传到云端。 |
| `TakeRemote` | 丢弃本地更改并下载云存档数据。 |

> [!NOTE]
>
> 冲突解决适用于整个存档，而非单个文件或文件夹。有关如何在原子单元级别检测冲突并在全局范围内解决的详细信息，请参阅[Game Saves 冲突](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/conflicts.md)。

### 存储空间不足

在 `PFGameSaveFilesAddUserWithUiAsync` 期间，当本地设备没有足够的磁盘空间从云端下载存档数据时触发。回调接收 `requiredBytes`，指示所需的空间量。

**回调**：`PFGameSaveFilesUiOutOfStorageCallback`

**响应 API**：`PFGameSaveFilesSetUiOutOfStorageResponse`

| 操作 | 效果 |
| - | - |
| `Cancel` | 取消操作。`XAsyncBlock` 回调触发并携带 `E_PF_GAMESAVE_USER_CANCELLED`。 |
| `Retry` | 在用户释放本地存储空间后重试。如果空间仍然不足，此回调会再次触发。 |

## 进程内与进程外（系统 UI）

Game Saves 的运行位置取决于平台，这会影响 UI 事件的处理方式：

* **进程外（XBOX 和 Windows）**：在 XBOX 和 Windows 上，Game Saves 始终在进程外运行。平台（Connected Storage / GRTS）执行同步，并针对同步失败、冲突、活动设备争用和存储空间不足提供内置系统 UI 对话框。玩家直接与这些对话框交互，平台会将结果转换回与您的游戏在进程内模式下收到的相同的异步完成代码。您仍然可以注册回调来替换内置 UI。
* **进程内（其他平台，例如 Steam Deck）**：在平台服务不可用的情况下，您的游戏托管 Game Saves 同步引擎，并通过上述回调处理 UI 事件。您的游戏通过响应 API 完全驱动状态机。

### 取消始终是可能的结果

无论采用哪种路径，**每个** UI 流程都可能以用户在未成功同步的情况下退出而结束，因此您的游戏必须始终处理最终的取消：

| 用户意图 | 异步完成结果 | `IsConnectedToCloud()` | 存档文件夹？ |
| - | - | - | - |
| 继续在线（成功） | `S_OK` | `true` | 是 |
| 离线游玩 | `S_OK` | `false` | 是 |
| 取消/退出（玩家） | `E_PF_GAMESAVE_USER_CANCELLED`（`0x800704C7`） | 不适用 | **否** |
| 取消（游戏调用了 `XAsyncCancel`） | `E_ABORT`（`0x80004004`） | 不适用 | **否** |

唯一仍允许玩家继续的“退出”操作是在同步失败提示中选择 **Use Offline**（一种成功结果）。其他所有取消都是最终结果，且不会留下存档文件夹。请将其视为*不允许游玩*。请参阅 [Game Saves 离线模式](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/offline.md#when-game-saves-enters-offline-mode-or-returns-an-error)。

在内置（进程外）系统 UI 中，这些选项以平台措辞显示。例如，同步失败对话框提供 **"Try again"** / **"Play offline"**，争用对话框提供 **"Stop sync & continue"** / **"Quit game"**。"Play offline" 对应离线模式（`S_OK`，未连接）；"Quit game" 对应 `E_PF_GAMESAVE_USER_CANCELLED`。

在两种路径上，取消**冲突**提示都是最终结果。它以 `E_PF_GAMESAVE_USER_CANCELLED` 完成，并且不会级联到同步失败提示。

对于自定义跨平台 UI（例如与您的 Steam Deck 实现保持一致），请在所有平台上注册回调，并在目标硬件上验证进程外流程。无论采用哪种路径，都应根据完成结果控制游玩：`S_OK` + 已连接 → 在线游玩；`S_OK` + 未连接 → 离线游玩（警告玩家）；取消/失败且无文件夹 → 阻止游玩。

## 平台要求

| 平台 | UI 回调 |
| - | - |
| **XBOX 和 Windows** | 可选。设置回调可覆盖内置 UI。 |
| **其他平台**（Steam Deck 等） | **必需**。没有可用的内置 UI；您的游戏必须处理所有回调。 |

有关 Steam Deck 实现的详细信息，请参阅 [Steam Deck 实现指南](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/steam-deck-implementation.md)。

## 相关内容

* [Game Saves 快速入门](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/quickstart.md)
* [Game Saves 离线模式](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/offline.md)
* [Game Saves 冲突](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/conflicts.md)
* [Game Saves 活动设备变更](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/activedevicechanges.md)


## Related topics

- [Game Saves 错误处理常见问题](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/error-handling-faq.md)
- [Game Saves 离线模式](https://docs.xbox.com/zh-CN/services/playfab/player-progression/game-saves/offline.md)
- [PFGameSaveFilesSetUiCallbacks](https://docs.xbox.com/zh-CN/services/playfab/api-references/c/pfgamesavefilesui/functions/pfgamesavefilessetuicallbacks.md)
- [服务 C API 概览 - PFGameSaveFilesUi.h](https://docs.xbox.com/zh-CN/services/playfab/api-references/c/pfgamesavefilesui/pfgamesavefilesui_members.md)
- [PFGameSaveFilesUiConflictCallback](https://docs.xbox.com/zh-CN/services/playfab/api-references/c/pfgamesavefilesui/functions/pfgamesavefilesuiconflictcallback.md)
