This document is about: QUANTUM 2
SWITCH TO

Backend Integration


Available in the Gaming Circle and Industries Circle
Circle

カスタムアセットサーバー統合

カスタムアセットサーバーからアイテム・通貨を読み込み、トーナメントの賞金やエントリー料として使用できます。
また、これらのアイテムを差し引いたりトーナメントへのサインアップを拒否したりできる(例:ユーザーの所持金が足りないなど)カスタムサーバー経由ですべてのサインアップをルートできます。

カスタムサーバーでログイン

カスタムサーバーでログインを認証するためには、ユーザーの基本情報を特定のJSONフォーマットで返すHTTPエンドポイントを作成する必要があります。
トーナメントアカウントにユーザーIDを関連付けたい場合は、これを行わなければなりません。
関連付けられたユーザーIDは、他のカスタムサーバーとの統合コール(例:カスタムサーバーへのトーナメント結果の報告)で提供されます。

ユーザー認証のためにクライアントから渡されるデータは、application/x-www-form-urlencodedとして添付されます。
ユーザー認証に必要なデータをクライアントから送信します(例:クリネットトークン)。

C#

// coroutine
// ...
// use custom external login provider, pass custom data from your backend to authenticate user
var loginOperation = BackboneManager.Client.Login(LoginProvider.CustomExternal(
    true,
    MyBackend.Username,
    new KeyValuePair<string, object>("clientToken", MyBackend.Token),
    new KeyValuePair<string, object>("userId", MyBackend.Id)));
yield return loginOperation;

クライアントから渡されたデータをチェックし、ユーザーが有効かどうかを判断します。
ユーザーIDユーザーニックネームを含むJSONフォーマットで応答する必要があります。

JavaScript

 { 
     "id": "123", 
     "nickName": "nick" 
 }

また、エンドポイントはステータスコード200で応答する必要があります。

External store image

必要な形式を返すエンドポイントができたら、Game Settings/Authentication providersに移動し、custom authenticationを有効にします。
有効なHTTPパスを挿入し、サーバーが必要とする認証のための必要なヘッダー/パラメータを追加します。

サーバーからストアアイテムを読み込む

アイテムや通貨をカスタムサーバーから読み込むためには、アイテムを特定のJSONフォーマットで返すHTTPエンドポイントを作成する必要があります。
サーバーがアイテムを内部使用のために読み込み、アイテムと通貨を外部リソースから「読み込み済」とマークするようにこの操作を行います。

JSON フォーマット:

JavaScript

{
   items:[
       {
           id:"itemId1", 
           name:"itemName1",
           image:"imageUrl (optional)"
       },
       {
           id:"itemId2", 
           name:"itemName2",
           image:"imageUrl (optional)"
       }],   
   currencies:[
       {
           id:"currencyId1", 
           name:"currencyName1"
       },
       {
           id:"currencyId2", 
           name:"currencyName2"
       }]
}

エンドポイントはまた、status code 200 にレスポンスする必要があります。
常にインポートするすべてのアイテムを返します。
既に終了したアイテムと通貨は更新されます。

External store image

リクエストされるフォーマットで返すエンドポイントが表示されたら、Game Settings/Store Integrationへ移動し、「external store hookup」を有効にします。
プロバイダはCustom URLを選択します。
有効なHTTPパスと、サーバーからリクエストされる認証用のヘッダ・パラメータを挿入します。

Sync data nowボタンをクリックしてアイテムと通貨をインポートします。
トーナメントテンプレート賞とエントリー料の編集のときに、インポートしたアイテムにアクセスできるようになりました。

アセットにアップデートが生じた場合は、再度Sync data nowボタンをクリックしてインポートを行ってください。

カスタムサーバー経由でのサインアップ

トーナメントサインアップをカスタムサーバーを通して計画するには、特定のJSONフォーマットを受け入れるHTTPエンドポイントを作成する必要があります。
各ユーザーのアインアップごとに、トーナメント、ユーザー、エントリー料などの情報が盛り込まれたペイロードでカスタムサーバーへのコールが行われます。

