检查更新
游戏可以选择性地检查和应用更新(如果它对于游戏是最新的至关重要的)。 当游戏涉及多人游戏功能时,这通常很重要,在这些功能中,连接的游戏客户端必须具有相同的功能。
游戏可以并且使用 xstoreQueryGame 和DlcPackageUpdatesAsync 检查更新,如以下示例代码所示:
void CALLBACK QueryGameAndDlcPackageUpdatesCallback(XAsyncBlock* async)
{
unsigned int numUpdates = 0;
HRESULT hr = XStoreQueryGameAndDlcPackageUpdatesResultCount(async, &numUpdates);
if (SUCCEEDED(hr))
{
if (numUpdates > 0)
{
std::vector<XStorePackageUpdate> packages(numUpdates);
hr = XStoreQueryGameAndDlcPackageUpdatesResult(async, numUpdates, packages.data());
if (SUCCEEDED(hr))
{
for (auto &package : packages)
{
printf("Update available %s\n", package.packageIdentifier);
}
}
}
else
{
printf("No updates are available\n");
}
}
delete async;
}
void CheckForUpdates()
{
auto asyncBlock = new XAsyncBlock{};
asyncBlock->queue = m_asyncQueue;
asyncBlock->callback = QueryGameAndDlcPackageUpdatesCallback;
if (FAILED(XStoreQueryGameAndDlcPackageUpdatesAsync(m_xStoreContext, asyncBlock)))
{
delete asyncBlock;
}
}
如果游戏具有许多对检查更新并不重要的 DLC(例如,空 DLC 包,仅用于许可),则上述操作可能会导致检查每个程序包时产生不必要的开销。
请改用 XStoreQueryPackageUpdatesAsync 筛选 ckageEnumeratePackages 的输出:
void CALLBACK QueryPackageUpdatesCallback(XAsyncBlock* async)
{
unsigned int numUpdates = 0;
HRESULT hr = XStoreQueryPackageUpdatesResultCount(async, &numUpdates);
if (FAILED(hr))
{
printf("XStoreQueryPackageUpdatesResultCount failed : 0x%x\n", hr);
return;
}
printf("Number of updates: %d", numUpdates);
if (count > 0)
{
std::vector<XStorePackageUpdate> packages(numUpdates);
hr = XStoreQueryPackageUpdatesResult(async, numUpdates, packages.data());
if (SUCCEEDED(hr))
{
for (auto &package : packages)
{
printf("Update available %s\n", package.packageIdentifier);
}
}
}
}
void QueryPackageUpdates()
{
std::vector<std::string> packageIds;
HRESULT hr = XPackageEnumeratePackages(
XPackageKind::Game,
XPackageEnumerationScope::ThisOnly, // this will return for the base game only
&packageIds, [](void* context, const XPackageDetails* details) -> bool
{
auto packageIds = reinterpret_cast<std::vector<std::string>*>(context);
printf("Identifier: %s name: %s\n", details->packageIdentifier, details->displayName);
packageIds->push_back(details->packageIdentifier);
});
// packageIds now populated with just the base game package Id
auto asyncBlock = new XAsyncBlock();
asyncBlock->queue = m_asyncQueue;
asyncBlock->context = m_storeContext;
asyncBlock->callback = QueryPackageUpdatesCallback;
hr = XStoreQueryPackageUpdatesAsync(
m_storeContext,
packageIds.data(),
packageIds.size(),
asyncBlock);
if (FAILED(hr))
{
printf("XStoreQueryPackageUpdatesAsync failed: 0x%x\n", hr);
return;
}
}
下载并安装更新
确定更新集后,有两个选项:
1. 在一次操作中下载并安装更新
使用 XStoreDownloadAndInstallPackageUpdatesAsync 将排队并下载所有更新,下载完成后,游戏将终止以应用更新。 这将在没有警告的情况下发生。 如果游戏在下载过程中不允许任何功能,并且显示表示正在进行更新,且游戏将终止的通知,则这可能是可以接受的。
void DownloadUpdates()
{
std::vector<const char*> packageIds;
for (XStorePackageUpdate package : m_updates)
{
// m_updates populated from the update check
packageIds.push_back(package.packageIdentifier);
}
if (!packageIds.empty())
{
auto asyncBlock = new XAsyncBlock{};
asyncBlock->context = this;
asyncBlock->queue = m_asyncQueue;
asyncBlock->callback = [](XAsyncBlock* asyncBlockInner)
{
// Called when update is complete
auto pThis = reinterpret_cast<Sample*>(asyncBlockInner->context);
HRESULT hr = XStoreDownloadAndInstallPackageUpdatesResult(asyncBlockInner);
delete asyncBlockInner;
};
HRESULT hr = XStoreDownloadAndInstallPackageUpdatesAsync(m_xStoreContext, packageIds.data(), packageIds.size(), asyncBlock);
}
}
2. 先下载,然后安装
可以选择将下载和安装部分分开。 游戏可以开始下载,并允许玩家继续玩游戏的选定部分(这些部分可以在不受更新影响的情况下运行,例如离线游戏模式)。
为此,只需先调用 XStoreDownloadPackageUpdatesAsync
HRESULT hr = XStoreDownloadPackageUpdatesAsync(m_xStoreContext, packageIds.data(), packageIds.size(), asyncBlock);
完成后,回调验证 xstoreDownloadPackageUpdateResult 是否成功,然后游戏可以等到适当的时间(例如在比赛后或在主菜单中),然后可以进行相同的
HRESULT hr = XStoreDownloadAndInstallPackageUpdatesAsync(m_xStoreContext, packageIds.data(), packageIds.size(), asyncBlock);
调用,此时游戏将立即终止以安装更新,因为下载已经完成。 在调用这个 API 之前,最好通知玩家并请求确认关闭。
监视下载进度
在游戏运行和更新下载过程中,XPackageCreateInstallationMonitor 可用于监视下载进度:
void CreateInstallationMonitor(const char* packageId)
{
XPackageInstallationMonitorHandle pimHandle;
HRESULT hr = XPackageCreateInstallationMonitor(packageId, 0, nullptr, 1000, m_asyncQueue, &pimHandle);
if(SUCCEEDED(hr))
{
XTaskQueueRegistrationToken callbackToken;
XPackageRegisterInstallationProgressChanged(
m_pimHandle,
this,
[](void* context, XPackageInstallationMonitorHandle pimHandle)
{
XPackageInstallationProgress progress;
XPackageGetInstallationProgress(pimHandle, &progress);
if(!progress.completed)
{
printf("%llu%% installed\n", static_cast<double>(progress.installedBytes) / static_cast<double>(progress.totalBytes);
}
else
{
XPackageCloseInstallationMonitorHandle(pimHandle);
}
}, &callbackToken);
}
}
测试更新
在开发期间,只能使用本地程序包测试可用性更新。
- 创建具有更新检查和下载代码的 V1 程序包
- 创建具有相同标识但递增版本号的 V2 程序包
-
xbapp/wdapp install
V1 -
xbapp/wdapp update
V2/a
- 启动 V1
确保程序包之间的内容 ID 匹配。
这样做的结果是,V1 的 XStoreQueryGameAndDlcPackageUpdatesAsync
调用将找到可用的更新,并使用 XStoreDownload(AndInstall)PackageUpdatesAsync
播发的 V2 包更新 V1。
当正在进行 V2 的“下载”时,它将作为队列中的项目显示在 Microsoft Store 应用程序中。
安装完成后,应安装 V2,可使用 wdapp list
或 get-appxpackage
进行验证。
使用 /m
而不是 /a
使更新“强制”,这只会影响 XStorePackageUpdate.isMandatory
字段。
由于签名差异,无法从开发版本更新到从 Microsoft Store 下载的程序包。
有关如何创建高效更新和不同测试方法的详细信息,请参阅下面引用的内容更新页。