检查更新

游戏可以选择性地检查和应用更新(如果它对于游戏是最新的至关重要的)。 当游戏涉及多人游戏功能时,这通常很重要,在这些功能中,连接的游戏客户端必须具有相同的功能。

游戏可以并且使用 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);
    }
}

测试更新

在开发期间,只能使用本地程序包测试可用性更新。

  1. 创建具有更新检查和下载代码的 V1 程序包
  2. 创建具有相同标识但递增版本号的 V2 程序包
  3. xbapp/wdapp install V1
  4. xbapp/wdapp update V2 /a
  5. 启动 V1

确保程序包之间的内容 ID 匹配。

这样做的结果是,V1 的 XStoreQueryGameAndDlcPackageUpdatesAsync 调用将找到可用的更新,并使用 XStoreDownload(AndInstall)PackageUpdatesAsync 播发的 V2 包更新 V1。 当正在进行 V2 的“下载”时,它将作为队列中的项目显示在 Microsoft Store 应用程序中。 安装完成后,应安装 V2,可使用 wdapp listget-appxpackage 进行验证。

使用 /m 而不是 /a 使更新“强制”,这只会影响 XStorePackageUpdate.isMandatory 字段。

由于签名差异,无法从开发版本更新到从 Microsoft Store 下载的程序包。

有关如何创建高效更新和不同测试方法的详细信息,请参阅下面引用的内容更新页。

另请参阅

商业概述

启用 XStore 开发和测试

内容更新最佳做法要求授权

创建、检查和测试内容更新

XStore API 参考