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

# 内容更新最佳实践

> 内容更新最佳实践

<Tip>**正在使用 MSIXVC2?** 本页所述的对齐和布局约束适用于原始 MSIXVC 格式。MSIXVC2 使用基于内容的分段,消除了这些要求。有关 MSIXVC2 的内容更新指导,请参见 [使用 MSIXVC2 进行内容更新](/build/core-features/common/packaging/packaging-updates-msixvc2)。</Tip>

## 概述

你可以在发布后通过修改、添加或移除数据来更新内容包。要发起更新,请将完整的更新后包上传到 Partner Center 并发布。之后的数字方式安装会直接下载更新后的包。系统的内容更新技术会修改现有安装,使其与更新后的包保持一致。

Microsoft 游戏开发工具包(GDK)内容会被打包为 XVC 或 MSIXVC 包,以子文件粒度操作。
你可以就地覆盖数据。也可以以 4 kibibyte(KiB)(4,096 字节)的倍数从文件中插入或删除数据。
但你无法高效地移动数据。

当用户从游戏原始光盘安装一款已经有内容更新的游戏时,安装过程会同时从光盘和云端获取数据。包中未变化的部分从光盘安装,而更新过的数据通过流式安装从云端下载。此过程在系统 UI 和所有游戏内 API 看来,和普通的流式安装完全一致。

当主机或 PC 更新已经安装的内容时,更新过程会就地修改已安装的 XVC 或 MSIXVC 包文件,以适应新增或删除的数据,然后下载新的数据。在更新开始安装和更新完成安装之间,游戏无法运行。

## 高效地编写包

内容更新会尽量不干扰游戏开发流程。无论你对游戏内容做出何种更改,都可以正确地更新包。但是,过程在下载大小方面(对应玩家无法游玩游戏的时间)或占用硬盘空间方面可能并不高效。以下技巧可帮助你打包游戏,以确保最佳的用户体验。

**简单:**

