GS2-Version

バージョンチェック機能

アプリケーションのバージョンや、追加アセットのバージョン、利用規約に同意しているバージョンなどを判定する機能を提供します。

バージョンチェックを通過した際に、新しい一時的なGS2のクライアントID/シークレットを発行できます。

この機能を利用することで、アプリ内に組み込んだGS2のクライアントID/シークレットは、ログインを行いバージョンチェックを行うAPIしか呼び出す権限を持たず、バージョンチェックを通過した後で、実際にゲームをプレイするのに十分な権限のクライアントID/シークレットを受け取れるようにできます。

graph TD
  Boot["アプリ起動"] --> Login["GS2-Account でログイン"]
  Login -- "最小権限の<br/>クライアントID/シークレット" --> Check["GS2-Version<br/>CheckVersion"]
  Check -- "OK" --> Token["新しい ProjectToken を取得<br/>(本来の権限)"]
  Check -- "Warning" --> Notify["プレイヤーに更新を促す"]
  Check -- "Error" --> Force["強制バージョンアップ"]
  Notify --> Token
  Token --> Game["ゲーム本編"]

バージョンモデル

ネームスペースには最大10個のバージョンモデルを宣言できます。バージョンチェックには複数の項目を設定でき、全てのバージョンチェックが通過した場合にのみ、チェックを通過できます。

バージョンモデルには2種類あり「アプリケーションが送信してきたバージョンを元にバージョン判定をする」「ログイン中のユーザーが過去に同意した規約のバージョンを元にバージョン判定をする」ことができます。
前者を「パッシブバージョンチェック」後者を「アクティブバージョンチェック」と呼びます。

