0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Azure Functions で、Economy V2 の インベントリ関連の API を呼び出す

Last updated at Posted at 2025-01-04

PlayFab の Economy V2 は、クライアント側インベントリ関連の API を呼び出し、ログインしているプレイヤー以外のプレイヤーのインベントリにアイテムを追加または参照しようとすると、NotAuthorized エラーが発生します。

他のプレイヤーとアイテムを取引したり、プレゼントを送ったり、といった用途に利用したい場合は、CloudScript を利用してサーバーサイドで処理を行う必要があります。本記事では、Azure Functions を用いて、指定したプレイヤーのインベントリにアイテムを追加する方法について解説します。

TransferInventoryItems は、クライアントサイドでプレイヤー自身のインベントリ内のコレクション間・スタック間でのみアイテムを転送でき、他のプレイヤーのインベントリには適用できません。

作成する機能

Azure Functions を利用して CloudScript を実装し、指定したプレイヤーのインベントリにアイテムを追加する方法を説明します。

この機能では、対象プレイヤーのマスタープレイヤーアカウント ID (PlayFab ID) を事前に把握していることを前提とします。Azure Functions 呼び出しの際のパラメーターとして、対象プレイヤーのマスタープレイヤーアカウント ID と追加するアイテムの フレンドリ ID を指定します。

Azure Functions で PlayFab CloudScript を作成する方法は、以下のドキュメントを参照してください。

今回のサンプルの実装の Azure Functions、分離ワーカープロセス .NET 8 で作成しています。

処理の流れ

インベントリに追加する相手プレイヤーのタイトルプレイヤーアカウント ID を取得

インベントリに追加する際、プレイヤーのタイトルプレイヤーアカウント ID が必要になるため、PlayFab Server API の GetUserAccountInfoAsync メソッドを利用して、クライアントから渡されたマスタープレイヤーアカウント ID (PlayFab ID) をもとに、プレイヤーのタイトルプレイヤーアカウント ID を取得します。

// アイテムを追加するプレイヤーのタイトルプレイヤーアカウント ID を取得する
var requestGetUserAccountInfo = new GetUserAccountInfoRequest()
{
    PlayFabId = playerId,
};

var getUserAccountInfoResult = await serverApi.GetUserAccountInfoAsync(requestGetUserAccountInfo);

if (getUserAccountInfoResult.Error != null)
{
    throw new Exception($"GetUserAccountInfoAsync failed. Error={getUserAccountInfoResult.Error.GenerateErrorReport()}");
}

// アイテムを追加するプレイヤーのタイトルプレイヤーアカウント ID
var titlePlayerAccountId = getUserAccountInfoResult.Result.UserInfo.TitleInfo.TitlePlayerAccount.Id;

インベントリにアイテムを追加する

AddInventoryItemsAsync メソッドを利用し、指定したプレイヤーのインベントリにアイテムを追加します。

その際 AddInventoryItemsRequest.Entity に、アイテムを追加するプレイヤーのタイトルプレイヤーアカウント ID を指定します。

  // アイテムを追加するプレイヤーのタイトルプレイヤーアカウント ID
  var titlePlayerAccountId = getUserAccountInfoResult.Result.UserInfo.TitleInfo.TitlePlayerAccount.Id;

  var playerEntity = new PlayFab.EconomyModels.EntityKey()
  {
      Id = titlePlayerAccountId,
      Type = "title_player_account",
  };

  // Economy V2 API
  var economyApi = new PlayFabEconomyInstanceAPI(apiSettings, authContext);

  // 指定されたプレイヤーのインベントリにアイテムを追加する
  var addInventoryItemsAsyncRequest = new AddInventoryItemsRequest()
  {
      Item = new InventoryItemReference
      {
          AlternateId = new AlternateId()
          {
              Type = "FriendlyId",
              Value = itemId, // 追加するアイテムの FriendlyId を指定
          },
      },
      Amount = 1,
      Entity = playerEntity, // 追加するプレイヤーのエンティティを指定
      IdempotencyId = idempotencyId,
  };

  var addInventoryItemsAsyncResponse = await economyApi.AddInventoryItemsAsync(addInventoryItemsAsyncRequest);

  if (addInventoryItemsAsyncResponse.Error != null)
  {
      throw new Exception($"AddInventoryItemsAsync failed. Error={addInventoryItemsAsyncResponse.Error.GenerateErrorReport()}");
  }

制限

AddInventoryItemsAsync は、Entity にプレイヤーを指定しての呼び出しには、90 秒間に 60回の制限(スロットリング)があります。

制限以上呼び出すと、TooManyRequests エラーが返されます。