* 如果你使用 pack 文件将多个资源聚合到一个文件中,请确保 pack 文件创建工具将每个资源的起始位置对齐到 4-KiB 边界。此技巧在 [简单对齐示例](#AS) 中展示。
* 如果你压缩资源,请确保压缩是确定性的,且每个资源独立压缩。
* 不要更改区块 ID 或在区块 ID 之间移动文件。
* 不要在包内重排区块、区块内的文件或文件内的资源。考虑按资源名称或其他稳定标识符排序。

**进阶:**

* 如果你使用 pack 文件将多个资源聚合到一个文件中,请确保 pack 文件创建工具以一致的方式对齐每个资源的起始位置。此指南是“始终对齐到 4 KiB 起始位置”的进阶版本。

  考虑一个示例:某资源在先前包中的偏移为 `PriorAssetOffset`,而在正在构建的更新包中的偏移为 `AssetOffset`。要确保 pack 文件创建工具将每个资源的起始位置对齐到 4-KiB 边界,请令 `PriorAssetOffset % 4096 == 0` 和 `AssetOffset % 4096 == 0`。此条件始终满足 `PriorAssetOffset % 4096 == AssetOffset % 4096`。以其他任何方式确保该等式成立也同样可行。这一条件可以让 pack 文件创建工具使用更少的填充。

  此技巧在 [有状态对齐示例](#AAS) 中展示。
* 如果你使用非确定性的压缩,且未压缩的资源未发生变化,请从先前的打包中复制其压缩后的数据,以使压缩后的数据同样保持不变。
* 如果资源被自动排序以匹配预期的运行时加载顺序,请考虑在首次发布时冻结这种自动排序。
* 如果某个 pack 文件中有许多在更新中频繁变化的微小资源,考虑将它们放在相邻位置。修改 400 KiB 比 100 次修改 4 KiB 更高效。

## pack 文件中良好与糟糕对齐的示例

有些游戏会将许多资源合并到称为 *pack 文件* 的大文件中。子文件级内容更新显著改善了下载体积和硬盘空间的效率。要利用此技术,游戏的 pack 文件创建工具在文件内保持资源的 4-KiB 对齐至关重要。本节展示了在 pack 文件中对齐资源的好坏示例。

为了说明,每个示例都以一系列写有字母的单元格表示。为简化说明,示意图中每个单元格为 1 KiB。单元格之间的实线表示 4-KiB 边界所在。

### 数据覆盖

在下图中,更新中覆盖了单个单元格。唯一需要下载的是包含此单元格的 4-KiB 页。

<img src="https://mintcdn.com/microsoft-4404708b/sIrPFR_ir_sNKVEv/images/gdk/features/common/contentupdate_overwrite.png?fit=max&auto=format&n=sIrPFR_ir_sNKVEv&q=85&s=5f7f4411ee0a3d16b6451b7fea33d1ec" alt="示意图显示一个单元格为“变更数据”,而组成 4 KiB 页的四个单元格为“下载数据”。" width="1280" height="452" data-path="images/gdk/features/common/contentupdate_overwrite.png" />

### 插入

在下图中,更新中插入了一个单元格。因此,插入后每个单元格都会向后移一位。这种移动使单元格跨越了 4-KiB 边界,使得插入之后新旧包中所有 4-KiB 页都不相同。插入之后的每一个 4-KiB 页都被下载。

<img src="https://mintcdn.com/microsoft-4404708b/sIrPFR_ir_sNKVEv/images/gdk/features/common/contentupdate_insert_non4k.png?fit=max&auto=format&n=sIrPFR_ir_sNKVEv&q=85&s=f59f2be0322605d3bc6145bac1900caf" alt="示意图显示“ABCD”“EFGH”“IJKL”“MNOP”“QRST”“UVXW”,在“J”和“K”之间插入数据“Z”,并显示下载结果“IJZK”“LMNO”“PQRS”“TUVW”“X”,箭头标示字母在页之间的位移。" width="1280" height="480" data-path="images/gdk/features/common/contentupdate_insert_non4k.png" />

在下图中,更新中插入了一个单元格。此外,还插入了三个填充单元格。这四个单元格合起来实际上是一次 4-KiB 插入,从而防止其余单元格跨越 4-KiB 边界。仅下载包含插入和填充的 8 KiB。

<img src="https://mintcdn.com/microsoft-4404708b/sIrPFR_ir_sNKVEv/images/gdk/features/common/contentupdate_insert_4k.png?fit=max&auto=format&n=sIrPFR_ir_sNKVEv&q=85&s=ffd813c89872b96b018e47e31f222710" alt="示意图显示“ABCD”“EFGH”“IJKL”“MNOP”“QRST”“UVXW”,在“J”和“K”之间插入数据“Z”,并显示下载结果“IJZK”和“L padding padding padding”。" width="1280" height="480" data-path="images/gdk/features/common/contentupdate_insert_4k.png" />

### 删除

在下图中,更新中删除了一个单元格。因此,删除之后每个单元格都向前移一位。这种移动使单元格跨越了 4-KiB 边界。删除之后的每一个 4-KiB 页都被下载。

<img src="https://mintcdn.com/microsoft-4404708b/sIrPFR_ir_sNKVEv/images/gdk/features/common/contentupdate_delete_non4k.png?fit=max&auto=format&n=sIrPFR_ir_sNKVEv&q=85&s=cc79ed7e3642c373f68cb8cf1f8fb375" alt="示意图显示“ABCD”“EFGH”“IJKL”“MNOP”“QRST”“UVXW”,删除数据“K”,并显示下载结果“IJLM”“NOPQ”“RSTU”“VWX”,箭头标示字母在页之间的位移。" width="1280" height="510" data-path="images/gdk/features/common/contentupdate_delete_non4k.png" />

在下图中,更新中删除了八个单元格。仅下载 4 KiB —— 即删除范围起止 4-KiB 页内的单元格。

如果删除按 4 KiB 对齐,并且其长度是 4 KiB 的倍数,则无需下载数据。

<img src="https://mintcdn.com/microsoft-4404708b/sIrPFR_ir_sNKVEv/images/gdk/features/common/contentupdate_delete_4k.png?fit=max&auto=format&n=sIrPFR_ir_sNKVEv&q=85&s=7eef5d5ad6f21a30374495979f914188" alt="示意图显示“ABCD”“EFGH”“IJKL”“MNOP”“QRST”“UVXW”,删除数据“KLMNOPQR”,并显示下载结果“IJST”。" width="1280" height="510" data-path="images/gdk/features/common/contentupdate_delete_4k.png" />

<a id="AS" />

### 对齐:简单

下图显示了六个资源。每个资源存储在任意数量的单元格中。每个资源的起始位置都在 4-KiB 边界。因此,你可以独立地添加、更改(包括增加或减少大小)或删除资源。

这种对齐方式是在 pack 文件内对齐资源最简单的方法。

<img src="https://mintcdn.com/microsoft-4404708b/sIrPFR_ir_sNKVEv/images/gdk/features/common/contentupdate_packfile1.png?fit=max&auto=format&n=sIrPFR_ir_sNKVEv&q=85&s=38675f40bc9f2e9609f0bc39ddca37dc" alt="示意图显示跨多个单元格的资源示例。每个资源都从 4 KiB 边界开始。当前一个资源的长度不是 4 KiB 的倍数时,资源之间会有填充单元格。" width="1280" height="290" data-path="images/gdk/features/common/contentupdate_packfile1.png" />

### 对齐:进阶无状态

下图显示了七个资源。每个资源存储在任意数量的单元格中。每个大型资源的起始位置对齐到 4-KiB 边界,但小型资源(图中的 Asset 6)与前一个资源紧邻排列,没有填充。此对齐方式有三个好处:

1. 用于填充的空间更少。
2. 当你更改 Asset 5 时,下载 Asset 6 实际上是免费的。
3. 当你更改 Asset 6 时,反正会下载整个 4 KiB。它究竟是填充还是 Asset 5 的末尾都无关紧要。

如果某个资源能够容纳到所需的填充中,则可将其紧密排布,不加填充。

<img src="https://mintcdn.com/microsoft-4404708b/sIrPFR_ir_sNKVEv/images/gdk/features/common/contentupdate_packfile2.png?fit=max&auto=format&n=sIrPFR_ir_sNKVEv&q=85&s=733719567c04e2f7ed2566277fc46ef3" alt="示意图显示跨多个单元格的资源示例。每个资源都从 4 KiB 边界开始。当前一个资源的长度不是 4 KiB 的倍数时,资源之间会有填充单元格。Asset 6 是紧邻 Asset 6 的一个单元格,没有填充。" width="1280" height="290" data-path="images/gdk/features/common/contentupdate_packfile2.png" />

<a id="AAS" />

### 对齐:进阶有状态

下图显示了七个资源。每个资源存储在任意数量的单元格中。在第一个包中,每个资源的起始位置都是紧邻排布,没有填充。在新包中,每个资源的起始位置在一个 4-KiB 页内的偏移与其在旧包中的偏移相同。

例如,Asset 3 在旧包中处于 4-KiB 页内起始处后的第一个单元格。在新包中,Asset 3 之前添加了填充,以便它同样从 4-KiB 页起始处后的第一个单元格开始。

这种方式在保留内容更新所需对齐的同时,尽量减少填充的数量。它的缺点是要求 pack 文件工具了解旧 pack 文件的布局,以匹配其偏移。

<img src="https://mintcdn.com/microsoft-4404708b/sIrPFR_ir_sNKVEv/images/gdk/features/common/contentupdate_packfile3.png?fit=max&auto=format&n=sIrPFR_ir_sNKVEv&q=85&s=00cbcaaf404c9a2eb2b52e4efe0c2506" alt="示意图显示跨多个单元格的资源示例。第一个包中没有填充单元格。在新包中,Asset 3 和 Asset 5 之前有填充单元格。从第一个包到新包的连线突出显示,填充单元格使得 Asset 3 和 Asset 5 在两个包中都从最近的 4 KiB 边界后开始相同的距离。" width="1280" height="464" data-path="images/gdk/features/common/contentupdate_packfile3.png" />

## 碎片化

对于更新历史悠久、对已有文件有大量变化,以及包中对大文件做了大量小改动的游戏,内容更新算法可能产生次优的更新大小。有两种方式判断更新大小是否次优:

1. 查看使用 `/priorpackage` 运行 `makepkg pack` 时的输出。它会显示警告:“More than # page-level XTS entries. Page-level update efficiency will be compromised for # pages.”
2. 查看 `makepkg pack` 或 `packageutil compare` 生成的比较报告。如果这些报告显示你确定未变化的文件被 100% 重新下载,这种情况可能是 XVC 文件格式的加密片段数据结构空间用尽的一种表征。

在你的 Microsoft 客户经理的指导下,考虑对 `makepkg pack` 使用 `/maxencryptionfragments` 选项以生成更优的差异计算。将此值设置得过高会增加游戏启动或 DLC 挂载时发生内存不足错误的风险。

当你插入数据时,内容更新会导致包在物理硬盘上的数据发生碎片化。内容更新会尝试通过在更新期间对包进行部分碎片整理来最大限度地降低此影响。它保证在硬盘可用空间允许的前提下,任意两个相邻碎片的大小之和至少为 100 mebibyte(MiB)。内容更新会尝试让所有碎片都至少达到 100 MiB。

XBOX One ERA 上先前几代的内容更新不进行碎片整理。

## 工具用法

* **`makepkg /contentid GUID`:** 内容 ID 参数与包家族名称一起,跨版本标识一个包。要测试包之间的更新,请使用相同的内容 ID 和包家族名称创建两个包。
* **`packageutil compare`:** 此工具生成一个更新计划,描述如何从旧包更新到新包。它还会生成一份报告,列出更新过程中会下载的文件以及这些文件中的数据范围。使用此报告识别意外变化。
* **`xbapp update`(PC:`wdapp update`):** 此工具将之前安装在开发套件或 PC 上的包更新为新包。它使用与零售主机相同的方法,因此你可以用它来测试更新后的用户体验和 IO 性能。

## 详细信息

本节介绍关于内容更新如何工作的实现细节。这些细节可能会发生变化,面向对内容更新极为感兴趣、希望理解和精细调整所有内容的开发者。

**什么是更新流式计划(update streaming plans)?**

更新流式计划是 XBOX FastStart 技术的演进。内容更新 v3 使用这些计划告诉 XBOX 主机和 PC 如何将旧包转换为新包。此方法使得在发布时能够在云端进行比主机或 PC 在下载期间所能进行的更深入的分析。

**为什么选择 4 KiB 作为允许的插入或删除倍数?**

4 KiB 是 XBOX 和受支持的 PC 文件系统的 NTFS 簇大小。以不同于 NTFS 簇大小的倍数进行插入或删除,将需要大量磁盘 IO 来安装更新。

内容包以 4-KiB 块为单位进行加密和完整性保护。以不同于加密块大小的倍数进行插入或删除,将需要重新加密数据以安装更新。这种要求会在合适的用户未登录或游戏光盘不可用而无法获取许可证的场景下,阻碍后台更新。

有了这一要求,包可以高效地更新,而不需要在更新过程结束时进行耗时的解包步骤。

**在 makepkg/packageutil 阶段与发布阶段分别会发生什么?**

两者的处理是相同的。在发布过程中,包会使用云端为其选择的最佳先前包(等同于 makepkg /priorpackage)的数据进行加密。然后从多个旧包生成更新流式计划(等同于 packageutil compare)。

**在 makepkg 中指定的 /priorpackage 会影响零售包吗?**

不会。云端的发布过程始终会覆盖此指定。`makepkg` 选项仅用于在发布之前进行本地估算。

**是否只下载更新过的游戏数据?**

不是。包中包含大量系统数据,包括所有数据页的加密哈希、Game OS(仅主机)以及嵌入的 NTFS 文件系统。它们会在必要时被下载。

此外,某些未变化的数据也可能被重新下载,以最小化 HTTP 请求的开销。当前小于 64 KiB 且被变化数据包围的未变化数据可能会被重新下载,但此配置可能为优化 *下载时间* 而发生变化。

packageutil compare 生成的报告在其估算中包含前述信息。

### 另请参见

[创建、检查和测试内容更新](/build/core-features/common/packaging/packaging-testing-updates)

[Checking for updates](https://learn.microsoft.com/build/store/commerce/fundamentals/xstore-checking-for-updates)


## Related topics

- [创建、检查和测试内容更新](/zh-CN/build/core-features/common/packaging/packaging-testing-updates.md)
- [使用 MSIXVC2 进行内容更新](/zh-CN/build/core-features/common/packaging/packaging-updates-msixvc2.md)
- [检查更新](/zh-CN/publishing/xstore-commerce/xstore-checking-updates.md)
- [游戏打包、更新与流式安装测试](/zh-CN/build/core-features/common/packaging/title-packaging-streaming-install-testing.md)
- [流式安装与智能交付概述](/zh-CN/build/core-features/common/packaging/overviews/streaming_install-intelligent_delivery.md)
