ダウンロード コンテンツ (DLC) の管理とライセンス

定義上、ダウンロード コンテンツ (DLC) は、購入時にユーザーにダウンロード パッケージを与える製品で構成されます。 これらのコンテンツにアクセスするには、個別にライセンスを取得してマウントする必要があります。

DLC パッケージは、ゲームの製品共有モデルに記載されているように、各デバイスのコンテンツ共有動作に従ってライセンスを取得できます。

パートナー センター の新機能は、パッケージを持たない 非消費型アイテムです。 これらは、ゲームで使用できるデータが含まれていないライセンス専用製品に適しています。 この機能によって、空のパッケージを作成してパートナー センターに送信する必要がなくなり、ストレージ スペースを取るが使用することがないパッケージをダウンロードする必要がなくなります。 詳細については、「パッケージなしの非消費型アイテムを使用する方法」をご覧ください。

DLC 用の開発ワークフロー

MicrosoftGame.config でベース ゲームとの関連付けを行う方法など、DLC コンテンツを構成する方法の詳細については、「ダウンロード コンテンツ (DLC) パッケージ」をご覧ください。

DLC をインストールする方法は 3 とおりあります。

  1. ルース DLC 配置
  2. DLC パッケージのローカル インストール
  3. Microsoft Store からの DLC パッケージのインストール

1. ルース DLC 配置

ルース DLC MicrosoftGame.config およびアセット ファイルを指すディレクトリをインストールします。

Xbox:

> xbapp deploy <DLC directory>

Package Full Name: 41336MicrosoftATG.DLC1_2020.10.14.0_neutral__dspnxghe87tn0
The operation completed successfully.

PC:

> wdapp register <DLC directory>  

Registered 41336MicrosoftATG.DLC1_2020.10.14.0_x64__dspnxghe87tn0
Copied temporary generated AppXManifest.xml file to C:\Users\<ntuser>\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 <xvc package>  

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.

PC:

> 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 パッケージのインストール

テスト アカウントで開発サンドボックスにサインインし、Store アプリの DLC 製品ページを探すか、そこに直接リンクし、インストールします。

DLC インストールの検証

Xbox:

> xbapp listdlc

Registered DLC by Package Full Name:

   41336MicrosoftATG.DLC1_2020.10.14.0_neutral__dspnxghe87tn0

The operation completed successfully.

PC:

> wdapp listdlc

Registered DLC packages by Package Full Name:

41336MicrosoftATG.DLC1_2020.10.14.0_x64__dspnxghe87tn0

The operation completed successfully.

PC では、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 はインストールされているパッケージの種類、None はローカルにビルドされ、インストールされたパッケージ、Store は Store からインストールされたパッケージを示します。

DLC を購入してインストールする

カタログを列挙し、アドオンを購入する機能を提供する方法については、「基本的なストア操作」をご覧ください。 アドオン製品は、XStoreProduct.hasDigitalDownload を調べることによって、DLC かどうかを確認できます。

DLC が Store から購入された場合、DLC はキューに登録されてダウンロードされます。

XStoreShowPurchaseUiAsync を使用して DLC を購入した場合、DLC はダウンロードするキューに登録されません。 代わりに、ゲームは以下のコードに従って手動でダウンロードを要求する必要があります。 必要に応じて、進行状況を追跡するためにモニターを作成できます。

パッケージ識別子は、特定のパッケージを識別する非透過文字列です。 パッケージごとに一意のものですが、ゲームの起動されたインスタンスごとに異なるため、現在のセッションを超えて再利用するためには格納しないでください。

シナリオに応じて、次のメソッドを使用してパッケージのパッケージ識別子を取得できます。

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<char[XPACKAGE_IDENTIFIER_MAX_LENGTH]> 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<double>(progress.installedBytes) / static_cast<double>(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 パッケージのインストール方法が上記の 3 つの方法のいずれかにかかわりなく、これがトリガーされるはずです。

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 が

  1. ライセンス可能な場合は、購入オプションを提示します。以下でない場合:
  2. ディスクまたはデジタル ライセンスによるライセンス可能
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 パッケージに含まれていない場合の一般的なシナリオです。 確認する唯一の方法は、その package.appxmanifest の ERA DLC の AllowedProduct ID (GUID) が、パートナー センターで製品に割り当てられたレガシ Xbox 製品 ID と一致するかどうかです。

一致しない場合、XDP から移行された可能性があり、Xbox Series X/S が Xbox One の製品 ID として割り当てられるため、Microsoft Store からダウンロードされたパッケージでのみ機能します。 開発目的の場合、「トラブルシューティング」セクションの注意事項をご覧ください。

異なる製品の DLC

別の製品の DLC を列挙して使用することができます。 別の製品の DLC を使用するタイトルの場合は、パートナー センターの商品の関連付けをセットアップする セクションのDLC 製品へ「販売と使用が可能」 を割り当てます。 利用可能な製品の選択は、発行元アカウントに制限されます。

開発中の DLC ライセンスのテスト

特にコンソールの場合は、「ライセンス テストの有効化 」を参照してください。

/contentid パラメーターを使用してローカル DLC パッケージを作成する必要があります。

各 DLC パッケージには、既定値から EKBID がオーバーライドされている必要があります。

関連項目

コマースの概要

ライセンス テストの有効化

パッケージなしの非消費型アイテムを使用する方法

XStore API リファレンス