NAV
Unity (UniTask) Unity Unreal Engine 4

Game Server Services

Game Server Services(GS2) とはモバイルゲーム開発に特化したバックエンドサーバサービス(BaaS)です。

ゲーム開発に必要なネットワーク機能をコンポーネント化してサービスとして提供します。
ゲーム内から必要な一部のコンポーネント単位で利用することができるよう設計されており、手軽に・手頃な価格で・高性能なサーバ機能を利用できます。

利用料金

Individual プラン

過去12か月間の売上高が1000万円未満の法人または個人は全ての機能を無料で使用できます。
(GS2を利用するゲームによる売り上げではなく、他の事業の売り上げも含みます)

月の平均リクエスト回数が秒間10回または、一定期間継続して秒間リクエスト回数が100回を超える場合はその限りではありません。
またその際もアプリケーションの改修に必要な時間は考慮しますが、速やかに対応ください。

Professional プラン

過去12か月間の売上高が1000万円以上の場合はこちらの料金に基づきます。

一般的な利用料金

サービス内に特別な記載がない限り、以下の料金でサービスを利用できます。

無料枠

2万円を超過するまでは無料でご利用いただけます。
2万円を超過すると超過分のみではなく、全利用額の請求が発生します。

Enterprise プラン

MAUあたり30円~150円(ゲームがサーバに依存している程度によって決定)の固定額で全ての機能を使用できます。
実際の利用料金請求時には Professional プランの利用料金と比較して”安いほう”で請求が行われます。

利用をご希望の際は GS2 にご相談ください。

処理性能

GS2 はゲームサーバを性能を気にすることなく利用いただけるよう設計されています。
リリース前のベンチマークで秒間10万回のアクセスを処理できることを確認しています。

しかし、すべてのAPIを性能を気にする必要なく提供することは困難であり、
一部のAPIには性能を保証するラインを別途設けている場合があります。
個別の性能保証が定義されている場合は各コンポーネントで定義しています。

はじめかた

はじめに

このセクションでは、Unity / Unreal Engine 4 のプロジェクトを作成して GS2 の機能を利用するために必要な

  1. GS2のマネージメントコンソールでの設定手順
  2. Unity / Unreal Engine 4 プロジェクトへのGS2 SDK for Game Engineの組み込み手順

についてまとめています。

GS2アカウントを作成する

まずはじめに、 GS2 のサイトからGS2開発者アカウントの登録を行なってください。

登録後、メールアドレスの確認メールが届きます。

メール記載のURLにアクセスしてメールアドレスを認証後、登録したアカウント情報でマネージメントコンソールにログインしてください。

プロジェクトを作成する

つぎに、マネージメントコンソールプロジェクトを作成してください。

プロジェクトは、開発ゲームタイトルと対になるものです。
GS2 のリソース(設定した内容、マスターデータ等)はプロジェクトごとに保持されます。
他のプロジェクトのリソースには干渉できません。

プロジェクトの作成は新たにゲームタイトルを作り始めるときでも、開発の途中からでも可能です。

  1. マネージメントコンソールにログインします。
  2. Project > プロジェクト一覧 のページを開きます。
  3. プロジェクトの新規作成をクリックします。
  4. プロジェクト名説明文を入力し、作成ボタンをクリックします。

プロジェクト名には 英数字大文字小文字とハイフン、アンダーバーのみが使用可能です。
 例:MySuperCool_Stg_Game-001
漢字、ひらがな等は使用できません。

認証情報を追加する

プロジェクトにクレデンシャル(認証情報)を追加します。

アプリケーションはクレデンシャルに含まれるクライアントID、クライアントシークレットの情報を使って GS2-Identifier認証 を行うことで、各種APIへのアクセスが可能となります。

認証はGS2-Identifierユーザー単位で行います。 GS2-IdentifierユーザーはGS2のサービス利用に関する権限の定義を持っています。
ここではアプリケーション向けのアクセス権限を持つユーザーの追加を行います。

  1. マネージメント コンソールの左側でIdentifierを選択し、Usersをクリックします。
  2. 『Identifier > ユーザー一覧』ページが開きます。右側のユーザーの新規作成を選択します。
  3. 『Identifier > ユーザー一覧 > ユーザーの新規作成』ページが開きます。ユーザー名説明文の項目を適宜入力し、作成をクリックします。(例:ユーザー名:Application 等)
  4. 『Identifier > ユーザー一覧』ページで、今作成したユーザーを選択します。
  5. 『Identifier > ユーザー一覧 > ユーザー情報』ページで、ユーザークレデンシャルを追加します。クレデンシャルタブでクレデンシャルの新規作成をクリックします。
  6. 『Identifier > ユーザー一覧 > ユーザー情報 > クレデンシャル > クレデンシャルの新規作成』ページで作成をクリックすると、Client IdとClient Secretが発行されます。
    発行されたクライアントIDとクライアントシークレットはお手元に保存してください。
  7. 新規作成したユーザーセキュリティポリシー割り当てます。
    ここでは標準的なアプリケーションのアクセスポリシーとして定義済みの、ApplicationAccess というセキュリティーポリシーを割り当てます。
    『Identifier > ユーザー一覧 > ユーザー情報』ページのセキュリティポリシータブでセキュリティポリシー名ApplicationAccess割り当てボタンをクリックします。

これでアプリケーションがGS2のプロジェクトにアクセスするための準備ができました。

GS2-Deploy による設定

ユーザークレデンシャル(認証情報)の追加は GS2-Identifierの管理画面から行えますが、GS2-Deployを利用するとより簡単です。

GS2-Deployは継続的デリバリーの実現を目的としたサービスです。
YAML形式で記述されたコードを使ってリソースの自動構築を行えます。

例えば、ユーザーを新規追加するテンプレートを使ってスタックを作成すると、自動で新規ユーザーをGS2-Identifierに追加することができます。

スタックについて

GS2-Deployは、テンプレート(GS2内のリソースをどう操作し構築するかの手順が記述されたもの) を元に、実際に各サービスのリソースを操作します。
このテンプレートで構築したリソースを集約したものをスタックといいます。

スタックに対して、内容を変更したテンプレートを再適用すると、更新の処理が実行されます。
変更の差分を検出し、新たに作成が必要になったリソースは新規作成、更新が必要なリソースは更新、削除が必要なリソースは自動で削除を、それぞれのリソースに対して行います。
この仕組みによって開発者は、要らなくなったリソースを削除する、などのオペレーションを行う必要がなくなり、望んでいるリソースの状態をテンプレートに記述するだけで自動で構築が可能になります。

それでは実際にテンプレートを使ってスタックを作成してみましょう。

クレデンシャル(認証情報)を発行する GS2-Deploy テンプレートの例 (YAML形式)

GS2TemplateFormatVersion: "2019-05-01"
Description: GS2 SDK identifier template Version 2019-07-10

Globals:
  Alias:
    ApplicationUserName: application

Resources:
  IdentifierApplicationUser:
    Type: GS2::Identifier::User
    Properties:
      Name: ${ApplicationUserName}

  IdentifierApplicationUserAttachPolicy:
    Type: GS2::Identifier::AttachSecurityPolicy
    Properties:
      UserName: ${ApplicationUserName}
      SecurityPolicyId: grn:gs2::system:identifier:securityPolicy:ApplicationAccess
    DependsOn:
      - IdentifierApplicationUser

  IdentifierApplicationIdentifier:
    Type: GS2::Identifier::Identifier
    Properties:
      UserName: ${ApplicationUserName}
    DependsOn:
      - IdentifierApplicationUser

Outputs:
  ApplicationClientId: !GetAttr IdentifierApplicationIdentifier.Item.ClientId
  ApplicationClientSecret: !GetAttr IdentifierApplicationIdentifier.ClientSecret

例に示したのは、 ApplicationAccess 権限を設定したユーザーを作成する テンプレート のサンプルです。
ApplicationUserName: application の部分を変更すると、GS2-Identifier に作成されるユーザー名を変更できます。

テンプレートをダウンロード

この テンプレートファイルを保存し、 GS2-Deploy の スタックの新規作成テンプレートデータ に指定してスタックを作成します。

作成したスタックに登録された設定の反映状態は、スタックの 実行状態 の項目で確認できます。
数秒後 CREATE_COMPLETE になり、 クレデンシャルの設定が完了します。

反映完了後に アウトプット タブを選択すると、クレデンシャルクライアントIDクライアントシークレット が表示されます。
この値は後ほど ゲームアプリ側に設定します。

GS2 SDK for Game Engine のダウンロードと導入

GS2 SDK for Game Engine から GS2 SDK for Unity もしくは GS2 SDK for Unreal Engine 4 を入手できます。
お使いの環境にあったパッケージをダウンロードし、示された手順に従って導入してください。

これでゲームアプリで GS2 を利用するための準備が整いました。

アカウント管理機能の実装手順

ゲームアプリに GS2-Identifier、GS2-Accountのサービスを利用したログイン処理を実装する方法を、サンプルコードを交えて説明します。

サンプルコード(Unity)をダウンロード

ZIPファイルにはサンプルソース GS2AccountSample.cs PlayerPrefsAccountRepository.cs が入っています。
新規のプロジェクトを作成しGS2 SDK for Unityをインストールしたあと、
展開した GS2AccountSample.cs PlayerPrefsAccountRepository.cs ファイルを Scriptsフォルダ内に配置してください。
シーンにゲームオブジェクトを作成し、GS2AccountSampleコンポーネント、PlayerPrefsコンポーネントをアタッチします。
PlayerPrefsAccountRepository.cs はPlayerPrefsにアカウント情報を保存する補助ユーティリティです。

サンプルコード(Unreal Engine 4)をダウンロード
※プロジェクト名がMyProjectの環境のソースコードになっていますので、適宜ソース内のMyProjectをプロジェクト名に置き換える編集ののち、実行してください。

GS2-Accountの設定

GS2-Accountサービスの利用を開始するにあたり、ネームスペースを追加する必要があります。

ネームスペース(名前空間)は、一つのプロジェクト内で同じサービスを、異なった複数の用途で利用できるようにするための仕組みです。
異なるネームスペース内であれば、同じサービスでも全く別のデータ空間として取り扱われます。

それでは、サンプルのテンプレートを使って、GS2-Accountのネームスペース名game-0001を追加し、
アカウントの認証に使用する暗号鍵を発行して、アカウントの新規作成が可能な状態にします。

GS2-Deploy の スタックの新規作成 で、以下のテンプレートを テンプレートデータ に指定してスタックを作成します。

サンプルテンプレートをダウンロード

GS2-Account の利用を開始するためのサンプルテンプレート

GS2TemplateFormatVersion: "2019-05-01"
Description: GS2-Account initialize template Version 2010-06-26

Globals:
  Alias:
    AccountNamespaceName: game-0001
    KeyNamespaceAccountAuthentication: account-encryption-key-namespace
    KeyAccountAuthentication: account-encryption-key

Resources:
  KeyNamespaceAccountAuthentication:
    Type: GS2::Key::Namespace
    Properties:
      Name: ${KeyNamespaceAccountAuthentication}

  KeyAccountAuthentication:
    Type: GS2::Key::Key
    Properties:
      NamespaceName: ${KeyNamespaceAccountAuthentication}
      Name: ${KeyAccountAuthentication}
    DependsOn:
      - KeyNamespaceAccountAuthentication

  AccountNamespace:
    Type: GS2::Account::Namespace
    Properties:
      Name: ${AccountNamespaceName}

Outputs:
  AccountNamespaceName: !GetAttr AccountNamespace.Item.Name
  KeyAccountAuthenticationKeyId: !GetAttr KeyAccountAuthentication.Item.KeyId

GS2 SDKの初期化

_profile = new Gs2.Unity.Util.Profile(
    clientId: clientId, // クレデンシャル の クライアントID
    clientSecret: clientSecret, // クレデンシャル の クライアントシークレット
    reopener: new Gs2BasicReopener() // 再接続を行うハンドラ
);

{
    var future = _profile.Initialize();

    yield return future;

    // 初期化に失敗した場合はエラー処理へ
    if (future.Error != null)
    {
        OnError(future.Error);
        yield break;
    }
}

var gs2 = new Gs2.Unity.Core.Gs2Domain(_profile);
_profile = new Gs2.Unity.Util.Profile(
    clientId: clientId, // クレデンシャル の クライアントID
    clientSecret: clientSecret, // クレデンシャル の クライアントシークレット
    reopener: new Gs2BasicReopener() // 再接続を行うハンドラ
);

{
    var future = _profile.Initialize();

    yield return future;

    // 初期化に失敗した場合はエラー処理へ
    if (future.Error != null)
    {
        OnError(future.Error);
        yield break;
    }
}

var gs2 = new Gs2.Unity.Core.Gs2Domain(_profile);
ProfilePtr = std::make_shared<gs2::ez::Profile>(
    TCHAR_TO_ANSI(*ClientId),
    TCHAR_TO_ANSI(*ClientSecret),
    gs2::ez::Gs2BasicReopener()
);

ClientPtr = std::make_shared<gs2::ez::Client>(
    *ProfilePtr
);

ProfilePtr->initialize(
    [this](gs2::ez::Profile::AsyncInitializeResult r)
    {
        if (r.getError())
        {
            // 初期化に失敗した場合はエラー処理へ
            GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "profile.initialize failed.");
        }
        else
        {
            AccountCreate();
        }
    }
);

例では GS2へのアクセスのためのユーティリティクラス Profileの初期化を行っています。

clientIdclientSecret にアクセスに使用する対象のGS2-Identifierユーザークレデンシャルを設定します。
実際の値は、認証情報を追加するでGS2-Deployを使用して作成したスタックアウトプットタブ で確認できる ApplicationClientId ApplicationClientSecret の値を設定してください。

AccountNamespaceName KeyAccountAuthenticationKeyId も同様に、アカウント管理のスタックアウトプットタブ の内容を設定してください。

Unityのコンポーネントでは以下のプロパティに値を設定します。

reopener には通信の再接続をするためのハンドラを設定します。
ここではライブラリにあらかじめ用意されているGs2BasicReopener を設定しています。

ユーティリティクラス Profileを使用して後述するログイン処理を実行した場合、アクセストークンを自動でリフレッシュすることが可能です。

アカウントの新規作成

// アカウントを新規作成

Debug.Log("アカウントを新規作成");

var future = gs2.Account.Namespace(
    accountNamespaceName
).Create();

yield return future;

// アカウント作成に失敗
if (future.Error != null)
{
    OnError(future.Error);
    yield break;
}

// 作成したアカウント情報を取得
var future2 = future.Result.Model();
yield return future2;

account = future2.Result;

// PlayerPrefs にアカウント情報(EzAccount)を保存
_accountRepository.Save(account);
// アカウントを新規作成

Debug.Log("アカウントを新規作成");

var future = gs2.Account.Namespace(
    accountNamespaceName
).Create();

yield return future;

// アカウント作成に失敗
if (future.Error != null)
{
    OnError(future.Error);
    yield break;
}

// 作成したアカウント情報を取得
var future2 = future.Result.Model();
yield return future2;

account = future2.Result;

// PlayerPrefs にアカウント情報(EzAccount)を保存
_accountRepository.Save(account);
ClientPtr->account.create(
    [this](gs2::ez::account::AsyncEzCreateResult r)
    {
        if (r.getError())
        {
            // アカウントが作成できなかった場合は終了
            GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "account.create failed.");
        }
        else
        {
            // 作成したアカウント情報を取得
            EzAccount = r.getResult()->getItem();

            ProfileLogin();
        }
    },
    TCHAR_TO_ANSI(*AccountNamespaceName)
);

ここではゲームプレイヤーを識別するためのアカウントの作成を行っています。
accountNamespaceName にはアカウントを追加する先のネームスペース名を指定します。
アカウント管理のスタックアウトプットタブAccountNamespaceName の値を設定します。

Create メソッドのコールバックは、アカウントの作成処理が完了したときに呼び出されます。
エラーが発生した場合は r.Error に例外オブジェクトが格納され、成功したときには r.Result に結果が格納されます。

なお、このサンプルでは毎起動時に新規のアカウントでログインする挙動になっていますが、通常は、アカウントの作成はアプリケーションの初回起動時にのみ行ないます。
実際のアプリケーションではこの応答のアカウント情報をローカルストレージ等に保存し、2回目以降はローカルストレージ等から取得した作成済みのアカウント情報で、既存のゲームプレイヤーとしてログインを行います。

ログイン処理

// ログイン処理

Debug.Log("ログイン処理");

GameSession gameSession = null;

{
    var future = _profile.LoginFuture(
        new Gs2AccountAuthenticator(
            session: _profile.Gs2RestSession,
            accountNamespaceName: accountNamespaceName,
            keyId: accountEncryptionKeyId,
            userId: account.UserId,
            password: account.Password
        )
    );
    yield return future;
    if (future.Error != null)
    {
        OnError(future.Error);
        yield break;
    }

    // ログイン状態を表すアクセストークン を保持する GameSession オブジェクト を生成
    gameSession = future.Result;
}
// ログイン処理

Debug.Log("ログイン処理");

GameSession gameSession = null;

{
    var future = _profile.LoginFuture(
        new Gs2AccountAuthenticator(
            session: _profile.Gs2RestSession,
            accountNamespaceName: accountNamespaceName,
            keyId: accountEncryptionKeyId,
            userId: account.UserId,
            password: account.Password
        )
    );
    yield return future;
    if (future.Error != null)
    {
        OnError(future.Error);
        yield break;
    }

    // ログイン状態を表すアクセストークン を保持する GameSession オブジェクト を生成
    gameSession = future.Result;
}
ProfilePtr->login(
    [this](gs2::ez::Profile::AsyncLoginResult r)
    {
        if (r.getError())
        {
            // ゲームセッションオブジェクトが作成できなかった場合は終了
            GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "profile.login failed.");
        }
        else
        {
            // ログイン状態を表すゲームセッションオブジェクトを取得
            GameSession = *r.getResult();

            AccountListTakeOverSettings();
        }
    },
    gs2::ez::Gs2AccountAuthenticator(
        ProfilePtr->getGs2Session(),
        TCHAR_TO_ANSI(*AccountNamespaceName),
        TCHAR_TO_ANSI(*AccountEncryptionKeyId),
        EzAccount.getUserId(),
        EzAccount.getPassword()
    )
);

つづいて GS2 へのログイン処理です。
accountNamespaceName には作成したアカウントが存在するネームスペース名を指定し、keyId にはアカウントの認証結果に付与する署名の計算に使用する暗号鍵を指定します。
更に、 userId password には作成したアカウントのユーザIDとパスワードを指定します。

ログイン処理が完了すると、エラーが発生した場合は future.Error に例外オブジェクトが格納され、成功したときには future.Result に結果が格納されます。

future.Result にはログイン状態を表現するための GameSession オブジェクトが返ります。
以降 GS2 の API を呼び出すときにログイン状態でなければ呼び出せないAPIの引数には GameSession を渡して使用することとなります。

ログイン後呼び出し可能なAPIの呼び出し

// 引き継ぎ情報の一覧を取得

Debug.Log("引き継ぎ情報の一覧を取得");

{
    var it = gs2.Account.Namespace(
        accountNamespaceName
    ).Me(
        gameSession
    ).TakeOvers();

    var takeOverModels = new List<EzTakeOver>();
    while (it.HasNext())
    {
        yield return it.Next();
        if (it.Error != null)
        {
            if (it.Error is BadRequestException || it.Error is NotFoundException)
            {
                OnError(it.Error);
                break;
            }

            OnError(new CanIgnoreException(it.Error));
            break;
        }

        if (it.Current != null)
        {
            takeOverModels.Add(it.Current);
        }
    }

    foreach (var takeOverModel in takeOverModels)
    {
        // 引き継ぎに関する情報が取得される
    }
}
// 引き継ぎ情報の一覧を取得

Debug.Log("引き継ぎ情報の一覧を取得");

{
    var it = gs2.Account.Namespace(
        accountNamespaceName
    ).Me(
        gameSession
    ).TakeOvers();

    var takeOverModels = new List<EzTakeOver>();
    while (it.HasNext())
    {
        yield return it.Next();
        if (it.Error != null)
        {
            if (it.Error is BadRequestException || it.Error is NotFoundException)
            {
                OnError(it.Error);
                break;
            }

            OnError(new CanIgnoreException(it.Error));
            break;
        }

        if (it.Current != null)
        {
            takeOverModels.Add(it.Current);
        }
    }

    foreach (var takeOverModel in takeOverModels)
    {
        // 引き継ぎに関する情報が取得される
    }
}
ClientPtr->account.listTakeOverSettings(
    [this](gs2::ez::account::AsyncEzListTakeOverSettingsResult r)
    {
        if (r.getError())
        {
            // 引き継ぎ情報の一覧が取得できなかった場合は終了
            GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "account.listTakeOverSettings failed.");
        }
        else
        {
            auto& items = r.getResult()->getItems();
            for (auto i = 0; i < items.getCount(); ++i)
            {
                // 引き継ぎに関する情報が取得される
            }

            ProfileFinalize();
        }
    },
    GameSession,
    TCHAR_TO_ANSI(*AccountNamespaceName)
);

ログイン状態でなければ使用できないAPIの一例として、引き継ぎ設定の一覧を取得するAPIを呼び出しています。
GameSession を渡して、ログイン中のゲームプレイヤーに設定されている引き継ぎ設定の一覧を取得できます。

GS2 SDKの終了処理

// GS2 SDK の終了処理

Debug.Log("GS2 SDK の終了処理");

{
    // ゲームを終了するときなどに呼び出してください。
    // 頻繁に呼び出すことは想定していません。
    var current = _profile.Finalize();

    yield return current;
}
void AMyActor::ProfileFinalize()
{
    // GS2 SDK の終了処理

    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, "profile.finalize");

    // ゲームを終了するときなどに呼び出してください。
    // 頻繁に呼び出すことは想定していません。
    ProfilePtr->finalize(
        [this]()
        {
            GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, "successfull.");
        }
    );
}
// GS2 SDK の終了処理

Debug.Log("GS2 SDK の終了処理");

{
    // ゲームを終了するときなどに呼び出してください。
    // 頻繁に呼び出すことは想定していません。
    var current = _profile.Finalize();

    yield return current;
}
void AMyActor::ProfileFinalize()
{
    // GS2 SDK の終了処理

    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, "profile.finalize");

    // ゲームを終了するときなどに呼び出してください。
    // 頻繁に呼び出すことは想定していません。
    ProfilePtr->finalize(
        [this]()
        {
            GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, "successfull.");
        }
    );
}

Profileクラスの終了処理です。
GS2との接続を終了します。

サンプルコード全文

サンプルコード全体をコード欄に示します。

using System;
using System.Collections;
using System.Collections.Generic;
using Gs2.Core.Exception;
using Gs2.Unity.Core.Exception;
using Gs2.Unity.Gs2Account.Model;
using Gs2.Unity.Util;
using UnityEngine;

public class GS2AccountSample : MonoBehaviour
{
    // GS2-Identifier で発行したクライアントID
    public string clientId;

    // GS2-Identifier で発行したクライアントシークレット
    public string clientSecret;

    // アカウントを作成する GS2-Account のネームスペース名
    public string accountNamespaceName;

    // アカウントの認証結果に付与する署名を計算するのに使用する暗号鍵
    public string accountEncryptionKeyId;

    private PlayerPrefsAccountRepository _accountRepository;

    private Profile _profile;

    // Start is called before the first frame update
    void Start()
    {
        _accountRepository = GetComponentInParent<PlayerPrefsAccountRepository>() ?? GetComponent<PlayerPrefsAccountRepository>();

        StartCoroutine(CreateAndLoginAction());
    }

    public IEnumerator CreateAndLoginAction()
    {
        // GS2 SDK の初期化

        Debug.Log("GS2 SDK の初期化");
        Debug.Log("Initialization of GS2 SDK");

        _profile = new Gs2.Unity.Util.Profile(
            clientId: clientId, // クレデンシャル の クライアントID
            clientSecret: clientSecret, // クレデンシャル の クライアントシークレット
            reopener: new Gs2BasicReopener() // 再接続を行うハンドラ
        );

        {
            var future = _profile.Initialize();

            yield return future;

            // 初期化に失敗した場合はエラー処理へ
            if (future.Error != null)
            {
                OnError(future.Error);
                yield break;
            }
        }

        var gs2 = new Gs2.Unity.Core.Gs2Domain(_profile);

        // PlayerPrefs から保存されているアカウント情報(EzAccount)を取得
        EzAccount account = _accountRepository.Load();

        if (account == null)
        {
            // アカウントを新規作成

            Debug.Log("アカウントを新規作成");

            var future = gs2.Account.Namespace(
                accountNamespaceName
            ).Create();

            yield return future;

            // アカウント作成に失敗
            if (future.Error != null)
            {
                OnError(future.Error);
                yield break;
            }

            // 作成したアカウント情報を取得
            var future2 = future.Result.Model();

            yield return future2;

            account = future2.Result;

            // PlayerPrefs にアカウント情報(EzAccount)を保存
            _accountRepository.Save(account);
        }

        // ログイン処理

        Debug.Log("ログイン処理");

        GameSession gameSession = null;

        {
            var future = _profile.LoginFuture(
                new Gs2AccountAuthenticator(
                    session: _profile.Gs2RestSession,
                    accountNamespaceName: accountNamespaceName,
                    keyId: accountEncryptionKeyId,
                    userId: account.UserId,
                    password: account.Password
                )
            );

            yield return future;

            if (future.Error != null)
            {
                OnError(future.Error);
                yield break;
            }

            // ログイン状態を表すアクセストークン を保持する GameSession オブジェクト を生成
            gameSession = future.Result;
        }

        // 引き継ぎ情報の一覧を取得

        Debug.Log("引き継ぎ情報の一覧を取得");

        {
            var it = gs2.Account.Namespace(
                accountNamespaceName
            ).Me(
                gameSession
            ).TakeOvers();

            var takeOverModels = new List<EzTakeOver>();
            while (it.HasNext())
            {
                yield return it.Next();
                if (it.Error != null)
                {
                    if (it.Error is BadRequestException || it.Error is NotFoundException)
                    {
                        OnError(it.Error);
                        break;
                    }

                    OnError(new CanIgnoreException(it.Error));
                    break;
                }

                if (it.Current != null)
                {
                    takeOverModels.Add(it.Current);
                }
            }

            foreach (var takeOverModel in takeOverModels)
            {
                // 引き継ぎに関する情報が取得される
            }
        }

        // GS2 SDK の終了処理

        Debug.Log("GS2 SDK の終了処理");

        {
            // ゲームを終了するときなどに呼び出してください。
            // 頻繁に呼び出すことは想定していません。
            var current = _profile.Finalize();

            yield return current;
        }
    }

    private void OnError(Exception e)
    {
        Debug.Log(e.ToString());
    }

    // Update is called once per frame
    void Update()
    {

    }
}
using System;
using System.Collections;
using System.Collections.Generic;
using Gs2.Core.Exception;
using Gs2.Unity.Core.Exception;
using Gs2.Unity.Gs2Account.Model;
using Gs2.Unity.Util;
using UnityEngine;

public class GS2AccountSample : MonoBehaviour
{
    // GS2-Identifier で発行したクライアントID
    public string clientId;

    // GS2-Identifier で発行したクライアントシークレット
    public string clientSecret;

    // アカウントを作成する GS2-Account のネームスペース名
    public string accountNamespaceName;

    // アカウントの認証結果に付与する署名を計算するのに使用する暗号鍵
    public string accountEncryptionKeyId;

    private PlayerPrefsAccountRepository _accountRepository;

    private Profile _profile;

    // Start is called before the first frame update
    void Start()
    {
        _accountRepository = GetComponentInParent<PlayerPrefsAccountRepository>() ?? GetComponent<PlayerPrefsAccountRepository>();

        StartCoroutine(CreateAndLoginAction());
    }

    public IEnumerator CreateAndLoginAction()
    {
        // GS2 SDK の初期化

        Debug.Log("GS2 SDK の初期化");
        Debug.Log("Initialization of GS2 SDK");

        _profile = new Gs2.Unity.Util.Profile(
            clientId: clientId, // クレデンシャル の クライアントID
            clientSecret: clientSecret, // クレデンシャル の クライアントシークレット
            reopener: new Gs2BasicReopener() // 再接続を行うハンドラ
        );

        {
            var future = _profile.Initialize();

            yield return future;

            // 初期化に失敗した場合はエラー処理へ
            if (future.Error != null)
            {
                OnError(future.Error);
                yield break;
            }
        }

        var gs2 = new Gs2.Unity.Core.Gs2Domain(_profile);

        // PlayerPrefs から保存されているアカウント情報(EzAccount)を取得
        EzAccount account = _accountRepository.Load();

        if (account == null)
        {
            // アカウントを新規作成

            Debug.Log("アカウントを新規作成");

            var future = gs2.Account.Namespace(
                accountNamespaceName
            ).Create();

            yield return future;

            // アカウント作成に失敗
            if (future.Error != null)
            {
                OnError(future.Error);
                yield break;
            }

            // 作成したアカウント情報を取得
            var future2 = future.Result.Model();

            yield return future2;

            account = future2.Result;

            // PlayerPrefs にアカウント情報(EzAccount)を保存
            _accountRepository.Save(account);
        }

        // ログイン処理

        Debug.Log("ログイン処理");

        GameSession gameSession = null;

        {
            var future = _profile.LoginFuture(
                new Gs2AccountAuthenticator(
                    session: _profile.Gs2RestSession,
                    accountNamespaceName: accountNamespaceName,
                    keyId: accountEncryptionKeyId,
                    userId: account.UserId,
                    password: account.Password
                )
            );

            yield return future;

            if (future.Error != null)
            {
                OnError(future.Error);
                yield break;
            }

            // ログイン状態を表すアクセストークン を保持する GameSession オブジェクト を生成
            gameSession = future.Result;
        }

        // 引き継ぎ情報の一覧を取得

        Debug.Log("引き継ぎ情報の一覧を取得");

        {
            var it = gs2.Account.Namespace(
                accountNamespaceName
            ).Me(
                gameSession
            ).TakeOvers();

            var takeOverModels = new List<EzTakeOver>();
            while (it.HasNext())
            {
                yield return it.Next();
                if (it.Error != null)
                {
                    if (it.Error is BadRequestException || it.Error is NotFoundException)
                    {
                        OnError(it.Error);
                        break;
                    }

                    OnError(new CanIgnoreException(it.Error));
                    break;
                }

                if (it.Current != null)
                {
                    takeOverModels.Add(it.Current);
                }
            }

            foreach (var takeOverModel in takeOverModels)
            {
                // 引き継ぎに関する情報が取得される
            }
        }

        // GS2 SDK の終了処理

        Debug.Log("GS2 SDK の終了処理");

        {
            // ゲームを終了するときなどに呼び出してください。
            // 頻繁に呼び出すことは想定していません。
            var current = _profile.Finalize();

            yield return current;
        }
    }

    private void OnError(Exception e)
    {
        Debug.Log(e.ToString());
    }

    // Update is called once per frame
    void Update()
    {

    }
}
//※(プロジェクト名)の部分を適宜プロジェクト名に置き換えて実行してください。

// MyActor.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include <gs2/ez/Gs2Ez.hpp>
#include <memory>
#include "MyActor.generated.h"

UCLASS()
class (プロジェクト名)_API AMyActor : public AActor
{
    GENERATED_BODY()

public:
    // Sets default values for this actor's properties
    AMyActor();

    // GS2-Identifier で発行したクライアントID
    UPROPERTY(EditAnywhere, Category = "GS2")
    FString ClientId;

    // GS2-Identifier で発行したクライアントシークレット
    UPROPERTY(EditAnywhere, Category = "GS2")
    FString ClientSecret;

    // アカウントを作成する GS2-Account のネームスペース名
    UPROPERTY(EditAnywhere, Category = "GS2")
    FString AccountNamespaceName;

    // アカウントの認証結果に付与する署名を計算するのに使用する暗号鍵
    UPROPERTY(EditAnywhere, Category = "GS2")
    FString AccountEncryptionKeyId;

private:
    std::shared_ptr<gs2::ez::Profile> ProfilePtr;
    std::shared_ptr<gs2::ez::Client> ClientPtr;

    gs2::ez::GameSession GameSession;
    gs2::ez::account::EzAccount EzAccount;

    void ProfileInitialize();
    void AccountCreate();
    void ProfileLogin();
    void AccountListTakeOverSettings();
    void ProfileFinalize();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:
    // Called every frame
    virtual void Tick(float DeltaTime) override;
};
// MyActor.cpp

#include "MyActor.h"
#include "Engine.h"

// Sets default values
AMyActor::AMyActor()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
    Super::BeginPlay();

    ProfileInitialize();
}

void AMyActor::ProfileInitialize()
{
    // GS2 SDK のクライアントを初期化

    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, "profile.initialize");

    ProfilePtr = std::make_shared<gs2::ez::Profile>(
        TCHAR_TO_ANSI(*ClientId),
        TCHAR_TO_ANSI(*ClientSecret),
        gs2::ez::Gs2BasicReopener()
        );

    ClientPtr = std::make_shared<gs2::ez::Client>(
        *ProfilePtr
        );

    ProfilePtr->initialize(
        [this](gs2::ez::Profile::AsyncInitializeResult r)
        {
            if (r.getError())
            {
                // クライアントの初期化に失敗した場合は終了
                GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "profile.initialize failed.");
            }
            else
            {
                AccountCreate();
            }
        }
    );
}

void AMyActor::AccountCreate()
{
    // アカウントを新規作成

    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, "account.create");

    ClientPtr->account.create(
        [this](gs2::ez::account::AsyncEzCreateResult r)
        {
            if (r.getError())
            {
                // アカウントが作成できなかった場合は終了
                GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "account.create failed.");
            }
            else
            {
                // 作成したアカウント情報を取得
                EzAccount = r.getResult()->getItem();

                ProfileLogin();
            }
        },
        TCHAR_TO_ANSI(*AccountNamespaceName)
    );
}

void AMyActor::ProfileLogin()
{
    // ログイン

    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, "profile.login");

    ProfilePtr->login(
        [this](gs2::ez::Profile::AsyncLoginResult r)
        {
            if (r.getError())
            {
                // ゲームセッションオブジェクトが作成できなかった場合は終了
                GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "profile.login failed.");
            }
            else
            {
                // ログイン状態を表すゲームセッションオブジェクトを取得
                GameSession = *r.getResult();

                AccountListTakeOverSettings();
            }
        },
        gs2::ez::Gs2AccountAuthenticator(
            ProfilePtr->getGs2Session(),
            TCHAR_TO_ANSI(*AccountNamespaceName),
            TCHAR_TO_ANSI(*AccountEncryptionKeyId),
            EzAccount.getUserId(),
            EzAccount.getPassword()
        )
    );
}

void AMyActor::AccountListTakeOverSettings()
{
    // 引き継ぎ情報の一覧を取得

    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, "account.listTakeOverSettings");

    ClientPtr->account.listTakeOverSettings(
        [this](gs2::ez::account::AsyncEzListTakeOverSettingsResult r)
        {
            if (r.getError())
            {
                // 引き継ぎ情報の一覧が取得できなかった場合は終了
                GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "account.listTakeOverSettings failed.");
            }
            else
            {
                auto& items = r.getResult()->getItems();
                for (auto i = 0; i < items.getCount(); ++i)
                {
                    // 引き継ぎに関する情報が取得される
                }

                ProfileFinalize();
            }
        },
        GameSession,
        TCHAR_TO_ANSI(*AccountNamespaceName)
    );
}

void AMyActor::ProfileFinalize()
{
    // GS2 SDK の終了処理

    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, "profile.finalize");

    // ゲームを終了するときなどに呼び出してください。
    // 頻繁に呼び出すことは想定していません。
    ProfilePtr->finalize(
        [this]()
        {
            GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, "successfull.");
        }
    );
}

// Called every frame
void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

GS2 YouTubeチャンネル

Game Server Services の使い方や機能の解説を行う動画を、以下のチャンネルでご覧頂けます。

Game Server Services JP

実装サンプルプロジェクト

Unityで動作する実装サンプルのプロジェクトを入手できます。

GS2 Sample Project for Unity

以下の機能のゲーム内での動作をイメージした実装サンプルになっています。

ダウンロード

GS2 SDK for Game Engine

Game Server Services は 主要なゲームエンジンから利用できるSDKを提供しています。
ゲームエンジン向け SDK では、ゲーム内から利用することにフォーカスして通常の SDK より高レベルなAPI形式で提供しています。

GS2 SDK for Unity

本 SDK は Apache License 2.0 で提供されています。
プログラムのどこかで Apache License 2.0 で提供されているライブラリを使用していることを明示する必要があります。

本 SDK にはパブリックドメインである LitJson(Gs2/Plugin/LitJSON.dll) が含まれています。
この dll は GS2 SDK の動作の根幹に関わる動作に利用されているため、削除することが出来ません。

本 SDK には MIT License である websocket-sharp(Gs2/Plugin/websocket-sharp.dll) が含まれています。
表記すべきライセンスは以下です。
https://raw.githubusercontent.com/sta/websocket-sharp/master/LICENSE.txt
この dll は GS2 SDK の動作の根幹に関わる動作に利用されているため、削除することが出来ません。

