更新プログラムの確認
ゲームを最新の状態にすることが重要な場合、オプションでタイトルの更新プログラムを確認し、適用できます。 これは、接続されたゲーム クライアントが同じ機能を持つ必要があるマルチプレイヤー機能を含む場合によく重要になります。
次のコード例に示すように、XStoreQueryGameAndDlcPackageUpdatesAsync を使用して、そのタイトルに更新プログラムがあるかをチェックできます。
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 パッケージ)、上記の方法により、各DLC をチェックすることで不必要なオーバーヘッドが発生する可能性があります。
代わりに、XStoreQueryPackageUpdatesAsync を使用して、XPackageEnumeratePackages の出力をフィルター処理します。
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;
}
}
更新プログラムのダウンロードとインストール
更新プログラムのセットが特定されると、次の 2 つのオプションが利用できます。
1. 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);
これが完了し XStoreDownloadPackageUpdatesResult が成功であることをコールバックが確認すると、ゲームは適切なタイミング (マッチ後やメイン メニューなど) まで待機し、それから同じ呼び出しを行います。
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 パッケージを作成します
- 同じ ID でバージョン番号を上げた V2 パッケージを作成します
-
xbapp/wdapp install
V1 -
xbapp/wdapp update
V2/a
- V1 を起動する
パッケージ間でコンテンツ ID が一致していることを確認します。
その結果、V1 の XStoreQueryGameAndDlcPackageUpdatesAsync
呼び出しは利用可能な更新プログラムを検出し、XStoreDownload(AndInstall)PackageUpdatesAsync
はアドバタイズされた V2 パッケージを使用して V1 を更新します。
V2の "ダウンロード" が進行中の場合は、Microsoft Store アプリのキューにアイテムとして表示されます。
インストール完了後は、wdapp list
または get-appxpackage
で確認できる V2 をインストールする必要があります。
/a
の代わりに /m
を使用すると更新が "必須" になりますので、XStorePackageUpdate.isMandatory
フィールドには影響があります。
署名が異なるため、開発ビルドから Microsoft Store でダウンロードしたパッケージにアップデートすることはできません。
効率的な更新プログラムを作成する方法とテストするさまざまな方法の詳細については、以下で参照されているコンテンツの更新ページを参照してください。