このHTTPコールは、JSONペイロードを含む userTicket と呼ばれるパラメータでのPOST送信です。

JSON フォーマット:

JavaScript

{
   isSignup:true,
   isSignout:false,
   userId:"64bit number as string",
   userExternalId:"string",
   ticketId:"64bit number as string",
   tournamentId:"64bit number as string",
   customRequirements:[{
    name:"string",
    value:"string"
   }],
   fees:{
    items:[{
       id:"64bit number as string",
       externalId:"string",
       amount:1
    }],
    currencies:[{
       id:"64bit number as string",
       externalId:"string",
       amount:1
    }]
   }
}

レスポンスが正常に行われると、HTTP status code 200 (拒否された場合は、別のコード)で待機状態となります。

External signup image

リクエストされるJSONフォーマットを受け入れるエンドポイントができたら、Game Settings/Tournament settings Integrationへ移動し、「external signup」を有効にします。
有効なHTTPパスと、サーバーからリクエストされる認証用のヘッダ・パラメータを挿入します。

この設定を行ったあと、クライアントからのすべてのサインアップが受け入れおよび確認前にカスタムサーバー経由でルーティングされるようになります。

カスタムサーバーへのトーナメント結果レポート

カスタムサーバー経由でトーナメント結果を受信するには、特定のJSONフォーマットを受け入れるHTTPエンドポイントを作成する必要があります。
各トーナメントが終了するごとに、トーナメント、ユーザー、賞金などの情報が盛り込まれたペイロードでカスタムサーバーへのコールが行われます。

このHTTPコールは、JSONペイロードを含む jsonPayload と呼ばれるパラメータでのPOST送信です。

JSON フォーマット:

JavaScript

{
   "tournamentId":"64bit number as string",   
   "prizes":[{
       "ticketId":"64bit number as string",
       "userId":"64bit number as string",
       "userExternalId":"string",
       "place":1,
       "items":[{
           "id":"64bit number as string",
           "amount":1,
           "externalId":"string"}],
       "currencies":[{
           "id":"64bit number as string",
           "amount":1,
           "externalId":"string"}]
    }]
}

レスポンスが正常に行われると、HTTP status code 200 で待機状態となります。
失敗した場合、何度か繰り返されます。

External prize delivery image

リクエストされるJSONフォーマットを受け入れるエンドポイントができたら、Game Settings/Tournament settings Integrationへ移動します。
有効なHTTPパスと、サーバーからリクエストされる認証用のヘッダ・パラメータを挿入します。

PlayFabアセットサーバー統合

Playfabバックエンドからアイテム・通貨を読み込み、トーナメントの賞金やエントリー料として使用できます。
また、これらのアイテムを差し引いたりトーナメントへのサインアップを拒否したりできる(例:ユーザーの所持金が足りないなど)カスタムCloudScript経由ですべてのサインアップをルートできます。

PlayFab秘密鍵を作成する

PlayFab統合には、TitleIdSecretKey が必要となります。 
playfabダッシュボードにログインして、ゲームを選択し、設定画面を開きましょう。

PlayFab game settings

API Featuresタブに TitleId があります。新しい SecretKey の作成はSecret Keyタブで行えます。
秘密鍵に有効期限を設定しないようにしてください。トーナメントダッシュボードで新しい秘密鍵を設定し忘れた場合、ゲームが停止状態になってしまいます。

PlayFabプロバイダ経由でログインする

If you integrating with PlayFab services we strongly recommend enabling only PlayFab login provider in tournament dashboard. Also NOTE that if you will not use PlayFab login provider other integration features such as tournament signup or delivering prizes via cloudscript fail to work.

Dashboard playfab login provider image

Enter here TitleId & SecretKey and save changes.

In your game client, after you initialize and login with playfab you will get a session ticket.
This session ticket is then passed to playfab login provider to authenticate user and login to tournaments.
This process will ensure that system has correct and valid playfab id associated with user account.