本 SDK には BSD License である protobuf(Gs2/Plugin/Google.Protobuf.dll) が含まれています。
表記すべきライセンスは以下です。
https://github.com/protocolbuffers/protobuf/blob/master/LICENSE
この dll は GS2-Realtime を使用しなければ削除することが出来ます。

導入手順

Unity 2019.3 以降をご利用の場合

GS2 SDK for Unity Installer Version 2022.3.1

Unity Editor を起動し、メニューの Import Package から .unitypackage ファイルをインポートします。
その後、Unity Editor のメニューから 『Window > Game Server Services > SDK インストーラー』 を選択して、ウィザードの指示に従ってください。
インストール完了後、正しく動作しない場合は一度Unity Editorを再起動してみてください。

Unity 2019.2 以前をご利用の場合

GS2 SDK for UnityGS2-CSharp-SDKGS2 SDK for Unity で構成されています。まず、

GS2-CSharp-SDK GitHub

こちらのページで 『Code > Download Zip』を選択し、ダウンロードしたソースコードをプロジェクト内に配置します。
(Unityプロジェクト)/Assets/Scripts/Runtime/Sdk/Gs2

つぎに、

GS2 SDK for Unity GitHub

こちらのページで 『Code > Download Zip』 を選択し、ダウンロードしたソースコードをプロジェクト内に配置します。
(Unityプロジェクト)/Assets/Scripts/Runtime/Sdk/Gs2/Unity

Unity Release Note 2019.3.0
Package Manager: Fixed an issue where using some npm registry backends as scoped registries would fail with an error like com.foo.bar: Cannot read property '0.0.1' of undefined. (1177614)

実装上の諸注意

Windows向けにビルドするときはBuildSettingsでx86からx86_64に変更してください。x86ではエラーが出ます。

GS2 SDK for Unreal Engine 4

本 SDK 自身は Apache License 2.0 で提供されています。
プログラムのどこかで Apache License 2.0 で提供されているライブラリを使用していることを明示する必要があります。

本 SDK には Game Server Services 株式会社によってカスタマイズされた以下のソフトウェアが含まれています。

これらは SDK の動作の根幹に関わる動作に利用されているため、削除することができません。
これらのソフトウェアにおける、 Game Server Service 株式会社による改変部分のライセンスは、各ソフトウェアの規定するところに基づき、 Apache License 2.0 で提供されています。
それ以外の部分については、各ソフトウェアのライセンスに従います。
いずれのライセンスも、各ソフトウェアの著作権表示とライセンスの全文を、配布するソフトウェアの複製に含める必要があります。

ダウンロード

GS2 SDK for Unreal Engine 4 Version 2021.7.3

導入手順

SDK のダウンロードパッケージを展開し、ご使用の Unreal Engine 4 プロジェクトに対して、以下の変更を行います。

  1. Source/(プロジェクト名) 以下の任意の場所に gs2-unreal-engine-sdk/gs2 ディレクトリをコピーします。
  2. (プロジェクト名).Build.cs に以下の変更を加えます。
    • PublicDependencyModuleNames に "HTTP" および "WebSockets" を追加します。
      • 例: PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HTTP", "WebSockets" });
    • PublicSystemIncludePaths に GS2 SDK for Unreal Engine 4 の gs2 ディレクトリがある場所を追加します。
      • 例: PublicSystemIncludePaths.Add(System.IO.Path.Combine(ModuleDirectory, "gs2-unreal-engine-sdk/"));
  3. (プロジェクト名).uproject のコンテキストメニューから Visual Studio プロジェクトを再生成します。
iOS ビルドをご利用の場合

Unreal Engine 4.26 現在、 iOS ビルドではエンジンの持つルート CA 証明書が正しく読み込まれない問題があり、そのままでは GS2 サーバとの接続に失敗するため、以下の手順で回避してください。

  1. エンジンの Engine/Content/Certificates/ThirdParty/cacert.pem を、プロジェクトの Content/Certificates/cacert.pem へコピーする
  2. 「プロジェクト設定 (Project Settings)」の「パッケージ化 (Packaging)」詳細設定の中にある「コピーする追加の非アセットディレクトリ (Additional Non-Assets Directories to Copy)」に Certificates を追加する

なお、上記設定は iOS 以外をターゲットとする場合でも変更する必要はありません。

実装上の諸注意

GS2 SDK

本 SDK 自身は Apache License 2.0 で提供されています。
プログラムのどこかで Apache License 2.0 で提供されているライブラリを使用していることを明示する必要があります。

GS2-Java-SDK

https://github.com/gs2io/gs2-java-sdk

Mavenによる取得

Maven よりパッケージを取得できます。

gs2-java-sdk

GS2-PHP-SDK

https://github.com/gs2io/gs2-php-sdk

Composerによる取得

GS2-PHP-SDK のインストール

composer require gs2/gs2-php-sdk

Composer でパッケージを取得できます。

gs2-php-sdk

GS2-Golang-SDK

https://github.com/gs2io/gs2-golang-sdk

GS2-Python-SDK

https://github.com/gs2io/gs2-python-sdk

PyPIによる取得

GS2-Python-SDK のインストール

pip install gs2

Python Package Index (PyPI) よりパッケージを取得できます。

gs2

GS2-Typescript-SDK

https://github.com/gs2io/gs2-typescript-sdk

npmによる取得

GS2-Typescript-SDK のインストール

npm i gs2

npm よりパッケージを取得できます。

gs2

GS2-CSharp-SDK

https://github.com/gs2io/gs2-csharp-sdk

NuGet による取得

NuGetよりパッケージを取得できます。

GS2.CSharp.Sdk

GS2 CDK

CDK とは Cloud Development Kit の略で、GS2 のオーケストレーションをコードで行えるようにするための開発キットです。
より具体的に機能の内容を説明すると、CDK で GS2 リソースに相当するオブジェクトをインスタンス化するコードを記述し、ダンプAPIを呼び出すことで GS2-Deploy のテンプレートを生成することができます。
GS2-Deploy のテンプレートは YAML 形式で記述できますが、YAML ファイルはプログラムのコードではないため、外部ファイルを取り込んだりループを実行することはできず、ワークフローを最適化するためには、テンプレートを生成するためのコードを記述することが求められました。
CDK はこのようなワークフローを最適化するためのソリューションで、CDKを使用したツール内でマスターデータのExcelファイルをパースしたり、イベントの期間をループで定義しつつ GS2-Deploy のテンプレートを生成できます。

まず、単純な GS2 のクレデンシャルを定義するテンプレートファイルをみてみましょう。

import core from "gs2cdk/core";
import identifier from "gs2cdk/identifier";

class IdentifierForApplicationStack extends core.Stack {

    public constructor() {
        super();

        let identifier_resource = new identifier.model.User(
            this,
            "App"
        ).attachGrn(
            identifier.model.SecurityPolicy.ApplicationAccessGrn()
        ).identifier();

        this.output(
            "ClientId",
            identifier_resource.getAttrClientId().str()
        );
        this.output(
            "ClientSecret",
            identifier_resource.getAttrClientSecret().str()
        );
    }
}

これで、App という名前のユーザーを作成して「ApplicationAccess」権限を付与し、クレデンシャルを生成します。
そして、発行された ClientId と ClientSecret を記録します。

console.log(new IdentifierForApplicationStack().yaml());

このコードで GS2-Deploy のテンプレートを出力することができます。

GS2TemplateFormatVersion: "2019-05-01"
Resources:
  Identifier_User_App:
    Type: GS2::Identifier::User
    Properties:
      Name: App
    DependsOn: []
  Identifier_AttachSecurityPolicy_App:
    Type: GS2::Identifier::AttachSecurityPolicy
    Properties:
      UserName: App
      SecurityPolicyId: grn:gs2::system:identifier:securityPolicy:ApplicationAccess
    DependsOn:
      - Identifier_User_App
  Identifier_Identifier_:
    Type: GS2::Identifier::Identifier
    Properties:
      UserName: App
    DependsOn:
      - Identifier_User_App
Outputs:
  ClientId: !GetAttr Item.ClientId
  ClientSecret: !GetAttr ClientSecret

出力内容は上記の通りです。
この結果をファイルに書き出してマネージメントコンソールからアップロードするか、GS2-SDK を使用して GS2-Deploy に反映することで環境を構築できます。

GS2-Java-CDK

https://github.com/gs2io/gs2-java-cdk

Mavenによる取得

Maven よりパッケージを取得できます。

gs2-java-cdk

GS2-PHP-CDK

https://github.com/gs2io/gs2-php-cdk

Composerによる取得

GS2-PHP-CDK のインストール

composer require gs2/gs2-php-cdk

Composer でパッケージを取得できます。

gs2-php-cdk

GS2-Golang-CDK

https://github.com/gs2io/gs2-golang-cdk

GS2-Python-CDK

https://github.com/gs2io/gs2-python-cdk

PyPIによる取得

GS2-Python-CDK のインストール

pip install gs2cdk

Python Package Index (PyPI) よりパッケージを取得できます。

gs2-cdk

GS2-Typescript-CDK

https://github.com/gs2io/gs2-typescript-cdk

npmによる取得

GS2-Typescript-CDK のインストール

npm i gs2-cdk

npm よりパッケージを取得できます。

gs2-cdk

GS2-CSharp-CDK

https://github.com/gs2io/gs2-csharp-cdk

NuGet による取得

NuGetよりパッケージを取得できます。

GS2.CSharp.Sdk

更新履歴

GS2-Insight

GS2-Insight GitHub

新機能

GS2 SDK for Unity / GS2 C# SDK

[2022.3.4] - 2022-03-10

変更

実装例: var notification = JoinNotification.FromJson(JsonMapper.ToObject(message.payload));

ガイド

GS2-Identifierガイド

GS2-Identifierとは

GS2の各サービスにアクセスするための、クレデンシャル(認証情報)やアクセス権限の管理をする
仕組みが GS2-Identifier です。

各サービスにアクセスが可能かの権限管理は、GS2-Identifierユーザー(以下ユーザー)ごとに行います。

ユーザーにはGS2のどういったサービスを利用可能かの定義(セキュリティポリシー)を割り当てます。

GS2はクレデンシャルを使ってアクセスしてきたユーザーを認証し、プロジェクトトークンを返します。
ゲームアプリ/管理ツールはこのプロジェクトトークンを使用して各サービスにアクセスを行います。

権限の詳細な設定はセキュリティポリシーで定義され、これを各ユーザーごとに割り当てます。

ユーザー

ユーザーが持つデータ構造の概要は、以下のようになります。

セキュリティポリシー

セキュリティポリシーにはあらかじめ以下のポリシーが定義済みです。

セキュリティポリシー名 説明
AdministratorAccess 全てのAPIを利用する権限
ApplicationAccess チート行為に繋がらないAPIのみが呼び出せる権限
DeliveryAccess GS2-Distributor がリソースの溢れ処理を実行するために必要となる権限
UnauthenticatedAccess バージョンチェック(GS2-Version)の実行に必要な処理(ログイン処理含む)のみ行える権限

AdministratorAccess ポリシー は、GS2のすべてのサービスにアクセス可能な権限です。

ゲームアプリ内に組み込んで使用する想定の ApplicationAccess ポリシーは、
チート行為につながるようなサーバの操作が可能なAPIの呼び出しができないよう設定されています。

セキュリティポリシーのフォーマット

ポリシードキュメントのフォーマットの例 (JSON)

{
  "Version": "2016-04-01",
  "Statements": [
    {
      "Actions": [
        "*"
      ],
      "Effect": "Allow",
      "Resources": [
        "*"
      ]
    }
  ]
}

セキュリティポリシーはJSON形式で記述します。

Statements の中に EffectActionsResources の各要素を含めます。
*(アスタリスク)はワイルドカードを表します。

最も単純なセキュリティポリシーは例のようになります。
このセキュリティポリシーは、あらゆる操作をあらゆるサービス、
APIに対して許可する、というものです。

“Actions”

Actionsの指定の例 (JSON)

{
  "Version": "2016-04-01",
  "Statements": [
    {
      "Actions": [
        "Gs2Inbox:*"
      ],
      "Effect": "Allow",
      "Resources": [
        "*"
      ]
    }
  ]
}

例に示したセキュリティポリシーは GS2-Inboxサービスに対しあらゆる操作が行える、というものになります。
この例のセキュリティポリシーが割り当てられているユーザーの権限は、GS2-Inbox 以外のサービスにアクセスできません。

*(アスタリスク)はワイルドカードを表しています。

アクションの列挙

Actionsにメソッドを列挙する例 (JSON)

{
  "Version": "2016-04-01",
  "Statements": [
    {
      "Actions": [
        "Gs2Inbox:SendMessage",
        "Gs2Inbox:DescribeMessage",
        "Gs2Inbox:ReadMessage",
        "Gs2Inbox:DeleteMessage"
      ],
      "Effect": "Allow",
      "Resources": [
        "*"
      ]
    }
  ]
}

Actions には アクション、アクセスし実行が可能/不可能なメソッドの指定を
列挙することができます。
各サービスのリファレンスに記載されているメソッドを指定します。

[サービス名]:[メソッド名] のように :(コロン) を間に挟んでください。

サービス名の指定は
Gs2Inbox
のように、-(ハイフン)を含まず、Gs2に各サービスの名称が連なったものになります。

メソッド名は
SendMessage
のように、先頭の文字は大文字(アッパーキャメルケース)になります。

“Effect”

このポリシーでの、APIへのアクセスの 許可/不許可の指定をします。

指定 アクセス
Allow 許可
Deny 不許可

“Resources”

このポリシーの影響を受けるリソースの範囲を指定する変数になります。
現在は*(アスタリスク)のみ指定が可能です。

GS2-Deploy テンプレートのフォーマット

GS2-Deployでスタックを作成する際に使用するテンプレートファイルの書式仕様です。
JSON形式、もしくはYAML形式で記述できます。

セクションの種類

GS2-Deploy のテンプレートでのリソース作成の例 (YAML)

GS2TemplateFormatVersion: "2019-05-01"
Description: GS2-Account initialize template Version 2021-12-03

Globals:
  Alias:
    AccountNamespaceName: game-0001
    KeyNamespaceAccountAuthentication: account-encryption-key-namespace
    KeyAccountAuthentication: account-encryption-key
    GatewayNamespaceName: gateway-0001

Resources:
  GatewayNamespace:
    Type: GS2::Gateway::Namespace
    Properties:
      Name: ${GatewayNamespaceName}

  KeyNamespaceAccountAuthentication:
    Type: GS2::Key::Namespace
    Properties:
      Name: ${KeyNamespaceAccountAuthentication}

  KeyAccountAuthentication:
    Type: GS2::Key::Key
    Properties:
      NamespaceName: ${KeyNamespaceAccountAuthentication}
      Name: ${KeyAccountAuthentication}
    DependsOn:
      - KeyNamespaceAccountAuthentication

  AccountNamespace:
    Type: GS2::Account::Namespace
    Properties:
      Name: ${AccountNamespaceName}

Outputs:
  AccountNamespaceName: !GetAttr AccountNamespace.Item.Name
  KeyAccountAuthenticationKeyId: !GetAttr KeyAccountAuthentication.Item.KeyId
  GatewayNamespaceName: !GetAttr GatewayNamespace.Item.Name

テンプレートは以下のセクションで構成されます。

セクション 必須 説明
GS2TemplateFormatVersion テンプレートの形式を指定します。現在は 2019-05-01 のみ定義できます。
Description テンプレートに関する説明等の記入ができます。
Globals Alias属性(文字列の置き換え指定)の定義を行うセクションです。
Resources スタックに含める、GS2の各サービスのリソースを定義します。
Outputs マネージメントコンソールで表示する出力値を定義します。

Globals セクション

Alias属性

Alias属性の定義例 (YAML)

Globals:
  Alias:
    AccountNamespaceName: game-0001

指定の文字列でResourcesセクション、Outputsセクションにある文字列の置き換えを行います。
左辺の文字列を ${...} のように${}で囲んだ部分を、右辺の文字列で置き換えます。

置き換え指定の例: ${AccountNamespaceName}

Resources セクション

Resources セクション (YAML)

Resources:
  KeyNamespaceAccountAuthentication:  # リソース定義の名前
    Type: GS2::Key::Namespace  # リソースの指定
    Properties:
      Name: ${KeyNamespaceAccountAuthentication}

  KeyAccountAuthentication:  # リソース定義の名前
    Type: GS2::Key::Key  # リソースの指定
    Properties:
      NamespaceName: ${KeyNamespaceAccountAuthentication}
      Name: ${KeyAccountAuthentication}
    DependsOn:  # 依存するリソース定義の名前の指定
      - KeyNamespaceAccountAuthentication  # KeyNamespaceAccountAuthentication の生成に依存

  AccountNamespace:  # リソース定義の名前
    Type: GS2::Account::Namespace  # リソースの指定
    Properties:
      Name: ${AccountNamespaceName}

生成するリソースの定義を行います。Type属性、Properties属性 を持ち、必要であれば DependsOn属性を持ちます。

Type属性

サービスに対し生成を行うリソースの指定。
各サービスの GS2-Deploy リファレンス ページ のエンティティ名を指定します。

 例: Type: GS2::Account::Namespace

Properties属性

リソースの生成時にプロパティ値に与える値を指定します。

DependsOn属性

リソースが他のリソースに続けて作成されるように、依存しているリソースの名前を指定します。

Outputs セクション

Outputs セクション (YAML)

Outputs:
  AccountNamespaceName: !GetAttr AccountNamespace.Item.Name
  KeyAccountAuthenticationKeyId: !GetAttr KeyAccountAuthentication.Item.KeyId
  GatewayNamespaceName: !GetAttr GatewayNamespace.Item.Name

マネージメントコンソールで表示する出力値を定義します。

!GetAttr タグ

!GetAttr タグの使用例 (YAML)

AccountNamespace:
    Type: GS2::Account::Namespace
    Properties:
      Name: ${AccountNamespaceName}
Outputs:
  AccountNamespaceName: !GetAttr AccountNamespace.Item.Name

!GetAttrタグを使用することで各リソースの生成結果のプロパティ値を取得することができます。

この例では、AccountNamespace で GS2::Account::Namespace を指定していますので, GS2-AccountNamespace が生成されます。
その結果が AccountNamespace に取得され、 AccountNamespace.ItemNameSpaceモデル が入っています。
!GetAttr AccountNamespace.Item.Name で、ネームスペース名が取得できます。

また、!GetAttrタグには予約語として以下が割り当てられており、値の取得が可能です。

説明
Gs2::Region string リージョンの種類
Gs2::OwnerId string オーナーID

!Join タグ

!Joinタグは、続く配列の文字列の連結を行います。区切り記号(デリミタ)が指定できます。

 フォーマット: !Join [ 区切り記号の指定, [連結する文字列のリスト] ]

!Join タグ、!GetAttr Gs2::Region、!GetAttr Gs2::OwnerId の使用例 (YAML)

  QueueNamespace:
    Type: GS2::JobQueue::Namespace
    Properties:
      Name: ${QueueNamespaceName}
      PushNotification:
        GatewayNamespaceId: !Join
          - ':'
          - - 'grn'
            - 'gs2'
            - !GetAttr Gs2::Region
            - !GetAttr Gs2::OwnerId
            - 'gateway'
            - ${GatewayNamespaceName}

使用例ではGRN(GS2リソース名)の文字列生成を行っています。

マスターデータの管理

用語の定義

用語 意味
モデルマスター 管理画面から更新するゲームプレイヤーごとに変わらないデータ
モデル ゲーム内から使用するゲームプレイヤーごとに変わらないデータ
プロパティ モデルをもとに作成したゲームプレイヤーごとに異なるデータ

ゲームを構成する要素には、所持アイテムのパラメータやクエストの構成データなど、
ゲームプレイヤーごとには変わらないデータがあります。
このようなデータをGS2では モデル と呼んでいます。
そして、モデルをもとにゲームプレイヤーの所持するデータになったものを プロパティ と呼んでいます。

一般的なマスターデータは GS2 においては モデル にあたり、GS2 においては モデルマスター という概念が存在します。
モデル モデルマスター の違いは、データの内容は同一ですが、実際にゲームからアクセスされる状態にあるかどうか、という違いがあります。

GS2 の管理画面上で編集できるデータは モデルマスター で、それを実際にゲーム内から使用できる状態に変換すると モデル に変わります。
この変換の工程が必要な理由は、 モデルマスター に対する変更を一括してゲーム内に反映するためです。
この工程がない場合、管理画面でデータを更新していく過程で、途中のデータがゲーム内に反映されてしまうことになります。

マスターデータの作成

GS2-Deploy のテンプレートでのマスターデータ管理例 (YAML)

GS2TemplateFormatVersion: "2019-05-01"
Description: GS2 master data template Version 2010-06-26

Globals:
  Alias:
    NamespaceName: inventory

Resources:
  Namespace:
    Type: GS2::Inventory::Namespace
    Properties:
      Name: ${NamespaceName}

  NamespaceSettings:
    Type: GS2::Inventory::CurrentItemModelMaster
    Properties:
      NamespaceName: ${NamespaceName}
      Settings:  # ここから下のデータが本来はJSONで指定する部分ですが、yaml として記述して反映できます
        version: 2019-02-05
        inventoryModels:
          - name: item
            metadata: ITEM
            initialCapacity: 40
            maxCapacity: 60
            itemModels:
              - name: item-0001
                metadata: ITEM_0001
                maxCount: 99
                sortValue: 1
              - name: item-0002
                metadata: ITEM_0002
                maxCount: 99
                sortValue: 2
              - name: item-0003
                metadata: ITEM_0003
                maxCount: 99
                sortValue: 3
          - name: character
            metadata: CHARACTER
            initialCapacity: 30
            maxCapacity: 50
            itemModels:
             - name: character-0001
               metadata: CHARACTER_0001
               maxCount: 1
               sortValue: 1

    DependsOn:
      - Namespace

この変換工程は、すべての モデルマスター を一旦 JSON 形式のファイルにエクスポートし、そのJSONファイルをアップロードすることで一括して モデル として反映する仕組みになっています。
GS2 の管理画面で モデルマスター を操作して JSON 形式のファイルにエクスポートして利用しても構いませんが、Excel や独自の管理ツールを作成し GS2 上に モデルマスター を一切登録せずに モデル にデータを反映することもできます。

また、GS2-Deploy のテンプレート内でマスターデータを管理することもできます。この場合 git などの バージョン管理ツール で取り扱いやすくなりますので、こちらも検討してみてください。

運営上都合の良い方法でマスターデータを管理してください。

GS2-Script GS2サービスの拡張

GS2-Script を使って GS2-Inventory にアイテムを付与する例 (Lua)

-- 引数からモデル情報を取得
inventory_namespaceName = 'inventory-0001'
userId = args.account.userId
inventoryName = 'inventory'
itemName = 'item-0001'
acquireCount = 1
expiresAt = 0
itemSetName = ''

inventory_client = gs2('inventory')

-- リクエストを発行
result = inventory_client.acquire_item_set_by_user_id({
    namespaceName=inventory_namespaceName,
    inventoryName=inventoryName,
    itemName=itemName,
    userId=userId,
    acquireCount=acquireCount,
    expiresAt=expiresAt,
    createNewItemSet=nil,
    itemSetName=itemSetName,
})

-- レスポンスを応答
if(result['isError']) then
    fail(result['statusCode'], result['errorMessage'])
end

result = {}

GS2 は一般的な用途においては標準の機能のみでゲームシステムを構築できるように設計されています。
しかし、ゲームの仕様によっては GS2 の標準機能では実現できないことが出てくるかもしれません。

そのようなケースに対応できるよう GS2 では Lua言語で記述したスクリプトによる機能拡張に対応しています。 GS2-Account であれば、アカウントの新規作成時 アカウントの認証時 引き継ぎ設定登録時
引き継ぎ実行時 といったタイミングで GS2-Script に登録したスクリプトを実行できるようにしています。
これによって、 引き継ぎ設定をしたときに報酬をプレゼントボックスに届けたい というようなニーズに
応えられるようにしています。

何度も引き継ぎ登録して報酬を受け取り放題になるのはゲームシステムに破綻をきたすので、
1回だけ付与できるようにしたい場合は GS2-Limit の提供する回数制限機能と組み合わせて実装するなど、
スクリプトの中で工夫を凝らしてください。

マネージメントコンソールでの設定

イベントトリガーの設定

イベントトリガーの設定はマネージメントコンソールの各サービスの ネームスペースリスト > ネームスペース情報 > ネームスペースの更新 のページのスクリプトの項目より行います。

スクリプトの設定

スクリプトの設定はマネージメントコンソールのGS2-Scriptサービスの Script > ネームスペースリスト > ネームスペース情報 > スクリプトの新規作成 のページより行います。

引数にはテスト実行に必要なパラメータをJSON形式で渡すことができます。
パラメータの構造は、引数のモデルの構造に適宜合わせる必要があります。

テスト実行のための引数を渡す例 (JSON)

{
  "namespace": {
    "namespaceId": "grn:gs2:ap-northeast-1:xxxxxxxx-xxxxxxxxxxx:account:account-0001",
    "name": "account-0001"
  },
  "account": {
    "accountId": "grn:gs2:ap-northeast-1:xxxxxxxx-xxxxxxxxxxx:account:account-0001:account:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "userId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
  }
}

ユーティリティメソッド

GS2-Scriptの拡張スクリプト(Lua言語)で使用可能な拡張メソッドです。

util.table_to_json

実装例(Lua)

result = util.table_to_json({a="a", b=1, c=false})
if result.isError then
  fail(result['statusCode'], result['errorMessage'])
end
json_str = result["result"]

実行結果

{"a":"a","b":1,"c":false}

Luaのテーブル型(配列)を、JSON形式の文字列に変換します。

Request
説明
table table テーブル
Result
説明
isError bool エラーの有無
statusCode int ステータスコード
errorMessage string エラーメッセージ
result string 変換結果のJSON文字列

util.json_to_table

実装例(Lua)

result = util.json_to_table("{\"a\": \"a\", \"b\": 1, \"c\": false}")
if result.isError then
  fail(result['statusCode'], result['errorMessage'])
end
json_table = result["result"]

JSON形式の文字列を、Luaのテーブル型(配列)に変換します。

Request
説明
jsonText string JSON形式の文字列
Result
説明
isError bool エラーが発生しているか。
statusCode int ステータスコード
errorMessage string エラーメッセージ
result Luaテーブル型 変換結果のLuaテーブル

util.split

実装例(Lua)

result = util.split("a,b,c", ",")
if result.isError then
  fail(result['statusCode'], result['errorMessage'])
end
split_table = result["result"]
print(split_table[1])
print(split_table[2])
print(split_table[3])

実行結果

a
b
c

文字列を分割します。

Request
説明
value string 元の文字列
sep string デリミタ、区切り文字列
Result
説明
isError bool エラーが発生しているか。
statusCode int ステータスコード
errorMessage string エラーメッセージ
result Luaテーブル型 分割した文字列のLuaテーブル

http.get

実装例(Lua)

result = http.get("https://example.com")
if result.isError then
  fail(result['statusCode'], result['errorMessage'])
end
get_result = result["result"]

HTTPのGETリクエストを発行します。

Request
説明
url string 接続先のURL
Result
説明
isError bool エラーが発生しているか。
statusCode int ステータスコード
errorMessage string エラーメッセージ
result string HTTPレスポンスのボディ

http.post

実装の例(Lua)

result = http.post("https://example.com", "application/json", "{}")
if result.isError then
  fail(result['statusCode'], result['errorMessage'])
end
post_result = result["result"]

HTTPのPOSTリクエストを発行します。

Request
説明
url string 接続先のURL
contentType string HTTPヘッダーのContent-Type
body string HTTPリクエストメッセージのボディ
Result
説明
isError bool エラーが発生しているか。
statusCode int ステータスコード
errorMessage string エラーメッセージ
result string HTTPレスポンスのボディ

Tips:イベントトリガーの実行タイミングと高速化

イベントトリガーには、実行タイミングが2種類用意されているものがあります。
対象のイベントの実行前にスクリプトを実行し、その結果を受けて対象イベントの内容に影響を与えることができるトリガーと、対象イベントの処理の完了後にスクリプトの実行を行うトリガーです。
発生前にスクリプトを実行する場合は、スクリプト実行とその対象のイベントで順に処理を行いますので、対象のイベントの完了までの待ち時間は、スクリプト+イベントの実行時間分となります。
スクリプト処理の結果で対象イベントが実行する内容を変更したり、スクリプト処理を待つ必要がない場合は、完了後にスクリプト実行を行うトリガーに設定することで、対象イベントの完了までの待ち時間には影響を与えずにスクリプトを実行し、対象イベントの待ち処理を減らすことができます。

スタンプシートの概要

GS2には スタンプシート という、GS2内の各サービス間で連携して処理をおこなうための仕組みがあります。
GS2を使用するうえで、スタンプシートの利用は不可欠なものになると思われます。
本項はこれからお読みいただいても、実際にスタンプシートをご利用いただく際にご参照いただいてもかまいません。

例えば、課金通貨を処理するGS2-Moneyとゲーム内の所持品の処理をおこなうGS2-Inventoryを組み合わせて、
課金通貨でアイテムを購入するという処理をスタンプシートで実現することができます。
ここでGS2-Moneyに課金通貨を消費するリクエストを、GS2-Inventoryにアイテムを取得するリクエストを別々に送る場合、
処理の途中でアプリが停止した場合やチート行為により、片方のリクエストだけを処理してしまう可能性があります。

一方で、スタンプシート消費アクション(ここでは課金通貨の消費)と
入手アクション(ここではアイテムの入手)がひとまとまりになったものです。
今回はアイテムを課金通貨で購入する例ですので、ゲーム内の商品を実現するGS2-Showcaseの商品マスターに商品を登録します。
この商品に購入したときに発行するスタンプシートを登録します。

GS2-Showcaseスタンプシートを登録出来たら、次はクライアントでそのスタンプシートを入手して実行します。
GS2-Showcaseでは Buy というメソッドでスタンプシートを取得できます。
次に入手したスタンプシートを実行することで課金通貨の消費とアイテムの取得が処理されます。

ここではアイテムの購入を例に紹介しましたが、他にもGS2-Quest
スタミナを消費してクエストをクリアするアイテムを取得する、というように
スタンプシートを使う場面は多数想定されます。

また、一つのスタンプシートに複数の消費アクションと入手アクションを設定することもできます。
ただし、複数の入手アクションを設定した場合は、GS2-JobQueueと連携して処理を行う必要があります。
これは、報酬を付与する際に報酬を管理するサーバーがダウンしていると、連鎖して関連したサービスの
利用ができなくなることを避けるためです。
そういったケースのために、スタンプシート実行時に報酬処理をGS2-JobQueueに登録し、
報酬処理の実行が一度失敗してもリトライできるような仕組みを提供しています。

スタンプシートを実行するサンプルコード(Unity)

スタンプシートを実行するサンプルコード (Unity)

    // UnityEvent<Gs2Exception>型を使用するための準備
    [System.Serializable]
    public class OnErrorCallback : UnityEngine.Events.UnityEvent<Gs2Exception>
    {
    }

    // スタンプシート実行エラー時の動作を定義
    private void OnError(Exception e)
    {
        Debug.Log(e.ToString());
    }

    private IEnumerator StampSheetTest()
    {
        var machine = new StampSheetStateMachine(stampSheet, client, distributorNamespeceName, stampSheetEncryptKeyId);
        UnityEvent<Gs2Exception> m_events = new OnErrorCallback();
        m_events.AddListener(OnError);
        yield return machine.Execute(m_events);
    }

スタンプシート実行完了時に動かすメソッドを登録する例 (Unity)

    machine.OnCompleteStampSheet.AddListener(Machine_OnCompleteStampSheet);//machine.Executeを実行する前に登録しておく

    private void Machine_OnCompleteStampSheet(EzStampSheet sheet, Gs2.Unity.Gs2Distributor.Result.EzRunStampSheetResult result)
    {
     //スタンプシート実行完了時の処理を書く
    }

まず4つの引数を使ってスタンプシートステートマシンを作ります。

次にスタンプシートステートマシンの持つExecuteを実行します。
Executeの引数には UnityEvent型でエラー時の処理を登録する必要があります。

またスタンプシートステートマシンには、スタンプシートの実行が完了したときに動作させるメソッド、コールバックを登録することができます。
Executeを実行する前に2つ目のサンプルコードのようにコールバックを追加しておきます。
こうしておくことでExecuteを実行後、エラー時と成功時でそれぞれのコールバックが実行されます。

GS2 UIKit for Unity

GS2 UIKit for Unityは、コードを書かずに、UnityのUI上にGS2の機能の実装を可能にすることを目的にしています。
このセクションではUIKit for Unityを使用してのUIの作成方法についてまとめています。

インストール方法

本 SDK の使用には GS2 SDK for Unity のインストールが必要です。
本 SDK は Apache License 2.0 で提供されています。

Unity 2019.3 以降をご利用の場合

GS2 SDK for Unity Installer Version 2022.3.1

Unity Editor を起動し、メニューの Import Package から .unitypackage ファイルをインポートします。
その後、Unity Editor のメニューから 『ウィンドウ > Game Server Services > UIKit for Unity インストーラー』 を選択して、ウィザードの指示に従ってください。
インストール完了後、正しく動作しない場合は一度Unity Editorを再起動してみてください。

Unity 2019.2 以前をご利用の場合

GS2 UIKit for Unity GitHub

Clone or Download を選択し、ダウンロードしたソースコードをプロジェクト内に配置します。

UIKitの使い方

UIKitには機能別に2種類のプレハブがあります。

また、プレハブに含まれるコンポーネントは、それぞれ以下のような機能を持っています。

コンポーネント 機能
Fetcher コンポーネント GS2サービスから情報を取得、保持します。
View コンポーネント UIにプロパティ値を反映します。
Enabler コンポーネント 条件により対象GameObjectのアクティブ/非アクティブ化の制御をおこないます。
Action コンポーネント 各種アクションを実行します。

UIの追加方法

Unityに新規プロジェクトを作成し、GS2の初期化のためのプレハブ GS2.prefab をヒエラルキーに配置します。

Gs2.prefabの設定手順

  1. Core/Environment/Gs2Environment ScriptableObject を生成し、マネージメントコンソールからクレデンシャル情報を設定します。
    Create > Game Server Services > Core > Environment
  2. Gs2ClientHolder コンポーネントの Environment に、1.で作成した Gs2Environment ScriptableObject への参照を登録します。
  3. GS2-Distributorの機能を使用する場合は、Gs2Distributor/Namespace ScriptableObject を生成し、ネームスペース名を設定します。
    Create > Game Server Services > Gs2Distributor > Namespace
  4. Gs2ClientHolder コンポーネントの Distributor Namesppace に、3.で作成した Gs2Distributor NameSpace ScriptableObject への参照を登録します。

Gs2AccountAutoLogin.prefabの設定手順

  1. GS2Account/NameSpace ScriptableObject を生成し、Namespace Name プロパティにGS2-Accountのネームスペース名を設定します。
    Create > Game Server Services > GS2Account > Namespace
  2. Gs2AccountAccountCreateAction コンポーネントの Namespace プロパティに、1.で作成した GS2Account/NameSpace ScriptableObject への参照を登録します。
  3. Gs2ProfileLoginAction コンポーネントの Namespace プロパティに、作成した GS2Account/NameSpace ScriptableObject への参照を登録します。
  4. Gs2Key/NameSpace ScriptableObject を生成し、Namespace Name にGS2-Keyのネームスペース名を設定します。
    Create > Game Server Services > Gs2Key > Namespace
  5. Gs2Key/Key ScriptableObject を生成し、Namespace プロパティに、4.で作成した Gs2Key/NameSpace ScriptableObject への参照を登録します。 Key Name プロパティに、GS2-Key のKeyエンティティの暗号鍵名を設定します。 Create > Game Server Services > Gs2Key > Key
  6. Gs2ProfileLoginAction コンポーネントの Key に、5.で作成した Gs2Key/Key ScriptableObject への参照を登録します。

UI表示prefabの配置

ヒエラルキーにCanvasを配置し、GS2 UIKitのUI表示prefabを配置します。
ここではスタミナのUI表示を例に説明します。

Gs2StaminaStaminaPrefabをCanvasの子に配置します。

Gs2StaminaStaminaPrefabでGS2-Staminaへアクセスするための設定を行います。

  1. Gs2Stamina/NameSpace ScriptableObject を生成し、Namespace Name にGS2-Staminaのネームスペース名を設定します。
    Create > Game Server Services > Gs2Stamina > Namespace
  2. Gs2Stamina/Stamina ScriptableObject を生成し、Namespace に、作成した Gs2Key/NameSpace ScriptableObject への参照を登録します。 Stamina Name にスタミナモデル名を設定します。
    Create > Game Server Services > Gs2Stamina > Stamina
  3. Gs2StaminaStaminaFetcher コンポーネントの Stamina に、作成した Gs2Stamina/Stamina ScriptableObject への参照を登録します。

ここまでの設定を行うことで、ゲーム再生をするとUIにスタミナが表示されます。

GS2 UIkit for Unity リファレンス

各サービスのプレハブ/コンポーネントのリファレンスは以下になります。

GS2 UIkit for Unity サンプル

