管理和许可可下载内容 (DLC) - Microsoft Game Development Kit

管理和许可可下载内容 (DLC) - Microsoft Game Development Kit

根据定义,可下载内容 (DLC) 由产品组成,其在购买时为用户提供一个可下载的包。

需要对这些内容进行独立许可和装载,然后才能访问其内容。

DLC 包可根据每台设备的内容共享行为获得许可,如游戏产品共享模型中所述。

合作伙伴中心的新增功能是创建一种没有包的耐用品。

它们适用于只有许可证、不包含游戏可用的任何数据的产品。

这样就无需创建虚拟包来提交到合作伙伴中心,用户也不需要下载占用存储空间但不使用的包。

有关详细信息,请参阅如何使用不带包的耐用品。

DLC 开发工作流

请参阅可下载内容 (DLC) 包,详细了解如何配置 DLC 内容,包括如何通过 MicrosoftGame.config 与基础游戏进行关联。

可通过 3 种方法安装 DLC:

松散 DLC 部署

本地安装 DLC 包

从 Microsoft Store 安装 DLC 包

1. 松散 DLC 部署

安装指向松散 DLC MicrosoftGame.config 和资产文件的目录:

Xbox:

> xbapp deploy

Package Full Name: 41336MicrosoftATG.DLC1_2020.10.14.0_neutral__dspnxghe87tn0

The operation completed successfully.

电脑:

> wdapp register

Registered 41336MicrosoftATG.DLC1_2020.10.14.0_x64__dspnxghe87tn0

Copied temporary generated AppXManifest.xml file to C:\Users\\AppData\Local\Temp\41336MicrosoftATG.DLC1_2020.10.14.0_x64__dspnxghe87tn0_AppXManifest.xml

The operation completed successfully.

2. 本地安装 DLC 包

安装 makepkg 创建的 .xvc/.msixvc:

Xbox:

> xbapp install

12:16:21.800 Registered for streaming: 41336MicrosoftATG.DLC1_2020.10.14.0_neutral__dspnxghe87tn0_xs.xvc

12:16:22.645 Launch 0.00% Package 0.00%

12:16:32.650 Launch 100.00% Package 100.00%

12:16:32.651 Streaming install finished

The operation completed successfully.

电脑:

> wdapp install <.msixvc package>

Starting installation. See the Microsoft Store app for further details.

Launch 100% Package 100%

Installed 100%.

Installed 41336MicrosoftATG.DLC1_2020.10.14.0_x64__dspnxghe87tn0

3. 从 Microsoft Store 安装 DLC 包

在测试帐户登录到开发者沙盒后,搜索或直接链接到 Microsoft Store 应用中的 DLC 产品页面并进行安装。

验证 DLC 安装

Xbox:

> xbapp listdlc

Registered DLC by Package Full Name:

41336MicrosoftATG.DLC1_2020.10.14.0_neutral__dspnxghe87tn0

The operation completed successfully.

电脑:

> wdapp listdlc

Registered DLC packages by Package Full Name:

41336MicrosoftATG.DLC1_2020.10.14.0_x64__dspnxghe87tn0

The operation completed successfully.

在电脑上,还可在 PowerShell 中使用 get-appxpackage 查看为特定游戏安装的 DLC,请注意依赖项部分:

> get-appxpackage 41336MicrosoftATG.DownloadableContent

Name : 41336MicrosoftATG.DownloadableContent

Publisher : CN=A4954634-DF4B-47C7-AB70-D3215D246AF1

Architecture : X64

ResourceId :

Version : 2020.10.14.0

PackageFullName : 41336MicrosoftATG.DownloadableContent_2020.10.14.0_x64__dspnxghe87tn0

InstallLocation : E:\Repos\ATGgit\gx_dev\Samples\Live\DownloadableContent\Gaming.Desktop.x64\Debug

IsFramework : False

PackageFamilyName : 41336MicrosoftATG.DownloadableContent_dspnxghe87tn0

PublisherId : dspnxghe87tn0

IsResourcePackage : False

IsBundle : False

IsDevelopmentMode : True

NonRemovable : False

Dependencies : {41336MicrosoftATG.DLC1_2020.10.14.0_x64__dspnxghe87tn0}

IsPartiallyStaged : False

SignatureKind : None

Status : Ok

SignatureKind 将指示安装了哪种包,本地构建和安装的包为无,从 Microsoft Store 安装的包为 Store。

购买和安装 DLC

请参阅商店的基本操作文章,了解如何枚举目录和提供购买附加内容的能力。

可以通过检查 XStoreProduct.hasDigitalDownload来确定附加产品为 DLC。

如果从 Microsoft Store 购买 DLC,DLC 将排队等待下载。

