업데이트 확인
게임이 최신 상태로 유지되는 것이 중요한 경우 타이틀은 필요에 따라 업데이트를 확인하고 적용할 수 있습니다. 이것은 연결된 게임 클라이언트가 모두 동일한 기능을 갖는 것이 중요한 멀티플레이어 기능이 게임에 포함되어 있는 경우 종종 중요합니다.
타이틀은 다음 예제 코드에서처럼 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 패키지) 위의 내용으로 인해 각각을 확인하는 데 불필요한 오버헤드가 발생할 수 있습니다.
대신 XPackageEnumeratePackages의 출력을 필터링하는 XStoreQueryPackageUpdatesAsync를 사용합니다.
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);
이것이 완료되고 콜백이 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 앱에 대기열의 항목으로 표시됩니다.
설치가 완료되면 V2가 설치되어야 합니다. 이것은 wdapp list
또는 get-appxpackage
을(를) 사용하여 확인할 수 있습니다.
/a
대신 /m
을(를) 사용하면 업데이트가 "필수"가 되어 단순히 XStorePackageUpdate.isMandatory
필드에 영향을 줍니다.
서명 차이로 인해 개발 빌드에서 Microsoft Store에서 다운로드하는 패키지로 업데이트할 수 없습니다.
효율적인 업데이트를 만드는 방법 및 테스트하는 다양한 방법에 대한 자세한 내용은 아래에서 참조하는 콘텐츠 업데이트 페이지를 참조하세요.