GS2 UIKit for Unityの機能実装サンプルです。

インストール方法

GS2 UIKit Sample Version 2022.10.1

上記よりサンプルパッケージファイルをダウンロードします。
Unity Hub を起動し、サンプルの展開用の 新しいプロジェクト を作成します。
もしくは、Unity Editorを起動しプロジェクトを新規作成します。
Unity Editorの Assets メニューの Import Package から 上記unitypackage ファイルをプロジェクトにインポートします。

GS2 UIKit サンプル GitHubリポジトリ

起動の準備

サンプルを実行するための準備を行います。
ここでは例として、GS2-Experienceで経験値を表示するサンプルを実行する手順について扱います。

Unityでサンプルシーンを開く

サンプルをインポートしたプロジェクトで、シーンを開きます。
ここでは例としてExperience.unityファイルを開いています。

Experience サンプルシーン:
(Unityプロジェクト)/Assets/Game Server Services/Samples/UIKit/Gs2Experience/Scenes/Experience.unity

クレデンシャルの設定

UnityクライアントからGS2へのアクセスのためのクレデンシャル(認証情報) の設定を行います。
GS2のマネージメントコンソールプロジェクトを作成してください。
マネージメントコンソールの GS2-Deploy の スタックの新規作成 で、以下のテンプレートファイルでスタックを作成してください。

クレデンシャルを設定するテンプレートのパス:
(Unityプロジェクト)/Assets/Game Server Services/Samples/UIKit/Core/Settings/initialize_credential_template.yaml

スタックのアウトプットのApplicationClientId、ApplicationClientSecretの値を、以下のSampleEnvironment.assetに設定します。

(Unityプロジェクト)/Assets/Game Server Services/Samples/UIKit/Core/Settings/SampleEnvironment.asset

フィールド名 条件
Name プロジェクト名
Region リージョン
Client Id クレデンシャルのClientId
Client Secret クレデンシャルのClientSecret

Deployテンプレートの設定

サンプルの動作に必要なリソースの準備を行います。
マネージメントコンソールの GS2-Deploy の スタックの新規作成 で以下のテンプレートファイルでスタックを作成してください。
スタック名には例として Experience、説明文は任意で設定、テンプレートデータでファイルを選択し、テンプレートをアップロードします。

経験値を設定するテンプレートのパス:
(Unityプロジェクト)/Assets/Game Server Services/Samples/UIKit/Gs2Experience/Settings/initialize_experience_template.yaml

実行状態作成完了になれば設定は完了です。

各サンプルの解説

サービス紹介・利用料金

GS2-Account

サービス概要

ユーザを識別するためのアカウント管理機能を提供するサービスです。

デバイスの機種変更時にデータを引き継げるようにするために メールアドレス+パスワード や ソーシャルアカウントを利用したデータの引き継ぎ機能に対応しています。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

引き継ぎ情報に関して、同一ユーザの情報の登録・更新・削除は1秒間に3回以内に抑えるようにしてください。
異なるユーザであればこの制限はありません。

基本的な使い方

ネットワーク機能を実装するゲームにはアカウントの管理機能が必要になります。
個人を特定出来ていないと、所持品の管理やフレンドの管理といったネットワーク機能の多くの要素が実現できないからです。

アカウント管理に関して、GS2では2種類の実装方法を用意しています。

アカウント管理の仕組み

GS2 では、アカウントの管理機能を実現するために GS2-Account というサービスを提供しています。

匿名アカウント

匿名アカウントの新規発行 実装例 (Unity)

var domain = gs2.Account.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
);
var result = await domain.CreateAsync();
var item = await result.ModelAsync();

Debug.Log(Item.UserId); // ユーザーID
Debug.Log(Item.Password); // パスワード
Debug.Log(Item.CreatedAt); // 作成日時
var domain = gs2.Account.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
);
var future = domain.Create(
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(Item.UserId); // ユーザーID
Debug.Log(Item.Password); // パスワード
Debug.Log(Item.CreatedAt); // 作成日時

はじめてゲームを開始したときに GS2-Account が発行するアカウントは 匿名アカウント と呼ばれるカテゴリのアカウントです。 匿名アカウントとは 利用者を特定することはできますが、ゲームプレイヤーが誰なのかを特定する情報を持たないアカウントです。

なぜそのようなアカウントの発行をするのかというと、はじめてゲームを開始するときにメールアドレスの登録等を必須にすることで、ゲームからの離脱率が上がってしまうからです。
そのため、利用者を特定するのに十分な要素として、GS2-Account はアカウントの新規作成APIを呼び出すとランダムなIDとパスワードを発行します。
アクセス元のクライアントは発行されたIDとパスワードをローカルストレージに保存し、ログイン処理にはその情報を利用します。

引き継ぎ設定

引き継ぎ設定登録 実装例 (Unity)

var domain = gs2.Account.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).TakeOver(
    type: 0 // スロット番号
);
var result = await domain.AddTakeOverSettingAsync(
    userIdentifier: "user-0001@gs2.io", // 引き継ぎ用ユーザーID
    password: "password-0001" // パスワード
);
var item = await result.ModelAsync();

Debug.Log(item.UserId); // ユーザーID
Debug.Log(item.Type); // スロット番号
Debug.Log(item.UserIdentifier); // 引き継ぎ用ユーザーID
Debug.Log(item.CreatedAt); // 作成日時
var domain = gs2.Account.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: gameSession // GameSessionオブジェクト(アクセストークン)
).TakeOver(
    type: 0
);
var future = domain.AddTakeOverSetting(
    userIdentifier: "user-0001@gs2.io", // 引き継ぎ用ユーザーID
    password: "password-0001" // パスワード
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(result.UserId); // ユーザーID
Debug.Log(result.Type); // スロット番号
Debug.Log(result.UserIdentifier); // 引き継ぎ用ユーザーID
Debug.Log(result.CreatedAt); // 作成日時

引き継ぎ実行の実装例 (Unity)

var domain = gs2.Account.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
);
var result = await domain.DoTakeOverAsync(
    type: 0, // スロット番号
    userIdentifier: "user-0001@gs2.io", // 引き継ぎ用ユーザーID
    password: "password-0001" // パスワード
);
var item = await result.ModelAsync();

Debug.Log(item.UserId); // ユーザーID
Debug.Log(item.Password); // パスワード
Debug.Log(item.CreatedAt); // 作成日時
var domain = gs2.Account.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
);
var future = domain.DoTakeOver(
    type: 0, // スロット番号
    userIdentifier: "user-0001@gs2.io", // 引き継ぎ用ユーザーID
    password: "password-0001" // パスワード
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(result.UserId); // アカウントID
Debug.Log(result.Password); // パスワード
Debug.Log(result.CreatedAt); // 作成日時

匿名アカウントは登録シーケンスが不要というメリットがありますが、当然デメリットがあります。
それは、ゲームプレイヤー自身が自分のユーザーIDとパスワードを把握していないということです。

そこで、GS2-Account では 引き継ぎ設定 という機能を用意しています。
引き継ぎ設定とは、匿名アカウントをよりゲームプレイヤーが認識しやすいID/パスワードとして表現するためのもので
一つの匿名アカウントに対して複数の引き継ぎ設定を行えるようになっています。

たとえば、スロット1 には メールアドレス任意のパスワード を設定できるようにし、
スロット2 には SNS の アカウント名 などの情報を登録することになります。
あとは、機種変更を行ったり、異なるデバイスで同じアカウントにログインしたいと思ったときには
引き継ぎ設定で登録したID/パスワード情報を入力することで、再度匿名アカウントのログイン情報を取得できます。

異なるデバイスからのログインを止めたい

GS2-Gateway で同時ログインを許可しない例 (Unity)

var domain = gs2.Gateway.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).WebSocketSession(
);
var result = await domain.SetUserIdAsync(
    allowConcurrentAccess: false // 同時に異なるクライアントからの接続を許容するか
);
var item = await result.ModelAsync();