如果使用 XStoreShowPurchaseUiAsync 购买 DLC,则 DLC 将不会排队等待下载。

相反,游戏应按照下面的代码手动请求下载。

(可选)可以创建监视器来跟踪进度。

包标识符是标识特定包的不透明字符串。

它对于每个程序包是唯一的,但与游戏的每个启动实例不同,因此不应将其存储为在当前会话之外重用。

可以使用以下方法获取包的包标识符,具体取决于以下方案:

调用 XStoreDownloadAndInstallPackagesAsync 以下载和安装包后,可以通过调用 XStoreDownloadAndInstallPackagesResult来获取这些包的包标识符。

可以先调用XPackageEnumeratePackages,然后从传递回XPackageEnumerationCallback回调函数的XPackageDetails结构中检索包标识符,以获取已下载并安装的包的包标识符。

可以通过调用 XPackageGetCurrentProcessPackageIdentifier来获取当前游戏的包标识符。

void StartDownload()

{

auto async = new XAsyncBlock{};

async->queue = m_asyncQueue;

async->context = this;

async->callback = [](XAsyncBlock* asyncBlockInner)

{

uint32_t count = 0;

HRESULT hr = XStoreDownloadAndInstallPackagesResultCount(asyncBlockInner, &count);

std::vector packageIds(count);

XStoreDownloadAndInstallPackagesResult(asyncBlockInner, count, packageIds.data());

for(auto packageId : packageIds)

{

hr = XPackageCreateInstallationMonitor(

packageId,

0,

nullptr,

1000,

m_asyncQueue,

&m_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(progress.installedBytes) / static_cast(progress.totalBytes);

}

else

{

XPackageCloseInstallationMonitorHandle(pimHandle);

}

}, &callbackToken);

// Persist callbackToken to unregister upon completion

}

}

delete asyncBlockInner;

}

const char* storeIds[] =

{

"9PLNMXRKNM4C",

"9PLNMXRKNM5D"

};

HRESULT hr = XStoreDownloadAndInstallPackagesAsync(storeContext, storeIds, ARRAYSIZE(storeIds), async);

if (FAILED(hr))

{

delete async;

return;

}

}

枚举 DLC 包

无论如何安装 DLC,游戏都需要在使用前枚举已安装的 DLC。

通常首先在这里获取包标识符。

bool CALLBACK DlcCallback(void* context, const XPackageDetails* details)

{

printf("DLC found: name: %s packageId: %s\n", details->displayName, details->packageIdentifier);

return true;

}

void RefreshInstalledPackages()

{

HRESULT hr = XPackageEnumeratePackages(

XPackageKind::Content,

XPackageEnumerationScope::ThisAndRelated,

this,

DlcCallback);

}

检测是否已安装 DLC 包

void RegisterPackageInstalledEvent()

{

XPackageRegisterPackageInstalled(

m_asyncQueue,

this,

[](void *context, const XPackageDetails *package)

{

printf("Package Installed event received: %s\n", package->displayName);

},

&m_packageInstallToken);

}

如果这似乎未如预期的那样被命中,请参阅疑难解答部分。

无论 DLC 包是通过上述三种可能性的哪种方法安装的,都应触发此操作。

为 DLC 获取许可证

在开发中,请参阅在开发中测试 DLC 许可中的说明。

基础游戏需要获取 DLC 的许可证,来确定是否应向用户授予对其内容的访问权限。

未能获取许可证可能会导致游戏终止,因为平台可能会由于多种原因(如租用到期)而使许可证失效。

游戏通常使用限制性许可,这意味着在游戏获取了包的许可证之后,会为该设备和产品实例锁定对包的访问。

游戏必须先释放许可证,然后其他实例或设备才能获取访问权限和许可证。

请与你的 Microsoft 帐户代表联系,确保你的游戏配置为使用限制性许可(未在合作伙伴中心配置)。

有关限制性许可的详细信息,请参阅开放许可与限制性许可。

因为游戏必须释放许可证,所以由游戏负责跟踪它获取的许可证,并在不再需要许可证或是自己终止时释放它们。

如果游戏未能释放许可证,则许可证会在超时期限之后自动释放。

void CALLBACK AcquireLicenseForPackageCallback(XAsyncBlock* async)

{

XStoreLicenseHandle licenseHandle = nullptr;

HRESULT hr = XStoreAcquireLicenseForPackageResult(

async,

&licenseHandle);

if (FAILED(hr))

{

printf("Failed retrieve the license handle: 0x%x\n", hr);

return;

}

bool isValid = XStoreIsLicenseValid(licenseHandle);

printf("isValid: %s\n", isValid ? "true" : "false");

hr = XStoreRegisterPackageLicenseLost(licenseHandle, m_asyncQueue, context,

[](void *context)

{

// Check if the license lost corresponded to any mounted DLC

// If so, it is up to the game to determine an appropriate time

// to unmount the DLC, e.g. after the current match is completed

});

delete async;

}