マスター項目 説明
name バージョンモデル名(チェック時の識別子)
scope passive(パッシブ) / active(アクティブ)
type simple(単一のしきい値) / schedule(時刻でしきい値を切り替え)
currentVersion アクティブバージョンチェックで、初回参加時に自動的に承認したことにするバージョン
warningVersion 警告を出すバージョン閾値(このバージョン以下で warnings を返却)
errorVersion エラーとするバージョン閾値(このバージョン以下で errors を返却)
scheduleVersions type: schedule 時に時刻ごとのしきい値を定義
needSignature バージョン情報に署名を要求するか
signatureKeyId 署名検証に使用する GS2-Key の鍵ID
approveRequirement アクティブバージョンチェック時の承認要件(required / optional

バージョン番号のフォーマット

{major}.{minor}.{micro}
形式のバージョン番号を利用でき、各項目には整数値が指定できます。 バージョン値の比較は majorminormicro の順に行われ、辞書順ではなく数値として比較されます。

パッシブバージョンチェック

ゲームの実行バイナリや、ゲームがダウンロードしたアセットごとのバージョンチェックに利用します。

マスターデータでは、バージョンモデルごとに「警告を発するバージョンの閾値」「エラーとするバージョンの閾値」を設定できます。
クライアントは CheckVersion API に対して、現在のアプリのビルド番号やダウンロード済みアセットのバージョンを送信し、サーバー側で閾値判定された結果を受け取ります。

判定結果は以下のいずれかになります。

結果 条件 期待される挙動
通過 全項目が warningVersion を上回る ゲーム本編へ進む
警告 (warnings) いずれかが warningVersion 以下、かつ errorVersion 超過 プレイヤーに更新を促しつつ続行可能
エラー (errors) いずれかが errorVersion 以下 強制バージョンアップ

アクティブバージョンチェック

EULA・プライバシーポリシー・特定地域の規約改定のように、「プレイヤーがどのバージョンに同意済みか」を判定する用途に利用します。

AcceptVersion API を呼び出すことで、ユーザーが任意のバージョンを承認状態にできます。 クライアントから送信されるバージョンではなく、サーバー側で記録された承認バージョンが閾値判定の対象となるため、なりすましで規約同意を回避することができません。

スケジュールによるしきい値切り替え

type: schedule を選択すると、scheduleVersions に GS2-Schedule のイベントID と そのイベント期間中のバージョンを設定できます。 例えば「12月1日からは利用規約 v2 を必須にする」といった運用を、デプロイなしで予約できます。

署名の検証

needSignature を有効にすると、CheckVersion 時に署名付きバージョン情報を要求できます。 署名は GS2-Key で発行された鍵(signatureKeyId)で検証されるため、改ざんされたバージョン情報を排除できます。

スクリプトトリガー

ネームスペースに checkVersionTriggerScriptIdacceptVersionScript を設定すると、バージョンチェック時やバージョン承認時にカスタムスクリプトを実行できます。

設定できる主なイベントトリガーとスクリプト設定名は以下の通りです。

  • checkVersionTriggerScriptId: バージョンチェック処理時に呼び出されるスクリプト。動的な判定ロジックを追加できます。
  • acceptVersionScript: バージョン承認時に呼び出されるスクリプト。

トランザクションアクション

GS2-Version では以下のトランザクションアクションを提供しています。

入手アクション

アクション 用途
Gs2Version:AcceptByUserId 指定したバージョンを承認状態にします(アクティブバージョンチェック用)。

「指定したバージョンの承認」を入手アクションとして利用することで、特定のアイテムを入手した際や、ミッションを達成した際などに、自動的に特定の規約やバージョンを承認済みにするといった処理が可能になります。

マスターデータ運用

マスターデータを登録することでマイクロサービスで利用可能なデータや振る舞いを設定できます。

マスターデータの種類には以下があります。

  • VersionModel: バージョンモデル定義

以下はマスターデータ JSON の例です。

{
  "version": "2019-08-19",
  "versionModels": [
    {
      "name": "app",
      "metadata": "アプリ本体",
      "scope": "passive",
      "type": "simple",
      "warningVersion": { "major": 1, "minor": 2, "micro": 0 },
      "errorVersion":   { "major": 1, "minor": 0, "micro": 0 }
    },
    {
      "name": "eula",
      "metadata": "利用規約",
      "scope": "active",
      "type": "simple",
      "currentVersion": { "major": 1, "minor": 0, "micro": 0 },
      "warningVersion": { "major": 1, "minor": 0, "micro": 0 },
      "errorVersion":   { "major": 1, "minor": 0, "micro": 0 },
      "approveRequirement": "required"
    }
  ]
}

マスターデータの登録はマネージメントコンソールから登録する他、GitHubからデータを反映したり、GS2-Deployを使ってCIから登録するようなワークフローを組むことが可能です。

実装例

バージョンチェックを実行

CheckVersion の結果には、警告に該当した項目 (Warnings)・エラーに該当した項目 (Errors)・成功時に発行される ProjectToken が含まれます。 ProjectToken を受け取った場合は、それを用いて再認証を行うことで、本来のゲームプレイに必要な権限を持つセッションを取得できます。

    var result = await gs2.Version.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Checker(
    ).CheckVersionAsync(
        targetVersions: new [] {
            new Gs2.Unity.Gs2Version.Model.EzTargetVersion
            {
                VersionName = "app",
                Version = new Gs2.Unity.Gs2Version.Model.EzVersion
                {
                    Major = 1,
                    Minor = 2,
                    Micro = 3,
                },
            },
            new Gs2.Unity.Gs2Version.Model.EzTargetVersion
            {
                VersionName = "asset",
                Version = new Gs2.Unity.Gs2Version.Model.EzVersion
                {
                    Major = 1,
                    Minor = 2,
                    Micro = 3,
                },
            },
        }
    );
    var projectToken = result.ProjectToken;
    var warnings = result.Warnings;
    var errors = result.Errors;

    if (errors != null && errors.Count > 0) {
        // 強制バージョンアップ
    } else if (warnings != null && warnings.Count > 0) {
        // 更新推奨ダイアログを表示しつつ続行
    }
    const auto Future = Gs2->Version->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Checker(
    )->CheckVersion(
        []
        {
            const auto v = MakeShared<TArray<TSharedPtr<Gs2::Version::Model::FTargetVersion>>>();
            v->Add({'versionName': 'app', 'version': {'major': 1, 'minor': 2, 'micro': 3}});
            v->Add({'versionName': 'asset', 'version': {'major': 1, 'minor': 2, 'micro': 3}});
            return v;
        }() // targetVersions
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
    const auto ProjectToken = Result->ProjectToken;
    const auto Warnings = Result->Warnings;
    const auto Errors = Result->Errors;

アクティブバージョンチェックに同意

利用規約への同意ボタンを押した際などに AcceptAsync を呼び出します。 同意したバージョンは AcceptVersion モデルにユーザーごとに記録され、以降の CheckVersion で参照されます。

    var result = await gs2.Version.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).AcceptVersion(
        versionName: "eula"
    ).AcceptAsync(
    );
    const auto Future = Gs2->Version->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->AcceptVersion(
        "eula" // versionName
    )->Accept(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError()) return false;
    const auto Result = Future2->GetTask().Result();

バージョンモデルの取得

UI に「現在の規約バージョン」「警告対象のしきい値」を表示するために、バージョンモデルそのものを取得できます。

    var item = await gs2.Version.Namespace(
        namespaceName: "namespace-0001"
    ).VersionModel(
        versionName: "eula"
    ).ModelAsync();
    const auto Domain = Gs2->Version->Namespace(
        "namespace-0001" // namespaceName
    )->VersionModel(
        "eula" // versionName
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
    const auto Item = Future->GetTask().Result();

より実践的な情報

バージョン更新の運用手順

バージョン更新時に、一度プレイ中のプレイヤーを全員追い出して、最新のバージョンでのみ遊べるようにしたいことがあります。
GS2-Version では新規ログインを止めることはできますが、すでにログイン済みのプレイヤーに対してはバージョンチェック後に一時的に発行されるクライアントID/クライアントシークレット の有効期限が切れるまではアクセスし続けることが可能です。

そこで、全てのプレイヤーの GS2-Gateway が提供している通知用の常時接続セッションを切断し、ゲームはセッションの切断をハンドリングすると、バージョンチェック後に再接続処理を行うようにします。
バージョンチェックに失敗した場合は、そのままバージョンアップシーケンスに入ります。

これで、バージョンを更新して全てのプレイ中のプレイヤーにもバージョンチェックを強制することができます。

一時クライアントID の活用

ProjectToken を組み合わせると、起動時にアプリへ組み込まれているクライアントID/シークレットには「ログインとバージョンチェックのみ可能」な最低限の権限のみを付与しておき、バージョンチェック通過後に得られる ProjectToken を介して、ゲームプレイに必要な強力な権限を行使するという二段階認証構成を取れます。 これによりアプリのリバースエンジニアリングによってクライアントID/シークレットが漏洩した場合でも、被害をバージョンチェックの突破に限定できます。

詳細なリファレンス