C#

string playfabId;
string playfabSessionTicket;
bool isPlayfabLoginFinished;

// coroutine
// ...
// login playfab client
PlayFab.PlayFabClientAPI.LoginWithCustomID(
    new PlayFab.ClientModels.LoginWithCustomIDRequest() {
        CreateAccount = true,
        CustomId = "customPlayfabId"
    },
    (result) =>
    {
        isPlayfabLoginFinished = true;
        // get playfab user id and session ticket (we will use it for authentication)
        playfabId = result.PlayFabId;
        playfabSessionTicket = result.SessionTicket;
    },
    (error) =>
    {
        isPlayfabLoginFinished = true;
    });
// wait until playfab login process finishes
yield return new WaitUntil(() => isPlayfabLoginFinished);
// use playfab login provider, pass playfab id and session ticket
yield return BackboneManager.Client.Login(
    Gimmebreak.Backbone.User.LoginProvider.Playfab(
        true,
        "NickName-" + playfabId,
        playfabSessionTicket,
        playfabId));

playfab idをユーザーアカウントと関連付けるには、 BackboneManager.Client.User.GetLoginId(LoginProvider.Platform.Playfab)を呼び出します。
記録されたPlayfabクライアントが記録されたトーナメントクライアントと一致するかの比較に使用されます。

Playfabからストアのアイテムを読み込む

Game Settings/Store Integrationへ移動して「external store hookup」を有効にします。
プロバイダはPlayfabを選択します。
有効な title idsecret key を入力して変更を保存します。

External store playfab image

Sync data nowボタンをクリックして、アイテム・通貨を(プライマリカタログから)インポートします。
トーナメントテンプレート賞とエントリー料の編集のときにインポートしたアイテムにアクセスできるようになっているはずです。

アセットに何かしらのアップデートが生じた場合は、Sync data nowボタンをクリックして再度インポートを行ってください。

Playfab CloudScript経由でのサインアップ

トーナメントサインアップをPlayfab CloudScript経由でルートするには、特定のJSONフォーマットを受け入れる関数を作成する必要があります。
各ユーザーがサインアップするごとに、トーナメント、ユーザー、エントリー料などの情報を含むペイロードで、このcloudscript関数への呼び出しが行われます。

CloudScript関数のパラメータである args には、JSONペイロードが含まれます。

JSON フォーマット:

JavaScript

{
   serverSecret:"string",
   isSignup:true,
   isSignout:false,
   userId:"64bit number as string",
   userPlayfablId:"string",
   ticketId:"64bit number as string",
   tournamentId:"64bit number as string",
   customRequirements:[{
    name:"string",
    value:"string"
   }],
   fees:{
    items:[{
       id:"64bit number as string",
       externalId:"string",
       amount:1
    }],
    currencies:[{
       id:"64bit number as string",
       externalId:"string",
       amount:1
    }]
   }
}

サインアップ・サインアウトくを処理するCloudScript関数のサンプルです:

JavaScript