void AcquireLicenseForPackage(const char* packageIdentifier)

{

auto async = new XAsyncBlock{};

async->context = this;

async->queue = m_asyncQueue;

async->callback = AcquireLicenseForPackageCallback;

HRESULT hr = XStoreAcquireLicenseForPackageAsync(

m_storeContext,

packageIdentifier,

async);

if (FAILED(hr))

{

delete async;

return;

}

}

确定 DLC 的许可证源

使用 XStoreCanAcquireLicenseForPackageAsync 或 XStoreCanAcquireLicenseForStoreIdAsync (具体取决于标识符类型),可以确定 DLC 是否为

可授权使用,以便在不可授权使用时提供购买选择

可通过光盘或数字许可证许可

void PreviewLicense(const char* storeId)

{

auto async = new XAsyncBlock{};

async->queue = m_asyncQueue;

async->callback = [](XAsyncBlock* async)

{

XStoreCanAcquireLicenseResult result;

HRESULT hr = XStoreCanAcquireLicenseForStoreIdResult(

async,

&result);

if (FAILED(hr))

{

printf("Error calling XStoreCanAcquireLicenseForStoreIdResult: 0x%x\n", hr);

}

else

{

// Status = 1 Licensable and

// a. licensableSku = "DISC" if disc licensed

// b. licensableSku = "0010" or similar if digital licensed

printf("Status: %u LicensableSku: %s\n", result.status, result.licensableSku);

}

delete async;

};

HRESULT hr = XStoreCanAcquireLicenseForStoreIdAsync(

m_xStoreContext,

storeId,

async);

if (FAILED(hr))

{

delete async;

printf("Error calling XStoreCanAcquireLicenseForStoreIdAsync: 0x%x", hr);

return;

}

}

装载和卸载 DLC

成功获取 DLC 许可证后,就可装载和访问 DLC 内容。

void CALLBACK MountPackageCallback(XAsyncBlock* async)

{

XPackageMountHandle mountHandle = {};

HRESULT hr = XPackageMountWithUiResult(async, &mountHandle);

if (SUCCEEDED(hr))

{

// Access DLC goodness

}

else

{

XStoreCloseLicenseHandle(license);

printf("Error mounting package: 0x%x\n", hr);

}

delete async;

};

void MountPackage(const char* packageIdentifier)

{

auto async = new XAsyncBlock{};

async->queue = m_asyncQueue;

async->context = context;

async->callback = MountPackageCallback;

HRESULT hr = XPackageMountWithUiAsync(packageIdentifier, async);

if (FAILED(hr))

{

printf("XPackageMountWithUiAsync failed : 0x%x\n", hr);

delete async;

}

}

卸载时,释放所有令牌和句柄:

void UnmountPackage(XPackageMountHandle mountHandle, XStoreLicenseHandle license, XTaskQueueRegistrationToken licenseLostToken)

{

XStoreUnregisterPackageLicenseLost(license, licenseLostToken, false);

XPackageCloseMountHandle(mountHandle);

XStoreCloseLicenseHandle(license);

}

卸载 DLC

使用 XPackageUninstallPackage 卸载 DLC 包。

必须先卸载包。

智能传递和 DLC

Xbox Series X/S 产品可许可和装载为 Xbox One 产品创建的 DLC。

当 DLC 包实际上不包含游戏使用的数据时,这种方案很典型。

唯一要检查的是 ERA DLC 的 AllowedProduct ID 在其 package.appxmanifest(即 GUID)中是否与合作伙伴中心内分配给产品的旧版 Xbox 产品 ID 匹配。

如果不匹配,则这可能是从 XDP 迁移的游戏,并且这仅适用于从 Microsoft Store 下载的包,因为 Xbox Series X/S 将获得 Xbox One 的产品 ID。

出于开发目的,请参阅疑难解答部分中的说明。

不同产品中的 DLC

可枚举和使用不同产品的 DLC。

对于希望从其他产品使用 DLC 的游戏,请在合作伙伴中心的产品关系设置部分中为 DLC 产品分配"可销售和使用"关系。

所选的可用产品将仅限于发布者帐户。

在开发中测试 DLC 许可

有关详细信息,请参阅启用许可证测试,尤其是针对主机。

本地 DLC 包必须使用 /contentid 参数制作。

每个 DLC 包必须从默认值重写其 EKBID。

另请参阅

商业概述

启用许可证测试

如何使用不带包的耐用品

XStore API 参考

相关推荐