Server API は、10 秒間に 1,000 回の制限があります。Economy V2 のプレイヤー指定の制限は、それに比べて厳しいですが、Entity ごとの制限のため、サーバーでの処理でもプレイヤーがばらける用途なら、むしろ使い安いように感じます。
この制限に関しては、用途に応じた事前検証を推奨します。

Function 全体のコード

Function 全体のコードは以下の通りです。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Microsoft.Azure.Functions.Worker;
using PlayFab;
using PlayFab.EconomyModels;
using PlayFab.ServerModels;
using PlayFab.Samples;

namespace Samples.Function
{
    public class AddPlayerInventoryItem
    {
        private readonly ILogger<AddPlayerInventoryItem> _logger;

        public AddPlayerInventoryItem(ILogger<AddPlayerInventoryItem> logger)
        {
            _logger = logger;
        }

        [Function("AddPlayerInventoryItem")]
        public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequest req)
        {
            _logger.LogInformation("C# HTTP trigger function processed a request.");

            string body = await new StreamReader(req.Body).ReadToEndAsync();
            _logger.LogInformation($"body={body}");
            var functionContext = JsonConvert.DeserializeObject<FunctionExecutionContext<dynamic>>(body);
            var args = functionContext.FunctionArgument;

            // CloudScript の呼び出し元のプレイヤーの MasterPlayerAccountId
            string masterPlayerAccountId = functionContext.CallerEntityProfile.Lineage.MasterPlayerAccountId;
            _logger.LogInformation($"masterPlayerAccountId={masterPlayerAccountId}");


            // クライアントから渡された PlayerId と ItemId を取得
            dynamic request = null;
            if (args != null && args["Request"] != null)
                request = args["Request"];

            string playerId = request.PlayerId;
            if (string.IsNullOrWhiteSpace(playerId))
            {
                throw new ArgumentException("PlayerId is empty", "PlayerId");
            }

            string itemId = request.ItemId;
            if (string.IsNullOrWhiteSpace(itemId))
            {
                throw new ArgumentException("ItemId is empty", "ItemId");
            }

            string idempotencyId = request.IdempotencyId;

            _logger.LogInformation($"playerId={playerId}, itemId={itemId}, idempotencyId={idempotencyId}");


            // PlayFab Server API のインスタンスを取得
            var apiSettings = new PlayFabApiSettings
            {
                TitleId = functionContext.TitleAuthenticationContext.Id,
                DeveloperSecretKey = Environment.GetEnvironmentVariable("PLAYFAB_DEV_SECRET_KEY", EnvironmentVariableTarget.Process),
            };
            var serverApi = new PlayFabServerInstanceAPI(apiSettings);

            PlayFabAuthenticationContext authContext = new PlayFabAuthenticationContext()
            {
                EntityToken = functionContext.TitleAuthenticationContext.EntityToken
            };

            // アイテムを追加するプレイヤーのタイトルプレイヤーアカウント ID を取得する
            var requestGetUserAccountInfo = new GetUserAccountInfoRequest()
            {
                PlayFabId = playerId,
            };

            var getUserAccountInfoResult = await serverApi.GetUserAccountInfoAsync(requestGetUserAccountInfo);

            if (getUserAccountInfoResult.Error != null)
            {
                throw new Exception($"GetUserAccountInfoAsync failed. Error={getUserAccountInfoResult.Error.GenerateErrorReport()}");
            }

            // アイテムを追加するプレイヤーのタイトルプレイヤーアカウント ID
            var titlePlayerAccountId = getUserAccountInfoResult.Result.UserInfo.TitleInfo.TitlePlayerAccount.Id;

            var playerEntity = new PlayFab.EconomyModels.EntityKey()
            {
                Id = titlePlayerAccountId,
                Type = "title_player_account",
            };

            // Economy V2 API
            var economyApi = new PlayFabEconomyInstanceAPI(apiSettings, authContext);

            // 指定されたプレイヤーのインベントリにアイテムを追加する
            var addInventoryItemsAsyncRequest = new AddInventoryItemsRequest()
            {
                Item = new InventoryItemReference
                {
                    AlternateId = new AlternateId()
                    {
                        Type = "FriendlyId",
                        Value = itemId, // 追加するアイテムの FriendlyId を指定
                    },
                },
                Amount = 1,
                Entity = playerEntity, // 追加するプレイヤーのエンティティを指定
                IdempotencyId = idempotencyId,
            };

            var addInventoryItemsAsyncResponse = await economyApi.AddInventoryItemsAsync(addInventoryItemsAsyncRequest);

            if (addInventoryItemsAsyncResponse.Error != null)
            {
                throw new Exception($"AddInventoryItemsAsync failed. Error={addInventoryItemsAsyncResponse.Error.GenerateErrorReport()}");
            }

            return new OkObjectResult("Success");
        }
    }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?