// This is a example tournament signup/signout handler. If signup is requested
// check if user can sign up and deduct any required fees. If signout is requested
// return any deducted fees back to users account.
handlers.tournamentSignup = function(args, context){
  if (args.serverSecret == "secret") {
        if (args.isSignup) {
            // Check any custom requirement for the tournament. This can be
            // e.g. having required minimum rank. If player does not meet
            // specified criteria, the signup should be rejected.
            for (var i = 0; i < args.customRequirements.length; i++) {
                var name = args.customRequirements[i].name;
                var value = args.customRequirements[i].value;
                if (name == "testRequirement" &&
                    value == "reject") {
                    return { success: false };
                }
            }
            // Check if user has required signup fees.
            if (args.fees.items.length > 0 ||
                args.fees.currencies.length > 0) {
                // Get user inventory
                var userInvetoryResult = server.GetUserInventory({PlayFabId: currentPlayerId});
                // Check if user has enough currency
                for (var i = 0; i < args.fees.currencies.length; i++) {
                    var currencyFee = args.fees.currencies[i];
                    var userCurrency = userInvetoryResult.VirtualCurrency[currencyFee.externalId];
                    if (!userCurrency ||
                        userCurrency < currencyFee.amount) {
                        // User does not have required currency or amount
                        return { success: false };
                    }
                }
                // Sort user invetory items by id
                var userInventory = {};
                for (var i = 0; i < userInvetoryResult.Inventory.length; i++) {
                    var item = userInvetoryResult.Inventory[i];
                    if (!userInventory[item.ItemId]) {
                        userInventory[item.ItemId] = [];
                    }
                    userInventory[item.ItemId].push(item);
                }
                // Check if user has enough items
                for (var i = 0; i < args.fees.items.length; i++) {
                    var itemFee = args.fees.items[i];
                    var userItems = userInventory[itemFee.externalId];
                    if (!userItems ||
                        userItems.length < itemFee.amount) {
                        // User does not have required item or amount
                        return { success: false };
                    }
                }

                // Substract user's currencies
                for (var i = 0; i < args.fees.currencies.length; i++) {
                    var currencyFee = args.fees.currencies[i];
                    server.SubtractUserVirtualCurrency({PlayFabId: currentPlayerId, VirtualCurrency: currencyFee.externalId, Amount: currencyFee.amount });
                }

                // Revoke user's items
                var revokedItems = { Items: [] };
                for (var i = 0; i < args.fees.items.length; i++) {
                    var itemFee = args.fees.items[i];
                    for (var p = 0; p < itemFee.amount; p++) {
                        revokedItems.Items.push({PlayFabId: currentPlayerId, ItemInstanceId: userInventory[itemFee.externalId][p].ItemInstanceId});
                        // Maximum 25 items can be removed at once (Playfab documentation)
                        // If container is filled with 25 items, revoke and continue
                        if (revokedItems.Items.length == 25) {
                            server.RevokeInventoryItems(revokedItems);
                            revokedItems.Items = [];
                        }
                    }
                }
                // Check if any items should be revoked (last 25 items)
                if (revokedItems.Items.length > 0) {
                    server.RevokeInventoryItems(revokedItems);
                }

                // All fees deducted, confirm tournament signup
                return { success: true };
            }
            else {
                // No fees to deduct, confirm tournament signup
                return { success: true };
            }
        }
        if (args.isSignout) {
            // Return user's currencies
            for (var i = 0; i < args.fees.currencies.length; i++) {
                var currencyFee = args.fees.currencies[i];
                server.AddUserVirtualCurrency({PlayFabId: currentPlayerId, VirtualCurrency: currencyFee.externalId, Amount: currencyFee.amount });
            }
            // Return user's items
            var returnItems = { PlayFabId: currentPlayerId, ItemIds: [], Annotation: "Returned tournament fee items. TournamentId: " + args.tournamentId };
            for (var i = 0; i < args.fees.items.length; i++) {
                var itemFee = args.fees.items[i];
                for (var p = 0; p < itemFee.amount; p++) {
                    returnItems.ItemIds.push(itemFee.externalId);
                }
            }
            // Check if any items should be returned
            if (returnItems.ItemIds.length > 0) {
                server.GrantItemsToUser(returnItems);
            }
            // User fees has been returned
            return { success: true };
        }
    }
    return { success: false };
};

レスポンスが正常に行われると、{ success: true }(拒否された場は別)で待機状態となります。

External signup playfab image

リクエストされるJSONフォーマットを受け入れる関数ができたら、Game Settings/Tournament settings Integrationへ移動し、「external signup」を有効にします。
プロバイダはPlayfabを選択します。
有効な title idsecret keyCloudScript 関数名関数バージョン (修正番号)を挿入し、保存します。

希望する場合は変更可能な、すでに生成されたサーバーシークレットがあります。
Playfabユーザーが実行するのを防ぐため、cloud scriptでこちらを使用してください。