Debug.Log(item.ConnectionId); // コネクションID
Debug.Log(item.NamespaceName); // ネームスペース名
Debug.Log(item.UserId); // ユーザーID
var domain = gs2.Gateway.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).WebSocketSession(
);
var future = domain.SetUserId(
    allowConcurrentAccess: null // 同時に異なるクライアントからの接続を許容するか
);
yield return future;
if (future.Error != null)
{
    OnError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    OnError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(result.ConnectionId); // コネクションID
Debug.Log(result.NamespaceName); // ネームスペース名
Debug.Log(result.UserId); // ユーザーID

GS2-Account は引き継ぎを実行する際、引き継ぎ先のデバイスに保存する匿名アカウント情報の
パスワードを変更する機能があります。
この機能を利用すれば、引き継ぎ元のデバイスからのログインを不可にできます。

しかし、より厳密に多重デバイスによるログインの制限を行いたい場合、この方法では不十分です。
たとえば、デバイス間のデータ転送実行する等で、複数の異なるデバイスで同一のID/パスワードを持つ状態を作り出すことができます。
このようなケースに対応するには、 GS2-Gateway で二重ログインを制限する機能をご利用ください。

GS2-Gateway は WebSocket を使って GS2 のサーバと接続し、サーバで発生したイベント、
たとえば「フレンドの申請を受けた」「新しいプレゼントがプレゼントボックスに届いた」
「マッチメイキングが完了した」といった通知を受け取るための仕組みです。

この機能の性質上、WebSocket の通信セッションに対しログイン中のユーザIDを紐付ける必要があり、
この紐付けを実行する際に「すでに該当ユーザIDがログイン中の場合は接続を許可しない」ということが実現できます。

作成したアカウント情報をもとにログインする

GS2-Account で署名付きアカウント情報を取得する実装例 (Unity)

var domain = gs2.Account.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Account(
    userId: "user-0001" // ユーザーID
);
var result = await domain.AuthenticationAsync(
    keyId: "grn:gs2:ap-northeast-1:owner_id:key:namespace1:key:key-0001", // 認証トークンの暗号化に使用する暗号鍵 のGRN
    password: "password-0001" // パスワード
);
var item = await result.ModelAsync();
var body = result.Body;
var signature = result.Signature;

Debug.Log(item.UserId); // ユーザーID
Debug.Log(item.Password); // パスワード
Debug.Log(item.CreatedAt); // 作成日時
Debug.Log(body); // 署名対象のアカウント情報
Debug.Log(signature); // 署名
var domain = gs2.Account.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Account(
    userId: "user-0001" // ユーザーID
);
var future = domain.Authentication(
    keyId: "grn:gs2:ap-northeast-1:owner_id:key:namespace-0001:key:key-0001", // 認証トークンの暗号化に使用する暗号鍵 のGRN
    password: "password-0001" // パスワード
);
yield return future;
if (future.Error != null)
{
    OnError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    OnError.Invoke(future2.Error, null);
    yield break;
}
var item = future2.Result;
var body = future.Result.Body;
var signature = future.Result.Signature;

Debug.Log(item.UserId); // ユーザーID
Debug.Log(item.Password); // パスワード
Debug.Log(item.CreatedAt); // 作成日時
Debug.Log(body); // 署名対象のアカウント情報
Debug.Log(signature); // 署名

GS2-Auth で署名付きアカウント情報からアクセストークンを取得する実装例 (Unity)

var domain = gs2.Auth.AccessToken(
);
var result = await domain.LoginAsync(
    keyId: "grn:gs2:ap-northeast-1:owner_id:key:namespace-0001:key:key-0001", // 署名の作成に使用した暗号鍵 のGRN
    body: "body", // アカウント認証情報の署名対象
    signature: "signature" // 署名
);
var token = result.Token;
var userId = result.UserId;
var expire = result.Expire;

Debug.Log(token); // アクセストークン
Debug.Log(userId); // ユーザーID
Debug.Log(expire); // 有効期限
var domain = gs2.Auth.AccessToken();
var future = domain.Login(
    keyId: "grn:gs2:ap-northeast-1:owner_id:key:namespace-0001:key:key-0001", // 署名の作成に使用した暗号鍵 のGRN
    body: "body", // アカウント認証情報の署名対象
    signature: "signature" // 署名
);
yield return future;
if (future.Error != null)
{
    OnError.Invoke(future.Error, null);
    yield break;
}
var token = future.Result.Token;
var userId = future.Result.UserId;
var expire = future.Result.Expire;

Debug.Log(token); // アクセストークン
Debug.Log(userId); // ユーザーID
Debug.Log(expire); // 有効期限

GS2-Account のログイン処理は2ステップで実装されています。

  1. GS2-Account に匿名アカウントのユーザID/パスワードを送信して、パスワード検証を通過した場合に署名付きのアカウント情報を応答
  2. GS2-Auth に 署名付きアカウント情報 を送信し、署名検証に通過した場合に ユーザID のアクセストークンを発行

この2ステップをひとまとめにしたものが GS2-Profile::Login です。

アクセストークンの有効期限

アクセストークンには有効期限が設定されています。
そのため、取得したアクセストークンを永続化して利用することは推奨できません。

アクセストークンの有効期限は アクセストークン取得時の戻り値に含まれています。
有効期限を迎える前に再度アクセストークンの取得処理を行う必要があります。

GS2-Auth

サービス概要

GS2-Auth は GS2 内でゲームプレイヤーを特定するための”アクセストークン”を発行するサービスです。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

GS2-Chat

サービス概要

テキストベースのチャット機能を提供します。

メッセージデータはルームごとに管理され、過去24時間分のログが保持されます。
ルームにはパスワードを設定でき、設定した場合はメッセージの取得や投稿リクエストにパスワードが必要となります。
ルームにはユーザIDのホワイトリストを設定でき、設定した場合はホワイトリストに含まれるゲームプレイヤーにしかメッセージの取得や投稿ができなくなります。

メッセージにはカテゴリを設定でき、クライアント側でペイロードのパース方法を切り替えることができます。
たとえば、 カテゴリ0 はテキストがペイロードにそのまま含まれる。 カテゴリ1 はペイロードにスタンプのIDが含まれる。といった使い方ができます。

ルームを購読すれば、ルームに対して新しい投稿があったときにサーバからプッシュ通知を受け取ることができます。
このとき、通知を受けたいカテゴリを設定できますので、メッセージの種類や重要度別にカテゴリを分ければ、ゲームプレイヤーが通知を受け取るメッセージの種類を設定できるようになりユーザ体験を向上させることができます。
また、購読の設定には モバイルプッシュ通知に転送するか という設定もありますので、うまく活用してください。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

1つのルームに対して、1秒間に3回までしか投稿できません。
1ルームに参加できる人数を極端に多くする、一斉に投稿を促すような実装を避けてください。
1つのルームに対して購読できる最大人数は1000人です。

GS2-Datastore

サービス概要

任意のバイナリデータをサーバに保存する機能を提供します。
アップロードされたデータはアクセス権限管理(ACL - Access Control List)が適用されます。

データには誰でもデータにアクセスが可能な public
指定したユーザIDのゲームプレイヤーにのみアクセスが可能な protected
自身のみがアクセスが可能な private の3種類のACLが設定できます。

アップロードされたデータは自動的にバックアップが作成され、 データを更新・削除したとしても30日以内であれば復元が可能です。
アップロードされたデータには固有のID(UUID v4)と世代IDが割り当てられ、 データを更新・削除すると世代IDが更新されます。
データをダウンロードする際には世代IDを指定して、具体的なファイルを特定します。

データを更新するのと同時に他プレイヤーがデータにアクセスしようとしたときに 意図せず新しいバージョンのデータを取得しないようにダウンロードリクエストには世代IDを含めることができます。
世代IDをダウンロードリクエストに付加することで、確実にリストアップした時点でのデータをダウンロードすることを保証することができます。

ただし、いつまでも世代の古いデータにアクセスできることは望まないゲームもあるかもしれません。
そのため、データの所有者以外は、更新後60分以内かつ1つ前の世代に限り古い世代のデータをダウンロードを許可するオプションが存在します。

本機能を使用してセーブデータの管理をする際には、不正行為に対する耐性を考えてください。
他のGS2のサービスではゲームの仕様に合わせて加算・減算を自由に行えないようコントロールされています。
しかし、ローカルのセーブデータを信頼することはチート行為の原因となりえます。
GS2の機能が提供されている場合はそちらを優先して使用し、 UGCコンテンツの交換や、コンフィグの設定値など改ざんされても影響が軽微な要素のみを管理するようにしてください。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

ゲームによってはUGC、ユーザーが生成したコンテンツ(レースゲームのゴーストデータやステージのエディットデータ)を他のプレイヤーと共有したいことがあるかもしれません。
このようなときに使用できるのがこのデータストア機能です。

アクセス権限

データストアは任意のバイナリデータをサーバにアップロードできます。
アップロードしたデータにはアクセス権限を設定できます。
アクセス権限にはいくつかの方針があり、データごとに設定ができます。

パブリック

ランキングのスコアにゴーストデータを添付したいような場合に使用します。
スコアのメタデータとして、ゴーストデータのIDを格納しておくことで、ランキングを取得した際に、そのスコアを達成したレースのゴーストをデータストアから取得できるような設計ができます。

プロテクト

特定の相手にのみ公開したい情報。例えば「チャットルームに写真をアップロードして、ルームの参加者にのみアクセス権限を付与したい」というようなケースで使用できます。

プライベート

クラウドセーブのような機能を実現したい場合に使用できます。

データのアップロード

データのアップロードは3つのステップで構成されます。

3ステップに分かれている理由は、GS2-Datastoreはバイナリデータをサーバーに保存・読込するため管理機能を提供しますが、
実際バイナリデータを保存するのはGS2上ではなくファイルサーバーになるためです。
そのため、
1. GS2 にデータの名前やアクセス権等の管理情報を設定してリクエストし、ファイルサーバーにアップロードするためのURLを取得
2. 取得したURLにHTTPクライアントでデータをアップロード
3. アップロードが無事完了したことをGS2に報告する
の3ステップになっています。

アップロード用のURLを取得

ファイルをアップロードする前にデータ名やアクセス権等の管理情報をもとにアップロード用のURLを取得します。
ここで得られたURLに対して HTTP クライアントで PUT リクエストとしてアップロードすることになります。

アップロード用のURLを取得 実装例 (Unity)

var domain = gs2.Datastore.Namespace(
namespaceName: "namespace-0001" // ネームスペース名 ).Me(
gameSession: GameSession // GameSessionオブジェクト(アクセストークン) ); var result = await domain.PrepareUploadAsync(
name: "dataObject-0001", // データの名前
scope: "public", // ファイルのアクセス権
allowUserIds: null, // 公開するユーザーIDリスト
updateIfExists: null // 既にデータが存在する場合にエラーとするか、データを更新するか ); var item = await result.ModelAsync(); var uploadUrl = result.UploadUrl;

Debug.Log(item.DataObjectId); // データオブジェクトID Debug.Log(item.Name); // データの名前 Debug.Log(item.UserId); // ユーザーID Debug.Log(item.Scope); // ファイルのアクセス権 Debug.Log(item.AllowUserIds); // 公開するユーザーIDリスト Debug.Log(item.Status); // 状態 Debug.Log(item.Generation); // データの世代 Debug.Log(item.CreatedAt); // 作成日時 Debug.Log(item.UpdatedAt); // 最終更新日時 Debug.Log(uploadUrl); // アップロード処理の実行に使用するURL

var domain = gs2.Datastore.Namespace(
"namespace-0001" // ネームスペース名 ).Me(
GameSession // GameSessionオブジェクト(アクセストークン) ); var future = domain.PrepareUpload(
name: "dataObject-0001", // データの名前
scope: "public", // ファイルのアクセス権
allowUserIds: new string[] { }, // 公開するユーザーIDリスト
updateIfExists: null // 既にデータが存在する場合にエラーとするか、データを更新するか ); yield return future; if (future.Error != null) {
throw future.Error; } var future2 = future.Result.Model(); yield return future2; if (future2.Error != null) {
throw future2.Error; } var result = future2.Result; var uploadUrl = future.Result.UploadUrl; Assert.NotNull(result);

Debug.Log(result.DataObjectId); // データオブジェクトID Debug.Log(result.Name); // データの名前 Debug.Log(result.UserId); // ユーザーID Debug.Log(result.Scope); // ファイルのアクセス権 Debug.Log(result.AllowUserIds); // 公開するユーザーIDリスト Debug.Log(result.Status); // 状態 Debug.Log(result.Generation); // データの世代 Debug.Log(result.CreatedAt); // 作成日時 Debug.Log(result.UpdatedAt); // 最終更新日時 Debug.Log(uploadUrl); // アップロード処理の実行に使用するURL

データをアップロード

得られた URL に対してアップロードを実行します。
アップロードはGS2ではなくファイルサーバに HTTP クライアントで PUT リクエストを用いて行います。

アップロードの完了を報告

無事アップロードが完了したことを報告します。
この報告をもって、次にダウンロード用のURLを取得するときに最新のデータが置かれたURLをGS2から返すことができます。

アップロードの完了を報告 実装例 (Unity)

var domain = gs2.Datastore.Namespace(
namespaceName: "namespace-0001" // ネームスペース名 ).Me(
gameSession: GameSession // GameSessionオブジェクト(アクセストークン) ).DataObject(
dataObjectName: "dataObject-0001" // データの名前 ); var result = await domain.DoneUploadAsync( ); var item = await result.ModelAsync();

Debug.Log(item.DataObjectId); // データオブジェクトID Debug.Log(item.Name); // データの名前 Debug.Log(item.UserId); // ユーザーID Debug.Log(item.Scope); // ファイルのアクセス権 Debug.Log(item.AllowUserIds); // 公開するユーザーIDリスト Debug.Log(item.Status); // 状態 Debug.Log(item.Generation); // データの世代 Debug.Log(item.CreatedAt); // 作成日時 Debug.Log(item.UpdatedAt); // 最終更新日時

var domain = gs2.Datastore.Namespace(
namespaceName: "namespace-0001" // ネームスペース名 ).Me(
gameSession: GameSession // GameSessionオブジェクト(アクセストークン) ).DataObject(
dataObjectName: "dataObject-0001" // データの名前 ); var future = domain.DoneUpload( ); yield return future; if (future.Error != null) {
OnError.Invoke(future.Error, null);
yield break; } var future2 = future.Result.Model(); yield return future2; if (future2.Error != null) {
OnError.Invoke(future2.Error, null);
yield break; } var result = future2.Result;

Debug.Log(result.DataObjectId); // データオブジェクトID Debug.Log(result.Name); // データの名前 Debug.Log(result.UserId); // ユーザーID Debug.Log(result.Scope); // ファイルのアクセス権 Debug.Log(result.AllowUserIds); // 公開するユーザーIDリスト Debug.Log(result.Status); // 状態 Debug.Log(result.Generation); // データの世代 Debug.Log(result.CreatedAt); // 作成日時 Debug.Log(result.UpdatedAt); // 最終更新日時

ヘルパー関数

GS2 SDK for Unity では3ステップのアップロードプロセスを1つのAPIにまとめたヘルパー関数が用意されています。

ヘルパー関数の利用例 (Unity)

var result = gs2.Datastore.Namespace(
namespaceName: "namespace-0001" // ネームスペース名 ).Me(
gameSession: GameSession // GameSessionオブジェクト(アクセストークン) ).UploadAsync(
scope: "public", // ファイルのアクセス権
allowUserIds: new List<string>(), // 公開するユーザーIDリスト
data: new byte[] {0x00, 0x01, 0x02} // byte配列 のバイナリデータ );

Debug.Log(result.DataObjectId); // データオブジェクトID Debug.Log(result.Name); // データの名前 Debug.Log(result.UserId); // ユーザーID Debug.Log(result.Scope); // ファイルのアクセス権 Debug.Log(result.AllowUserIds); // 公開するユーザーIDリスト Debug.Log(result.Status); // 状態 Debug.Log(result.Generation); // データの世代 Debug.Log(result.CreatedAt); // 作成日時 Debug.Log(result.UpdatedAt); // 最終更新日時

var future = gs2.Datastore.Namespace(
namespaceName: "namespace-0001" // ネームスペース名 ).Me(
gameSession: GameSession // GameSessionオブジェクト(アクセストークン) ).Upload(
scope: "public", // ファイルのアクセス権
allowUserIds: new List<string>(), // 公開するユーザーIDリスト
data: new byte[] {0x00, 0x01, 0x02} // byte配列 のバイナリデータ ); yield return future; var result = future.Result;

Debug.Log(result.DataObjectId); // データオブジェクトID Debug.Log(result.Name); // データの名前 Debug.Log(result.UserId); // ユーザーID Debug.Log(result.Scope); // ファイルのアクセス権 Debug.Log(result.AllowUserIds); // 公開するユーザーIDリスト Debug.Log(result.Status); // 状態 Debug.Log(result.Generation); // データの世代 Debug.Log(result.CreatedAt); // 作成日時 Debug.Log(result.UpdatedAt); // 最終更新日時

データのダウンロード

データのダウンロードは2つのステップで構成されます。
- ダウンロード用のURLを取得
- データをダウンロード
アップロードと違い完了を報告する必要がないため2ステップになります。

ダウンロード用のURLを取得

データを上げたユーザーIDとデータの名前をもとにダウンロード用のURLを取得します。
ここで得られたURLに対して HTTP クライアントで GET リクエストをしてダウンロードすることになります。

ダウンロード用のURLを取得する例 (Unity)

var domain = gs2.Datastore.Namespace(
namespaceName: "namespace-0001" // ネームスペース名 ).User(
userId: "user-0001" // ユーザーID ).DataObject(
dataObjectName: "dataObject-0001" // データの名前 ); var result = await domain.PrepareDownloadByUserIdAndDataObjectNameAsync( ); var item = await result.ModelAsync(); var fileUrl = result.FileUrl; var contentLength = result.ContentLength;

Debug.Log(item.DataObjectId); // データオブジェクトID Debug.Log(item.Name); // データの名前 Debug.Log(item.UserId); // ユーザーID Debug.Log(item.Scope); // ファイルのアクセス権 Debug.Log(item.AllowUserIds); // 公開するユーザーIDリスト Debug.Log(item.Status); // 状態 Debug.Log(item.Generation); // データの世代 Debug.Log(item.CreatedAt); // 作成日時 Debug.Log(item.UpdatedAt); // 最終更新日時 Debug.Log(fileUrl); // ファイルをダウンロードするためのURL Debug.Log(contentLength); // ファイルの容量

var domain = gs2.Datastore.Namespace(
namespaceName: "namespace-0001" // ネームスペース名 ).User(
userId: "user-0001" // ユーザーID ).DataObject(
dataObjectName: "dataObject-0001" // データの名前 ); var future = domain.PrepareDownloadByUserIdAndDataObjectName( ); yield return future; if (future.Error != null) {
OnError.Invoke(future.Error, null);
yield break; } var future2 = future.Result.Model(); yield return future2; if (future2.Error != null) {
OnError.Invoke(future2.Error, null);
yield break; } var result = future2.Result; var fileUrl = future.Result.FileUrl; var contentLength = future.Result.ContentLength;

Debug.Log(result.DataObjectId); // データオブジェクトID Debug.Log(result.Name); // データの名前 Debug.Log(result.UserId); // ユーザーID Debug.Log(result.Scope); // ファイルのアクセス権 Debug.Log(result.AllowUserIds); // 公開するユーザーIDリスト Debug.Log(result.Status); // 状態 Debug.Log(result.Generation); // データの世代 Debug.Log(result.CreatedAt); // 作成日時 Debug.Log(result.UpdatedAt); // 最終更新日時 Debug.Log(fileUrl); // ファイルをダウンロードするためのURL Debug.Log(contentLength); // ファイルの容量

データをダウンロード

得られた URL に対してダウンロードを実行します。
ダウンロードはGS2ではなくファイルサーバに HTTP クライアントで GET リクエストを用いて行います。

GS2-Deploy

サービス概要

サービスのデプロイを補助するサービスです。

ゲームが必要とするリソースを記述したテンプレートを適用することで、GS2の各サービスのリソースを作成できるサービスです。
GS2-Deploy を使ったリソース管理を行うことで、開発環境と製品環境で設定を揃えたり、開発中に開発者ごとの環境を作成したりするのが容易となります。
また、イベントを開催するときにも、イベントに必要な要素をテンプレート化して登録し、終わったらそれを削除すれば、イベント開催前の状態に容易に戻すことができます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

クレデンシャルを発行する GS2-Deploy テンプレートの例

GS2TemplateFormatVersion: "2019-05-01"
Description: GS2 SDK identifier template Version 2019-07-10

Globals:
  Alias:
    ApplicationUserName: application

Resources:
  IdentifierApplicationUser:
    Type: GS2::Identifier::User
    Properties:
      Name: ${ApplicationUserName}

  IdentifierApplicationUserAttachPolicy:
    Type: GS2::Identifier::AttachSecurityPolicy
    Properties:
      UserName: ${ApplicationUserName}
      SecurityPolicyId: grn:gs2::system:identifier:securityPolicy:ApplicationAccess
    DependsOn:
      - IdentifierApplicationUser

  IdentifierApplicationIdentifier:
    Type: GS2::Identifier::Identifier
    Properties:
      UserName: ${ApplicationUserName}
    DependsOn:
      - IdentifierApplicationUser

Outputs:
  ApplicationClientId: !GetAttr IdentifierApplicationIdentifier.Item.ClientId
  ApplicationClientSecret: !GetAttr IdentifierApplicationIdentifier.ClientSecret

GS2では基本的にネームスペースの作成やアイテムの定義などのリソースの作成を、マネージメントコンソール上で一つ一つ操作・入力しながら作成します。
このマネージメントコンソール上での操作・入力をYAML形式のテンプレートファイルで記述して、リソースを作成できるようにしたものがGS2-Deployです。
また特徴として、一度インプットしたテンプレートファイルの一部を追記・削除した場合、即座にリソースに反映されます。
インプットしたテンプレートファイルそのものを削除すれば、そのテンプレートファイルで作成されたリソースは削除されます。

以上の特徴から以下の使い道が考えられます。

GS2-Dictionary

サービス概要

アイテム図鑑やキャラクター図鑑のようなプレイヤー・エントリー毎に「手に入れた」「手に入れない」の二値を持つデータベースを実現します。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

使い方は3ステップです。

  1. エントリーモデルを定義する
  2. アイテムやキャラクターなどを入手するイベントを契機にエントリーを「手に入れた」状態にする
  3. ゲーム内からエントリーを取得する

エントリーモデルを定義する

マネージメントコンソールなどからアイテムやキャラクターの存在を表す、エントリーモデルを定義します。
この時、図鑑の種類ごとにネームスペースを分けることを推奨します。
これは図鑑を開く時にエントリーの一覧を取得しますが、ネームスペースごとに取得するためです。 GS2-InventoryのItemの入手経験をGS2-Dictionaryで管理する場合はItemNameとEntryNameを一致させると、プログラムで扱いやすいことが考えられます。
また、使用用途として図鑑で説明していますが、図鑑に限らず2値でサーバーに保管したい情報があればご活用ください。

アイテムやキャラクターなどを入手するイベントを契機にエントリーを「手に入れた」状態にする

クライアントからチートによる嘘の入手報告が行えないように、セキュリティポリシー ApplicationAccess ではエントリーの入手報告ができません。 GS2のイベントが入手の契機になる場合は、スタンプシートやGS2-Scriptから入手報告をしてください。
またはサーバーからGS2に入手報告をすることもできます。

ゲーム内からエントリーを取得する

ゲーム内から記録されたエントリーを取得してUIを構築します。

GS2-Distributor

サービス概要

プロパティの増加処理を中継し、対象の所持数量の上限に達していた場合に GS2-Inbox に転送する機能を提供します。
この機能を利用することで、所持数量の上限に到達したときの複雑な分岐処理を無くすことができ、意図しないデータ消失のリスクを回避できます。

その他に、GS2内で一貫性のある処理を実現するための仕組みであるスタンプシートの実行処理にも使用します。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

本機能はAPIを呼び出すものではなく設定をして用意しておくものになります。
使い方は2つです。

スタンプシートの実行処理に利用する

GS2ではプロパティ(ゲーム内通貨やスタミナ、所持品など)の増減の処理にはスタンプシートという仕組みを使用します。
スタンプシートの詳細はスタンプシートのセクションをご覧ください。
スタンプシートの実行をする際、引数の一つにGS2-Distributorのネームスペースが必要になります。
スタンプシートを実行するだけならネームスペースの作成だけで大丈夫です。

所持限界のプロパティをGS2-Inboxに転送する機能

スタンプシートを実行するとき、プロパティの所持上限を超える可能性があります。
このとき、超えたプロパティを自動でGS2-Inboxに転送することができます。
こちらはマネージメントコンソールから配信設定マスターを作ることで使用できます。
また転送するプロパティを指定することもできます。
これは例えば、経験値上限の時に取得した経験値がGS2-Inboxに転送され続け一杯になってしまう事態を回避するためなどに使用できます。

GS2-Enhance

サービス概要

GS2-Inventory で管理された素材アイテムを消費し、GS2-Experience の経験値を得る仕組みを提供します。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

GS2-Exchange

サービス概要

アイテムの売却価格や、レアリティが異なる進化素材間の交換レートなど、常設される交換処理を実現します。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

使い方は2ステップです。

  1. 交換モデルを定義する
  2. 交換を実行する

交換モデルを定義する

マネージメントコンソールから交換モデル設定します。
お金とゲーム内通貨の交換のほか、ゲーム内通貨でガチャを回したりスタミナを増やしたりなど様々な定義ができます。

交換を実行する

交換を実行する実装例 (Unity)

var domain = gs2.Exchange.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Exchange(
);
var result = await domain.ExchangeAsync(
    rateName: "rate-0001", // 交換レートの種類名
    count: 1, // 交換するロット数
    config: null // 設定値(オプション)
);

Debug.Log(result.TransactionId); // 交換処理の実行に使用するスタンプシートのトランザクションID
var domain = gs2.Exchange.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Exchange(
);
var future = domain.Exchange(
    rateName: "rate-0001", // 交換レートの種類名
    count: 1, // 交換するロット数
    config: null // 設定値(オプション値)
);
yield return future;
if (future.Error != null)
{
    OnError.Invoke(future.Error, null);
    yield break;
}
var result = future.Result;

Debug.Log(result.TransactionId); // 交換処理の実行に使用するスタンプシートのトランザクションID

ここで注意する点は exchange を実行するだけではリソースの交換は行われないことです。 exchangeの返り値であるスタンプシートを処理することで処理が行われます。

また一つの交換モデルに複数の報酬を設定する場合はGS2-JobQueueと連携する必要があります。

GS2-Experience

サービス概要

キャラクターやスキルのレベル、親愛度など、経験値を積み重ねてランクアップしていく仕組みの実現に使用します。
ランクキャップ機能も有しており、キャラクターやスキルなど個別の要素ごとに上限管理を行うことができます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一プロパティに対する経験値付与は1秒間に3回までにおさえてください。
異なるユーザや同一ユーザでも異なるプロパティに対する経験値付与であればこの制限はありません。

基本的な使い方

キャラクターやアイテム、スキルなどの成長を管理するためのサービスとして、 GS2 には GS2-Experience が用意されています。

GS2-Experience が提供するのは、主に以下の機能です。

以下の機能については、 GS2-Experience は関知しません。

ただし、 GS2-Stamina など別の GS2 のサービスと連携することで、 GS2-Experience のランクに連動してスタミナの最大値を拡張させたりすることができます。

経験値モデルの登録

経験値モデルは JSON 形式のマスターデータとして登録します。
マスターデータのフォーマットは こちら に資料があります。

マスターデータは、マネージメントコンソールで要素ごとにデータを登録し、その結果をエクスポートすることで用意することもできます。
エクスポートしたマスターデータを明示的に登録するまで GS2-Experience の挙動には反映されないことに注意してください。

経験値モデルの設定値

経験値モデルはランク体系、たとえばキャラクターレベルやスキルレベルといった成長要素の種類を表現するものです。
『どのランクアップ閾値テーブルを使用するか』や『デフォルトのランクキャップ』、『ランクキャップの引き上げ限界』といった設定が可能です。

ランクアップ閾値テーブル

ランクアップ閾値テーブルはランクアップの閾値となる経験値の配列です。
たとえば、[100, 200, 300] と設定した場合、

経験値の値 ランク
0~99 1
100~199 2
200~299 3
300 4

となり、 300 を超えて経験値を獲得しようとしても、超えた分の経験値は切り捨てられます。

ランクキャップ

ランクキャップ は、到達可能なランクの最大値です。
たとえば、ランクアップ閾値テーブルに指定されている値が [100, 200, 300] で、ランク4までの設定があったとしても、ランクキャップが 2 に設定されている場合、到達できるランクは 2 までとなり、また 100 を超えて経験値を入手できなくなります。
現在のランクキャップの値は、経験値や現在のランクの値同様に個々のキャラクターやスキルなどごとに持つことができます。
ランクキャップは経験値モデルごとの ランクキャップの引き上げ限界 まで引き上げることができ、ランクキャップを引き上げることによって、入手できる経験値の最大量も同時に拡張されます。

経験値の入手

現在獲得している経験値は、経験値モデルとプロパティIDの組み合わせごとに保持されます。

そのため、経験値の入手処理を実行する際にも、獲得する経験値量のほかに、経験値モデルとプロパティIDの組み合わせを指定する必要があります。
プロパティIDとは経験値を付与する対象の識別子で、 GS2-Experience への事前の登録なく任意の値を指定できます。

たとえば、GS2-Inventory で管理している所持キャラクターに対して経験値を付与したい場合は、GS2-Inventory の『所持品ID』をそのままプロパティIDに指定できます。
ゲームプレイヤー自身に経験値を付与したい場合は、GS2-Account の『ユーザID』をそのまま指定できます。

また、所持キャラクターに複数の経験値を持たせたい場合、たとえば『戦闘スキル』と『信頼度』のようにランク体系が異なるのであれば、異なる経験値モデルを指定することで、 GS2-Inventory の『所持品ID』1つに対して複数の経験値を持たせることができます。
一方、『剣スキル』と『弓スキル』のように、武器スキルという同じランク体系のなかで複数の経験値を持たせたい場合は、同様の設定を持つ経験値モデルを複数用意する方法もありますが、 GS2-Inventory の『所持品ID』のうしろに -sword や -bow などの文字列を追加してプロパティIDを区別する方法もあります。

経験値の入手とセキュリティ

経験値の入手はセンシティブな操作です。
経験値の入手に関してクライアントから任意の操作を受け付けることは、不正に経験値を増殖させ、ゲーム性を大きく損なうことになりかねません。

そこで、GS2 ではスタンプシートの報酬としてしか経験値の入手を行えないようにしています。
スタンプシートの報酬の内容はサーバに定義を持ち、また報酬を得る前に対価の支払いやゲーム内目標の達成をおこなったことをスタンプシートの仕組みが保証するため、経験値の不正な増殖を防げます。
したがって、経験値の入手には、ゲーム内ストアでの購入や、クエストの報酬として入手するような報酬設計が必要です。

現在の経験値の取得

経験値を取得する方法は2種類あります

特定のキャラクターのプロパティページを表示したい場合、後者を使用するほうが効率がいいでしょう。

経験値の取得 実装例 (Unity)

var domain = gs2.Experience.Namespace(
namespaceName: "namespace-0001" // ネームスペース名 ).Me(
gameSession: GameSession // GameSessionオブジェクト(アクセストークン) ).Status(
experienceName: "character_ssr", // 経験値の種類の名前
propertyId: "property-0001" // プロパティID ); var item = await domain.ModelAsync();

Debug.Log(item.ExperienceName); // 経験値の種類の名前 Debug.Log(item.PropertyId); // プロパティID Debug.Log(item.ExperienceValue); // 累計獲得経験値 Debug.Log(item.RankValue); // 現在のランク Debug.Log(item.RankCapValue); // 現在のランクキャップ

var domain = gs2.Experience.Namespace(
namespaceName: "namespace-0001" // ネームスペース名 ).Me(
gameSession: GameSession // GameSessionオブジェクト(アクセストークン) ).Status(
experienceName: "character_ssr", // 経験値の種類の名前
propertyId: "property-0001" // プロパティID ); var future = domain.Model(); yield return future; var item = future.Result;

Debug.Log(item.ExperienceName); // 経験値の種類の名前 Debug.Log(item.PropertyId); // プロパティID Debug.Log(item.ExperienceValue); // 累計獲得経験値 Debug.Log(item.RankValue); // 現在のランク Debug.Log(item.RankCapValue); // 現在のランクキャップ

TIPS: パーティに編成されたキャラクターへの経験値付与

GS2 では複数のリソースを編成するためのサービスとして GS2-Formation が用意されています。
GS2-Formation を利用すると、個別に指定をおこなうことなく、編成されたリソースの全体に報酬として経験値を付与することができます。
これによって、パーティに編成されているキャラクターなどに対して、一括で簡単に経験値を付与することができます。

TIPS: ステータスの正当性保証

ランクに応じて何らかの機能を開放するなどのユースケースで、クライアントが送信してきたランク値が正当なのかを検証したくなることがあります。
GS2-Experience はそのようなニーズに応えるために、署名(※)付きのステータスを応答する機能があります。

この機能を利用することで、サーバ間通信を行わず、クライアントが送信してきたステータスの値を信用していいかを判断することができるようになります。
繰り返しステータスを使用した操作を行う場合、署名付きのステータスをクライアントからサーバに渡すことで、通信回数の削減や通信時間の削減ができます。

ただし、署名付きステータスは取得時の値でしかありませんので、ステータスに変化があった場合はクライアント側で再取得を行ったうえでサーバに渡す必要があります。
署名付きステータスには応答時刻が付与されており、 GS2 のサービスでは、取得から1時間以上経過した署名付きステータスは使用できなくなります。

※ 署名とは暗号技術の一種で、とあるデータが改ざんされていないかを検出するために使用できる値です。

GS2-Formation

サービス概要

装備機能やパーティ編成機能を実現するためのサービスです。

編成情報を記録する フォーム(Form) と、編成した フォーム を保存する 保存したフォーム(Mold) があります。
パーティ編成を例にすると、キャラクターを フォーム に設定し、パーティを編成します。
編成したパーティ情報を複数記録する場合は 保存したフォーム を使って実現できます。

保存したフォーム に保存できる数にはキャパシティを設定でき、キャパシティの値は引き上げることができます。

フォーム に編成するプロパティはプロパティIDで指定することになりますが、プロパティの正規表現で値を限定することができます。
また、GS2-Inventory のリソースを設定する場合は、GS2-Inventory が発行する 署名付きアイテムセット を使って編成対象に指定できます。
署名付きアイテムセット とは、該当のアイテムを所有していることを保証する署名付きプロパティ情報で、こちらを使う場合はクライアントから直接編成することを許します。

この 所有していることを保証する とは編成時点での保証であり、編成後にプロパティを手放した場合のフォローはありません。
GS2-Inventory でプロパティを消費したり売却したりする場合は、事前に GS2-Formation で編成に使用されていないかの判定はクライアントまたはスクリプトで実装する必要があります。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一フォームの更新処理は1秒間に3回以内に抑えてください。

基本的な使い方

使い方は4ステップです。

  1. フォームモデルマスターを定義する
  2. フォームの保存領域(Mold)モデルマスターを定義する
  3. フォームを作成する
  4. フォームをフォームの保存領域(Mold)に保存する

フォームモデルマスターを定義する

フォームモデルマスターはマネージメントコンソールで作成できます。
例えばキャラクター5人のパーティー情報を保存したい場合は、スロットを5つ設定しスロットモデル名にchara1~5のように入力するとよいでしょう。
注意点として、マネージメントコンソールからはフォーム一つ当たり10までしかスロットを設定できません。
それ以上のスロットを設定する場合は一度スロットが足りない状態で保存しておいて、マスターデータをエクスポートし編集してアップロードしてください。

フォームの保存領域(Mold)モデルマスターを定義する

フォームの保存領域(Mold)モデルマスターはマネージメントコンソールで作成できます。
フォームの保存領域(Mold)モデルマスターは作成したフォームモデルマスターにしたがって作成したフォームをいくつ保存できるようにするかを設定します。
初期キャパシティと最大キャパシティを設定できます。
例えばゲームの進行度に合わせてキャパシティを増やしたり、ゲーム内通貨でキャパシティを購入するといったことができます。

フォームを作成する

署名付きアイテムセットを取得する実装例 (Unity)

var domain = gs2.Inventory.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Inventory(
    inventoryName: "inventory-0001" // インベントリの種類名
).ItemSet(
    itemName: "item-0001", // アイテムモデルの種類名
    itemSetName: null // アイテムセットを識別する名前(オプション)
);
var result = await domain.GetItemWithSignatureAsync(
    keyId: "grn:gs2:ap-northeast-1:owner_id:key:namespace-0001:key:key-0001" // 署名の発行に使用する暗号鍵のGRN
);
var items = await result.ModelAsync();
var body = result.Body;
var signature = result.Signature;

Debug.Log(items); // 有効期限毎のアイテム所持数量リスト
Debug.Log(body); // 署名対象のアイテムセット情報
Debug.Log(signature); // 署名
Debug.Log(result.OverflowCount); // 所持数量の上限を超えて受け取れずに GS2-Inbox に転送したアイテムの数量
var domain = gs2.Inventory.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Inventory(
    inventoryName: "inventory-0001" // インベントリの種類名
).ItemSet(
    itemName: "item-0001", // アイテムモデルの種類名
    itemSetName: null // アイテムセットを識別する名前(オプション)
);
var future = domain.GetItemWithSignature(
    keyId: "grn:gs2:ap-northeast-1:owner_id:key:namespace-0001:key:key-0001" // 署名の発行に使用する暗号鍵のGRN
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;
var body = future.Result.Body;
var signature = future.Result.Signature;

Debug.Log(result); // 有効期限毎のアイテム所持数量リスト
Debug.Log(body); // 署名対象のアイテムセット情報
Debug.Log(signature); // 署名
Debug.Log(domain.OverflowCount); // 所持数量の上限を超えて受け取れずに GS2-Inbox に転送したアイテムの数量

基本的な使い方として、クライアント側でGS2-Inventoryで管理されている所持品を編成してフォームを作成する例を説明します。
この際、所持していないアイテムを編成できないように、GS2-Inventory が発行する'署名付きアイテムセット' を使用します。
これを'署名付きスロットのリスト' に当てはめてフォームを作成します。

署名付きスロットのリストを作成する例 (Unity)

List<EzSlotWithSignature> slots = new List<EzSlotWithSignature>();

EzSlotWithSignature slot = new EzSlotWithSignature();

slot.Name = "chara1"; //スロット名 (例えばキャラクター5人のパーティであれば、chara1 から chara5 まで作成する)
slot.PropertyType = "gs2_inventory"; // プロパティの種類 gs2_inventoryのプロパティを使用
slot.Body = result.Body; // ペイロード
slot.Signature = result.Signature; // プロパティIDのリソースを所有していることを証明する署名
slots.Add(slot); // 実際はフォームのスロットの数だけ、スロットを作成してAddする
List<EzSlotWithSignature> slots = new List<EzSlotWithSignature>();

EzSlotWithSignature slot = new EzSlotWithSignature();

slot.Name = "chara1"; //スロット名 (例えばキャラクター5人のパーティであれば、chara1 から chara5 まで作成する)
slot.PropertyType = "gs2_inventory"; // プロパティの種類 gs2_inventoryのプロパティを使用
slot.Body = result.Body; // ペイロード
slot.Signature = result.Signature; // プロパティIDのリソースを所有していることを証明する署名
slots.Add(slot); // 実際はフォームのスロットの数だけ、スロットを作成してAddする

フォームをフォームの保存領域(Mold)に保存する

署名付きスロットのリストを作成する例 (Unity)

var domain = gs2.Formation.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Mold(
    moldName: "mold-0001" // フォームの保存領域の名前
).Form(
    index: 0 // 保存領域のインデックス
);
var result = await domain.SetFormAsync(
    slots: new[]
    {
        new EzSlotWithSignature
        {
            Name = "slot-0001",
            PropertyType = "gs2_dictionary",
            Body = "body",
            Signature = "signature"
        }
    }, // 編成するスロットのリスト
    keyId: "grn:gs2:ap-northeast-1:owner_id:key:namespace-0001:key:key-0001" // 署名の発行に使用する暗号鍵のGRN
);
var item = await result.ModelAsync();

Debug.Log(item.Name); // フォームの保存領域の名前
Debug.Log(item.Index); // 保存領域のインデックス
Debug.Log(item.Slots); // スロットリスト
var domain = gs2.Formation.Namespace(
    namespaceName: "namespace-0001"
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Mold(
    moldName: "mold-0001"
).Form(
    index: 0
);
var future = domain.SetForm(
    slots: new [] {
        new EzSlotWithSignature
        {
            Name = "slot-0001",
            PropertyType = "gs2_dictionary",
            Body = "body",
            Signature = "signature"
        }
    }, // 編成するスロットのリスト
    keyId: "grn:gs2:ap-northeast-1:owner_id:key:namespace-0001:key:key-0001" // 署名の発行に使用する暗号鍵のGRN
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(result.Name); // フォームの保存領域の名前
Debug.Log(result.Index); // 保存領域のインデックス
Debug.Log(result.Slots); // スロットリスト

作成した '署名付きスロットのリスト' を引数にsetFormを実行します。
この時 '署名付きアイテムセット' の発行に使用した暗号鍵も引数に指定します。

GS2-Friend

サービス概要

ゲーム内の友達関係を実現するときに使用します。

リクエストを出して、承認されて初めて関係の構築されるフレンドと、 相手の承認手続きなしに構築できるフォローがあります。

ゲームプレイヤーのメタデータとしてプロフィール情報を持たせることができます。
プロフィール情報は 公開 フォロワーに公開 フレンドに公開 のスコープがあり、スコープに応じて他プレイヤーに公開されます。

フレンド・フォロワー数の上限はそれぞれ1万人ですが、連携するサービスによっては各サービスのポリシーに従って追加の制限が加わる可能性があります。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一ゲームプレイヤーによるプロフィールの更新は1秒間に3回までにおさえてください。
異なるユーザや同一ユーザでも異なるプロパティに対する経験値付与であればこの制限はありません。

1人のプレイヤーにフレンドリクエストが集中する仕様は避けてください。
1人のプレイヤーがフレンドリクエストを受け付けられるのは1秒間に3回までです。

基本的な使い方

使い方は大きく3つあります。

プロフィール

プロフィールを設定する 実装例 (Unity)

var domain = gs2.Friend.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Profile(
);
var result = await domain.UpdateProfileAsync(
    publicProfile: "public", // 公開されるプロフィール(オプション)
    followerProfile: "follower", // フォロワー向けに公開されるプロフィール(オプション)
    friendProfile: "friend" // フレンド向けに公開されるプロフィール(オプション)
);
var item = await result.ModelAsync();

Debug.Log(item.UserId); // ユーザーID
Debug.Log(item.PublicProfile); // 公開されるプロフィール
Debug.Log(item.FollowerProfile); // フォロワー向けに公開されるプロフィール
Debug.Log(item.FriendProfile); // フレンド向けに公開されるプロフィール
var domain = gs2.Friend.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Profile(
);
var future = domain.UpdateProfile(
    publicProfile: "public", // 公開されるプロフィール(オプション)
    followerProfile: "follower", // フォロワー向けに公開されるプロフィール(オプション)
    friendProfile: "friend" // フレンド向けに公開されるプロフィール(オプション)
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(result.UserId); // ユーザーID
Debug.Log(result.PublicProfile); // 公開されるプロフィール
Debug.Log(result.FollowerProfile); // フォロワー向けに公開されるプロフィール
Debug.Log(result.FriendProfile); // フレンド向けに公開されるプロフィール

他人に公開する情報を設定します。
全体向け・フォロワー向け・フレンド向けのそれぞれ設定することができます。

フォロー

フォローを行う 実装例 (Unity)

var domain = gs2.Friend.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).FollowUser(
    targetUserId: "user-0002", // フォローするユーザーID
    withProfile: true // プロフィールも一緒に取得するか
);
var result = await domain.FollowAsync(
);
var item = await result.ModelAsync();

Debug.Log(item.UserId); // ユーザーID
var domain = gs2.Friend.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).FollowUser(
    targetUserId: "user-0002", // フォローするユーザーID
    withProfile: true // プロフィールも一緒に取得するか
);
var future = domain.Follow(
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(result.UserId); // ユーザーID

相手の承認手続きなしに構築できる関係がフォローです。フォローの解除もできます。
フォローを行うとフォローしたプレイヤーの一覧とそのプレイヤーのフォロワー向けメッセージの取得ができます。

フレンド

リクエストを出して、承認されて構築される関係がフレンドです。

リクエストを出す側ができることは

リクエストを受けた側ができることは

GS2-Gateway

サービス概要

GS2-Gateway は WebSocket を経由して GS2 にアクセスするためのプロキシ機能を提供します。
GS2-Gateway を使用して GS2 のサーバを利用することで、常時接続しながら通信できるようになります。これによって、サーバーとの通信速度が改善することが期待できます。

また、常時接続できることから、 サーバーサイドからのプッシュ通知に対応 できます。
このプッシュ通知機能があると GS2-Mission で ミッションのタスク を達成したときや、 GS2-Matchmaking で ギャザリング の参加者の増減があった時などに通知を受け取ることができるようになります。

プッシュ通知対象のデバイスがオフラインだった場合、 Firebase (https://firebase.google.com/) を使用したモバイルプッシュ通知に転送する仕組みも提供します。
この機能を使用すれば、マッチメイキングが完了したときにプレイヤーがオフラインだった場合はモバイルプッシュ通知を出してゲームに戻ってくるようプレイヤーを誘導することができます。

GS2-Gateway で常時接続用のセッションを構築する際にゲームプレイヤーの識別を行えるよう GS2-Auth で発行した アクセストークン で認証処理を行うことになりますが、
すでに同一ユーザのセッションが存在する場合は、アクセスを拒否することができます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

WebSocket通信をするところから、プッシュ通知を受け取るところまで3ステップで説明します。

  1. WebSocket通信を開始する
  2. サーバーでプッシュ通知を送信する設定をする
  3. プッシュ通知を受信したときの処理を定義する

WebSocket通信を開始する

WebSocket通信を開始する 実装例 (Unity)

var domain = gs2.Gateway.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).WebSocketSession(
);
var result = await domain.SetUserIdAsync(
    allowConcurrentAccess: false // 同時に異なるクライアントからの接続を許容するか
);
var item = await result.ModelAsync();

Debug.Log(item.ConnectionId); // コネクションID
Debug.Log(item.NamespaceName); // ネームスペース名
Debug.Log(item.UserId); // ユーザーID
var domain = gs2.Gateway.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).WebSocketSession(
);
var future = domain.SetUserId(
    allowConcurrentAccess: false // 同時に異なるクライアントからの接続を許容するか
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(result.ConnectionId); // コネクションID
Debug.Log(result.NamespaceName); // ネームスペース名
Debug.Log(result.UserId); // ユーザーID

クライアントでログイン処理後、GS2-GatewayにユーザーIDを登録し、プッシュ通知を受け取れるようにします。

サーバーでプッシュ通知を送る設定をする

プッシュ通知を送る方法は3つあります。

  1. GS2が用意しているプッシュ通知を使う
  2. GS2-Scriptを使う
  3. 自作のサーバーからGS2 SDKを使う

GS2が用意しているプッシュ通知を使う

GS2-Mission で ミッションのタスク を達成したときや、 GS2-Matchmaking で ギャザリング の参加者の増減があったときなど、GS2が用意しているプッシュ通知を使うことができます。
プッシュ通知を送信するサービスのネームスペースからプッシュ通知の有無を設定できます。
例えばGS2-Matchmaking で ギャザリング のマッチングが完了したときは、Gs2Matchmaking:Complete というメッセージを送ります。

GS2-Scriptを使う

プッシュ通知を用意していないGS2のイベントからプッシュ通知を送りたい場合や、メッセージの内容を自分で決めたい場合はこちらを使用します。

自作のサーバーからGS2 SDKを使う

GS2のイベント以外を契機にプッシュ通知を送る場合はこちらの方法を利用ください。

サーバーからプッシュ通知を送信する例 (Python)

from gs2_gateway_client.request import SendNotificationRequest
from gs2_gateway_client.result import SendNotificationResult

result = client.send_notification(
    SendNotificationRequest()\
        .with_namespace_name(namespace_name)\
        .with_user_id(user_id)\
        .with_subject(subject)\
        .with_payload(payload)\
        .with_enable_transfer_mobile_notification(enable_transfer_mobile_notification)\
        .with_sound(sound)
)
# エラー発生時には例外が発生
# result に結果を格納

プッシュ通知受信時の処理を定義する

最後に、クライアントでプッシュ通知が届いたときにどのような処理を行うか定義します。
ここでは例として、GS2-Matchmakingでプッシュ通知を処理する例を記載します。

プッシュ通知を受け取ったときの処理の例 (Unity)

public void PushNotificationHandler(NotificationMessage message)
{
    Debug.Log(message.issuer); //プッシュ通知が起因したイベント
    Debug.Log(message.subject); //プッシュ通知のタイトル
    Debug.Log(message.payload); //プッシュ通知の内容

    if (message.issuer.StartsWith("Gs2Matchmaking:"))
    {
        if (message.issuer.EndsWith(":Join"))
        {
            //ギャザリングに新たに参加者が増えた時の処理
            var notification = JoinNotification.FromJson(JsonMapper.ToObject(message.payload));
            Debug.Log(notification.JoinUserId); //新たにギャザリングに参加したユーザーのID
        }
        else if (message.issuer.EndsWith(":Leave"))
        {
            //ギャザリングから参加者が減ったときの処理
            var notification = LeaveNotification.FromJson(JsonMapper.ToObject(message.payload));
            Debug.Log(notification.LeaveUserId); //ギャザリングから退室したユーザーのID
        }
        else if (message.issuer.EndsWith(":Complete"))
        {
            //マッチングが完了したときの処理
        }
    }
}
public void PushNotificationHandler(NotificationMessage message)
{
    Debug.Log(message.issuer); //プッシュ通知が起因したイベント
    Debug.Log(message.subject); //プッシュ通知のタイトル
    Debug.Log(message.payload); //プッシュ通知の内容

    if (message.issuer.StartsWith("Gs2Matchmaking:"))
    {
        if (message.issuer.EndsWith(":Join"))
        {
            //ギャザリングに新たに参加者が増えた時の処理
            var notification = JoinNotification.FromJson(JsonMapper.ToObject(message.payload));
            Debug.Log(notification.JoinUserId); //新たにギャザリングに参加したユーザーのID
        }
        else if (message.issuer.EndsWith(":Leave"))
        {
            //ギャザリングから参加者が減ったときの処理
            var notification = LeaveNotification.FromJson(JsonMapper.ToObject(message.payload));
            Debug.Log(notification.LeaveUserId); //ギャザリングから退室したユーザーのID
        }
        else if (message.issuer.EndsWith(":Complete"))
        {
            //マッチングが完了したときの処理
        }
    }
}

プッシュ通知を受信したときの呼び出し先の登録 (Unity)

gs2Client.profile.Gs2Session.OnNotificationMessage += PushNotificationHandler;
gs2Client.profile.Gs2Session.OnNotificationMessage += PushNotificationHandler;

定義した関数を、プッシュ通知が届いたときの呼び出し先に設定します。

GS2-Identifier

サービス概要

GS2にアクセスするためのクレデンシャルや権限管理をおこないます。

GS2にアクセスするときにはクレデンシャルを使用してAPIを呼び出します。
その際にはクレデンシャルの所有者であるユーザーに割り当てられたセキュリティポリシーに基づいて処理されます。
複数のセキュリティポリシーをユーザーに割り当てている場合、いずれかのセキュリティポリシーによって操作が許可されていればAPIを利用できます。

またマネージメントコンソールログインパスワードを作成することで複数人で1つのプロジェクトを管理できます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

使い方は3ステップに分かれています。

  1. セキュリティポリシーを用意する
  2. ユーザーを作成し、セキュリティポリシーを割り当てる
  3. 作成したユーザーでAPIを呼び出したり、マネージメントコンソールにログインする

セキュリティポリシーを用意する

GS2では初めから、開発者用の全てのAPIを利用する権限 AdministratorAccess と、
ユーザー用のチート行為に繋がらないAPIのみが呼び出せる権限 ApplicationAccess
用意されています。
また、セキュリティポリシーを作成することも可能です。

ポリシードキュメントのフォーマット (JSON)
Actions に呼び出せるアクションを羅列し、Resources に操作出来る対象リソースを羅列する
Actions / Resources には末尾にのみワイルドカードを使用出来る

{
    "Version": "2016-04-01",
    "Statements": [
        {
            "Effect": "Allow",
            "Actions": [
                "Gs2Account:Create*",
            ],
            "Resources": ["*"],
        },
    ]
}

ユーザーを作成し、セキュリティポリシーを割り当てる

セキュリティポリシーが準備できたらユーザーを作成します。
ユーザーには複数のセキュリティポリシーを割り当てることができます。
その場合は、いずれかのセキュリティポリシーで許可されているAPIが実行可能になります。

作成したユーザーでAPIを呼び出す

クレデンシャルを使ってユーザー認証する 実装例 (Unity)

var profile = new Gs2.Unity.Util.Profile(
    clientId: clientId, // クレデンシャル の クライアントID
    clientSecret: clientSecret, // クレデンシャル の クライアントシークレット
    reopener: new Gs2BasicReopener() // 再接続を行うハンドラ
);

{
    var future = profile.Initialize();

    yield return future;
{

var gs2 = new Gs2.Unity.Core.Gs2Domain(profile); // gs2(Gs2.Unity.Core.Gs2Domain)を使用して各種APIを呼び出すことが可能です
var profile = new Gs2.Unity.Util.Profile(
    clientId: clientId, // クレデンシャル の クライアントID
    clientSecret: clientSecret, // クレデンシャル の クライアントシークレット
    reopener: new Gs2BasicReopener() // 再接続を行うハンドラ
);

{
    var future = profile.Initialize();

    yield return future;
{

var gs2 = new Gs2.Unity.Core.Gs2Domain(profile); // gs2(Gs2.Unity.Core.Gs2Domain)を使用して各種APIを呼び出すことが可能です

ユーザーを作成したらクレデンシャルを作成します。
クレデンシャルとはそのユーザーであることを認証するためのIDとパスワードです。
このIDとパスワードをプログラムに組み込み、GS2に認証を要求することによってAPIを使用できるようになります。

作成したユーザーでマネージメントコンソールにログインする

マネージメントコンソールログインパスワードを設定することで、別の開発者がマネージメントコンソールにログインできるようになります。
パスワードを作成したらログイン用のURLが生成されますので、その二つを別の開発担当者に通知することでログインできます。
ユーザーに割り当てられているセキュリティポリシーによって利用できる機能は制限されます。

GS2-Inbox

サービス概要

プレゼントつきのメッセージ機能を提供するサービスです。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

使い方は4ステップになります。

  1. プレゼントボックスを作る
  2. メッセージを送信する
  3. メッセージを受信する
  4. メッセージについている報酬を受け取る

プレゼントボックスを作る

ネームスペースがそのままプレゼントボックスの役割をします。
メッセージの種類ごとにプレゼントボックスを分けたい場合は、ネームスペースを複数作成してください。

メッセージを送信する

個別メッセージを送る例 (Python)

from gs2_inbox_client.request import SendMessageByUserIdRequest
from gs2_inbox_client.result import SendMessageByUserIdResult

result = client.send_message_by_user_id(
    SendMessageByUserIdRequest()\
        .with_namespace_name('namespace-0001')\
        .with_user_id('user-0001')\
        .with_metadata('{"type": "message", "body": "hello"}')\
        .with_read_acquire_actions(read_acquire_actions)\
        .with_expires_at(expires_at)\
        .with_expires_time_span(expires_time_span)
)
# エラー発生時には例外が発生
# result が成功結果が格納

全ユーザーに送る例 (Python)

from gs2_inbox_client.request import CreateGlobalMessageMasterRequest
from gs2_inbox_client.result import CreateGlobalMessageMasterResult

result = client.create_global_message_master(
    CreateGlobalMessageMasterRequest()\
        .with_namespace_name('namespace-0001')\
        .with_name('globalMessageMaster-0001')\
        .with_metadata('{"type": "globalMessageMaster", "body": "hello"}')\
        .with_read_acquire_actions([
Object("AcquireAction", {"action": 'Gs2Inventory:AcquireItemSetByUserId', "request": 'Gs2Inventory:AcquireItemSetByUserId-request'})
])\
        .with_expires_time_span(expires_time_span)\
        .with_expires_at(expires_at)
)
# エラー発生時には例外が発生
# result が成功結果が格納

送信方法は2つあります。

メッセージの送信はサーバーからGS2 SDKを使うか、マネージメントコンソールからも送ることができます。
またメッセージには報酬をつけることができます。

メッセージを受信する

ここで気を付ける点は、個別メッセージとグローバルメッセージで受信するためのAPIが異なることです。
グローバルメッセージは一度受信すると以後個別メッセージとして保管されます。

個別メッセージを受け取る実装例 (Unity)

var domain = gs2.Inbox.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
);
var items = await domain.MessagesAsync(
).ToListAsync();

Debug.Log(items); // メッセージのリスト
var domain = gs2.Inbox.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
);
var it = domain.Messages(
);
List<EzMessage> items = new List<EzMessage>();
while (it.HasNext())
{
    yield return it.Next();
    if (it.Error != null)
    {
        onError.Invoke(it.Error, null);
        break;
    }
    if (it.Current != null)
    {
        items.Add(it.Current);
    }
    else
    {
        break;
    }
}

Debug.Log(items); // メッセージのリスト

全体メッセージを受け取る実装例 (Unity)

var domain = gs2.Inbox.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
);
var result = await domain.ReceiveGlobalMessageAsync(
);
var item = await result.ModelAsync();

Debug.Log(Item); // 受信したメッセージのリスト
var domain = gs2.Inbox.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
);
var future = domain.ReceiveGlobalMessage(
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(Item); // 受信したメッセージのリスト

メッセージについている報酬を受け取る

メッセージについている報酬を受け取る実装例 (Unity)

var domain = gs2.Inbox.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Message(
    messageName: "message-0001" // メッセージ名
);
var result = await domain.ReadAsync(
);
var item = await result.ModelAsync();

Debug.Log(item.MessageId); // メッセージGRN
Debug.Log(item.Name); // メッセージ名
Debug.Log(item.Metadata); // メッセージの内容に相当するメタデータ
Debug.Log(item.IsRead); // 既読状態
Debug.Log(item.ReadAcquireActions); // 開封時に実行する入手アクション
Debug.Log(item.ReceivedAt); // 作成日時
Debug.Log(item.ReadAt); // 最終更新日時
Debug.Log(item.ExpiresAt); // メッセージの有効期限
var domain = gs2.Inbox.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Message(
    messageName: "message-0001" // メッセージ名
);
var future = domain.Read(
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(result.MessageId); // メッセージGRN
Debug.Log(result.Name); // メッセージ名
Debug.Log(result.Metadata); // メッセージの内容に相当するメタデータ
Debug.Log(result.IsRead); // 既読状態
Debug.Log(result.ReadAcquireActions); // 開封時に実行する入手アクション
Debug.Log(result.ReceivedAt); // 作成日時
Debug.Log(result.ReadAt); // 最終更新日時
Debug.Log(result.ExpiresAt); // メッセージの有効期限

メッセージには報酬がついている場合があります。
メッセージを既読にするAPIを実行することで報酬の内容が記載されたスタンプシートを得ます。
このスタンプシートを実行することで報酬を得ます。

サンプルコードの引数'messageName' には受信で得た'message' の 'messageId' ではなく'name' を指定することに注意してください。
また一つのメッセージに複数の報酬を設定する場合はGS2-JobQueueと連携する必要があります。

GS2-Inventory

サービス概要

所持品の管理機能を提供します。

アイテムを入れるカバンのような存在をインベントリと呼びます。
インベントリにはキャパシティを設定でき、キャパシティは後で拡張することができます。
課金通貨を使ったり、クエストの報酬でキャパシティを拡張することができます。

アイテムには有効期限を設定できます。
有効期限の切れたアイテムは使用できなくなり、アイテムを使用する際には明示的に有効期限を指定しなければ有効期限の近いアイテムから消費されます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一プロパティに対する所持数量の増減処理は1秒間に3回までにおさえてください。
異なるプロパティでもインベントリにとって初めて入手する、インベントリのアイテムを使い切る操作の場合は、インベントリ内の操作で1秒間に3回までにおさえてください。
異なるプロパティに対する所持数量の増減であればこの制限はありません。

基本的な使い方

GS2 では所持品を管理するための仕組みとして GS2-Inventory というサービスを提供しています。
所持品と見なせるゲーム内要素にはさまざまな種類がありますが、GS2-Inventory はそれら全てを取り扱えるよう設計しています。
たとえば、以下のようなものです。

GS2-Inventory は所持数量のみを管理し、使用した際の効果の実装には GS2-Exchange などを用いてリソースの交換設計(たとえば、スタミナドリンクを対価としてスタミナ値を回復する報酬を得るというような)を行うことになります。

インベントリ・アイテムセット

インベントリの取得 実装例 (Unity)

var domain = gs2.Inventory.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Inventory(
    inventoryName: "inventory-0001"
);
var item = await domain.ModelAsync();

Debug.Log(item.InventoryId); // インベントリ
Debug.Log(item.InventoryName); // インベントリモデル名
Debug.Log(item.CurrentInventoryCapacityUsage); // 現在のインベントリのキャパシティ使用量
Debug.Log(item.CurrentInventoryMaxCapacity); // 現在のインベントリの最大キャパシティ
var domain = gs2.Inventory.Namespace(
    namespaceName: "namespace-0001"
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Inventory(
    inventoryName: "inventory-0001"
);
var future = domain.Model();
yield return future;
var item = future.Result;

Debug.Log(item.InventoryId); // インベントリ
Debug.Log(item.InventoryName); // インベントリモデル名
Debug.Log(item.CurrentInventoryCapacityUsage); // 現在のインベントリのキャパシティ使用量
Debug.Log(item.CurrentInventoryMaxCapacity); // 現在のインベントリの最大キャパシティ

GS2-Inventory では所持品を管理する単位として、 アイテムセットインベントリ という概念があります。 アイテムセット は、「スタミナドリンク×1個」や「傷薬×30個」といった、アイテムの種類とその個数の組み合わせです。
また、 インベントリ はアイテムセットを格納する鞄のようなものです。
実際にゲームで使用する際には、キャラクター・消費財・通貨 など、UIで一覧表示させたいカテゴリごとにインベントリを分割して利用するのが効果的です。

所持しているアイテムの取得 実装例 (Unity)

var domain = gs2.Inventory.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Inventory(
    inventoryName: "item" // インベントリの種類名
);
var items = await domain.ItemSetsAsync(
).ToListAsync();

Debug.Log(items); // 有効期限ごとのアイテム所持数量のリスト
var domain = gs2.Inventory.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Inventory(
    inventoryName: "item" // インベントリの種類名
);
var it = domain.ItemSets(
);
List<EzItemSet> items = new List<EzItemSet>();
while (it.HasNext())
{
    yield return it.Next();
    if (it.Error != null)
    {
        onError.Invoke(it.Error, null);
        break;
    }
    if (it.Current != null)
    {
        items.Add(it.Current);
    }
    else
    {
        break;
    }
}

Debug.Log(items); // 有効期限ごとのアイテム所持数量のリスト

所持品枠

インベントリは所持品枠を持ち、アイテムセット1個につき1個の所持品枠を占有します。
所持品枠の数は有限であり、これを超えてアイテムセットを所持することはできません。
所持品枠の数はゲームプレイヤーごとに拡張できるようになっており、課金通貨やリソースを使用して拡張できるよう設計できます。

同一アイテムの複数所持

GS2-Inventory では、アイテムの種類ごとに、1つのアイテムセットに格納可能な最大個数を設定できます。
キャラクターや装備品のように、一つ一つを区別する必要があるアイテムではこの最大個数を 1 に設定します。
一方、消費物や強化素材のように、たくさん所持することが前提となっているアイテムでは 99 や 999 などに設定します。

また、アイテムの種類ごとに、そのアイテムのアイテムセットを複数作成できるかを設定することができます。
この設定を有効にすることで、複数の所持品枠を占有することで、1アイテムセット当たりの最大個数を超えてアイテムを所持できるようになります。

アイテムの種類の特徴に応じて、たとえば以下のような設定が考えられます。

アイテムの種類 1つのアイテムセットに格納可能な最大個数 同一種類のアイテムセットを複数作成できるか
キャラクターや装備品など 1 OK
消費物や強化素材 99 OK
ゲーム内通貨 999999999 NG

有効期限

所持品には有効期限を設定できます。有効期限の切れた所持品は勝手に削除されます。 イベント期間中にのみ使用できるアイテムログインボーナスとして当日のみ使用できるスタミナドリンクを配布 というような使い方ができます。

種類が同じでも、有効期限の異なるアイテムは別のアイテムセットが作成されます。
その際、同一種類で複数のアイテムセットの作成ができないように設定されたアイテムの場合は入手ができない可能性があります。

インベントリモデル・アイテムモデルの登録

インベントリモデル・アイテムモデルは JSON 形式のマスターデータとして登録します。

マスターデータは、マネージメントコンソールで要素ごとにデータを登録し、その結果をエクスポートすることで用意することもできます。
エクスポートしたマスターデータを明示的に登録するまで GS2-Inventory の挙動には反映されないことに注意してください。

アイテムの入手

アイテムセットに入りきらなかったり、インベントリの所持品枠が足りなかったりして全量を入手できなかった場合は、入りきる分だけがインベントリに格納され、あふれた個数が応答されます。
また、あふれたアイテムは、ネームスペースに設定したスクリプトや、 GS2-Distributor を利用することで、プレゼントボックスへ転送することができます。

必ず新しいアイテムセットを作るように指定した場合、新しいアイテムセットがインベントリに入りきらなければ、既存のアイテムセットに空きがあっても、あふれたものとして扱われます。

アイテムを格納するアイテムセットを指定した場合、そのアイテムセットに入りきらなければ、ほかのアイテムセットやインベントリに空きがあっても、あふれたものとして扱われます。
また、指定したアイテムセットの内容と、入手しようとしたアイテムの種類や期限が一致しない場合はエラーになります。

いずれの指定もない場合、アイテムは、インベントリの所持枠の占有がもっともすくなくなるように格納されます。

アイテムの入手とセキュリティ

アイテムの入手はセンシティブな操作です。
アイテムの入手に関してクライアントから任意の操作を受け付けることは、不正にアイテムを増殖させ、ゲーム性を大きく損なうことになりかねません。

そこで、GS2 ではスタンプシートの報酬としてしかアイテムの入手を行えないようにしています。
スタンプシートの報酬の内容はサーバに定義を持ち、また報酬を得る前に対価の支払いやゲーム内目標の達成をおこなったことをスタンプシートの仕組みが保証するため、アイテムの不正な増殖を防げます。
したがって、アイテムの入手には、ゲーム内ストアでの購入や、クエストの報酬として入手するような報酬設計が必要です。

アイテムの消費

GS2の思想としては、スタンプシートの対価としてアイテムの消費は行われるべきです。
しかし、ローカルリソースに対して報酬を与えられるようにGS2では消費に関してはAPIを用意しています。

アイテム消費 実装例 (Unity)

var domain = gs2.Inventory.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Inventory(
    inventoryName: "inventory-0001" // インベントリの名前
).ItemSet(
    itemName: "item-0001", // アイテムモデルの名前
    itemSetName: null // アイテムセットを識別する名前
);
var result = await domain.ConsumeAsync(
    consumeCount: 1L // 消費する量
);
var items = await result.ModelAsync();

Debug.Log(items); // 消費後の有効期限ごとのアイテム所持数量のリスト
Debug.Log(items[0].ItemSetId); // インベントリモデル名
Debug.Log(items[0].Name); // アイテムモデルの種類名
Debug.Log(items[0].InventoryName); // インベントリモデル名
Debug.Log(items[0].ItemName); // アイテムモデルの名前
Debug.Log(items[0].Count); // 所持数量
Debug.Log(items[0].SortValue); // 表示順番
Debug.Log(items[0].ExpiresAt); // 有効期限
var domain = gs2.Inventory.Namespace(
    namespaceName: "namespace-0001"
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Inventory(
    inventoryName: "inventory-0001"
).ItemSet(
    itemName: "item-0001",
    itemSetName: null
);
var future = domain.Consume(
    consumeCount: 1L // 消費する量
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(result); // 消費後の有効期限ごとのアイテム所持数量のリスト
Debug.Log(result[0].ItemSetId); // インベントリモデル名
Debug.Log(result[0].Name); // アイテムモデルの種類名
Debug.Log(result[0].InventoryName); // インベントリモデル名
Debug.Log(result[0].ItemName); // アイテムモデルの名前
Debug.Log(result[0].Count); // 所持数量
Debug.Log(result[0].SortValue); // 表示順番
Debug.Log(result[0].ExpiresAt); // 有効期限

アイテムを消費するアイテムセットを指定した場合、指定したアイテムセットに格納された個数で足りなければ、ほかのアイテムセットに余分なアイテムがあったとしても、個数不足として扱われます。

アイテムを消費するアイテムセットを指定しなかった場合、複数のアイテムセットをまたいでアイテムを消費することができます。
またその際、期限が早いアイテムセットから優先的に使用されます。

GS2-JobQueue

サービス概要

ジョブキュー機能を提供するサービスです。

ジョブキューはFIFOを保証したキューで、結果整合処理に使用できます。
結果整合処理とはAPIが応答した時点では処理の完了が保証されず、しばらくの時間の経過をもって”結果的に意図した状態になる”仕組みです。

GS2 では報酬付与の多くの部分を結果整合で処理します。
これは、報酬を付与する際に報酬を管理するサーバーがダウンしていると、芋づる式にいろいろなサービスが利用できなくなることを避けるためです。
報酬付与の処理をジョブキューに登録し、ジョブを実行する際に対象のサーバーが利用できない状態だったとしても、ジョブのリトライで結果整合を実現します。
リトライ回数には最大値を指定でき、最大値に達しても成功しなかった場合はデッドレタージョブという失敗したジョブが蓄積するモデルに格納されます。
デッドレタージョブを使って、処理の失敗したジョブに関して、個別対応を行えるようになっています。

GS2-JobQueue のジョブは勝手には実行されず、明示的に実行APIを呼び出す必要があります。
これにはジョブの戻り値をクライアント側で把握できるようにする目的があります。
例えば、GS2-Quest上でクエストをクリアしたあと、ジョブキューに報酬を付与するジョブが登録され、
クライアントはジョブキューを終端まで実行することでクエストによって得られた報酬付与処理を明示的に実行することができます。
(結果整合の遅延処理の完了を待つことが可能) なおかつ、そのジョブの戻り値を参照することで得られた報酬をUIに反映することができます。

ジョブの登録・実行は1秒間に3回までしか行なえません。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一ユーザに対するキューに対するジョブ登録は1秒間に3回までにおさえてください。
1回のジョブ登録で最大10個までジョブを登録できますので工夫をしてください。
ジョブの実行は1秒間に1回におさえてください。
異なるユーザに対する処理であればこの制限はありません。

基本的な使い方

1回の処理で複数の報酬が発生する場合に使用することになります。
例えば、複数のアイテムをセット売りする場合や10連ガチャなどがあります。

使い方は2ステップです

  1. ネームスペースの設定
  2. ジョブの実行

ネームスペースの設定

まずジョブをためるための場所、GS2-JobQueueのネームスペースを作ります。
次にジョブが発生する元のサービスのネームスペースからジョブの送り先になるGS2-JobQueueのネームスペースを指定します。
例えばGS2-Exchangeのネームスペース設定があります。
ジョブの送り元のネームスペースで送り先のGS2-JobQueueのネームスペースを指定するため、
GS2-JobQueueのネームスペースを複数作成し送り元別にジョブを管理することもできます。

またジョブが追加されたときにプッシュ通知を行う設定もできます。こちらはGS2-Gatewayを参照ください。

ジョブの実行

ジョブを実行する実装例 (Unity Legacy)

AsyncResult<EzRunResult> asyncResult = null;
var current = gs2.JobQueue.Run(
    callback: r => { asyncResult = r; },
    session: session, // GameSessionオブジェクト(アクセストークン)
    namespaceName: "namespace-0001" // ネームスペース名
);

yield return current;
if (asyncResult.Error != null)
{
    OnError(asyncResult.Error);
    yield break;
}

var result = asyncResult.Result;
var item = result.Item;
var result = result.Result;
var isLastJob = result.IsLastJob;

Debug.Log(item.JobId); // ジョブGRN
Debug.Log(item.ScriptId); // ジョブの実行に使用するスクリプトのGRN
Debug.Log(item.Args); // 引数
Debug.Log(item.CurrentRetryCount); // 現在のリトライ回数
Debug.Log(item.MaxTryCount); // 最大試行回数
Debug.Log(result.TryNumber); // 試行回数
Debug.Log(result.StatusCode); // ステータスコード
Debug.Log(result.Result); // レスポンスの内容
Debug.Log(result.TryAt); // 実行日時
Debug.Log(isLastJob); // ジョブキューの終端か

ジョブを実行するにはrunを使います。注意点としてrun1回ではジョブ1回の実行しか行いません。
返り値の isLastJob がfalseの場合はまだジョブが残っていますので、trueになるまで繰り返し実行してください。

GS2-Key

サービス概要

暗号鍵を管理するサービス

GS2内の機密データや、署名作成に使用する暗号鍵を作成します。
暗号鍵のデータはGS2の外に取り出すことができないため、安全に処理を行えます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

単独で使用するものではなく、各サービスから呼び出して使用するものになります。

GS2-Limit

サービス概要

回数制限処理を実現するためのサービス

このサービスは単独で使うのではなく 他のサービスで回数制限機能を実現します。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一カウンターに対する操作は1秒間に3回までにおさえてください。
異なるユーザや、同一ユーザでも異なるカウンターに対する処理であればこの制限はありません。

基本的な使い方

使い方は3ステップです

  1. 回数制限マスターを作成する
  2. カウンターを増加する
  3. カウンターの値を取得する

回数制限マスターを作成する

マネージメントコンソールから作成できます。
カウンターの名前と定期的にリセットを行うかを設定することができます。

カウンターを増加する

カウンターを増加する方法は3つあります。

クライアントからAPIを呼び出す

クライアントからカウントアップする 実装例 (Unity)

var domain = gs2.Limit.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Counter(
    limitName: "daily", // 回数制限の種類の名前
    counterName: "counter1" // カウンターの名前
);
var result = await domain.CountUpAsync(
    countUpValue: 1, // カウントアップする量(オプション)
    maxValue: 100 // カウントアップを許容する最大値(オプション)
);
var item = await result.ModelAsync();

Debug.Log(item.CounterId); // カウンターGRN
Debug.Log(item.Name); // カウンターの名前
Debug.Log(item.Count); // カウント値
Debug.Log(item.CreatedAt); // 作成日時
Debug.Log(item.UpdatedAt); // 最終更新日時
var domain = gs2.Limit.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Counter(
    limitName: "daily", // 回数制限の種類の名前
    counterName: "counter1" // カウンターの名前
);
var future = domain.CountUp(
    countUpValue: 1, // カウントアップする量(オプション)
    maxValue: 100 // カウントアップを許容する最大値(オプション)
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(result.CounterId); // カウンターGRN
Debug.Log(result.Name); // カウンターの名前
Debug.Log(result.Count); // カウント値
Debug.Log(result.CreatedAt); // 作成日時
Debug.Log(result.UpdatedAt); // 最終更新日時

該当するアクションを起こしたときにクライアントからGS2にカウントアップを要求する方法です。
注意する点として、カウントアップしたほうが有利な場合にユーザーが不正にAPI呼び出す可能性があることです。
これを回避したい場合はGS2-Identifierでカウントアップを呼び出せないようにポリシーを設定します。

GS2イベント発生時の報酬として設定する

たとえばクエストモデルマスターのクエストクリア時の報酬としてカウントアップを設定できます。
失敗時の報酬にも設定してクエスト成否にかかわらずカウントアップすることもできます。
他にも商品購入時の報酬に設定して購入回数をカウントアップする使い方もできます。

サーバーからAPIを呼び出す

サーバーからカウントアップする例 (python)

from gs2_limit_client.request import CountUpByUserIdRequest
from gs2_limit_client.result import CountUpByUserIdResult

result = client.count_up_by_user_id(
    CountUpByUserIdRequest()\
        .with_namespace_name(namespace_name)\
        .with_limit_name(limit_name)\
        .with_counter_name(counter_name)\
        .with_user_id(user_id)\
        .with_count_up_value(count_up_value)\
        .with_max_value(max_value)
)

自作のサーバーから呼び出します。クライアントからAPIを呼び出すより安全です。

カウンターの値を取得する

カウンターの値を取得する 実装例 (Unity)

var domain = gs2.Limit.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Counter(
    limitName: "daily", // 回数制限の種類の名前
    counterName: "counter1" // カウンターの名前
);
var item = await domain.ModelAsync();

Debug.Log(item.CounterId); // カウンター
Debug.Log(item.Name); // カウンターの名前
Debug.Log(item.Count); // カウント値
Debug.Log(item.CreatedAt); // 作成日時
Debug.Log(item.UpdatedAt); // 最終更新日時
var domain = gs2.Limit.Namespace(
    namespaceName: "namespace-0001"
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Counter(
    limitName: "daily",
    counterName: "counter1"
);
var future = domain.Model();
yield return future;
var item = future.Result;

Debug.Log(item.CounterId); // カウンター
Debug.Log(item.Name); // カウンターの名前
Debug.Log(item.Count); // カウント値
Debug.Log(item.CreatedAt); // 作成日時
Debug.Log(item.UpdatedAt); // 最終更新日時

カウンターの現在値を確認します。この値によってクエスト挑戦を制限したり購入できる商品を変化させて下さい。

GS2-Lock

サービス概要

排他処理を実現するためのサービスです。

GS2内のAPIによるリソースの操作は、そのままでは排他されません。
スタンプシートによってリソースの増減を行うときは、自動的にこのサービスが利用され、対象リソースのIDをキーとしてロックを取得します。
スタンプシートを経由せずにリソースの増減を行う場合、明示的に GS2-Lock を使用してロック操作を行う必要があります。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一ミューテックスに対する操作は1秒間に3回までにおさえてください。
異なるミューテックスに対する処理であればこの制限はありません。

基本的な使い方

使い方は3ステップです。

  1. ネームスペースを作成する
  2. ロックする
  3. アンロックする

ネームスペースを作成する

マネージメントコンソールからネームスペースを作成します。特に設定の必要はありません。

ロックする

クライアントからロックする 実装例 (Unity)

var domain = gs2.Lock.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Mutex(
    propertyId: "property-0001" // プロパティID
);
var result = await domain.LockAsync(
    transactionId: "transaction-0001", // ロックを取得するトランザクションID
    ttl: 100000L // ロックを取得する期限(秒)
);
var item = await result.ModelAsync();

Debug.Log(item.MutexId); // ミューテックスGRN
Debug.Log(item.PropertyId); // プロパティID
Debug.Log(item.TransactionId); // ロックを取得したトランザクションID
var domain = gs2.Lock.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Mutex(
    propertyId: "property-0001" // プロパティID
);
var future = domain.Lock(
    transactionId: "transaction-0001", // ロックを取得するトランザクションID
    ttl: 100000L // ロックを取得する期限(秒)
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(result.MutexId); // ミューテックスGRN
Debug.Log(result.PropertyId); // プロパティID
Debug.Log(result.TransactionId); // ロックを取得したトランザクションID

サーバーからロックする例 (python)

from gs2_lock_client.request import LockByUserIdRequest
from gs2_lock_client.result import LockByUserIdResult

result = client.lock_by_user_id(
    LockByUserIdRequest()\
        .with_namespace_name(namespace_name)\
        .with_property_id(property_id)\
        .with_user_id(user_id)\
        .with_transaction_id(transaction_id)\
        .with_ttl(ttl)
)

クライアント、サーバーからそれぞれロックができます。
直接プロパティを編集する場合に用いるため、どちらかというとサーバーから使うものになります。
クライアントから実行する場合、ロックしてアンロックする前にクライアントが落ちるということがあれば、
プロパティがずっとロックされたままになりバグの原因になります。

アンロックする

クライアントからアンロックする 実装例 (Unity)

var domain = gs2.Lock.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Mutex(
    propertyId: "property-0001" // プロパティID
);
var result = await domain.UnlockAsync(
    transactionId: "transaction-0001" // ロックを取得したトランザクションID
);
var item = await result.ModelAsync();

Debug.Log(item.MutexId); // ミューテックスGRN
Debug.Log(item.PropertyId); // プロパティID
Debug.Log(item.TransactionId); // ロックを取得したトランザクションID
var domain = gs2.Lock.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Mutex(
    propertyId: "property-0001" // プロパティID
);
var future = domain.Unlock(
    transactionId: "transaction-0001" // ロックを取得したトランザクションID
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    throw future2.Error;
}
var result = future2.Result;

Debug.Log(result.MutexId); // ミューテックスGRN
Debug.Log(result.PropertyId); // プロパティID
Debug.Log(result.TransactionId); // ロックを取得したトランザクションID

サーバーからアンロックする例 (python)


from gs2_lock_client.request import LockByUserIdRequest
from gs2_lock_client.result import LockByUserIdResult

result = client.lock_by_user_id(
    LockByUserIdRequest()\
        .with_namespace_name(namespace_name)\
        .with_property_id(property_id)\
        .with_user_id(user_id)\
        .with_transaction_id(transaction_id)\
        .with_ttl(ttl)
)

GS2-Log

サービス概要

ログの集約・集計を実現するためのサービスです。

GS2に対するアクセスログや、スタンプシートによるリソース交換のログを検索・集計できるようになります。
ログの保管先は3種類用意されています。

どのマイクロサービスのログを集約するかを設定するには各サービスのネームスペースの設定値に GS2-Logs のネームスペースを設定しておこないます。
GS2でログを保管 / Google BigQuery でログを保管 を使用する場合は、ログの保存期間を設定できます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

使い方は2ステップです。

  1. ネームスペースを作成する
  2. ログを取得したいサービスのネームスペースからログの設定をする

ネームスペースを作成する

マネージメントコンソールで作成します。ログの書き出し方法(保管方法)の指定が必要です。

ログ出力を取得したいサービスのネームスペース情報でログ出力の設定をする

例えば、GS2-Accountにログ出力を設定する場合、
マネージメントコンソールのネームスペース情報からネームスペース更新で、 ログの出力設定でGS2-Logのネームスペースを選択すると、GS2-AccountのAPI呼び出しのログが保存されるようになります。
GS2-Accountのネームスペースが複数の場合はそれぞれ設定が必要になります。

GS2-Logのネームスペースを複数作成しておけば、各サービスの各ネームスペースごとにログの送り先を別々にすることができます。
単純にサービス別にログを分ける他、複数人で管理する場合は担当者ごとにログ出力を分ける等考えられます。

GS2-Lottery

サービス概要

抽選処理を実現するサービスです。

抽選処理は排出確率テーブルを元に行われます。 排出確率テーブルには、景品ごとの排出される重みを設定します。
例えば、景品Aに3を設定し、景品Bに1を設定した場合、景品Aの排出確率は75%、景品Bは排出確率は25%となります。

また、景品として他の排出確率テーブルを設定することもできます。
あらかじめSSR、SR、Rなどの同一レアリティごとに排出確率テーブルを用意しておき、それらを景品とした排出確率テーブルをつくることで、レアリティごとの排出確率を容易に設定できます。

抽選処理には2つのモードがあり、抽選モデルで設定できます。
1つ目は毎回変わらない確率で一様に抽選処理を行うモード、2つ目はボックスガチャモードです。
ボックスガチャとは、抽選処理を行うごとに景品を箱から取り出していき、最初に箱に入れた数と同回数抽選を行うと、必ずすべての景品が手に入るという方式です。
モードにボックスガチャを設定した場合は、排出テーブルに設定した重みの数だけ箱に景品が入った状態から抽選が始まります。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

使い方は2ステップです。

  1. 排出確立テーブルを作成する
  2. 抽選処理を実行する

マスターを作成する

まず排出確率テーブルマスターを作成します。各景品の出やすさを定義します。

排出確立は'重み' で設定します。例えば景品Aが重み1、景品Bが重み3の場合は景品Aが1/(1+3)=25%、景品Bが75%で排出されます。
また景品には排出確率テーブルを設定でき、木構造にできます。

例えば排出確率テーブル「レア」にレアアイテム1~2を重み1、排出確率テーブル「ノーマル」にノーマルアイテム1~7を重み1に設定します。
そのあと排出確率テーブル「ガチャ」に排出確率テーブル「レア」を重み1、排出確率テーブル「ノーマル」を重み7に設定します。
この場合、レアアイテム1が出る確率は(1/8)(1/2)=6.25%、ノーマルアイテム1が出る確率は(7/8)(1/7)=12.5%になります。

次に抽選の種類マスターを作成します。 

まず抽選モードを設定します。通常抽選は常に定められた確率で排出されます。BOX抽選は一度排出された景品は排出されなくなります。

次に抽選方法を設定します。先ほど作成した排出確率テーブルを使う場合は静的な抽選テーブルを選択します。その後、作成した排出確率テーブルを指定します。

抽選方法でGS2-Scriptによる動的抽選テーブルを選択した場合は、GS2-Scriptを利用して状態によって確率を変化させることができます。

抽選処理を実行する

ユーザー用のセキュリティポリシーであるApplicationAccess ではクライアントから直接抽選処理を行うAPIを呼び出すことはできません。
これは不正に抽選処理を実行するチート行為を阻止するためです。

クライアントから抽選処理を要求する場合は、他のサービスと連携することを想定しています。
たとえばマネージメントコンソールでGS2-Exchangeの交換レートマスターを開いてみてください。
入手アクションリスト⇒スタンプシートで実行するアクションの種類のリストの中にLottery: 抽選を実行があります。
GS2-Exchangeではリソースの交換を実現する機能です。消費アクションにゲーム内通貨やガチャチケットなどを指定すれば、ゲーム内通貨やチケットを消費してガチャを回す機能を実現できます。
他にもGS2-Questと連携してステージクリア報酬のアイテムを抽選するという使い方もあります。

GS2-Matchmaking

サービス概要

対戦相手や協力相手を実現するサービスです。

マッチメイキングの流れは以下です。

ホストがギャザリングの作成

参加者が条件にみあうギャザリングを検索

規定人数が集まったらマッチメイキング完了

GS2-Matchmaking はこの流れのうち 参加者が条件にみあうギャザリングを検索 に関してできるだけ多くのニーズをみたせるよう設計されています。
ここでは、ユースケースを交えつつ、その設計例を解説します。

ゲーム内には、カジュアルプレイヤー向けと、コアゲーマー向けの2種類のゲームモードがあります。
これらのプレイヤーを混ぜることなくマッチメイキング処理を行いたいとします。

ギャザリングを作成する際にも、ギャザリングを検索する際にも、最大5つの属性値を指定できます。
ギャザリングを作成する際には募集するプレイヤーが持つ属性値の範囲を指定し、
ギャザリングを検索する際には、自身の属性値の値を指定して処理依頼をします。

つまり、ホストがギャザリングを作成する際に 属性値1 に募集するゲームモードを指定し、
検索する側も自分が参加したいゲームモードを 属性値1 に指定することで、希望するゲームモードが同じプレイヤー同士がマッチメイキングされます。

ホストはギャザリングを作成する際に 属性値2 に募集するレベルの最小値と最大値を指定します。
検索する側は自分のレベルを 属性値2 に指定することで、同じレベル帯のプレイヤー同士がマッチメイキングされます。

複数の属性値を指定した場合はすべての属性値の範囲をみたす相手を探すよう処理されます。

最初は狭い範囲で募集し、なるべく同じレベルのプレイヤー同士でマッチメイキングするが、なかなか対戦相手が見つからない場合、募集するレベル帯を広げて募集したいとします。
ギャザリングを作成しておよそ1分ごとに発火する GS2-Script イベントがあります。
このスクリプトにて、ギャザリングの属性値の範囲を緩和することで、徐々に希望する条件を緩和する処理を実装できます。

ギャザリングを作成するときのオプションとして、参加を許可するユーザIDリストを指定できます。
これを使い、フレンドのユーザIDリストを指定することで、参加者を限定することができます。

ギャザリングの作成時・検索時にユーザIDのブラックリストを指定できます。
検索時に指定したブラックリストは、条件に一致するギャザリングを発見し、参加したギャザリングのブラックリストに加えられます。
検索する際に自身がブラックリストに入っている場合はマッチメイキングの対象から除外されます。

ゲームによっては盾役1人、回復役1人、攻撃役2人でマッチメイキングを行いたいことがあります。
このときに使用できるのが ロール属性 です。

盾役として tank を、回復役として healer を、攻撃役として attacker というロールを想定します。
ギャザリングを作成するときに各ロールの募集人数を設定します。
ギャザリングの検索時に自分のロールに設定することで、ロールに関して空きのあるギャザリングだけが参加可能なギャザリングとして処理されます。

募集ロールにはエイリアスを設定することもできます。
例えば、 tank を更に具体的なロールで表現し、 paladinwarriordark_knight のいずれかとしてギャザリングの検索を行うとします。
ギャザリングを作成するときに、 tank のエイリアスに paladindark_knight を指定した場合、 warrior は参加できないギャザリングが作成できます。

マッチメイキング完了後に、1名が離脱し 1名だけ補充したいときに使用します。
このときに使用するのがパーティトークンです。パーティトークンを発行するにはパーティを編成するプレイヤーのプレイヤートークンです。
プレイヤートークンはプレイヤー自身が自分の代わりにパーティの代表者がマッチメイキングを行うことを許可するトークンで、3分間の有効期限があります。
パーティメンバーはそれぞれ、プレイヤートークンを発行するAPIに依頼をしてプレイヤートークンを入手し、パーティの代表者に送信します。
パーティの代表者はパーティメンバーからプレイヤートークンを受け取り、3分以内にパーティトークン発行APIに送信することで、パーティトークンを入手できます。
パーティトークンには10分の有効期限があり、10分以内にパーティトークンを使用してギャザリング作成処理または、ギャザリング検索処理を呼び出します。
こうすることで、パーティメンバー全員が参加した状態のギャザリングを作成、またはパーティメンバー全員が参加できるギャザリングに参加します。

4 vs 4 で対戦するゲームで、事前に編成したパーティを敵味方に分散することなく対戦したいことがあります。
たとえば、事前に3人で編成したパーティと、事前に4人で編成したパーティ、そして1人の野良プレイヤーをマッチメイキングして、 事前に3人で編成したパーティ + 1人の野良プレイヤー vs 事前に4人で編成したパーティ という対戦を実現したいとします。
このようなケースでは、まずは4人のマッチメイキングを実行します。
すると、3人で編成したパーティ + 1人の野良プレイヤー事前に4人で編成したパーティ の2つのギャザリングができます。
その後、それぞれのパーティの代表者が 定員2人のマッチメイキングをおこない、パーティ同士がマッチメイキングされます。
こうすることで 事前に3人で編成したパーティ + 1人の野良プレイヤー vs 事前に4人で編成したパーティ を実現できます。

マッチメイキングが完了していない状態でプレイヤー間のやりとりを必要とすることがあります。
GS2-Matchmaking では、ギャザリングの作成時にプレイヤー間の通信をする手段として2種類の方法を提供します。

募集が終わっていない状態でも、プレイヤー間のでメタデータを低頻度で交換したい場合は前者を、
募集しながらNPCを交えたり人数が不足した状態でゲームを遊ばせたい場合は後者を使用します。

低頻度・高頻度の判断基準として使用できるのが GS2-Chat の持つ制限です。
GS2-Chat は1つのチャットルームに対して秒間3回しか発言ができません。この頻度を超える場合は GS2-Realtime を使用してください。

マッチメイキングが完了したときに GS2-Gateway を使用したゲーム内プッシュ通知GS2-JobQueue によるジョブ登録 でプレイヤーにマッチメイキングの完了を知らせることができます。
また、GS2-Script を使用して任意のスクリプトを実行することもできます。
GS2-Realtime を使用してマッチメイキング後の対戦・協力プレイを実現する場合は、マッチメイキング完了後に実行される GS2-Script で
GS2-Realtime のギャザリングを作成し、そのギャザリングのIPアドレス・ポートの情報を GS2-Gateway を使用したゲーム内プッシュ通知GS2-JobQueue によるジョブ登録 を使用して通知することで、ゲームサーバに誘導できます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

マルチプレイヤーゲームを実現するには 対戦相手や協力プレイの相手を見つける 仕組みが必要です。
GS2 ではこの仕組みを実現するためのサービスとして、GS2-Matchmaking を提供しています。

一般的にマッチメイキングのアプローチは2種類存在します。

GS2 では、後者の自動マッチメイキングのみを提供しています。
手動マッチメイキングは、よりゲームプレイヤーのニーズに合ったルームを見つけられる利点はあるものの 一覧取得参加 のアクションの間に 人間による選別 というタイムラグが発生するため 参加しようとした部屋が満室になっていた という状態に陥りやすく、UX設計が非常に難しい一方で、手動マッチメイキングを行う必要があるほど厳密に対戦相手を探したいユースケースは多くないためです。

ギャザリング

GS2-Matchmaking では、マッチメイキング条件の見合うゲームプレイヤー同士を引き合わせて ギャザリング を構築します。
ギャザリングはルームのようなものと考えて問題ありません。
ギャザリングはホストとなるプレイヤーが最初に作成し、ゲストは条件の見合うギャザリングへ割り当てられます。
ギャザリング内のゲームプレイヤーが規定人数に達すると、マッチメイキングが成立したとして通知の送信やスクリプトの実行が行われ、ギャザリングは消去されます。

条件の設定

属性値

マッチメイキング条件には属性値を最大5つ指定できます。
属性値は整数値で、最小値と最大値を指定できます。対戦相手を探す際にはすべての属性値が希望する範囲に含まれるゲームプレイヤーを探します。

ロール

役割に応じたマッチメイキングを実現するときに使用します。
役割とは 盾役 回復役 攻撃役 のような分類で、マッチメイキング条件として 盾役を1人 回復役を1人 攻撃役を2人 といった形で指定できます。

ロールのエイリアス

ロールにはエイリアス(別名)を設定できます。
たとえば、役割としては 盾役 ではあるものの、ゲーム内の職業としては 戦士ナイト があったとします。
どうしても対戦相手として参加してきてほしい職業は 戦士 であってほしい。という場合に使用できるのがエイリアスです。

自分の役割として直接 盾役 と申告するのではなく 戦士 ナイト という職業レベルで申告するようにしておき、一般的なマッチメイキングでは 盾役 のエイリアスには 戦士 ナイト を設定しておきます。
すると、 戦士 でも ナイト でも 盾役 として参加できるギャザリングが出来上がります。
どうしても 戦士 しかマッチメイキングしてほしくない場合は、エイリアスに 戦士 のみを含めます。
すると ナイト盾役 として認識されなくなりますので、マッチメイキング対象から外されます。

マッチメイキング条件を緩めていく

条件が厳密であれば厳密であるほど良質なマッチメイキングとなります。
たとえば、レーティングをマッチメイキング条件に設定する場合、まったく同じレーティング値であればあるほど良質なマッチメイキングといえるでしょう。
一方で、良質なマッチメイキングを実現するにはたくさんのゲームプレイヤーが必要です。なぜならそんなに都合よくスキルが拮抗するゲームプレイヤーはいないからです。

だからといって、最初から良質なマッチメイキングをあきらめて、マッチメイキング条件として使用するレーティングの幅を大きくとる必要はありません。

GS2-Matchmaking ではギャザリング開始から1分ごとにスクリプトを実行できるようにしています。
この機能を利用すれば待ち時間に応じてマッチメイキングの条件を緩和していくことができます。
これによって、相手がいる場合は良質なマッチメイキングを、そうでない場合はとりあえず対戦相手を見つけるよう設計することができます。

フレンドのみ参加可能なギャザリング

ギャザリングには参加可能なユーザIDを列挙することで、参加者に制限をかけることができます。
この機能を利用することで、フレンドのみが参加可能なマッチメイキングを実現することができます。

ブラックリスト

GS2-Matchmaking にはブラックリストの機能があります。
マッチメイキングの条件が見合っていても、自分のユーザIDがブラックリストに含まれているゲームプレイヤーは、そのギャザリングには参加できなくなります。
ブラックリストの情報は各プレイヤーが持てるようになっており、ギャザリングに参加している全プレイヤーのブラックリストがマージされたものがギャザリングのブラックリストとして使用されます。

パーティトークンとプレイヤートークン

1つのギャザリングへ複数のプレイヤーが一緒に参加するために、 パーティトークン が使用できます。
パーティトークンを利用することで、複数のプレイヤーが一緒にギャザリングを作成したり、一緒に同じギャザリングへ参加したりできます。

パーティトークンは、パーティの代表者が、パーティ内の各プレイヤーから プレイヤートークン を集めて作成します。
プレイヤートークンは3分、パーティートークンは10分の有効期限があります。

追加募集

一度マッチメイキングが完了したものの、何らかの理由で1名が離脱し再度募集を募りたいことがあります。
このような場合、残ったメンバーでパーティトークンを作成し、再度マッチメイキングを行うことで欠員を補充する追加募集を実現することができます。

パーティ同士のマッチメイキング

ゲームの仕様によっては 4vs4 のようなチーム戦を行うことを希望するかもしれません。
このようなケースにおいて、フレンド同士をチームにしたいという仕様もあるでしょう。
GS2-Matchmaking を使ってこのような仕様を実現するには、一旦4人でマッチメイキングを実行してチームを作成します。
この際、フレンド同士のチームはフレンド限定のマッチメイキングで作成します。
その後、各チームの代表者が2人のマッチメイキングを実行して、 4vs4 の対戦相手となるギャザリングを発見するように設計します。

マッチメイキング中のプレイヤー間通信

マッチメイキングが終わっていない状態でもプレイヤー間でメッセージングを行いたいことがあります。
このような時に使用できるのが GS2-Chat / GS2-Realtime 連携です。
この機能を利用すると、 ギャザリング作成時 または マッチメイキング完了時 に GS2-Chat または GS2-Realtime のルームを作成することができます。
今回の場合は ギャザリング作成時 にルームを作成することで、マッチメイキング実行中のメッセージングに使用できます。

GS2-Chat と GS2-Realtime のどちらを使用するべきかを判断するにはいくつかの指標がありますが、マッチメイキング完了後の対戦で GS2-Realtime を利用するのであれば、マッチメイキング中のコミュニケーションにも GS2-Realtime を使うのが実装の手間は少なく済むでしょう。
もう一つの指標は通信間隔です。GS2-Chat は 1つのルームに対するメッセージの送信は秒間3回まで という制約がありますので、これを超える頻度の通信が必要なら GS2-Realtime を使うことになります。
最後の指標はコストです。GS2-Chat は 一般的なAPIリクエスト回数 による課金ですが、GS2-Realtime はルームの維持時間1分当たり+ネットワーク通信量の課金で、APIリクエスト回数には費用は発生しません。

この辺りを材料に自分のゲームにとってどの方法で仕様を実現するのがいいかを検討してみてください。

マッチメイキングの進捗のハンドリング

GS2-Realtime はマッチメイキングの進捗を GS2-Gateway によるプッシュ通知でハンドリングすることができるようになっています。
通知の種類は以下のものが用意されています。

マッチメイキングが完了したときは GS2-Gateway による通知のほかに、GS2-JobQueue を使用したハンドリングと、GS2-Script を使用したハンドリングにも対応しています。

GS2-Realtime との連携機能を使用すると ギャザリングの作成時 または マッチメイキング完了時 にルームの作成リクエストが自動的に発行され、ルームの作成が完了すると GS2-Gateway による通知が届きます。
通知には起動したゲームサーバのIPアドレスおよび待ち受けポート。通信パケットの暗号化に使用する暗号鍵などの情報が含まれており、それらの情報を使用して対戦に引き継げるようになっています。

ギャザリングの作成

ギャザリングの作成 実装例 (Unity)

var domain = gs2.Matchmaking.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
);
var result = await domain.CreateGatheringAsync(
    player: new Gs2.Unity.Gs2Matchmaking.Model.EzPlayer
    {
        Attributes = new List<Gs2.Unity.Gs2Matchmaking.Model.EzAttribute>
        {
            new Gs2.Unity.Gs2Matchmaking.Model.EzAttribute
            {
                Name = "stage",
                Value = 1,
            },
            new Gs2.Unity.Gs2Matchmaking.Model.EzAttribute
            {
                Name = "level",
                Value = 10,
            }
        },
    }, // 自身のプレイヤー情報
    attributeRanges: new Gs2.Unity.Gs2Matchmaking.Model.EzAttributeRange[]
    {
        new Gs2.Unity.Gs2Matchmaking.Model.EzAttributeRange
        {
            Name = "stage",
            Min = 1,
            Max = 1,
        },
        new Gs2.Unity.Gs2Matchmaking.Model.EzAttributeRange
        {
            Name = "level",
            Min = 0,
            Max = 10,
        }
    }, // 募集枠
    capacityOfRoles: new Gs2.Unity.Gs2Matchmaking.Model.EzCapacityOfRole[]
    {
        new Gs2.Unity.Gs2Matchmaking.Model.EzCapacityOfRole
        {
            RoleName = "default",
            Capacity = 4,
        }
    }, // 募集条件(オプション)
    allowUserIds: null, // 参加を許可するユーザIDリスト(オプション)
    expiresAt: null, // ギャザリングの有効期限(オプション)
    expiresAtTimeSpan: null // 有効期限までの時間(オプション)
);
var item = await result.ModelAsync();

Debug.Log(item.GatheringId); // ギャザリングGRN
Debug.Log(item.Name); // ギャザリング名
Debug.Log(item.AttributeRanges); // 募集条件
Debug.Log(item.CapacityOfRoles); // 参加者
Debug.Log(item.AllowUserIds); // 参加を許可するユーザIDリスト
Debug.Log(item.Metadata); // メタデータ
Debug.Log(item.ExpiresAt); // ギャザリングの有効期限
Debug.Log(item.CreatedAt); // 作成日時
Debug.Log(item.UpdatedAt); // 最終更新日時
var domain = gs2.Matchmaking.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
);
var future = domain.CreateGathering(
    player: new Gs2.Unity.Gs2Matchmaking.Model.EzPlayer
    {
        Attributes = new List<Gs2.Unity.Gs2Matchmaking.Model.EzAttribute
        {
            new Gs2.Unity.Gs2Matchmaking.Model.EzAttribute()
            {
                Name = "stage",
                Value = 1,
            },
            new Gs2.Unity.Gs2Matchmaking.Model.EzAttribute()
            {
                Name = "level",
                Value = 10,
            }
        },
    }, // 自身のプレイヤー情報
    attributeRanges: new Gs2.Unity.Gs2Matchmaking.Model.EzAttributeRange[]
    {
        new Gs2.Unity.Gs2Matchmaking.Model.EzAttributeRange()
        {
            Name = "stage",
            Min = 1,
            Max = 1,
        },
        new Gs2.Unity.Gs2Matchmaking.Model.EzAttributeRange()
        {
            Name = "level",
            Min = 0,
            Max = 10,
        }
    }, // 募集枠
    capacityOfRoles: new Gs2.Unity.Gs2Matchmaking.Model.EzCapacityOfRole[]
    {
        new Gs2.Unity.Gs2Matchmaking.Model.EzCapacityOfRole()
        {
            RoleName = "default",
            Capacity = 4,
        }
    }, // 募集条件(オプション)
    allowUserIds: null, // 参加を許可するユーザIDリスト(オプション)
    expiresAt: null, // ギャザリングの有効期限(オプション)
    expiresAtTimeSpan: null // 有効期限までの時間(オプション)
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}

var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}

var result = future2.Result;

Debug.Log(result.GatheringId); // ギャザリングGRN
Debug.Log(result.Name); // ギャザリング名
Debug.Log(result.AttributeRanges); // 募集条件
Debug.Log(result.CapacityOfRoles); // 参加者
Debug.Log(result.AllowUserIds); // 参加を許可するユーザIDリスト
Debug.Log(result.Metadata); // メタデータ
Debug.Log(item.ExpiresAt); // ギャザリングの有効期限
Debug.Log(result.CreatedAt); // 作成日時
Debug.Log(result.UpdatedAt); // 最終更新日時

ギャザリングを作成するためには 募集条件参加可能なユーザIDリスト を設定する必要があります。
募集条件は 属性値募集人数 の2種類があります。

属性値

属性値のモデル構造

パラメータ データ型 必須 説明
name string true 属性名
min integer true ギャザリング参加可能な属性値の最小値
max integer true ギャザリング参加可能な属性値の最大値

最大5つの属性値を指定します。属性値は整数値の範囲で指定できるようになっています。
ゲームプレイヤーがマッチメイキングリクエストを出す際には自分の属性値の値を申告し、属性値の範囲に該当するギャザリングが参加候補のギャザリングとして判定されます。

募集条件

募集条件モデル構造

パラメータ データ型 必須 説明
roleName string true ロール名
roleAliases list[string] false ロール名の別名リスト
capacity integer true 募集人数
participants list[Player] true 参加者のプレイヤー情報リスト

募集人数は文字通り何人のゲームプレイヤーを見つけるかというパラメータです。
募集人数は 役割 ごとに設定できます。
役割 とは 盾役1人 回復役1人 攻撃役2人 というように設定できます。
特にこだわりがない場合は1つの役割だけ指定し、募集人数の値だけ埋めてギャザリングの作成を行うことになります。

マッチメイキングの実行

すでに存在するギャザリングの中で自分が参加可能なギャザリングを見つけて参加を行います。
マッチメイキングAPIは一定時間経過しても参加可能なギャザリングが見つからず、また全てのギャザリングの走査が終わっていない場合は コンテキストトークン を応答します。
コンテキストトークン はマッチメイキング処理を再開するために必要となる情報を文字列化したもので、マッチメイキングAPIの呼び出し時に指定すると、マッチメイキング処理を再開できます。
最後まで処理を実行したにも関わらず参加可能なギャザリングを見つけられなかった時には null を返しますので、その応答をもってギャザリングの作成処理を行うのがいいでしょう。

マッチメイキングリクエスト 実装例 (Unity)

var domain = gs2.Matchmaking.Namespace(
namespaceName: "namespace-0001" // ネームスペース名 ).Me(
gameSession: GameSession // GameSessionオブジェクト(アクセストークン) ); var items = await domain.DoMatchmakingAsync(
new EzPlayer
{
RoleName = "attacker",
} // 自身のプレイヤー情報 ).ToListAsync();

var item = items[0];

Debug.Log(item.GatheringId); // ギャザリングGRN Debug.Log(item.Name); // ギャザリング名 Debug.Log(item.AttributeRanges); // 募集条件 Debug.Log(item.CapacityOfRoles); // 参加者 Debug.Log(item.AllowUserIds); // 参加を許可するユーザIDリスト Debug.Log(item.Metadata); // メタデータ Debug.Log(item.ExpiresAt); // ギャザリングの有効期限 Debug.Log(item.CreatedAt); // 作成日時 Debug.Log(item.UpdatedAt); // 最終更新日時

var domain = gs2.Matchmaking.Namespace(
namespaceName: "namespace-0001" // ネームスペース名 ).Me(
gameSession: GameSession // GameSessionオブジェクト(アクセストークン) ); var it = domain.DoMatchmaking(
new EzPlayer
{
RoleName = "attacker",
} // 自身のプレイヤー情報 ); var items = new List<EzGathering>(); while (it.HasNext()) {
yield return it.Next();
if (it.Error != null)
{
onError.Invoke(it.Error, null);
break;
}
if (it.Current != null)
{
items.Add(it.Current);
}
else
{
break;
} } var item = items[0];

Debug.Log(item.GatheringId); // ギャザリングGRN Debug.Log(item.Name); // ギャザリング名 Debug.Log(item.AttributeRanges); // 募集条件 Debug.Log(item.CapacityOfRoles); // 参加者 Debug.Log(item.AllowUserIds); // 参加を許可するユーザIDリスト Debug.Log(item.Metadata); // メタデータ Debug.Log(item.ExpiresAt); // ギャザリングの有効期限 Debug.Log(item.CreatedAt); // 作成日時 Debug.Log(item.UpdatedAt); // 最終更新日時

マッチメイキングの中断

すでにいずれかのギャザリングに参加した状態で、ギャザリングから離脱したいときに使用します。

マッチメイキングキャンセル 実装例 (Unity)

var domain = gs2.Matchmaking.Namespace(
namespaceName: "namespace-0001" // ネームスペース名 ).Me(
gameSession: GameSession // GameSessionオブジェクト(アクセストークン) ).Gathering(
gatheringName: "gathering-0001" // ギャザリング名 ); var result = await domain.CancelMatchmakingAsync( ); var item = await result.ModelAsync();

Debug.Log(item.GatheringId); // ギャザリングGRN Debug.Log(item.Name); // ギャザリング名 Debug.Log(item.AttributeRanges); // 募集条件 Debug.Log(item.CapacityOfRoles); // 参加者 Debug.Log(item.AllowUserIds); // 参加を許可するユーザIDリスト Debug.Log(item.Metadata); // メタデータ Debug.Log(item.ExpiresAt); // ギャザリングの有効期限 Debug.Log(item.CreatedAt); // 作成日時 Debug.Log(item.UpdatedAt); // 最終更新日時

var domain = gs2.Matchmaking.Namespace(
namespaceName: "namespace-0001" // ネームスペース名 ).Me(
gameSession: GameSession // GameSessionオブジェクト(アクセストークン) ).Gathering(
gatheringName: "gathering-0001" // ギャザリング名 ); var future = domain.CancelMatchmaking( ); yield return future; if (future.Error != null) {
onError.Invoke(future.Error, null);
yield break; } var future2 = future.Result.Model(); yield return future2; if (future2.Error != null) {
onError.Invoke(future2.Error, null);
yield break; } var result = future2.Result;

Debug.Log(result.GatheringId); // ギャザリングGRN Debug.Log(result.Name); // ギャザリング名 Debug.Log(result.AttributeRanges); // 募集条件 Debug.Log(result.CapacityOfRoles); // 参加者 Debug.Log(result.AllowUserIds); // 参加を許可するユーザIDリスト Debug.Log(result.Metadata); // メタデータ Debug.Log(result.ExpiresAt); // ギャザリングの有効期限 Debug.Log(result.CreatedAt); // 作成日時 Debug.Log(result.UpdatedAt); // 最終更新日時

レーティング

同じレベル帯でのマッチングを実現するためにギャザリングの属性値にGS2-Matchmakingで提供するレーティングを使用することができます。
レーティングを使用するにあたって偽りのない勝敗結果をもとにレーティングを計算する必要があります。
ゲームサーバーで勝敗が判断できる場合は、ゲームサーバーが勝敗結果を報告すれば問題ありません。
またGS2-Realtimeからも勝敗結果の報告が可能になる予定です。
一方でP2P対戦などクライアントが勝敗結果を報告する場合、勝敗を偽るチート行為を対策する必要があります。
そこでGS2-Matchmakingではクライアントが勝敗結果を投票するという方法を用意しています。

投票用紙と投票

対戦に参加したクライアントは投票用紙と各プレイヤーの勝敗・順位を投票します。
GS2-Matchmakingは投票結果の多数決で勝敗・順位を判断しレーティングを計算します。
このように各クライアントが別々にGS2-Matchmakingに投票してもいいですが、投票が集まりレーティングが計算されるタイミングをクライアント側で把握できません。
そこで勝利したクライアントの一つを代表とし、各クライアントの投票用紙を代表クライアントに集め、
過半数の投票用紙がそろったタイミングでGS2-Matchmakingへ勝敗結果を投票、同時にその通知を代表クライアントから各クライアントに通知する方法を推奨します。
『勝利したクライアント』としているのは、敗北した側が自分たちが勝ったことにして報告することにインセンティブはありますが、その逆はないためです。
負けた側が投票用紙を渡してこない可能性がありますが、その場合も過半数の投票用紙があれば結果を通すことができます。

投票が成立しない可能性

チート対策として勝敗結果を投票する方式ご紹介しましたが、チート行為を完全に排除することはできません。
例えば1対1の場合は片方が偽りの勝敗を報告をすれば正しい勝敗を判断できません。
対戦人数が増えればその分信用度は増していきますが、正しい投票が成立しない可能性は完全にはなくなりません。
対戦人数が少人数である場合や完全なチート対策をする場合は、ゲームサーバーの構築を検討してください。

GS2-MegaField

サービス概要

10万人を超えるプレイヤーの位置情報を保持し、最大1秒間に5回更新し他のプレイヤーと共有するためのサービス。

ネームスペースは複数のエリアで構成され、1つのエリアは複数のレイヤーで構成されます。
明確にプレイヤーが交わることがない空間はエリアを分割し、プレイヤーとドロップアイテムなどの種類毎にレイヤーを分けることを推奨します。
ドロップアイテムやエネミーを空間に配置したい場合は、それらの状態を管理するデーモンを別途用意する必要があります。

このサービスでは三次元位置情報と向きに相当する三次元ベクトル以外のデータを共有することはできません。
それ以外データを共有したい場合は、GS2の他のサービスを組み合わせることを検討してください。

本サービスは非常に通信回数が多いサービスです。
1秒間に3回位置情報を送信する場合、1時間あたりおよそ1万回の通信が発生し、200円以上費用が発生します。
Individual プランでご利用を検討の場合も、月の平均リクエストが10回を超える場合はプランの除外対象となり
1秒間に3回位置情報を送信する場合は平均3.3人がオンラインの場合は除外対象に該当するようになりますのでご注意ください。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

ユーザーごとに1秒間に座標情報を更新可能な回数は5回までです

基本的な使い方

GS2-Mission

サービス概要

ミッション機能を提供するサービスです。

ミッション機能は「n回 クエストをクリアする」「n体のキャラクターを入手する」のような条件を設定し、達成することで報酬が得られる仕組みです。
これを実現するために ”ミッションの条件設定” ”カウンターの購読” ”カウンターの上昇” という仕組みを使います。

ミッションの条件設定とは、ミッションが達成されたことになるカウンターの閾値を設定するものです。

カウンターの購読とは、ミッションの対象となるカウンターの設定です。
1つのカウンターは複数のミッションから購読することができ、ミッションごとに異なるリセットタイミングを設定できます。
たとえば、「クエストを通算1000回クリアした」というミッションと、「クエストを今日10回クリアした」というデイリーミッションを作りたい場合、
クエストをクリアするごとにカウントアップされる1つのカウンターを両方のミッションから購読し、前者には 'リセットなし' 、後者には
'毎日' のリセットタイミングを設定することで、1回のカウンター操作で複数の条件のミッションを処理することができます。

カウンターの上昇 は文字通りカウンターを上昇させる操作で、例えばクエストをクリアしたときにクエストのクリア回数のカウンター上昇を報酬に含めて使用します。

ミッションには2つの期間を設定できます。
1つ目はカウンターを操作できる期間で、カウンターを上昇させるのをイベント開催期間に限定するようなケースで使用します。
2つ目はミッション達成報酬の受け取り可能期間で、イベントが終わった後もしばらくミッションの報酬だけは受け取れるようにしたい場合は2つの期間を別に設定して使用します。
期間の指定には GS2-Schedule のイベントを関連付けます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一カウンターに対する操作は1秒間に3回までにおさえてください。
異なるユーザや、同一ユーザでも異なるカウンターに対する処理であればこの制限はありません。

基本的な使い方

使い方は3ステップです。

  1. マスターの作成
  2. カウンターの増加
  3. ミッション報酬の受け取り

マスターの作成

マネージメントコンソールで作成します。
作成するマスターは'カウンター' と'ミッショングループ' 、また'ミッショングループ' の中で設定する'タスク' です。

カウンター

ミッションを達成したかどうかを判定するためカウンターです。
例えば10回対戦するミッションであれば対戦完了時に対戦回数をカウントアップします。
カウンターは定期的(毎日・毎週・毎月)にリセットすることもできます。
デイリーミッションなどを実現する場合に設定してください。

またGS2-Scheduleと連携してイベント中のみカウントアップできるカウンターの作成も可能です。

ミッショングループ

ミッショングループとは個々のミッションを束ねる役割をします。
ミッショングループにはミッションのリセットタイミングを設定できます。
例えばリセットタイミング別に、デイリーミッショングループ、ウィークリーミッショングループ、無期限ミッショングループを作り、それぞれのミッショングループにミッション登録することで管理が容易にできます。

タスク

GS2では個々のミッションのことをタスクと呼んでいます。ミッショングループの中に登録していきます。
この時、タスク達成の条件として作成したカウンターとそのカウンターの目標値を設定します。
次に報酬を設定します。この報酬でも任意のカウンターを増加させることができ、例えばデイリーミッションを3つ達成する、といったタスクを作ることもできます。

カウンターの増加

カウンターの増加のAPIはクライアントからは、ユーザー用のセキュリティポリシーであるApplicationAccessでは実行出来ません。
これは不正にカウンターを増加させるチート行為を出来なくするためです。

カウンター増加の方法としては3つあります。

スタンプシートの報酬として設定する

例えばマネージメントコンソールでGS2-Questのクエストモデルマスター開いてみてください。
その中のクエストクリア時の報酬に「Mission:カウンターに加算」があります。
この場合はクエストクリア回数カウンターの増加方法として使えます。
他のサービスからもカウンターの加算ができますので、適した方法を探してください。

GS2Scriptで増加させる

GS2イベントの報酬以外の方法でカウンターを増加させたい場合も考えられます。例えばログイン回数カウンターです。
この場合は、カウンターを増加させるGS2-Scriptを書いてGS2-Acountのネームスペースでログイン時に実行する設定をすることで実現できます。

サーバーからAPIを呼び出す

サーバーからカウンターを増加させる例 (Python)

from gs2_mission_client.request import IncreaseCounterByUserIdRequest
from gs2_mission_client.result import IncreaseCounterByUserIdResult

result = client.increase_counter_by_user_id(
    IncreaseCounterByUserIdRequest()\
        .with_namespace_name(namespace_name)\
        .with_counter_name(counter_name)\
        .with_user_id(user_id)\
        .with_value(value)
)

自作されているサーバーからAPIを呼び出してカウンターを増加させることもできます。

ミッション報酬の受け取り

ミッションの報酬を受け取る 実装例 (Unity)

var domain = gs2.Mission.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Complete(
    missionGroupName: "mission-group-0001" // ミッショングループ名
);
var result = await domain.ReceiveRewardsAsync(
    missionTaskName: "mission-task-0001" // タスク名
);

Debug.Log(result.TransactionId); // 発行されたスタンプシートのトランザクションID
var domain = gs2.Mission.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Complete(
    missionGroupName: "mission-group-0001" // ミッショングループ名
);
var future = domain.ReceiveRewards(
    missionTaskName: "mission-task-0001" // タスク名
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var result = future.Result;

Debug.Log(result.TransactionId); // 発行されたスタンプシートのトランザクションID

ミッションを達成したらミッション達成報酬を取得します。
ここで注意すべきは、ミッション達成報酬を取得だけでは報酬は得られません。
ミッションの報酬が1つである場合はスタンプシートの実行を、2つ以上である場合はGS2-JobQueueを使用する必要があります。

GS2-Money

サービス概要

課金通貨を管理するサービスです。

課金通貨とは、日本の資金決済法に定義された、前払式支払手段 (自家型) に該当するゲーム内通貨を指します。
資金決済法に準拠するために、GS2-Money では、残高や通貨のトランザクションの管理を行います。

異なるプラットフォーム間でウォレットを共有したくない場合、アクセス元のプラットフォームによって同一アカウント内のウォレットを分ける仕組みも提供します。
その場合でも、無償提供分は、異なるプラットフォーム間で共有することができます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

通常のAPI利用料金に加えて流通額に対するレベニューシェアが適用されます。

月間流通額の範囲 レベニュー
¥0 〜 ¥999,999 10%
¥1,000,000 〜 ¥9,999,999 5%
¥10,000,000 〜 ¥99,999,999 3%
¥100,000,000 〜 ¥999,999,999 1%
¥1,000,000,000 〜 ¥100,000,000,000 0%

レベニューシェアに対しては通常の無料枠の適用はありませんが、月間の流通額が 10,000円 に満たない場合は例外として利用料金を頂戴しません。

制限・制約

同一ウォレットに対する操作は1秒間に3回までにおさえてください。
異なるユーザや、同一ユーザでも異なるウォレットに対する処理であればこの制限はありません。

基本的な使い方

ゲームの収益化にはおおまかに3種類の方法があります。

この内、アプリ内課金を使って収益化を実現するための機能を提供するのが GS2-Moneyです。
GS2-Moneyにはゲーム内で課金通貨を扱うために必要な機能が一通り実装されています。

課金通貨とは

課金通貨は、ゲーム内通貨の中でも、現金を使って購入できる通貨を指します。
現金を使って購入した通貨は、日本国内において 資金決済に関する法律 通称 資金決済法 の制約を受け、その規定に従った管理をおこなう必要があります。

資金決済法

電子決済に関する法律で、ゲーム内の通貨は 前払式支払手段 に該当することが一般的です。
前払式支払手段 には 自家型第三者型 がありますが、自社で発行した通貨を自社で使用する場合は 自家型 となり、ゲーム内の通貨は 前払式支払手段(自家型) に該当することが一般的です。
※ 実際自分のタイトルのゲーム内通貨がどの方式に該当するかは法律の専門家にご相談ください。

GS2-Money の機能

GS2-Money は『前払式支払手段(自家型)』の法的要件を満たすよう設計されています。
具体的には以下の機能を有しています。

なお、 GS2-Money は課金通貨の無料付与分も統合的に管理できます。これにより、通貨の消費時には、ユーザーが有料で購入した通貨と、無料で入手した通貨を、自動で合算して使用することが可能です。

通貨の対現金単価

GS2-Money では、通貨の入手ごとに、現金に対する単価を管理します。
たとえば、200円でゲーム内通貨を 100 入手したあと、100円でゲーム内通貨を 100 入手した場合、 GS2-Money では対現金単価 1.5 のゲーム内通貨を 200 持っていると扱うのではなく、対現金単価 2 のゲーム内通貨と対現金単価 1 のゲーム内通貨をそれぞれ 100 持っているものとして管理します。無料入手分は、対現金単価 0 として扱われます。
また、ゲーム内通貨の消費時にも、どの対現金単価のゲーム内通貨がどれだけ消費され、どの対現金単価のゲーム内通貨がどれだけ残っているかが管理されます。

通貨の消費優先度

GS2-Money の内部では対現金単価の異なるゲーム内通貨が区別して管理されますが、消費の際に個別の所持量を意識する必要はありません。
たとえば、対現金単価 1 のゲーム内通貨だけで 100 持っている場合でも、対現金単価 1 と 2 のゲーム内通貨をあわせて 100 持っている場合でも、その違いを意識せずにそこからゲーム内通貨を 100 消費することができます。
異なる対現金単価のゲーム内通貨を複数持っている場合に、どこから優先して消費するかは、以下のポリシーから選択できます。

レシートの記録

GS2-Money は決済プラットフォームごとのレシートを検証し、使用済みのレシートを記録する機能が用意されています。
現在対応しているプラットフォームは以下です。

近日対応予定のプラットフォームは以下です

通貨の入手

セキュリティに関する注意事項

課金通貨の入手はきわめてセンシティブな操作です。
なぜなら、ゲーム性やゲーム内経済に影響を与えるだけでなく、現金的価値を有し、その管理について資金決済法の制約を受けるユーザ資産を増加させる操作だからです。
課金通貨の入手に関してクライアントから任意の操作を受け付けることは、その現金的価値を不正に増殖させることになりかねません。

そこで、GS2 ではスタンプシートの報酬としてしか課金通貨の入手を行えないようにしています。
スタンプシートの報酬の内容はサーバに定義を持ち、また報酬を得る前にゲーム内ストアでの購入処理などをおこなったことをスタンプシートの仕組みが保証するため、課金通貨の不正な増殖を防げます。
したがって、課金通貨の入手には、ゲーム内ストアでの購入や、クエストの報酬として入手するような報酬設計が必要です。

通貨の消費

通貨の消費 実装例 (Unity)

var domain = gs2.Money.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Wallet(
    slot: 0 // スロット番号
);
var result = await domain.WithdrawAsync(
    count: 50, // 消費する課金通貨の数量
    paidOnly: null // 有償課金通貨のみを対象とするか
);
var item = await result.ModelAsync();
var price = result.Price;

Debug.Log(item.Slot); // スロット番号
Debug.Log(item.Paid); // 有償課金通貨所持量
Debug.Log(item.Free); // 無償課金通貨所持量
Debug.Log(item.UpdatedAt); // 最終更新日時
Debug.Log(price); // 消費した通貨の価格
var domain = gs2.Money.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Wallet(
    slot: 0 // スロット番号
);
var future = domain.Withdraw(
    count: 50, // 消費する課金通貨の数量
    paidOnly: null // 有償課金通貨のみを対象とするか
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;
var price = future.Result.Price;

Debug.Log(result.Slot); // スロット番号
Debug.Log(result.Paid); // 有償課金通貨所持量
Debug.Log(result.Free); // 無償課金通貨所持量
Debug.Log(result.UpdatedAt); // 最終更新日時
Debug.Log(price); // 消費した通貨の価格

GS2では通貨の消費に関しても GS2-ShowcaseGS2-Exchange を利用して対価の交換と報酬の付与で表現することを推奨しています。
しかし、サーバで管理しない報酬を付与する場合にも利用できるよう、通貨の消費はAPIで実行できるようになっています。

二次通貨の管理

GS2-Money は二次通貨の取り扱いにも対応しています。
二次通貨とは、課金通貨と引き換えに入手したアイテムのことで、資金決済法の解釈によっては、これらも、直接現金と引き換えに入手した課金通貨に準じた扱いを求められる可能性があります。
たとえば、100円相当の課金通貨でスタミナドリンクを購入した場合、そのスタミナドリンクもまた、継承的に100円相当の価値を持つ課金通貨であるとも考えられるためです。
GS2 では、 GS2-Showcase や GS2-Exchange で対価に GS2-Money の課金通貨(上記の例では、ゲーム内通貨)を設定し、報酬にも GS2-Money の課金通貨(上記の例では、スタミナドリンク)を設定すると、課金通貨の種類をまたいでその価値を引き継げるようになっています。

TIPS

現金で課金通貨を購入する

月額サブスクリプションを実現する

事業者としてやらなければならないこと

3月31日/9月30日時点でゲーム内通貨の未使用残高が1,000万円を超えた段階で、該当ゲーム内通貨は資金決済法の対象となります。
それにあたって、GS2だけでなく、GS2を利用する事業者にも一定の責任が発生します。
どのような責任が発生するのか、詳しくは専門家にご相談いただきたいと思いますが、代表的なものを記載します。

コンプライアンス
利用者保護

GS2のサービスは Amazon Web Services および Google Cloud Platform を利用して提供されます。
これらの事業者は ISO 27001(セキュリティ) ISO 27018(個人情報保護) を始めとした認証を取得し、適切に運営されています。
GS2のマネージメントコンソールやAPIから取得したデータの保護に関しては事業者の責任で行う必要があります

苦情処理体制
事務運営

GS2-News

サービス概要

お知らせ機能を実現するサービスです。

お知らせで配信する記事データは Hugo で作成されている必要があります。
GS2-News に Hugo の記事データ一式を Zip で圧縮したデータをアップロードするか、GitHub のリポジトリを指定して反映します。
記事データは全体で100MB以内に収める必要があります。

GS2-News は受け取った記事データから /content/{sectionName}/{contentName}/index.html を走査します。
index.* の YAML Front matter を解析し、公開期間などの情報を抽出します。
そのため、記事データのツリー構造はこの /content/{sectionName}/{contentName}の形式に従っている必要があります。
コンテンツの上部に記述する Front Matter は必ず YAML形式で記述する必要があります。

Front Matter には後述のGS2独自の制御構文を記述することができます。
コンテンツの公開条件を 公開期間の設定 で定義した場合、GS2-News は公開条件を満たさないコンテンツを削除してサイトをビルドします。
GS2-News は公開条件が設定されたコンテンツの公開・非公開状態のすべてのパターンを事前にビルドし、Webサーバに配置します。
そのため、公開条件が設定されたコンテンツが多数ある場合は、組み合わせ爆発が発生し更新にかかる時間や費用が増大します。
これを避けるには、公開条件が設定されたコンテンツが最小になるような工夫をしてください。

記事データのビルドには 『-D, --buildDrafts』 オプションは含まれません。
Front Matter で draft: true と記述されたコンテンツは配信されません。

記事データの表示

クライアントが現在有効な記事データをWebViewやブラウザで表示する方法として、二種類の方法が提供されます。

Front Matter に記述可能な制御構文

公開期間の設定

x_gs2_scheduleEventId: ${GS2-Schedule のイベントGRN}

x_gs2_scheduleEventId を記述することで、そのコンテンツは該当イベントの開催期間にしか公開されなくなります。
イベントに連動したコンテンツの配信に利用できます。

また、GRNには {region}{ownerId} のプレースホルダを使用でき、それぞれ実行中のリージョン・オーナーIDに置換されます。

grn:gs2:{region}:{ownerId}:schedule:schedule-0001:event:event-0001

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

使い方は2ステップです。

  1. 記事を作成する
  2. 記事を閲覧する

記事を作成する

以下のリンクにサンプルと解説があります。

GitHub:gs2-news-sample

作成した記事はマネージメントコンソールから登録します。

実際に作成を始める前にサンプルをマネージメントコンソールから登録し、手順2でアクセスできるか試しておくといいでしょう。

記事を閲覧する

記事のURLを取得する実装例 (Unity)

var domain = gs2.News.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).News(
);
List<EzSetCookieRequestEntry> cookies = new List<EzSetCookieRequestEntry>();
var result = await domain.GetContentsUrlAsync(
);
var items = result.ToList();
foreach (var item in items)
{
    var entry = await item.ModelAsync();
    cookies.Add(entry);
}

var browserUrl = domain.BrowserUrl;
var zipUrl = domain.ZipUrl;

Debug.Log(cookies); // お知らせコンテンツにアクセスするために設定の必要なクッキー のリスト
Debug.Log(browserUrl); // お知らせコンテンツにアクセスするためのURL
Debug.Log(zipUrl); // ZIP形式のお知らせコンテンツにアクセスするためのURL Cookieの設定は不要
var domain = gs2.News.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).News(
);
var future = domain.GetContentsUrl(
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var result = future.Result;
List<EzSetCookieRequestEntry> cookies = new List<EzSetCookieRequestEntry>();
var items = result.ToList();
foreach (var item in items)
{
    var future2 = item.Model();
    yield return future2;
    var entry = future2.Result;
    cookies.Add(entry);
}
var browserUrl = domain.BrowserUrl;
var zipUrl = domain.ZipUrl;

Debug.Log(items); // お知らせコンテンツにアクセスするために設定の必要なクッキー のリスト
Debug.Log(browserUrl); // お知らせコンテンツにアクセスするためのURL
Debug.Log(zipUrl); // ZIP形式のお知らせコンテンツにアクセスするためのURL Cookieの設定は不要

サンプルコードで現在閲覧可能なお知らせ記事のURLを入手できます。このURLにアクセスして記事を閲覧します。

URLにアクセスする時、アクセスするために設定の必要なクッキーを使用してください。
ZIP形式のお知らせコンテンツにアクセスする場合は不要です。

GS2-Quest

サービス概要

クエストの進捗管理をします。

クエストには開始するために必要な対価、クエストに成功したときの報酬、クエストに失敗したときの報酬 を設定できます。

クエストを開始するために必要な対価にはスタミナを消費する処理や、回数制限のカウンター上昇などを設定します。

クエストに成功したときの報酬は複数設定でき、クエスト開始時に抽選する際に使用する確率を設定できます。
これを使用することで、クエストとしては同じだが、通常コンテンツとレアエネミーが出現するコンテンツを出し分けることができます。
クエスト成功時の報酬には、キャラクターの経験値やアイテム、ゲーム内通貨などを設定します。

クエストに失敗したときの報酬とは、クエストに失敗したときに開始時に受け取った対価を返却する場合などに設定します。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

クエストとはゲーム内のストーリー進行や報酬付きタスクを実現するための機能であり、GS2 ではこの機能を実現するために GS2-Quest を提供しています。

GS2-Quest で提供するのは、具体的には以下の保証です。

以下の内容については、 GS2-Quest は関知しません。

クエストモデルの登録

GS2-Quest では、クエスト内容の設定をサーバサイドで管理します。
そのため、この機能を利用するには、事前に GS2-Quest にマスターデータをアップロードしておく必要があります。

クエストのマスターデータの設定項目(GS2-Quest にはどんな機能があるのか)については、以下のドキュメントを参考にしてください。

前提クエスト

ゲーム仕様上、クエストにはシーケンシャルなアクセスを求めることが多くあります。
たとえば、2章をプレイするには1章をクリアしていなければならない、というものです。

このように、あるクエストを開始するのに、事前にクリアしている必要があるクエストを 前提クエスト と呼びます。
GS2 では、各クエストに対して前提クエストを設定でき、前提クエストのクリアフラグが立っていなければクエストを開始できないようにすることができます。

クエストの挑戦対価

クエストに挑戦するために対価を必要とするよう設定できます。
対価にはたとえば スタミナクエスト挑戦権利アイテム を設定でき、それらが足りない状態ではクエストを開始できないようにすることができます。

クエストの報酬

クエストの報酬には大きく分けて2種類が設定できます。

クリア報酬は文字通りのクエストをクリアしたときに得られる報酬です。
失敗報酬とは、クエストに失敗したときに得られる報酬で、一般的には挑戦に使用したスタミナを払い戻すような機能として用いられます。

クリア報酬のランダム化

毎回同じクリア報酬では少し味気ないかもしれません。
GS2-Quest では、複数のクリア報酬を設定し、ランダムに選択させることができます。
その際の確率は GS2-Lottery と同じく重みベースの確率設定が行えます。

クエスト報酬データにはメタデータを保存する領域も付随します。
たとえば、クリア報酬のメタデータにモンスターの出現パターンを載せておき、クリア報酬に応じたモンスターが出現するように設定するようなこともできます。

クエストの開始

クエストの開始 実装例 (Unity)

var domain = gs2.Quest.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
);
var result = await domain.StartAsync(
    questGroupName: "group-0001", // クエストグループ名
    questName: "quest-0001", // クエストモデル名
    force: null, // すでに開始しているクエストがある場合にそれを破棄して開始するか(オプション)
    config: null // スタンプシートの変数に適用する設定値(オプション)
);

Debug.Log(result.TransactionId); // 発行されたスタンプシートのトランザクションID
var domain = gs2.Quest.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
);
var future = domain.Start(
    questGroupName: "group-0001", // クエストグループ名
    questName: "quest-0001", // クエストモデル名
    force: null, // すでに開始しているクエストがある場合にそれを破棄して開始するか(オプション)
    config: null // スタンプシートの変数に適用する設定値(オプション)
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var result = future.Result;

Debug.Log(result.TransactionId); // 発行されたスタンプシートのトランザクションID

クエストを開始すると、内部では複数登録されたクエスト報酬から抽選を行い、報酬の内容を確定します。
つまり、GS2-Quest を使う場合、クエストを開始する時点でクリア時に得られる報酬が確定します。
これは クエストを開始後、完了するまでの間にアプリを強制終了 するなどして報酬の再抽選を行う不正行為が行えないようにするための仕様です。

クエストの開始処理のレスポンスには スタンプシートが応答されます。スタンプシートを実行するまでクエスト開始処理は保留されます。

クエストの完了

クエストの完了 実装例 (Unity)

var domain = gs2.Quest.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Progress(
);
var result = await domain.EndAsync(
    isComplete: true, // クエストをクリアしたか(オプション)
    rewards: new EzReward[] {
        {}
    }, // 実際にクエストで得た報酬(オプション)
    config: null // スタンプシートの変数に適用する設定値(オプション)
);
var progress = await domain.ModelAsync();

Debug.Log(result.TransactionId); // トランザクションID
Debug.Log(progress.ProgressId); // クエスト進行状況GRN
Debug.Log(progress.TransactionId); // トランザクションID
Debug.Log(progress.QuestModelId); // クエストモデル
Debug.Log(progress.RandomSeed); // 乱数シード
Debug.Log(progress.Rewards); // クエストで得られる報酬の上限
var domain = gs2.Quest.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Progress(
);
var future = domain.End(
    isComplete: true, // クエストをクリアしたか(オプション)
    rewards: new Gs2.Unity.Gs2Quest.Model.EzReward[] {
        {}
    }, // 実際にクエストで得た報酬(オプション)
    config: null // スタンプシートの変数に適用する設定値(オプション)
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future.Result;
var progress = future2.Result;

Debug.Log(result.TransactionId); // トランザクションID
Debug.Log(progress.ProgressId); // クエスト進行状況GRN
Debug.Log(progress.TransactionId); // トランザクションID
Debug.Log(progress.QuestModelId); // クエストモデル
Debug.Log(progress.RandomSeed); // 乱数シード
Debug.Log(progress.Rewards); // クエストで得られる報酬の上限

クエストの完了時には 実際にクエストで得た報酬 を指定できます。
指定しなかった場合は開始時に応答された報酬を全て入手します。ゲーム内で敵や宝箱を無視したことで報酬が減額される場合はここで数量を減らして報告してください。
開始時に決定された報酬より多くの報酬を得ようとしたり、得られない報酬を得ようとするとエラーになります。

クライアントがクエストの完了を呼び出せることによって、プレイヤーのレベルでは本来クリアできない難易度のステージをクリアしたと報告するチート行為の可能性があります。
これを回避するにはGS2-Identifierのセキュリティポリシー設定でクライアントからクエストの完了を呼び出せなくし、サーバーから呼び出すようにする方法があります。

クエストの完了報告のレスポンスには スタンプシートが応答されます。スタンプシートを実行するまでクエスト完了処理は保留されます。

GS2-Ranking

サービス概要

スコアやクリアタイムのランキング処理を実現します。

ランキングには全員が同じデータを参照するグローバルランキングと、
フレンド間やギルド内のランキングのようなユーザによって見えるランキングの内容の異なるスコープドランキングがあります。

グローバルランキングは、15分~24時間の間で指定した間隔でランキングの集計処理がおこなわれ、
ゲームユーザーからのリクエストは事前に集計された結果を応答します。
これによって、大量のゲームプレイヤーが居たとしてもレスポンスタイムは変わらないように設計されています。

スコープドランキングは各ゲームプレイヤーがスコアバケットをもち、購読しているゲームプレイヤーがスコアを登録するとスコアバケットにスコアが追加されます。
各ゲームプレイヤーは自分のスコアバケット内のスコアを使ってランキングを処理します。

グローバルランキング・スコープドランキング ともにユーザーID毎に保持できるスコアを1個にするか、複数保持できるかを設定できます。
ランキングを登録したときに最後に登録したスコアだけをランキング対象としたいか、登録したすべてのスコアをランキング対象としたいかで設定できます。
すべてのスコアをランキング対象とする場合、データ量が膨れ上がりやすくなります。その結果、GS2の利用料金にも影響を与えることとなります。
ゲームシステム上の大きな理由がなければ最後に登録された1つのスコアのみがランキング対象となるように設定することをお勧めします。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

使い方は3ステップです。

  1. カテゴリマスターを作成する
  2. スコアを登録する
  3. ランキングを見る

カテゴリマスターを作成する

マネージメントコンソールで作成します。
設定項目で注意する点は、特に理由がなければ「ユーザID毎にスコアを1つしか登録されないようにする」にチェックを入れることです。
ユーザーID毎に複数スコアを保存するとデータの量が膨れ上がり、利用料金の増加につながります。

スコアを登録する

クライアントからスコアを登録する 実装例 (Unity)

var domain = gs2.Ranking.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Ranking(
    categoryName: "category-0001" // カテゴリ名
);
var result = await domain.PutScoreAsync(
    score: 1000L, // スコア
    metadata: null // メタデータ(オプション)
);
var item = await result.ModelAsync();

Debug.Log(item.CategoryName); // カテゴリ名
Debug.Log(item.UserId); // ユーザID
Debug.Log(item.ScorerUserId); // スコアを獲得したユーザID
Debug.Log(item.Score); // スコア
Debug.Log(item.Metadata); // メタデータ
var domain = gs2.Ranking.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Ranking(
    categoryName: "category-0001" // カテゴリ名
);
var future = domain.PutScore(
    score: 1000L, // スコア
    metadata: null // メタデータ(オプション)
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var item = future2.Result;

Debug.Log(item.CategoryName); // カテゴリ名
Debug.Log(item.UserId); // ユーザーID
Debug.Log(item.ScorerUserId); // スコアを獲得したユーザID
Debug.Log(item.Score); // スコア
Debug.Log(item.Metadata); // メタデータ

サーバーから登録する例 (python)


from gs2_ranking_client.request import PutScoreByUserIdRequest
from gs2_ranking_client.result import PutScoreByUserIdResult

result = client.put_score_by_user_id(
    PutScoreByUserIdRequest()\
        .with_namespace_name(namespace_name)\
        .with_category_name(category_name)\
        .with_user_id(user_id)\
        .with_score(score)\
        .with_metadata(metadata)
)

スコアの登録はユーザー用のセキュリティポリシーであるApplicationAccessではクライアントからも実行できるようになっています。
ただしランキング報酬などを用意する場合は、不正なスコアを登録するチート行為が行われるかもしれません。
これを回避する場合はクライアントからスコアの登録ができないようにセキュリティポリシーを設定します。

また自作のサーバーからスコアを登録することができます。ゲームシステムを自作のサーバーで実現し、ゲームを遊んだ結果のスコアをサーバーからGS2に登録する方法が安全です。

ランキングを見る

ランキングを取得する 実装例 (Unity)

var domain = gs2.Ranking.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
);
var items = await domain.RankingsAsync(
    categoryName: "category-0001" // カテゴリ名
).ToListAsync();

Debug.Log(items); // ランキングのリスト
var domain = gs2.Ranking.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
);
var it = domain.Rankings(
    categoryName: "category-0001" // カテゴリ名
);
List<EzRanking> items = new List<EzRanking>();
while (it.HasNext())
{
    yield return it.Next();
    if (it.Error != null)
    {
        onError.Invoke(it.Error, null);
        break;
    }
    if (it.Current != null)
    {
        items.Add(it.Current);
    }
    else
    {
        break;
    }
}

Debug.Log(items); // ランキングのリスト

ランキングを上位から取得します。

GS2-Realtime

サービス概要

対戦・協力プレイ機能を実現します。

ルームを作成すると、IPアドレス ポート 暗号鍵 の3つの情報が発行されます。
ルームに参加するには、発行された IPアドレス ポート に WebSocket で接続します。
その通信には発行された 暗号鍵 を使用して通信します。

同時接続数や通信頻度の制限はありません。

ルームのライフサイクル

作成

GS2-Realtime はルームの作成リクエストを受け付けてからサーバを起動します。
この時、ルームの数が規定数未満の場合は規定数に達するようホットスタンバイとなるルームを追加で起動します。
規定数の数はGS2によって自動的に調整されます。

つまり、リクエストを受け付けてからサーバを起動する コールドスタート 時と ホットスタンバイされたルームを利用する ホットスタート でルームの作成リクエストを受け付けてから実際に割り当てられるまでの時間に差が生じます。

コールドスタート時の待ち時間の目安は 40秒〜60秒
ホットスタート時の待ち時間の目安は3〜5秒です。

終了

起動されたルームは作成後5分間誰も接続に来なかった場合終了されます。
この終了処理はホットスタンバイのルームにも適用されますが、ホットスタンバイの起動時間は利用料金に加算されません。

ルームに参加するすべてのプレイヤーから無通信状態が1分間続いても自動的に終了されます。

ルームの最大起動時間は3時間です。作成から3時間が経過すると自動的に終了されます。

コールドスタートの発生条件

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

ルームの起動時間1分あたりの利用料金と、データのアウトバウンド通信容量に対して費用が発生します。
ルームが1つであれば、ルームへの参加者が2人でも100人でも利用料金は変わりません。

正式サービス開始までにサーバのスペックの変更をできるようにします。
対戦人数が多い場合や、通信頻度が高い場合は高スペックなサーバを必要とする場合があります。

正式サービス開始後のマイルストーンとして、サーバサイドの実装を差し替える機能を提供予定です。
この機能が実装されると、サーバ側でロジックを定義できるようになり、パケットリレーよりも柔軟なサーバ機能を利用できるようになります。
この場合、サーバサイドのロジックの複雑さによって高スペックなサーバを必要とする場合があります。

現在利用可能なものは realtime1.nano のみです。

スペック 1分あたりの利用料金
realtime1.nano 0.04円
realtime1.micro 0.06円
realtime1.small 0.12円
realtime1.medium 0.24円
realtime1.large 0.5円

データ転送容量(アウトバウンド)

15円/1GB


コールドスタート が発生するのを許容できない場合は 1ゲームサーバ(realtime1.nano)あたり月額1500円+税(最低契約台数5台/最低契約期間1年) にてホットスタンバイを用意することが可能です。
詳細はお問い合わせください。

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

チュートリアル

GS2-Realtime は他のSDKとは異なるインターフェイスを持ちます。
このセクションでは、ゲームエンジン向けSDKの解説を行います。

概要

GS2-Realtime は WebSocket を通信プロトコルとし、通信に使用するバイナリデータフォーマットとして Protocol Buffers を用います。

GS2-Realtime は全プレイヤーとデータ同期が行われる プロフィール 機能と他のプレイヤーとデータ交換を行うための データ通信 の機能を持ちます。
データ通信を行う際には RelayRealtimeSession::Send を用い、プロフィールの更新には RelayRealtimeSession::UpdateProfile を用います。

これらの API の他に RelayRealtimeSession は以下のようなコールバックを持ちます。

コールバック 引数 説明
OnRelayMessage RelayBinaryMessage 他プレイヤーから データ通信 によって受け取ったときにバイナリが渡ってくる
OnJoinPlayer Player ルームに新しいプレイヤーが参加してきたときに新しく参加したプレイヤーの情報が渡ってくる。ルームに参加した直後には参加済みのプレイヤーの情報が渡ってくる
OnLeavePlayer Player ルームからプレイヤーが離脱したときに離脱したプレイヤーの情報が渡ってくる
OnUpdateProfile Player プロフィールを更新したプレイヤーの情報が渡ってくる
OnErrorHandler Error ゲームサーバーでエラーが発生したときにエラー内容が渡ってくる
OnGeneralErrorHandler Exception ゲームサーバー以外の要因でエラーが発生したときにエラー内容が渡ってくる
OnClose ゲームサーバーとの接続が切断されたときに呼び出される

モデル

名前 データ型 説明
connectionId uint32 ルームに参加しているゲームプレイヤーにランダムに割り当てられる数値ベースのID
data bytes バイナリデータ
targetConnectionId repeated uint32 データの宛先のリスト。省略するとルーム内の全員へ送信
名前 データ型 説明
connectionId uint32 ルームに参加しているゲームプレイヤーにランダムに割り当てられる数値ベースのID
userId string ログイン時に使用したアクセストークン所有者のユーザID
profile bytes プロフィールデータ
名前 データ型 説明
component string エラーの発生したコンポーネント名
statusCode uint32 ステータスコード(HTTPプロトコルに準拠)
message string エラーメッセージ

初期化

GS2-Matchmaking の GS2-Realtime 連携機能を利用するか、GS2-Realtime の GS2 SDK API Gs2RealtimeRestClient::WantRoom を呼び出し、ルームを作成します。
ルームの作成が終わると、 WantRoom の引数で指定したユーザIDに対してプッシュ通知が届きます。
そのプッシュ通知をトリガーとして GS2RealtimeClient::GetRoom を呼び出して ゲームサーバの IPアドレス 待受ポート 暗号鍵 を取得します。

ルームの作成時に参加者を確定できない場合も、ルーム名を知る手段さえあれば GS2RealtimeClient::GetRoom を呼び出して必要な情報を取り出せます。
GS2-Matchmaking の GS2-Realtime 連携機能を利用した場合は ギャザリング名 がルーム名に使用されます。

セッションの作成

using (
    var session = new RelayRealtimeSession(
        accessToken, // アクセストークン
        ipAddress, // ゲームサーバのIPアドレス
        port, // ゲームサーバのポート
        encryptionKey, // 暗号鍵
        ByteString.CopyFromUtf8("my profile") // プロフィールの初期値
    )
)
{
    // セッションが有効なスコープ
}

ゲームサーバの起動が終わったらゲームサーバへの接続を行います。 RelayRealtimeSession クラスがゲームサーバとの通信を表現します。

スコープを抜けると自動的に切断処理が行われます。

イベントハンドラの設定

イベントハンドラの設定

session.OnJoinPlayer += player => {
    // コールバック処理
};

必要に応じて、先に示したイベントハンドラにコールバック処理を割り当てます。

ゲームサーバへの接続

ゲームサーバへの接続

yield return session.Connect(
    r =>
    {
        if (r.Error != null)
        {
            // 接続処理に失敗した場合
        }
    }
)

Connect でゲームサーバへの接続を開始します。
エラーが発生した場合、コールバックにエラーの内容が渡ってきます。

データの送信

データの送信

yield return session.Send(
    r => {
        if (r.Error != null)
        {
            throw r.Error;
        }
    },
    ByteString.CopyFrom(0x00, 0x01, 0x02)
)

yield return session.Send(
    r => {
        if (r.Error != null)
        {
            throw r.Error;
        }
    },
    ByteString.CopyFrom(0x00, 0x01, 0x02),
    new uint[] {targetConnectionId1, targetConnectionId2} // 宛先を指定することも可能
)

Send で他のプレイヤーにデータを送信します。
第三引数に宛先の コネクションID の配列を指定することで、指定したプレイヤーにのみデータを送信できます。

たとえば、バトルロワイヤルのような参加人数が非常に多く広大なフィールドで表現されるゲームでは近くにいないプレイヤーの座標情報はあまり重要ではありません。
そのため プロフィール に一定間隔(たとえば10秒間隔)で記録されたおおよその座標を元に、近くにいるプレイヤーにのみ最新の座標データを送信するようにすることで通信を効率化できます。

プロフィールの更新

プロフィールの更新

yield return session.UpdateProfile(
    r =>
    {
        if (r.Error != null)
        {
            throw r.Error;
        }
    },
    ByteString.CopyFromUtf8("my profile2")
)

UpdateProfile でプロフィールを更新します。
プロフィールを更新すると、ルーム内のすべてのプレイヤーに対して OnUpdateProfile が呼び出されます。
プロフィールのサイズは参加人数によって適切なデータサイズが変わるため、明確に制限をしていません。

GS2-Schedule

サービス概要

ゲーム内のイベントの予定を管理します。

このサービスを単独で使うことはなく、 GS2-Showcase や GS2-Quest などと連携して、イベント開催期間のみストアに陳列する商品やクエストを実現します。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

使い方は2ステップです。

  1. イベントマスターを作成する
  2. 各サービスから利用する

イベントマスターを作成する

サーバーからトリガーを引く例 (python)


from gs2_schedule_client.request import TriggerByUserIdRequest
from gs2_schedule_client.result import TriggerByUserIdResult

result = client.trigger_by_user_id(
    TriggerByUserIdRequest()\
        .with_namespace_name('schedule-0002')\
        .with_trigger_name('trigger1')\
        .with_user_id('user-0001')\
        .with_trigger_strategy('renew')\
        .with_ttl(ttl)
)

マネージメントコンソールから作成します。
作成の際の注意点として、イベントの期間の種類が「固定時間」と「プレイヤー別期間」が選べます。

固定時間の場合は指定した日時が来れば全てのプレイヤーも平等に訪れます。

プレイヤー別期間の場合は開始のトリガーを引く必要があります。トリガーの引き方は自作のサーバーからAPIを実行するか、GS2-Scriptを使う方法があります。

各サービスから利用する

例えばマネージメントコンソールからGS2-Questのクエストグループモデルマスターを見てください。
挑戦可能期間の設定から作成したイベントを指定できます。
こうすることでイベント期間中だけ挑戦できるクエストを実現できます。

他にもGS2-Showcaseと組み合わせることで期間限定商品などを作れます。

GS2-Script

サービス概要

ゲーム固有の仕様を実現するためのサービス。

GS2内の様々なイベントをトリガーとしてスクリプトを実行できます。
例えば、アカウントの登録をしたときのイベントをトリガーとして、アカウントの初期設定スクリプトを実行したり、
プレイヤーのレベルアップイベントをトリガーとして、スタミナを回復するスクリプトを実行したり、
ゲームの仕様を標準のGS2の機能で実現できないときにスクリプトを使用して機能を拡張できます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

使い方は2ステップです。

マネージメントコンソールの Script > ネームスペースリスト のページでネームスペースを追加し、 Script > ネームスペースリスト > ネームスペース情報 のページでスクリプトを作成します。

つぎに、マネージメントコンソールの各サービスの ネームスペースリスト > ネームスペース情報 > ネームスペースの更新 のページのスクリプトの項目に作成したスクリプトを登録します。
例えばGS2-Accountのネームスペースには、アカウント新規作成時と認証時、引継ぎ情報登録時、引継ぎ実行時に実行するスクリプトを設定することができます。

ゲームプレイヤーがGS2サービスで対象となっているイベントを発生させたタイミングでスクリプトが実行されます。

GS2-JobQueueの経由

イベントトリガーの実行タイミングがイベントの実行時/完了後のトリガーは、GS2-JobQueueのネームスペースを任意で指定し、スクリプトをジョブとしてGS2-JobQueueに登録して実行できます。
GS2-JobQueueを経由する利点は、ジョブの実行に失敗した場合に一定回数までリトライできること、一定回数以上失敗したジョブはデッドレタージョブとして記録が残ること、APIの応答速度が落ちづらいことなどがあります。
GS2-JobQueueの使用方法について、詳しくはGS2-JobQueueを確認してください。

スクリプトの実行結果の確認

GS2-Scriptに限らず、スクリプトやAPI呼び出しの実行結果の確認はGS2-Logへのログ出力を行うことで可能です。
サービスのネームスペース設定にてログ出力の設定を行ってください。

スクリプトはゲームプレイヤーが各種イベント(API呼び出し)を発生させることによりトリガーされますが、API呼び出しの返り値に、スクリプト内で実行したAPI呼び出しの実行結果は含まれません。
クライアント側でゲームプレイヤーにスクリプトの実行結果を提示する必要がある場合などは、GS2-JobQueueを経由することで別途実行結果を受け取ることができます。

スクリプトサンプル

課金通貨を増やす(Lua)

client = gs2('money')
api_result = client.deposit_by_user_id({
    namespaceName='money',
    userId=args['account']['userId'],
    slot=0,
    price=0,
    count=10000,
})

if(api_result['isError']) then
    fail(api_result['statusCode'], api_result['errorMessage'])
end

result = api_result['result']

サンプルとしてアカウント作成完了時に初期ゲーム内通貨10000を付与するスクリプトを記載します。
作成されたアカウント(userId)に通貨を付与するため引数(args)を使用しています。トリガーによって使用できる引数は変化します。
スクリプトのサンプルコードは各サービスの GS2 SDKリファレンス の メソッドセクション の GS2-Scriptタブ で確認できます。

GS2-SerialKey

サービス概要

シリアルコードもしくは、キャンペーンコードを取り扱うためのサービスです。
シリアルコードは一回使用したら使えなくなるのに対して、キャンペーンコードは全プレイヤーが同じコードを利用し、GS2-Limit の回数制限を使用するなどして何度も使用できないように制御する前提で利用します。
シリアルコードを使用することで対価を得られる GS2-Exchange の交換レートを用意するなどして利用してください。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

シリアルコード発行 1件あたり 0.005円

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

GS2-Showcase

サービス概要

ゲーム内ストア機能を提供します。

販売する商品には対価と報酬を設定します。対価にも報酬にも複数のリソースを設定できます。

ステップガチャや、初回のみディスカウントといった仕組みを実現するために、商品グループという仕組みがあります。
商品グループは、商品ごとに優先度を付けて複数の商品をまとめる仕組みで、購入可能な条件を満たし、かつもっとも優先度の高い商品が陳列されます。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

使い方は2ステップです。

  1. マスターを作成する
  2. 商品を購入する

マスターを作成する

マスターは3種類です。

商品マスター

商品の値段と報酬を決めます。
お金とゲーム内通貨の交換のほか、ゲーム内通貨でガチャを回したりスタミナを増やしたりなど様々な定義ができます。

商品グループマスター

商品マスターをグループ化します。用途としては例えば初回割引を実現する場合に割引価格商品⇒通常価格商品の順に登録する、ステップアップガチャを順番に登録するなどがあります。

商品マスターは商品グループマスターでグループ化しなくても売ることが可能です。

陳列棚マスター

作成した商品マスターと商品グループマスターを登録します。
お店に商品を並べるイメージで、商品マスターと商品グループマスターは陳列棚マスターに登録しないと売ることができません。

商品を購入する

交換を購入する実装例 (Unity)

var domain = gs2.Showcase.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Showcase(
    showcaseName: "showcase-0001" // 商品名
);
var result = await domain.BuyAsync(
    displayItemId: "display-item-0001", // 陳列商品ID
    quantity: null, // 購入数量
    config: null //  スタンプシートの変数に適用する設定値(オプション)
);
var item = await result.ModelAsync();

Debug.Log(item.Name); // 商品名
Debug.Log(item.Metadata); // 商品のメタデータ
var domain = gs2.Showcase.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Showcase(
    showcaseName: "showcase-0001" // 商品名
);
var future = domain.Buy(
    displayItemId: "display-item-0001", // 陳列商品ID
    quantity: null, // 購入数量
    config: null //  スタンプシートの変数に適用する設定値(オプション)
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(result.Name); // 商品名
Debug.Log(result.Metadata); // 商品のメタデータ

ここで注意する点は'buy' 自体を実行するだけでは商品の購入は行われないことです。
'buy'の返り値であるスタンプシートを処理することで処理が行われます。

また一つの商品に複数の報酬を設定する場合はGS2-JobQueueと連携する必要があります。

GS2-Stamina

サービス概要

現実時間経過によって回復するスタミナ値を実現します。

スタミナには回復間隔と回復量を設定できます。
スタミナには2段階の最大値を設定できます。
1つ目は時間経過による自然回復の最大値。2つ目はUIの都合による最大値です。

GS2では、レベルアップ時にスタミナを全回復するときに公平性を期するため、自然回復の最大値を超えて回復できるようにしています。
一方で、これを無制限に許可してしまうと、UIに破綻をきたしてしまう可能性があります。
そのため、二段階目の最大値を設けており、仮にこちらを超えるようなことが会っても GS2-Distributor を使用していればプレゼントボックスにスタミナ回復リソースを届けられるようになっています。

この仕組みを応用すると、街づくりゲームの採集機能も実装できます。
スタミナモデルに建築物を設定し、スタミナ値を建築物の生産量をとして表現します。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

同一スタミナに対する操作は1秒間に3回までにおさえてください。
異なるユーザや、同一ユーザでも異なるスタミナモデルに対する処理であればこの制限はありません。

基本的な使い方

使い方は2ステップです。

  1. マスターを作成する
  2. スタミナを消費する

マスターを作成する

マスターは4種類あります。

スタミナモデルマスター

スタミナの最大値、回復間隔、回復量を定義します。

ただし、多くのスタミナ制ゲームではプレイヤーのランクによって最大値が増えるものが多いでしょう。
これを実現するにはスタミナモデルマスター設定ページの下部にある GS2-Experience 連携を使用します。
使用方法は次項で説明していきます。

スタミナの最大値テーブルマスター

プレイヤーのランクによってスタミナの最大値を変化させる設定です。
プレイヤーのランクはGS2-Experienceで実現します。
事前に経験値の種類マスター(経験値ごとにどのランクになるかを定義するもの)を作成します。

経験値の種類マスターを作成したらスタミナの最大値テーブルマスターに登録し、
今度はランクごとのスタミナの最大値を定義します。
スタミナの最大値テーブルマスターは作成後、スタミナモデルマスターに登録することを忘れないでください。

ランクアップ時にスタミナを回復させる

多くのスタミナ制ゲームではランクアップ時にスタミナが最大値分回復するものが多いです。
これを実現したい場合はGS2-Scriptを使用します。
スタミナの最大値分スタミナを回復するGS2-Scriptを作成し、
GS2-Experienceのネームスペース設定のランク変化したときに実行するスクリプトに登録します。

スタミナ回復間隔テーブルマスター&スタミナ回復量テーブルマスター

スタミナの最大値テーブルマスターに加え、ランクによって回復間隔と回復量も変化させるマスターです。
ただし多くのスタミナ制ゲームでは、スタミナの回復効率まで変化するものはあまりありません。

使い道としてゲーム内の畑や鉱山といった時間経過によって資源が得られるシステムが考えられます。
スタミナを資源の量と見立てて、施設のレベルに応じて入手効率を変化させることができます。

スタミナを消費する

スタミナの消費方法は2つあります。

スタンプシートでの消費

例えば、マネージメントコンソールでGS2-Quest⇒クエストグループモデルマスター⇒クエストモデルマスターを開いてみてください。
クエストの参加料として「Stamina:スタミナを消費」が選択できます。このように他のサービスと連携して、何かをするためにスタミナを消費する仕組みが実装できます。

クライアントもしくはサーバーからAPIを呼び出して消費

クライアントからスタミナを消費する例 (Unity)

var domain = gs2.Stamina.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Stamina(
    staminaName: "stamina-0001" // スタミナの種類名
);
var result = await domain.ConsumeAsync(
    consumeValue: 50 // 消費するスタミナ量
);
var item = await result.ModelAsync();

Debug.Log(item.StaminaName); // スタミナモデルの名前
Debug.Log(item.Value); // 最終更新時におけるスタミナ値
Debug.Log(item.MaxValue); // スタミナの最大値
Debug.Log(item.RecoverIntervalMinutes); // スタミナの回復間隔(分)
Debug.Log(item.RecoverValue); // スタミナの回復量
Debug.Log(item.NextRecoverAt); // 次回スタミナが回復する時間
var domain = gs2.Stamina.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Stamina(
    staminaName: "stamina-0001" // スタミナの種類名
);
var future = domain.Consume(
    consumeValue: 50
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(future.Error, null);
    yield break;
}
var future2 = future.Result.Model();
yield return future2;
if (future2.Error != null)
{
    onError.Invoke(future2.Error, null);
    yield break;
}
var result = future2.Result;

Debug.Log(result.StaminaName); // スタミナモデルの名前
Debug.Log(result.Value); // 最終更新時におけるスタミナ値
Debug.Log(result.MaxValue); // スタミナの最大値
Debug.Log(result.RecoverIntervalMinutes); // スタミナの回復間隔(分)
Debug.Log(result.RecoverValue); // スタミナの回復量
Debug.Log(result.NextRecoverAt); // 次回スタミナが回復する時間

サーバーからスタミナを消費する例 (Unity)


from gs2_stamina_client.request import ConsumeStaminaByUserIdRequest
from gs2_stamina_client.result import ConsumeStaminaByUserIdResult

result = client.consume_stamina_by_user_id(
    ConsumeStaminaByUserIdRequest()\
        .with_namespace_name('namespace-0002')\
        .with_stamina_name('stamina-0002')\
        .with_user_id('user-0001')\
        .with_consume_value(5)
)

前項と違い、ゲーム独自のシステムでスタミナを消費したい場合はこちらを使用します。

GS2-Version

サービス概要

ゲームのバージョンチェックを実現します。
バージョンチェックを通過すると、新しいプロジェクトトークンを取得できます。
つまり、ゲームに組み込むクレデンシャルはバージョンチェックを行うのに十分な権限のみ付与し、
バージョンチェックを通過することでゲームを動作させるのに十分な権限のプロジェクトトークンを取得できるようにすることで、
バージョンチェックを通らなければゲームが遊べない状態を作り出します。

ネームスペースには最大10個のバージョンモデルを宣言でき、すべてのバージョンが要求バージョン以上のときにのみバージョンチェックを通過とみなします。

利用料金

一般的な利用料金

APIリクエスト 1回あたり 0.02円

サービス固有の利用料金

サービス固有の利用料金はありません

制限・制約

利用いただくにあたって注意いただく制限・制約はありません

基本的な使い方

使い方は3ステップです。

  1. マスターデータを作成する
  2. バージョンチェックを実行できるセキュリティポリシーの作成
  3. バージョンをチェックする

マスターデータを作成する

マネージメントコンソールから作成します。
バージョンマスターデータに設定するバージョンは、クライアントに最新またはそれに類するバージョンではない、アップデートを促す対象になっていることを通知するするためのバージョンになります。
よってマスターには最新のバージョンは登録しません。

クライアントのバージョンが、マスターで登録したバージョンと一致しなかったことが、ゲームプレイに支障のないバージョンのアプリであることの証明となります。

バージョンマスターはネームスペースあたり10個まで登録することができ、最大10種類のバージョンチェックを通過出来ればゲームがプレイできる、ということが設定できます。

バージョンチェック通過時に昇格したセキュリティポリシーのプロジェクトトークンを返すテンプレートの例 (YAML)

GS2TemplateFormatVersion: "2019-05-01"

Globals:
  Alias:
    VersionNamespaceName: application-0001
    ApplicationUserName: application
    ElevationUserName: authorized-application

Resources:
  IdentifierElevationUserAttachPolicy:
    Type: GS2::Identifier::AttachSecurityPolicy
    Properties:
      UserName: ${ElevationUserName}
      SecurityPolicyId: grn:gs2::system:identifier:securityPolicy:ApplicationAccess
    DependsOn:
      - IdentifierElevationUser

  IdentifierElevationUser:
    Type: GS2::Identifier::User
    Properties:
      Name: ${ElevationUserName}

  VersionNamespace:
    Type: GS2::Version::Namespace
    Properties:
      Name: ${VersionNamespaceName}
      AssumeUserId: !GetAttr IdentifierElevationUser.Item.UserId
    DependsOn:
      - IdentifierElevationUser

  VersionSettings:
    Type: GS2::Version::CurrentVersionMaster
    Properties:
      NamespaceName: ${VersionNamespaceName}
      Settings:
        version: 2019-10-09
        versionModels:
          - name: application
            metadata: application
            warningVersion: 
              major: 1
              minor: 0
              micro: 0
            errorVersion:
              major: 0
              minor: 9
              micro: 0
            scope: passive
            needSignature: false
    DependsOn:
      - VersionNamespace
判定に使用するバージョン値の種類

マスターデータに設定されているバージョン値と比較するバージョン値は2種類あります。 EzVersionModelscope で指定します。

指定 判定に使用するバージョン値の種類 主な用途
passive クライアントから送信したバージョン値と比較 アプリ、アセットのバージョンなど
active 過去明示的に承認(accept)を行っているバージョンと比較 利用規約、セキュリティポリシーのバージョンなど

バージョンチェックのみを実行できるセキュリティポリシーの作成

想定している基本的な使い方は、以下のようになります。

  1. ゲームアプリケーション側でバージョンチェックの際に使用するクレデンシャルには、バージョンチェックを行うのに必要な権限(例:UnauthenticatedAccessセキュリティポリシー)のみを付与する。
  2. ゲームを起動したらまず 1. のクレデンシャルを使ってバージョンチェックを行う。
    • バージョンチェックが通ったら、ゲームを動作させるのに十分なプロジェクトトークンを取得させる
    • バージョンチェックが通らなければ、アプリストアなどに誘導してアプリケーションのアップデートを促す

バージョンチェックを行うのに必要な権限のみを持つクレデンシャルを作成する必要があります。

UnauthenticatedAccessセキュリティポリシーでクレデンシャルを生成するテンプレートの例 (YAML)

GS2TemplateFormatVersion: "2019-05-01"

Globals:
  Alias:
    ApplicationUserName: application

Resources:
  IdentifierApplicationUser:
    Type: GS2::Identifier::User
    Properties:
      Name: ${ApplicationUserName}

  IdentifierApplicationUserAttachPolicy:
    Type: GS2::Identifier::AttachSecurityPolicy
    Properties:
      UserName: ${ApplicationUserName}
      SecurityPolicyId: grn:gs2::system:identifier:securityPolicy:UnauthenticatedAccess
    DependsOn:
      - IdentifierApplicationUser

  IdentifierApplicationIdentifier:
    Type: GS2::Identifier::Identifier
    Properties:
      UserName: ${ApplicationUserName}
    DependsOn:
      - IdentifierApplicationUser

Outputs:
  ApplicationClientId: !GetAttr IdentifierApplicationIdentifier.Item.ClientId
  ApplicationClientSecret: !GetAttr IdentifierApplicationIdentifier.ClientSecret

バージョンチェックをする

バージョンチェック処理の前に、上記クレデンシャルでのGS2の初期化、ログイン処理を行います。

バージョン情報の準備

まずクライアントのバージョン情報を準備します。

クライアントのバージョン情報を準備する例 (Unity)

List<EzTargetVersion> targetVersions = new List<EzTargetVersion>();
EzTargetVersion targetVersion = new EzTargetVersion();
targetVersion.VersionName = "sample";

EzVersion version = new EzVersion();
version.Major = 1; //実際のクライアントのバージョンを入力 メジャーバージョン
version.Minor = 2; //実際のクライアントのバージョンを入力 マイナーバージョン
version.Micro = 3; //実際のクライアントのバージョンを入力 マイクロバージョン
targetVersion.Version = version;
targetVersions.Add(targetVersion);
List<EzTargetVersion> targetVersions = new List<EzTargetVersion>();
EzTargetVersion targetVersion = new EzTargetVersion();
targetVersion.VersionName = "sample";

EzVersion version = new EzVersion();
version.Major = 1; //実際のクライアントのバージョンを入力 メジャーバージョン
version.Minor = 2; //実際のクライアントのバージョンを入力 マイナーバージョン
version.Micro = 3; //実際のクライアントのバージョンを入力 マイクロバージョン
targetVersion.Version = version;
targetVersions.Add(targetVersion);

バージョンチェック

つぎに、バージョン情報を引数にバージョンチェックのAPIを実行します。

バージョンチェックを行う 実装例 (Unity)

var domain = gs2.Version.Namespace(
    namespaceName: "namespace-0001" // ネームスペース名
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Checker();
var result = await domain.CheckVersionAsync(
    targetVersions: targetVersions.ToArray()
);

Debug.Log(result.ProjectToken); // プロジェクトトークン
Debug.Log(result.Warnings); // 警告バージョンの検証結果のリスト
Debug.Log(result.Errors); // エラーバージョンの検証結果のリスト
var domain = gs2.Version.Namespace(
    namespaceName: "namespace-0001"
).Me(
    gameSession: GameSession // GameSessionオブジェクト(アクセストークン)
).Checker();
var future = domain.CheckVersion(
    targetVersions: targetVersions.ToArray()
);
yield return future;
if (future.Error != null)
{
    onError.Invoke(
        future.Error
    );
    yield break;
}
var result = future.Result;

Debug.Log(result.ProjectToken); // プロジェクトトークン
Debug.Log(result.Warnings); // 警告バージョンの検証結果のリスト
Debug.Log(result.Errors); // エラーバージョンの検証結果のリスト

返り値の 'Warnings' と 'Errors' には通過できなかったバージョン情報が返ります。
'Warnings' と'Errors'はリストですので、
それぞれのサイズが0であればバージョンに問題なし⇒ゲームを開始する、
どちらかのサイズが1以上であればバージョンに問題あり⇒バージョンアップを促す、
という実装を想定しています。

プロジェクトトークンの切り替え

返り値の projectToken には権限が昇格したセキュリティポリシー(例:ApplicationAccessセキュリティポリシー)を持つユーザーのプロジェクトトークンが返ります。
APIアクセスをこのプロジェクトトークンに切り替えるには、ユーティリティクラス Profile の UpdateProjectTokenprojectToken を渡し実行します。
以降のAPIアクセスは、昇格した権限に切り替わります。

// ProjectToken切り替え
Profile.UpdateProjectToken(projectToken);
// ProjectToken切り替え
Profile.UpdateProjectToken(projectToken);
署名検証

バージョンチェックの際、申告するクライアントが虐偽のバージョン値を申告することができないよう、署名での検証処理を追加することができます。
これは意図的にバージョンの古いクライアントアプリケーションでプレイし続けるチート行為等への対策となります。

バージョン値と紐づいた署名をあらかじめ計算し、送信する EzVersion に署名を設定します。
署名の計算はcalculateSignatureで行います。

バージョンの署名を計算する例 (C#)

var session = new Gs2RestSession(
    new BasicGs2Credential(
        'your client id',
        'your client secret'
    ),
    Region.ApNortheast1
);
yield return session.Open();
var client = new Gs2VersionRestClient(session);

AsyncResult<Gs2.Gs2Account.Result.CreateNamespaceResult> asyncResult = null;
yield return client.calculateSignature(
    new CalculateSignatureRequest()
        .withNamespaceName("namespace-0001")
        .withVersionName("sample")
        .withVersion({'major': 1, 'minor': 2, 'micro': 3}),
    r => asyncResult = r
);
if (asyncResult.Error != null) {
    throw asyncResult.Error;
}
var result = asyncResult.Result;

var body = result.Body;
var signature = result.Signature;

サーバー側ではバージョンモデルマスターデータ EzVersionModel の needSignature(署名検証を必要とするか)を有効にします。
また、実際にクライアント側で署名情報を静的に保持する際、ネイティブコード内への埋め込みや暗号化等を考慮頂くことを推奨致します。

バージョン情報に署名情報(body、signature)を設定する例 (Unity)

List<EzTargetVersion> targetVersions = new List<EzTargetVersion>();
EzTargetVersion targetVersion = new EzTargetVersion();
targetVersion.VersionName = "sample";

EzVersion version = new EzVersion();
version.Major = 1;
version.Minor = 2;
version.Micro = 3;
EzTargetVersion targetVersion = new EzTargetVersion();
targetVersion.Version = version;
targetVersion.VersionName = "sample";

targetVersion.Body = body;
targetVersion.Signature = signature;

targetVersions.Add(targetVersion);
List<EzTargetVersion> targetVersions = new List<EzTargetVersion>();
EzTargetVersion targetVersion = new EzTargetVersion();
targetVersion.VersionName = "sample";

EzVersion version = new EzVersion();
version.Major = 1;
version.Minor = 2;
version.Micro = 3;
EzTargetVersion targetVersion = new EzTargetVersion();
targetVersion.Version = version;
targetVersion.VersionName = "sample";

targetVersion.Body = body;
targetVersion.Signature = signature;

targetVersions.Add(targetVersion);

スタンプシート

スタンプシートとは

スタンプシートのコンセプト

GS2 には各サービス間を連携させる仕組みとして、 スタンプシート というシステムがあります。
ほかのサービスが受け持つリソース操作等を実行の条件とする処理の実装を可能にします。

スタンプシートは、企業における稟議書のような仕組みです。
各サービスが承認をするとスタンプ(署名)を押し、すべてが揃った状態であれば処理を実行できます。

承認に必要なリソースの消費等の処理を、スタンプタスクとよびます。
各サービスで要求されたスタンプタスクの実行が可能であれば、スタンプが得られます。
スタンプシートにすべてのスタンプが押された状態で、そのスタンプシートの処理と
スタンプタスクの処理が実行できます。

たとえば、クエストのクリア報酬の処理であれば、
スタミナ値の減少受注可能な回数の減少スタンプタスクで設定し、
その消費に対する報酬として 経験値を入手クエストクリアフラグの書き換え という
処理を最後にまとめて実行する、といった流れになります。

スタンプシートを使用したクエストのマスターデータの例(JSON形式)

{
  "version": "2019-05-14",
  "groups": [
    {
      "name": "main",
      "metadata": "Main Scenario",
      "quests": [
        {
          "name": "chapter-0001",
          "metadata": "Chapter 1. The beginning of the adventure",
          "contents": [
            {
              "metadata": "NORMAL",
              "completeAcquireActions": [
                {
                  "action": "Gs2Money:DepositByUserId",
                  "request": "{\"namespaceName\": \"money-0001\", \"userId\": \"#{userId}\", \"slot\": \"#{slot}\", \"price\": \"0\", \"count\": \"10\"}"
                },
                {
                  "action": "Gs2Money:DepositByUserId",
                  "request": "{\"namespaceName\": \"money-0001\", \"userId\": \"#{userId}\", \"slot\": \"#{slot}\", \"price\": \"0\", \"count\": \"10\"}"
                }
              ],
              "weight": 95
            },
            {
              "metadata": "RARE",
              "completeAcquireActions": [
                {
                  "action": "Gs2Money:DepositByUserId",
                  "request": "{\"namespaceName\": \"money-0001\", \"userId\": \"#{userId}\", \"slot\": \"#{slot}\", \"price\": \"0\", \"count\": \"20\"}"
                },
                {
                  "action": "Gs2Money:DepositByUserId",
                  "request": "{\"namespaceName\": \"money-0001\", \"userId\": \"#{userId}\", \"slot\": \"#{slot}\", \"price\": \"0\", \"count\": \"10\"}"
                }
              ],
              "weight": 5
            }
          ],
          "consumeActions": [
            {
              "action": "Gs2Stamina:ConsumeStaminaByUserId",
              "request": "{\"namespaceName\": \"stamina-0001\", \"staminaName\": \"main\", \"userId\": \"#{userId}\", \"consumeValue\": \"10\"}"
            }
          ],
          "failedAcquireActions": [
            {
              "action": "Gs2Stamina:RecoverStaminaByUserId",
              "request": "{\"namespaceName\": \"stamina-0001\", \"staminaName\": \"main\", \"userId\": \"#{userId}\", \"recoverValue\": \"10\"}"
            }
          ],
          "premiseQuestNames": [],
          "questModelId": "grn:gs2:ap-northeast-1:sampleproject:quest:quest-0001:group:main:quest:chapter-0001"
        }

      ],
      "questGroupModelId": "grn:gs2:ap-northeast-1:sampleproject:quest:quest-0001:group:main"
    }
  ]
}

スタンプシートの変数

スタンプシートスタンプタスクGS2 のそれぞれのサービスへリクエストを発行します。

スタンプタスクは、スタンプシートの対象サービスのAPIの実行に必要な条件を満たすためのタスクで、
ユーザーのリソースを減らすアクション(consumeActions)を設定できます。

スタンプシートには完了時にユーザーのリソースを増やすアクション(completeAcquireActions)、
失敗時にユーザーのリソースを戻すアクション(failedAcquireActions)を設定できます。

スタンプシート発行時のパラメータ設定

スタンプシートの発行リクエストで、Config(EzConfig)を使用して文字列を置換する例

ウォレットに残高を加算するスタンプシートの例(JSON形式)

"salesItem": {
  "name": "currency-120-jpy",
  "metadata": "price: 120 currencyCount: 50",
  "consumeActions": [
    {
      "action": "Gs2Money:RecordReceipt",
      "request": "{\"namespaceName\": \"money-0001\", \"contentsId\": \"io.gs2.sample.currency120\", \"userId\": \"#{userId}\", \"receipt\": \"#{receipt}\"}"
    }
  ],
  "acquireActions": [
    {
      "action": "Gs2Money:DepositByUserId",
      "request": "{\"namespaceName\": \"money-0001\", \"userId\": \"#{userId}\", \"slot\": \"#{slot}\", \"price\": 120, \"count\": 50}"
    }
  ]
}

ウォレットに残高を加算するスタンプシートの例(GS2-Deploy テンプレート YAML形式)

- type: salesItem
  salesItem:
    name: currency-120-jpy
    metadata: "price: 120 currencyCount: 50"
    consumeActions:
      - action: Gs2Money:RecordReceipt
        request:
          namespaceName: ${MoneyNamespaceName}
          contentsId: io.gs2.sample.currency120
          userId: "#{userId}"
          receipt: "#{receipt}"
    acquireActions:
      - action: Gs2Money:DepositByUserId
        request:
          namespaceName: ${MoneyNamespaceName}
          userId: "#{userId}"
          slot: "#{slot}"
          price: 120
          count: 50

Config(EzConfig)を使用してプレースホルダー文字列を置換する例

AsyncResult<EzBuyResult> result = null;
yield return client.Showcase.Buy(
    r => { result = r; },
    session,
    showcaseNamespaceName,
    showcaseName,
    selectedProduct.Id,
    new List<EzConfig>
    {
        new EzConfig
        {
            Key = "slot", // #{slot} を Slot(スロット番号)に置換
            Value = Slot.ToString(),
        },
        new EzConfig
        {
            Key = "receipt", // #{receipt} を receipt(レシート文字列)に置換
            Value = receipt,
        },
    }
);
AsyncResult<EzBuyResult> result = null;
yield return client.Showcase.Buy(
    r => { result = r; },
    session,
    showcaseNamespaceName,
    showcaseName,
    selectedProduct.Id,
    new List<EzConfig>
    {
        new EzConfig
        {
            Key = "slot", // #{slot} を Slot(スロット番号)に置換
            Value = Slot.ToString(),
        },
        new EzConfig
        {
            Key = "receipt", // #{receipt} を receipt(レシート文字列)に置換
            Value = receipt,
        },
    }
);

各サービスへのリクエストには、どのユーザーのリソースを操作するかという情報が必要ですが、
ゲーム内ストアやクエストのマスターデータに、あらかじめユーザーIDを静的に指定することができません。
そのためスタンプシートのリクエストに変数を埋め込むことができます。

マスターデータのアクションのリクエストに、#{userId} というプレースホルダー文字列を設定すると、
その部分はスタンプシートを発行する際にスタンプシートの発行をおこなったユーザーのユーザーIDに置換されます。

Config

スタンプシートの発行リクエストには Config(EzConfig)というパラメータが渡せるようになっています。
Config(EzConfig) はキー・バリュー形式で、渡したパラメータで #{Config で指定したキー値} のプレースホルダー文字列を置換することができます。

スタンプタスクの実行結果

アクションのリクエストの記述内容に、例として ${Gs2Money:WithdrawByUserId.price} というプレースホルダー文字列を設定すると、
その部分はスタンプタスクの実行結果に置換され、変数として利用することができます。
例に示したケースでは、実行したタスクのうち Gs2Money:WithdrawByUserId の実行結果を参照し、戻り値の price を値として使用します。
子要素を参照する場合は ${Gs2Money:WithdrawByUserId.item.paid} のようにドットで繋ぐことで参照できます。

同一のアクションがスタンプタスクとして複数登録されている場合に採用される値は不定です。

スタンプシートの一覧

Gs2Dictionary:AddEntriesByUserId

スタンプシートを使用してエントリーを追加

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "entryModelNames": "[string[]]エントリー名のリスト"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
entryModelNames string[] [] エントリー名のリスト

Gs2Enhance:DirectEnhanceByUserId

スタンプシートを使用して強化を実行

{
  "namespaceName": "[string]ネームスペース名",
  "rateName": "[string]強化レート名",
  "userId": "#{userId}",
  "targetItemSetId": "[string]有効期限ごとのアイテム所持数量GRN",
  "materials": "[Material[]]強化素材リスト",
  "config": "[Config[]]スタンプシートの変数に適用する設定値"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
rateName string ~ 128文字 強化レート名
userId string ~ 128文字 ユーザーID
targetItemSetId string ~ 1024文字 有効期限ごとのアイテム所持数量GRN
materials Material[] 強化素材リスト
config Config[] [] スタンプシートの変数に適用する設定値

Gs2Enhance:CreateProgressByUserId

スタンプシートを使用して強化を開始

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "rateName": "[string]レートモデル名",
  "targetItemSetId": "[string]有効期限ごとのアイテム所持数量GRN",
  "materials": "[Material[]]強化素材リスト",
  "force": "[bool]すでに開始している強化がある場合にそれを破棄して開始するか"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
rateName string ~ 128文字 レートモデル名
targetItemSetId string ~ 1024文字 有効期限ごとのアイテム所持数量GRN
materials Material[] 強化素材リスト
force bool false すでに開始している強化がある場合にそれを破棄して開始するか

Gs2Exchange:ExchangeByUserId

スタンプシートを使用して交換を実行

{
  "namespaceName": "[string]ネームスペース名",
  "rateName": "[string]交換レートの名前",
  "userId": "#{userId}",
  "count": "[int]交換するロット数",
  "config": "[Config[]]スタンプシートの変数に適用する設定値"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
rateName string ~ 128文字 交換レートの名前
userId string ~ 128文字 ユーザーID
count int 1 ~ 2147483646 交換するロット数
config Config[] [] スタンプシートの変数に適用する設定値

Gs2Exchange:CreateAwaitByUserId

スタンプシートを使用して交換待機 を作成

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "rateName": "[string]交換レート名",
  "count": "[int]交換数"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
rateName string ~ 128文字 交換レート名
count int 1 1 ~ 10000 交換数

Gs2Experience:AddExperienceByUserId

スタンプシートを使用して経験値を加算

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "experienceName": "[string]経験値モデルの名前",
  "propertyId": "[string]プロパティID",
  "experienceValue": "[long]獲得経験値"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
experienceName string ~ 128文字 経験値モデルの名前
propertyId string ~ 1024文字 プロパティID
experienceValue long 0 ~ 9223372036854775806 獲得経験値

Gs2Experience:AddRankCapByUserId

スタンプシートを使用してランクキャップを加算

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "experienceName": "[string]経験値モデルの名前",
  "propertyId": "[string]プロパティID",
  "rankCapValue": "[long]現在のランクキャップ"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
experienceName string ~ 128文字 経験値モデルの名前
propertyId string ~ 1024文字 プロパティID
rankCapValue long ~ 9223372036854775806 現在のランクキャップ

Gs2Experience:SetRankCapByUserId

スタンプシートを使用してランクキャップを更新

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "experienceName": "[string]経験値モデルの名前",
  "propertyId": "[string]プロパティID",
  "rankCapValue": "[long]現在のランクキャップ"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
experienceName string ~ 128文字 経験値モデルの名前
propertyId string ~ 1024文字 プロパティID
rankCapValue long ~ 9223372036854775806 現在のランクキャップ

Gs2Formation:AddMoldCapacityByUserId

スタンプシートを使用してキャパシティサイズを加算

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "moldName": "[string]編成モデルの名前",
  "capacity": "[int]現在のキャパシティ"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
moldName string ~ 128文字 編成モデルの名前
capacity int ~ 2147483646 現在のキャパシティ

Gs2Formation:SetMoldCapacityByUserId

スタンプシートを使用してキャパシティサイズを設定

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "moldName": "[string]編成モデルの名前",
  "capacity": "[int]現在のキャパシティ"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
moldName string ~ 128文字 編成モデルの名前
capacity int ~ 2147483646 現在のキャパシティ

Gs2Formation:AcquireActionsToFormProperties

スタンプシートを使用して編成対象のプロパティにリソースを加算

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "moldName": "[string]フォームの保存領域の名前",
  "index": "[int]保存領域のインデックス",
  "acquireAction": "[AcquireAction]フォームのプロパティに適用する入手アクション",
  "config": "[AcquireActionConfig[]]入手アクションに適用するコンフィグ"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
moldName string ~ 128文字 フォームの保存領域の名前
index int ~ 2147483646 保存領域のインデックス
acquireAction AcquireAction フォームのプロパティに適用する入手アクション
config AcquireActionConfig[] [] 入手アクションに適用するコンフィグ

Gs2Formation:AcquireActionsToPropertyFormProperties

スタンプシートを使用して編成対象のプロパティにリソースを加算

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "formModelName": "[string]フォームの保存領域の名前",
  "propertyId": "[string]プロパティID",
  "acquireAction": "[AcquireAction]フォームのプロパティに適用する入手アクション",
  "config": "[AcquireActionConfig[]]入手アクションに適用するコンフィグ"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
formModelName string ~ 128文字 フォームの保存領域の名前
propertyId string ~ 1024文字 プロパティID
acquireAction AcquireAction フォームのプロパティに適用する入手アクション
config AcquireActionConfig[] [] 入手アクションに適用するコンフィグ

Gs2Inbox:SendMessageByUserId

スタンプシートを使用してメッセージを送信

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "metadata": "[string]メッセージの内容に相当するメタデータ",
  "readAcquireActions": "[AcquireAction[]]開封時に実行する入手アクション",
  "expiresAt": "[long]有効期限日時",
  "expiresTimeSpan": "[TimeSpan_]メッセージの有効期限までの差分"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
metadata string ~ 4096文字 メッセージの内容に相当するメタデータ
readAcquireActions AcquireAction[] [] 開封時に実行する入手アクション
expiresAt long 有効期限日時
expiresTimeSpan TimeSpan_ メッセージの有効期限までの差分

Gs2Inventory:AddCapacityByUserId

スタンプシートを使用してキャパシティサイズを加算

{
  "namespaceName": "[string]ネームスペース名",
  "inventoryName": "[string]インベントリモデル名",
  "userId": "#{userId}",
  "addCapacityValue": "[int]加算するキャパシティサイズ"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
inventoryName string ~ 128文字 インベントリモデル名
userId string ~ 128文字 ユーザーID
addCapacityValue int 1 ~ 2147483646 加算するキャパシティサイズ

Gs2Inventory:SetCapacityByUserId

スタンプシートを使用してキャパシティサイズを設定

{
  "namespaceName": "[string]ネームスペース名",
  "inventoryName": "[string]インベントリモデル名",
  "userId": "#{userId}",
  "newCapacityValue": "[int]インベントリの新しい最大キャパシティ"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
inventoryName string ~ 128文字 インベントリモデル名
userId string ~ 128文字 ユーザーID
newCapacityValue int 1 ~ 2147483646 インベントリの新しい最大キャパシティ

Gs2Inventory:AcquireItemSetByUserId

スタンプシートを使用してアイテムをインベントリに追加

{
  "namespaceName": "[string]ネームスペース名",
  "inventoryName": "[string]インベントリの名前",
  "itemName": "[string]アイテムモデルの名前",
  "userId": "#{userId}",
  "acquireCount": "[long]入手する量",
  "expiresAt": "[long]有効期限",
  "createNewItemSet": "[bool]既存の ItemSet に空きがあったとしても、新しい ItemSet を作成するか",
  "itemSetName": "[string]アイテムセットを識別する名前"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
inventoryName string ~ 128文字 インベントリの名前
itemName string ~ 128文字 アイテムモデルの名前
userId string ~ 128文字 ユーザーID
acquireCount long 1 ~ 9223372036854775806 入手する量
expiresAt long 0 有効期限
createNewItemSet bool false 既存の ItemSet に空きがあったとしても、新しい ItemSet を作成するか
itemSetName string ~ 36文字 アイテムセットを識別する名前

Gs2Inventory:AddReferenceOfByUserId

スタンプシートを使用してアイテムに参照元を追加

{
  "namespaceName": "[string]ネームスペース名",
  "inventoryName": "[string]インベントリモデルの名前",
  "userId": "#{userId}",
  "itemName": "[string]アイテムモデルの名前",
  "itemSetName": "[string]アイテムセットを識別する名前",
  "referenceOf": "[string]この所持品の参照元"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
inventoryName string ~ 128文字 インベントリモデルの名前
userId string ~ 128文字 ユーザーID
itemName string ~ 128文字 アイテムモデルの名前
itemSetName string UUID ~ 36文字 アイテムセットを識別する名前
referenceOf string ~ 1024文字 この所持品の参照元

Gs2Inventory:DeleteReferenceOfByUserId

スタンプシートを使用してアイテムの参照元を削除

{
  "namespaceName": "[string]ネームスペース名",
  "inventoryName": "[string]インベントリモデルの名前",
  "userId": "#{userId}",
  "itemName": "[string]アイテムモデルの名前",
  "itemSetName": "[string]アイテムセットを識別する名前",
  "referenceOf": "[string]この所持品の参照元"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
inventoryName string ~ 128文字 インベントリモデルの名前
userId string ~ 128文字 ユーザーID
itemName string ~ 128文字 アイテムモデルの名前
itemSetName string UUID ~ 36文字 アイテムセットを識別する名前
referenceOf string ~ 1024文字 この所持品の参照元

Gs2JobQueue:PushByUserId

スタンプシートを使用してジョブを登録

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "jobs": "[JobEntry[]]追加するジョブの一覧"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
jobs JobEntry[] 追加するジョブの一覧

Gs2Limit:DeleteCounterByUserId

スタンプシートを使用してカウンターを削除

{
  "namespaceName": "[string]ネームスペース名",
  "limitName": "[string]回数制限モデルの名前",
  "userId": "#{userId}",
  "counterName": "[string]カウンターの名前"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
limitName string ~ 128文字 回数制限モデルの名前
userId string ~ 128文字 ユーザーID
counterName string ~ 128文字 カウンターの名前

Gs2Lottery:DrawByUserId

スタンプシートを使用して抽選処理を実行

{
  "namespaceName": "[string]ネームスペース名",
  "lotteryName": "[string]抽選モデルの種類名",
  "userId": "#{userId}",
  "count": "[int]抽選回数",
  "config": "[Config[]]スタンプシートのプレースホルダの適用する設定値"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 128文字 ネームスペース名
lotteryName string ~ 128文字 抽選モデルの種類名
userId string ~ 128文字 ユーザーID
count int 1 ~ 1000 抽選回数
config Config[] [] スタンプシートのプレースホルダの適用する設定値

Gs2Mission:IncreaseCounterByUserId

スタンプシートを使用してカウンターを加算

{
  "namespaceName": "[string]ネームスペース名",
  "counterName": "[string]カウンター名",
  "userId": "#{userId}",
  "value": "[long]加算する値"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
counterName string ~ 128文字 カウンター名
userId string ~ 128文字 ユーザーID
value long 1 ~ 9223372036854775806 加算する値

Gs2Money:DepositByUserId

スタンプシートを使用してウォレットに残高を加算

{
  "namespaceName": "[string]ネームスペースの名前",
  "userId": "#{userId}",
  "slot": "[int]スロット番号",
  "price": "[float]購入価格",
  "count": "[int]付与する課金通貨の数量"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペースの名前
userId string ~ 128文字 ユーザーID
slot int ~ 100000000 スロット番号
price float ~ 100000.0 購入価格
count int 1 ~ 2147483646 付与する課金通貨の数量

Gs2Quest:CreateProgressByUserId

スタンプシートを使用してクエストを開始

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "questModelId": "[string]進行中のクエストモデルGRN",
  "force": "[bool]すでに開始しているクエストがある場合にそれを破棄して開始するか",
  "config": "[Config[]]スタンプシートの変数に適用する設定値"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
questModelId string ~ 1024文字 進行中のクエストモデルGRN
force bool false すでに開始しているクエストがある場合にそれを破棄して開始するか
config Config[] [] スタンプシートの変数に適用する設定値

Gs2Stamina:RecoverStaminaByUserId

スタンプシートを使用してスタミナを回復

{
  "namespaceName": "[string]ネームスペース名",
  "staminaName": "[string]スタミナモデルの名前",
  "userId": "#{userId}",
  "recoverValue": "[int]スタミナの回復量"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
staminaName string ~ 128文字 スタミナモデルの名前
userId string ~ 128文字 ユーザーID
recoverValue int 1 ~ 2147483646 スタミナの回復量

Gs2Stamina:RaiseMaxValueByUserId

スタンプシートを使用してスタミナの最大値を加算

{
  "namespaceName": "[string]ネームスペース名",
  "staminaName": "[string]スタミナモデルの名前",
  "userId": "#{userId}",
  "raiseValue": "[int]上昇する最大スタミナ量"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
staminaName string ~ 128文字 スタミナモデルの名前
userId string ~ 128文字 ユーザーID
raiseValue int ~ 2147483646 上昇する最大スタミナ量

Gs2Stamina:SetMaxValueByUserId

スタンプシートを使用してスタミナの最大値を更新

{
  "namespaceName": "[string]ネームスペース名",
  "staminaName": "[string]スタミナモデルの名前",
  "userId": "#{userId}",
  "maxValue": "[int]スタミナの最大値"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
staminaName string ~ 128文字 スタミナモデルの名前
userId string ~ 128文字 ユーザーID
maxValue int 1 ~ 2147483646 スタミナの最大値

Gs2Stamina:SetRecoverIntervalByUserId

スタンプシートを使用してスタミナの最大値を更新

{
  "namespaceName": "[string]ネームスペース名",
  "staminaName": "[string]スタミナモデルの名前",
  "userId": "#{userId}",
  "recoverIntervalMinutes": "[int]スタミナの回復間隔(分)"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
staminaName string ~ 128文字 スタミナモデルの名前
userId string ~ 128文字 ユーザーID
recoverIntervalMinutes int 1 ~ 2147483646 スタミナの回復間隔(分)

Gs2Stamina:SetRecoverValueByUserId

スタンプシートを使用してスタミナの回復量を更新

{
  "namespaceName": "[string]ネームスペース名",
  "staminaName": "[string]スタミナモデルの名前",
  "userId": "#{userId}",
  "recoverValue": "[int]スタミナの回復量"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
staminaName string ~ 128文字 スタミナモデルの名前
userId string ~ 128文字 ユーザーID
recoverValue int 1 ~ 2147483646 スタミナの回復量

スタンプタスクの一覧

Gs2Enhance:DeleteProgressByUserId

スタンプタスクで 実行中の強化 を削除

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID

Gs2Exchange:DeleteAwaitByUserId

スタンプタスクで 交換待機 を削除

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "awaitName": "[string]交換待機の名前"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
awaitName string UUID ~ 36文字 交換待機の名前

Gs2Inbox:OpenMessageByUserId

スタンプシートを使用してメッセージを開封済みにマーキング

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "messageName": "[string]メッセージ名"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
messageName string UUID ~ 36文字 メッセージ名

Gs2Inventory:ConsumeItemSetByUserId

スタンプシートを使用してインベントリのアイテムを消費

{
  "namespaceName": "[string]ネームスペース名",
  "inventoryName": "[string]インベントリモデルの名前",
  "userId": "#{userId}",
  "itemName": "[string]アイテムモデルの名前",
  "consumeCount": "[long]消費する量",
  "itemSetName": "[string]アイテムセットを識別する名前"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
inventoryName string ~ 128文字 インベントリモデルの名前
userId string ~ 128文字 ユーザーID
itemName string ~ 128文字 アイテムモデルの名前
consumeCount long 1 ~ 9223372036854775806 消費する量
itemSetName string ~ 36文字 アイテムセットを識別する名前

Gs2Inventory:VerifyReferenceOfByUserId

スタンプシートを使用してインベントリのアイテムを検証

{
  "namespaceName": "[string]ネームスペース名",
  "inventoryName": "[string]インベントリモデルの名前",
  "userId": "#{userId}",
  "itemName": "[string]アイテムモデルの名前",
  "itemSetName": "[string]アイテムセットを識別する名前",
  "referenceOf": "[string]この所持品の参照元",
  "verifyType": "[string]検証の種類"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
inventoryName string ~ 128文字 インベントリモデルの名前
userId string ~ 128文字 ユーザーID
itemName string ~ 128文字 アイテムモデルの名前
itemSetName string UUID ~ 36文字 アイテムセットを識別する名前
referenceOf string ~ 1024文字 この所持品の参照元
verifyType string ~ 128文字 検証の種類

Gs2Limit:CountUpByUserId

スタンプシートを使用してカウントアップ

{
  "namespaceName": "[string]ネームスペース名",
  "limitName": "[string]回数制限モデルの名前",
  "counterName": "[string]カウンターの名前",
  "userId": "#{userId}",
  "countUpValue": "[int]カウントアップする量",
  "maxValue": "[int]カウントアップを許容する最大値"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
limitName string ~ 128文字 回数制限モデルの名前
counterName string ~ 128文字 カウンターの名前
userId string ~ 128文字 ユーザーID
countUpValue int 1 1 ~ 2147483646 カウントアップする量
maxValue int 1 ~ 2147483646 カウントアップを許容する最大値

Gs2Mission:ReceiveByUserId

達成状況を作成

{
  "namespaceName": "[string]ネームスペース名",
  "missionGroupName": "[string]ミッショングループ名",
  "missionTaskName": "[string]タスク名",
  "userId": "#{userId}"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
missionGroupName string ~ 128文字 ミッショングループ名
missionTaskName string ~ 128文字 タスク名
userId string ~ 128文字 ユーザーID

Gs2Money:WithdrawByUserId

スタンプシートを使用してウォレットから残高を消費

{
  "namespaceName": "[string]ネームスペースの名前",
  "userId": "#{userId}",
  "slot": "[int]スロット番号",
  "count": "[int]消費する課金通貨の数量",
  "paidOnly": "[bool]有償課金通貨のみを対象とするか"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペースの名前
userId string ~ 128文字 ユーザーID
slot int ~ 100000000 スロット番号
count int 1 ~ 2147483646 消費する課金通貨の数量
paidOnly bool false 有償課金通貨のみを対象とするか

Gs2Money:RecordReceipt

スタンプシートを使用してレシートを記録

{
  "namespaceName": "[string]ネームスペースの名前",
  "userId": "#{userId}",
  "contentsId": "[string]ストアプラットフォームで販売されているコンテンツID",
  "receipt": "[string]レシート"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペースの名前
userId string ~ 128文字 ユーザーID
contentsId string ~ 1024文字 ストアプラットフォームで販売されているコンテンツID
receipt string ~ 1048576文字 レシート

Gs2Quest:DeleteProgressByUserId

スタンプタスクで クエスト進行状況 を削除

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID

Gs2SerialKey:UseByUserId

スタンプタスクを使用してシリアルコードを消費

{
  "namespaceName": "[string]ネームスペース名",
  "userId": "#{userId}",
  "code": "[string]シリアルコード"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
userId string ~ 128文字 ユーザーID
code string ~ 48文字 シリアルコード

Gs2Stamina:ConsumeStaminaByUserId

スタンプタスクを使用してスタミナを消費

{
  "namespaceName": "[string]ネームスペース名",
  "staminaName": "[string]スタミナモデルの名前",
  "userId": "#{userId}",
  "consumeValue": "[int]消費するスタミナ量"
}
必須 デフォルト 値の制限 説明
namespaceName string ~ 32文字 ネームスペース名
staminaName string ~ 128文字 スタミナモデルの名前
userId string ~ 128文字 ユーザーID
consumeValue int 1 ~ 2147483646 消費するスタミナ量

用語集

一般

GRN のフォーマット

grn:gs2:{リージョン}:{オーナーID}:{サービス名}:{モデル名}:{代替キー}:{モデル名}:{代替キー}...

リージョンの種類

ap-northeast-1 = 日本

ポリシードキュメントのフォーマット(JSON)
Actions に呼び出せるアクションを羅列し、Resources に操作出来る対象リソースを羅列する
Actions / Resources には末尾にのみワイルドカードを使用出来る

{
"Version": "2016-04-01",
"Statements": [
{
"Effect": "Allow",
"Actions": [
"Gs2Account:Create*",
],
"Resources": ["*"],
},
] }
用語 意味
GRN Gs2 Resource Name
GS2 が扱うすべての情報に付与される一意なリソース名
サービス名 GS2 のマイクロサービスの種類
リージョン GS2 が動作しているデータセンターの場所。
アカウントID GS2 のアカウントを一意に識別するためのID
プロジェクトID ゲームを一意に識別するためのID。アカウントIDに複数紐づけて管理
オーナーID {アカウントID}-{プロジェクトID}
GS2内のデータは基本的にこの情報に紐づけて保存される
ユーザID ゲームプレイヤーを識別するID
アクセストークン GS2-Auth によってゲームプレイヤーの認証がおこなわれた証明として払い出されるトークン
各マイクロサービスが利用者のユーザIDを特定する為に使用される

有効期限が設定されており、発行から1時間で再取得が必要となる。
プロジェクトトークン クレデンシャル を使用して取得する一時的な認証トークン。
Open ID Connect ベースで実装されており、Open ID Connect 的に言うと IDトークン。

有効期限が設定されており、発行から10時間で再取得が必要となる。
セキュリティポリシー GS2-Identifier のユーザに紐付けて管理される権限
クライアントIDを使用してどのAPI操作を行えるかを定義する
ポリシードキュメント セキュリティポリシー の権限設定を定義した JSON ドキュメント
モデル GS2 内のエンティティの型
エンティティ モデルを実体化したもの
代替キー エンティティのGRN以外のエンティティ固有の値を格納したフィールド名
GS2-Account の Game: gameName, Account: userId, TakeOver: type, userIdentifier
アクション API の種類を表す識別子 Gs2Inbox:CreateInbox のように定義され、前半部はサービスを表し、後半部はアクションの種類を表す。
プロパティID ゲーム内のキャラクターやアイテムを一意に識別するためのID
リソースGRN ゲームプレイヤーの所持物のGRNの総称
GS2-Inventory の所持物や、GS2-Experience の経験値情報、GS2-Stamina のスタミナ値など
リクエストID GS2 に対するAPIリクエスト毎に割り当てられるID
そのAPIより派生した GS2-Script 内のAPIアクセスなどは同じリクエストIDが共有される

リクエストID によって一連の処理の流れを追跡することが可能となり、
トランザクション処理においては、同一処理系によるデッドロックが発生することを避けることができる
コンテキスト GS2 のリクエストIDに割り当てられたスタック領域の名前
あるいは同一リクエストIDの処理系におけるスタック領域の状態。
スタック領域 リクエストIDが同一の処理系において処理がチェインする毎に積み重ねるメモリ空間

GS2-Account

用語 意味
アカウント認証情報・署名 GS2-Account によって認証が成功したときに発行される情報。署名をあわせて利用することで改ざん検出が可能
引き継ぎ設定 GS2-Account が発行したユーザIDとパスワードを復元するための設定。引き継ぎを実行するために必要となる任意の値を設定可能なユーザID・パスワードを保持する

GS2-Identifier

用語 意味
クライアントID API の利用者を識別する為のID
GS2-Identifier のユーザに紐付けて管理される
クライアントシークレット クライアントID が正規の利用者によって使用されていることを識別するためのキー
クレデンシャル クライアントID と クライアントシークレット の組み合わせ

GS2-Money

用語 意味
課金通貨 資金決済法の前払式支払手段(自家型)に該当するゲーム内通貨

GS2-Matchmaking

用語 意味
ギャザリング マッチメイキングによって作成されたプレイヤーの集合
ロール マッチメイキング時の条件に使用するプレイヤーの役割(タンク・ヒーラーなど)

エラー・例外

エラーメッセージの構成

例:[{"component":"progress","message":"quest.progress.progress.error.notFound"}]

セクション 説明
"component": エラーが発生した対象を示し、メンバー名、メソッド名が入ります。
"message": エラー箇所の詳細、エラーを表すキーワードが入ります。
サービス名.コンポーネント.error.エラー内容
主要なエラー内容 説明
failed 処理に失敗しました。
invalid 不正なパラメータです。
require 必要な引数がありません。
tooLong 引数の文字数が長すぎます。
exists 既に存在しています。
duplicate 重複、既に存在しています。
notFound 見つかりませんでした。
notAuthorized 認証ができませんでした。
notMatch 合致しませんでした。
deny 拒否されました。
banned BANされました。

例外の種類

GS2でエラーが発生したとき、以下のような例外が発生します。

例外 エラー内容 ステータスコード
BadRequestException リクエストの内容が不正です。 400
UnauthorizedException GS2との接続に必要な権限の認証ができませんでした。APIアクセスに使用しているアクセストークンの期限が切れた場合にも発生します。 401
QuotaLimitExceededException クォータ(割り当て)の制限を超えました。 402
NotFoundException リクエストの対象が見つかりませんでした。 404
ConflictException コンフリクトが発生しました。 409
InternalServerErrorException サーバーでエラーが発生しました。 500
BadGatewayException サーバーが無効なレスポンスを受け取りました。GS2-Scriptのエラー時に発生します。 502
ServiceUnavailableException サービスでエラーが発生しました。 503
RequestTimeoutException リクエストがタイムアウトしました。 504
UnknownException 不明な例外が発生しました。

機能-サービス 索引

ユーザーデータの管理

機能 サービス名
アカウント管理 GS2-Account GS2-Auth
経験値・ランク GS2-Experience
装備 GS2-Formation
所持品 GS2-Inventory
インベントリ GS2-Inventory
ゴールド ゲーム内通貨管理 GS2-Inventory
課金通貨 GS2-Money
スタミナ GS2-Stamina
図鑑 GS2-Dictionary

ゲームサイクル

機能 サービス名
アイテム強化・合成 GS2-Enhance
リソース交換 GS2-Exchange
プレゼントボックス/ギフトボックス GS2-Inbox
抽選 GS2-Lottery
ミッション GS2-Mission
クエスト GS2-Quest
ゲーム内ストア GS2-Showcase

コミュニケーション

機能 サービス名
チャット GS2-Chat
バイナリデータの保存 GS2-Datastore
パーティ編成 GS2-Formation
フレンド GS2-Friend
マッチメイキング機能 GS2-Matchmaking
ランキング GS2-Ranking
対戦・協力プレイ機能 GS2-Realtime

運営

機能 サービス名
プレゼントボックス/ギフトボックス GS2-Inbox
お知らせ GS2-News
イベントスケジュール管理 GS2-Schedule
バージョン管理 GS2-Version

システム

機能 サービス名
所持数オーバー時の退避 GS2-Distributor
プッシュ通知(ゲーム内) GS2-Gateway
非同期ジョブの実行 GS2-JobQueue
暗号鍵の管理 GS2-Key
回数制限 GS2-Limit
排他処理 GS2-Lock

マネージメント

機能 サービス名
リソースの構築 GS2-Deploy
認証・権限管理 GS2-Identifier
ログ出力 GS2-Log

サービス間の依存関係

GS2-Account

依存するサービス 依存する理由 依存する条件
GS2-Key::Key 認証結果の署名発行 必ず
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-Auth

依存するサービス 依存する理由 依存する条件
GS2-Key::Key GS2-Account の認証結果の署名検証 GS2-Account の認証結果を使って アクセストークンの発行 をしようとしたとき

GS2-Chat

依存するサービス 依存する理由 依存する条件
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-Distributor

依存するサービス 依存する理由 依存する条件
GS2-Identifier::User 報酬付与を実行する際に実行権限を設定したユーザが必要 必ず
GS2-Inbox::Namespace 報酬が溢れたときに GS2-Inbox に届けるのに必要 GS2-Inventory/GS2-Stamina への報酬付与を実行した場合に依存
その他すべて スタンプシートの実行のため スタンプシートの対価や報酬に設定されたマイクロサービスに依存

GS2-Exchange

依存するサービス 依存する理由 依存する条件
GS2-JobQueue::Namespace 報酬が複数存在する場合に報酬の付与に使用 報酬を複数設定した場合に依存
GS2-Key::Key スタンプシートの署名発行 必ず
その他すべて スタンプシートの発行のため スタンプシートの対価や報酬に設定するマイクロサービスに依存

GS2-Experience

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートで報酬を付与する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-Formation

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートで報酬を付与する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-Friend

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートで報酬を付与する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存
GS2-Gateway::Namespace フレンドリクエスト通知 常時接続通信セッションを利用してプッシュ通知を受ける設定をした場合に依存

GS2-Gateway

依存するサービス 依存する理由 依存する条件
Firebase::FCM モバイルプッシュ通知を送信する プッシュ通知リクエストの際に対象ユーザがオフラインだった場合、モバイルプッシュに転送するオプションが有効な場合に依存

GS2-Identifier

依存するサービス 依存する理由 依存する条件

GS2-Inbox

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートでメッセージを送信する場合に依存
GS2-Key::Key スタンプシートの発行 メッセージに報酬を添付する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存
GS2-JobQueue::Namespace 報酬が複数存在する場合に報酬の付与に使用 メッセージに複数の報酬を設定した場合に依存
GS2-Gateway::Namespace 新着メッセージ通知 常時接続通信セッションを利用してプッシュ通知を受ける設定をした場合に依存

GS2-Inventory

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートでアイテムを付与する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-JobQueue

依存するサービス 依存する理由 依存する条件
GS2-Gateway::Namespace ジョブが登録された通知 常時接続通信セッションを利用してプッシュ通知を受ける設定をした場合に依存

GS2-Key

依存するサービス 依存する理由 依存する条件

GS2-Limit

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートでカウンターを加算する場合に依存

GS2-Lock

依存するサービス 依存する理由 依存する条件

GS2-Lottery

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートで抽選する場合に依存
GS2-Key::Key スタンプシートの発行 抽選する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存
GS2-JobQueue::Namespace 報酬が複数存在する場合に報酬の付与に使用 報酬に複数の報酬を設定した場合に依存

GS2-Matchmaking

依存するサービス 依存する理由 依存する条件
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存
GS2-Gateway::Namespace マッチメイキング進行通知 常時接続通信セッションを利用してプッシュ通知を受ける設定をした場合に依存

GS2-Mission

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートでカウンターを操作する場合に依存
GS2-Key::Key スタンプシートの発行 達成報酬を受け取る場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存
GS2-JobQueue::Namespace 報酬が複数存在する場合に報酬の付与に使用 報酬に複数の報酬を設定した場合に依存
GS2-Schedule::Event ミッション開催期間を設定 開催期間のあるタスクを設定した場合に依存

GS2-Money

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートで残高を加算する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存

GS2-Quest

依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートでクリアフラグを操作する場合に依存
GS2-Key::Key スタンプシートの発行 クエスト開始・完了処理を実行する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存
GS2-JobQueue::Namespace 報酬が複数存在する場合に報酬の付与に使用 報酬に複数の報酬を設定した場合に依存
GS2-Schedule::Event クエスト公開期間を設定 公開期間のあるクエストを設定した場合に依存

GS2-Ranking

<
依存するサービス 依存する理由 依存する条件
GS2-Key::Key スタンプシートの実行 スタンプシートでスコアを登録する場合に依存
GS2-Script::Script スクリプトの実行 イベントにスクリプトを設定した場合に依存
GS2-JobQueue::Namespace 各種登録完了スクリプトを遅延実行 完了イベントにスクリプトを設定し、なおかつジョブキューを設定した場合に依存