根据定义,可下载内容 (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\
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
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
}
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 参考