この設定を行ったあと、クライアントからのすべてのサインアップが受け入れおよび確認前にCloud script経由でルーティングされるようになります。

Playfab CloudScript経由のトーナメント結果

Playfab CloudScript経由でトーナメント結果を受信するには、特定のJSONフォーマットを受け入れる関数を作成する必要があります。
各トーナメントが終わるごとに、トーナメント、ユーザー、賞金などの情報が盛り込まれたペイロードでcloudscript関数に呼び出しが行われます。

CloudScript関数パラメータである args にはJSONペイロードが含まれます。

JSON フォーマット:

JavaScript

{
   "serverSecret":"string",
   "tournamentId":"64bit number as string",   
   "prizes":[{
       "ticketId":"64bit number as string",
       "userId":"64bit number as string",
       "userPlayfabId":"string",
       "place":1,
       "items":[{
           "id":"64bit number as string",
           "amount":1,
           "externalId":"string"}],
       "currencies":[{
           "id":"64bit number as string",
           "amount":1,
           "externalId":"string"}]
    }]
}

賞金の受け渡しを処理するCloudScript関数のサンプルです。:

JavaScript

// This is example tournament prize delivery handler. Once tournament is finished
// all results will be provided togather with information which items and currencies
// should be granted to user.
handlers.tournamentPrizeDelivery = function(args, context){
  if (args.serverSecret == "secret" &&
        args.prizes) {
        for (var i = 0; i < args.prizes.length; i++) {
            var prize  = args.prizes[i];
            // Grant user currency prizes
            for (var p = 0; p < prize.currencies.length; p++) {
                var wonCurrency = {
                    PlayFabId: prize.userPlayfabId,
                    VirtualCurrency: prize.currencies[p].externalId,
                    Amount: prize.currencies[p].amount

                };
                server.AddUserVirtualCurrency(wonCurrency);
            }
            // Grant user item prizes
            var wonItems = {
                PlayFabId: prize.userPlayfabId,
                ItemIds: [],
                Annotation: "Won items in tournament. Place: " + prize.place + " TournamentId: " + args.tournamentId
            };
            for (var p = 0; p < prize.items.length; p++) {
                var itemPrize = prize.items[p];
                for (var c = 0; c < itemPrize.amount; c++) {
                    wonItems.ItemIds.push(itemPrize.externalId);
                }
            }
            if (wonItems.ItemIds.length > 0) {
                server.GrantItemsToUser(wonItems);
            }
        }
    }
};

レスポンスが正常に行われると、HTTP status code 200 で待機状態となります。失敗した場合、何度か再試行されます。

External prize delivery playfab image

リクエストされるフォーマットで返すエンドポイントができたら、Game Settings/Tournament settings Integrationへ移動し、「external prize delivery」を有効にします。
プロバイダはPlayfabを選択します。
有効な title idsecret keyCloudScript関数名関数バージョン (修正番号) を挿入して、保存します。

希望する場合は変更可能な、すでに生成されたサーバーシークレットがあります。
Playfabユーザーが実行するのを防ぐため、cloud scriptでこちらを使用してください。

ゲームサーバーの統合

カスタムサーバーからゲームセッションの結果を送信する

外部サーバーからの結果を処理するために、まず、Game Settings/Tournament settings Integration に移動し、「external result submission」を有効にします。

専用サーバーまたはカスタムバックされたサーバーからゲームセッションの結果を送信するには、特定のHTTPコールを行う必要があります。
HTTPコールはPOSTで、フォーマットはapplication/x-www-form-urlencodedでなければなりません。

サーバーアクセス用トークンは、お客様のサーバーにのみ適用されるものです。 ゲームクライアントには含まれません。

エンドポイントURL:

https://backbone-client-api.azurewebsites.net/api/v1/gameSessionSetResultFromServer

ヘッダー:

BACKBONE_APP_ID: YOUR-TSDK-GAME-CLIENT-ID
ACCESS_TOKEN: YOU-WILL-BE-GIVEN-SERVER-ACCESS-TOKEN
Content-Type: application/x-www-form-urlencoded

application/x-www-form-urlencoded パラメーター:

gameSessionData

gameSessionData 値はJSON文字列でなければならない(コメントは含まない):

JavaScript

{
   // this is 64 bit number as string, id is provided from create game session API call, 
  "gameSessionId":"491197014055328536",
   // type of game session is anything you want between 0-127, default is 0
  // e.g.    0 - 1v1    1 - 2v2   etc you can give certain type to your games
  "type":"0",
  // this is 64 bit number as string, id is provided from match object e.g. match.Id
   // NB: NOT match.MatchId that is something different
  "tournamentMatchId":"491196930559318805",
   // time in seconds your game session lasted
  "time":"100",
   // list of users and their respective placements
  "users":[
    {
      "teamId":"1",
      "userId":"491174601636715209",
      "place":"2"
    },    
    {
      "teamId":"2",
      "userId":"491174351375178434",
      "place":"1"
    }
  ],
   // game session stats e.g. what type of map was played (optional)
  "gameStats":[
    {
      "statId":"2",
      "textValue":"test"
    }
  ],
  // user game session stats, e.g. how much damage specific user did (optional)
  "userStats":[
    {
      "statId":"1",
      "userId":"491174351375178434",
      "floatValue":"134.234"
    },
     {
      "statId":"1",
      "userId":"491174601636715209",
      "floatValue":"12.234"
    }
  ]
}

サーバーからマッチデータを取得する

ゲームサーバーからトーナメントの試合データを取得します。

エンドポイントURL:

https://backbone-client-api.azurewebsites.net/api/v1/tournamentGetMatchDataForServer

ヘッダー:

BACKBONE_APP_ID: YOUR-TSDK-GAME-CLIENT-ID
Accept-Encoding: gzip
Content-Type: application/x-www-form-urlencoded

application/x-www-form-urlencoded パラメーター:

accessToken
tournamentId
matchId
  • accessToken の値は、SERVER ACCESS TOKENでなければなりません。
  • tournamentId 値は、マッチが属するトーナメントのIDでなければならない。
  • matchId 値は特定のトーナメント戦のIDでなければならない
マッチングがまだ相手を探しているときは、すべてのユーザーが結果に現れるわけではありません。 マッチングが始まると、返されたユーザーは不変かつ最終的なものであると考えることができます。

Response:

JavaScript

{
    "id": "620604471341234",
    "secret": "lz59JQiTfdgsdfg",
    "deadline": "2022-09-09T13:06:33.797Z",
    "matchId": 1,
    "phaseId": 1,
    "groupId": 1,
    "roundId": 1,
    "playedGameCount": 1,
    "maxGameCount": 1,
    "status": 8,
    "users": [
        {
            "userId": "6156293913241234",
            "userExternalId": "XXXX",
            "userPlayfabId": "XXXX",
            "teamId": 1,
            "checkedIn": false,
            "userScore": 0,
            "teamScore": 0,
            "userPoints": 0,
            "teamPoints": 0,
            "matchPoints": 0,
            "matchWinner": false
        },
        {
            "userId": "6203549123412344",
            "userExternalId": "XXXX",
            "userPlayfabId": "XXXX",
            "teamId": 2,
            "checkedIn": true,
            "userScore": 0,
            "teamScore": 0,
            "userPoints": 0,
            "teamPoints": 0,
            "matchPoints": 0,
            "matchWinner": false
        }
    ],
    "roundSettings": [
        {
            "partySize": 1,
            "maxTeamsPerMatch": 16,
            "minGameTime": 3,
            "maxRoundTime": 6
        }
    ],
    "propertySettings": [
        {
            "name": "property1",
            "value": "value1"
        },
        {
            "name": "property2",
            "value": "value2"
        }
    ],
    "gameSessions": [
        {
            "gameSessionId": "6206051532412344"
        }
    ]
}
Back to top