Liberent-Dev’s blog

株式会社リベル・エンタテインメントのテックブログです。

Firebaseを使ったGooglePlayGamesログインの落とし穴

こんにちは!
システム開発部のK.Mです。

今回の記事の経緯

弊社内で制作してリリースしたカジュアルゲーム「FingerRocket」にてリリース直前になり、 本番想定の環境でのみ発生する落とし穴に連続でハマってしまい機能が正常に動かないという事がありました。

何も知らないと罠にハマってしまう設定周りの話だったため、忘備録兼同じように困った人が居た場合の道標として残しておきたいと思い記事化しました。

FingerRocketとは?

その前に、「FingerRocket」とは何ぞ?というところですが、

操作は簡単、ロケットを引っ張って向きとパワーを調節し、地球に向けて飛ばすだけ!
ロケットを引き寄せる重力を持った惑星や、宇宙を漂う隕石を避けながら、地球着陸を目指しましょう!
惑星の中には重力が強力なもの、移動するもの、ロケットの動きを阻害するものなど、
様々な効果をもった惑星があります。
それらの障害を回避し、時には活用してクリアを目指していきましょう!

といった内容のハイパーカジュアルゲームとなります。


最初はこのように簡単なステージですが、進むにつれて


このような感じで少しずつ難しくなっていきます。

少しでも気になった方は、下記バナーからDLをお願いします。
Google Play で手に入れよう

発生していた事象

UnityにてFirebaseを使用したGooglePlayGamesへのログインを使っていましたが、本番環境下で記載の現象が発生していました。

  1. GooglePlayGamesへのログインが出来なくなっていた
  2. ランキング用に用意していたリーダーズボードが見れなくなっていた
  3. 得点を登録した後にリーダーズボードを表示すると永遠に読み込み中表示になっていた
続きを読む

Matrixプロトコルを試してみよう

こんにちは。
システム開発部のこたつみかんと申します。
夏はガツンとみかんが美味しいので楽しみです。

はじめに

さて、オンラインゲームにおいて、プレイヤー同士、またはファンとのコミュニケーションが取れることは非常に重要な要素となりました。
この手のサービスはゲーム外で賄うことが多く、最近はDiscordのシェアが非常に高いですが、代替手段を検討することは技術的な観点からも有効です。

現在Discordの代替サービスとして、Matrixに注目しています。

本記事では、分散型リアルタイム通信プロトコルであるMatrixと、そのクライアント実装であるElement、サーバー実装であるSynapse(次回以降予定)について簡単に紹介します。

今回はクライアント実装までをご紹介しますので、どなたも簡単に試してみることができると思います。

ゲームにおけるコミュニケーションの例

Matrixについてお話しする前に、ゲームにおいて通信を使ったコミュニケーションといえば皆さんはどのようなものを思い浮かべるでしょうか。
以下に少し例を挙げてみます。

プレイヤー情報の表示

通信をあまり使わないコミュニケーションの例です。
遅延を気にしなくてよく、通信量も少ないため以前よりよく使われています。
プレイヤー情報をあらかじめ記録しておき、必要に応じて取り出して表示することでそれぞれの個性が出せます。

ゲーム内チャット・掲示板・通話

ジャンルを問わず普及しました。
もはや必須機能と言っても過言ではありません。
また通信の発達とともに即時性が求められるようになりました。
特に戦闘中の仲間とのやり取りなどは遅延がひどいと致命的です。

観戦、実況

動画サイトの普及とともに急激に伸びてきた分野です。
人気が出れば出るほど広帯域、高負荷への対策が必要になります。
また、視聴者とのコミュニケーションの手段があるとさらに楽しくなります。

Discordのようなサービスは主に後者の目的で利用されています。

Matrixとは

Matrixは分散型のリアルタイム通信プロトコルです。
公式サイトであるmatrix.orgではさまざまな情報を公開していますので、興味の出てきた方はご覧になることをおすすめします。

基本的な通信の仕組み

Matrixはアプリケーション層のプロトコルで、その通信はRESTful APIで行われます。
クライアントは接続するサーバーを指定して通信を行います。
このサーバーのことをホームサーバーと呼びます。
ホームサーバーに自分を登録することになるため、クライアントのアカウントは@account:homeserverとなります。

クライアントからは「events」と呼ばれるJSONオブジェクトが送出されます。
全ての通信はこのeventsのやり取りです。

クライアント-サーバー間の通信はClient-Server APIにより行われます。

分散型

分散型の通信というとMastodonを思い出す方もいるかと思いますが、Matrixも同様にどこかのサーバーに接続すれば他のサーバーと連携しており、どのサーバーの誰とでも会話が可能です。

仕組みとしてはクライアントとホームサーバー間の通信ログを他のホームサーバーと同期することで実現しており、結果としてルームの全ログを参加している全てのサーバーが保持することとなります。
この同期は「Federation」と呼ばれ、Server-Server API により行われます。

相互接続性

Matrix独自の大きな特徴がその相互接続性です。

「Bridge」という仕組みを使った複数の通信システムとの相互接続が可能であり、様々なサービスをまたいだコミュニケーションが取れます。
現在公式ではSlack、IRCXMPP、Gitterへのブリッジがメンテナンスされており、さらにコミュニティによってDiscord、Facebook、Hangoutsなどへのブリッジが提供されています。

通信の秘匿性

すべての通信はHTTPSで行われます。
End to Endでの暗号化により、ユーザー間の通信内容をサーバーが知ることはできません。

採用実績

プロトコルがオープン標準でオープンソースのリファレンス実装が提供されていることからオープンソースと親和性のある欧州での採用例が多く、フランス政府やドイツ政府の独自のインスタントメッセージングツールとして利用されています。
また、Mozillaのように開発コミュニティでのコミュニケーション手段をIRC(Internet Relay Chat)から切り替えた例もあります。

他サービスとの違い

Mastodon

Matrixの分散型のシステムはMastodonと近い部分です。
サービスとしては以下のように役割が違いますので、使い分けが可能です。

Discord

こちらはMastodonと違い、サービスの形としては両方ともプライベートなインスタントメッセージングサービスです。

Discordは内部の仕組みが一部紹介されてはいますが、1社提供のプロプライエタリなサービスです。
インフラを意識する必要なく使え、DiscordAPIを使った開発も可能で自由度は比較的高いですが、自分でサーバーを立てることはできません。
Matrixはオープンソースであるため技術情報は全て公開されており、開発の自由度は非常に高いです。
加えてリファレンス実装で自分のサーバーも立てられるので、誰でもサービス提供者になることが可能ですが、サービスの運用も自ら行う必要があります。

環境やケースに応じて適したものを利用するのが良いかと思います。

クライアントのリファレンス実装 Element

Matrixのクライアント側リファレンス実装として、Elementがあります。
WindowsMacOSXLinuxiOSAndroidのアプリとWebブラウザに対応しており、幅広いプラットフォームからの利用が可能です。

今回はWindowsで試用してみます。

インストール

Element公式Webサイトの「Get Started」から入手できます。
「Desktop」の「Windows (64bit)」をダウンロードしたら、インストーラーに従って、インストールします。

インストール後の起動画面がこちらです。
ElementはDiscordと同様にElectronを使用しているアプリで、この画面自体はWebブラウザ版と全く同じです。

アカウント作成

起動できましたら次はアカウントの作成です。
「アカウントを作成」を選ぶと以下の画面になります。
デフォルトのホームサーバーは「matrix.org」です。
特に接続するホームサーバーがなければこのまま進めます。

また、OpenIDでの接続に対応していますので、認証情報を入力して新規登録を行わなくてもアカウントの作成は可能です。
今回は説明の都合上、認証情報を入力します。

ホームサーバーの運営方針を確認し、問題なければ「同意」を選びます。

確認のためのメールが送られます。

メール内のVerifyを選ぶとアカウントの作成が完了します。

使ってみる

では早速使ってみましょう。
サインインします。

サインインすると暗号鍵のバックアップ方法について確認が入ります。
今回はセキュリティーキーを生成します。
生成されたものは適宜保存してください。

お待たせしました。
こちらがホーム画面です。
まず基本となる公開ルームを使ってみましょう。

「公開ルームを探索」を選びます。

こちらはmatrix.org内のルームです。
「プレビュー」が表示されているところは参加前に内部を閲覧可能なルームです。
matrixについての公式ルームである「Matrix HQ」をプレビューしてみましょう。

こちらがプレビュー画面です。
DiscordやSlackを使っていた方々には見慣れた画面かと思います。
問題なさそうであれば参加ボタンを押します。

参加しました。
これで発言できます。

自分でルームを作成することも可能です。
では非公開のルームを作成してみましょう。
ホームの右側にあるプラスを選び、「新しいルーム」を選びます。

名称を決めて暗号化を有効にするかを選び、ルームを作成します。
高度な設定を表示すると他のホームサーバーと連携しないルームの設定もできます。

空のルームができたので他のユーザーと共有します。
ユーザー名を指定して招待します。

招待された方はこのように表示されます。
同意するとルームに入れます。

投稿が出来ました。
それぞれの投稿に対してはリアクションの絵文字を返すことができます。
また、ルーム内ではビデオ会議を開催できます。
こちらはDiscordと同様にWebRTCを利用しています。

次はダイレクトメッセージを使ってみます。
「ホーム」の右側にあるプラスを押し、「チャットを開始」を選びます。

送りたいユーザー名を記入して「続行」を選びます。

ダイレクトメッセージのやり取りができました。

まとめ

Elementの使い勝手はいかがだったでしょうか。
Matrixは、リアルタイム通信が身近になった今こそ知っておくべきプロトコルです。
ぜひ一度試してみてください。
次の機会には、サーバー側のリファレンス実装Synapseについて説明する予定です。


リベル・エンタテインメントでは、このような最新技術などの取り組みに興味のある方を募集しています。 もしご興味を持たれましたら下記サイトにアクセスしてみてください。 https://liberent.co.jp/recruit/

iOS、Androidのサブスクリプションのサーバ通知の実装について

こんにちは!
システム開発部のK.Mです。

iOSAndroidの各プラットフォームに用意されている自動更新のサブスクリプション(以下、サブスク)に関して、調査のため実装を行い、サーバ部分に関して得た知見を記載していきます。

前置き

近年、アプリゲームにて自動更新はされないが、購入すれば30日間ログイン時にガチャ用の石が一定数貰える+αという定期購入的な課金アイテムが増えてきております。
ただ、このような課金アイテムは各プラットフォームが用意している自動更新のサブスクを使わずに通常の課金アイテム扱いとして、ゲームサーバ側で30日間のチェックを行い、30日間経過したら無効状態にし、またユーザーに購入してもらうという形となっていたりするものが多いです。

この部分を各プラットフォームが用意している自動更新のサブスク機能を使って実装すれば、ユーザーは毎回購入する必要は無く、一度購入すれば自発的に解約しない限り毎月自動で更新する形が実装できます。
(提供者側からすると売上の予想が立てやすくなるメリットがあります。)

ただ、自動更新のサブスクに関しては通常の課金アイテムとは異なる実装が必要になってきます。
ここでは実際に自動更新のサブスクをどのように実装したかを、主にサーバ観点での情報を記載しております。

概要

Unityを使った実装を想定しており、クライアント(アプリ)側はUnityIAPで処理を行い、サーバ側の下記部分に関しての内容に言及しています。

  • サブスクのレシート検証
  • サブスクの自動更新方法

また、AppStoreConnectとGooglePlayConsoleにてサブスクのアイテムは登録済を前提としております。

iOSAndroid共にサブスクで決まった期間、例えば1ヶ月分のサブスクを購入し1ヶ月後の更新タイミングにて更新処理をする必要があるのですが、クライアントのみで対応するか・サーバにて対応するかの2パターンがあります。
今回記載している内容はサーバにて更新するケースとなります。

クライアントのみで更新する場合に考えられるのは

  • 毎回の起動時に各プラットフォームのAPIへアクセスしてサブスク有効かを確認
    • 無効ならサブスク解除
    • 有効ならサブスク更新

または

  • サブスク購入後、1ヶ月以上経過した後の初回起動時に各プラットフォームのAPIへアクセスしてサブスク有効かを確認
    • 無効ならサブスク解除
    • 有効ならサブスク更新

が考えられますが、前者に関しては

  • 起動するたびに各プラットフォームにアクセスするため無駄な通信が発生する
  • プラットフォームによってはAPIのアクセス回数が制限されている

という難点があり、後者に関しては

  • 時間の判定を端末内の時計で判断していると、端末の時間を変更して更新処理を回避される
    • サーバから日時を取得して判断すれば対応自体は可能だがメモリや通信を弄られると対策が難しい

という難点があるため、クライアントのみでサブスクの更新を行うのは少々リスクが伴います。
そのため、サブスクの更新処理はサーバで行うのが安全かと考えられます。

(現時点で自動更新のサブスクを使わずに通常の課金アイテムでサブスクっぽい動きをするものが多いのは、実装・テストが大変なのとセキュリティ的な問題でゲームアプリでは使用しているものが少ないのでは?というのが個人的な所感です。)

続きを読む

公式のgo-swaggerに機能追加した

https://static1.smartbear.co/swagger/media/assets/images/swagger_logo.svg

こんにちは。
システム開発部ネットワーク課のsupercontinueです。

swaggerとは?

  • swaggerは定義したスキーマからAPIコードを生成してくれるジェネレータです。
  • オープンソースです。
  • go-swaggerは定義したスキーマからgo言語のサーバのコードを生成します。
  • 弊社でも実際にリリースしたゲームで使いました。

  • もうずいぶん前になりますが、公式のgo-swaggerに機能追加したプルリクエストを送って、取り込まれた話をします。

何を追加したのか?

  • 例として、swaggerでは下記のようにAPIスキーマを定義します。
  sessionData:
    type: object
    properties:
      sessionId:
        type: string
      deviceId:
        type: string
      uMain:
        $ref: '#/definitions/u_main'
  • すると、下記のようなコードが生成されます。
type SessionData struct {

    DeviceID string

    SessionID string

    UMain *UMain
}
  • プロパティの順番に注目してください。

    • スキーマでは、sessionId、deviceId、uMain の順番になっています。
    • ところが、go-swaggerでは、アルファベット順にプロパティを並べ替えてコードを生成します
    • 生成されたコードでは、DeviceID、SessionID、UMain の順番になっています。
  • 順番が違うと何が困るのか?

    • MsgpackやProtobufではプロパティ名でなく、プロパティの順番や番号でシリアライズを行います。
    • 型と順番さえ同じなら、プロパティが追加されても、シリアライズ・デシリアライズすることが可能です。もちろん定義が無いプロパティは扱えませんが、存在するプロパティは扱えます。
    • スキーマ変更の前後で、プロパティの順番が違ってしまうと、コンパチビリティが失われます
    • また、スキーマにプロパティを追加したり、順番を変更すると、生成されるコードに意図ぜず大きな差分が発生し、差分が見づらくなります
  • そこで、プロパティの順番を明示的に指定する、x-orderというカスタム属性を追加することにしました。

    • swaggerでは、x-なんとかという属性は、プラットフォームごとの独自拡張の属性として扱われています。
    • たとえば、下記のようなカスタム属性があります。
      • x-go-name: "string": give explicit type name to the generated model
      • x-nullable: true|false (or equivalently x-is-nullable:true|false): accepts null values (i.e. rendered as a pointer)
  • x-orderを定義したスキーマ

  sessionData:
    type: object
    properties:
      sessionId:
        x-order: 0
        type: string
      deviceId:
        x-order: 1
        type: string
      uMain:
        x-order: 2
        $ref: '#/definitions/u_main'
  • すると、下記のようなコードが生成されます。
    • 構造体のプロパティの順番がx-orderで指定した順番になります
type SessionData struct {

    SessionID string

    DeviceID string

    UMain *UMain
}

実際に機能追加提案したコード

  • まずはレポジトリをForkします。

  • 主要な変更は下記です。

// 変更前

func (g GenSchemaList) Less(i, j int) bool { return g[i].Name < g[j].Name }
// 変更後

func (g GenSchemaList) Less(i, j int) bool {
    a, ok := g[i].Extensions[xOrder].(float64)
    if ok {
         b, ok := g[j].Extensions[xOrder].(float64)
       if ok {
            return a < b
       }
    }
    return g[i].Name < g[j].Name
}
  • 変更点はわずかですが、「どこを変更すればいいのか?」を正確に把握するには、swaggerのコードを読み込む必要があります。

  • 今回は、たまたまLessというメソッドが実装されていたので、変更が最小限ですみました。

  • テスト用のコードの方が多いです。

  • 実際のプルリクエストは下記です。

  • 結構さくっとマージしてもらえました。

結果

  • 公式のマニュアルにもx-orderカスタム属性が記述されています。

  • 当時はさくっと採用されて嬉しかったです!

    • オープンソースへの貢献は嬉しいものですが、負荷が高くなると辛くなるので。
  • 独自レポジトリで改変して利用していると、公式が更新された場合、追っかけなくてはなりません。公式にマージしてもらうほうが楽です。

  • さっき見たら、下記のような2020年のissueがありました。(まだOpenしたまま)


リベル・エンタテインメントでは、このような最新技術などの取り組みに興味のある方を募集しています。もしご興味を持たれましたら下記サイトにアクセスしてみてください。 https://liberent.co.jp/recruit/

試してみたlocustバージョンアップ対応

こんにちは!
システム開発部のK.Mです。

以前こちらで紹介した負荷試験ツールのlocustですが、当時使っていたバージョンがv1.6.0でしたが、既にv2.8.2(記事執筆時、現在の最新はv2.8.6)までバージョンアップされている状況でしたので、最新のものにバージョンアップを行い以前に実施した各種手順やテストシナリオに影響が出ていないかを確認していきます。

メジャーバージョンアップ時に破壊的な仕様変更が入っていたりするので要注意ですが、v2.0.0のリリース内容を見る限りそこまで影響があるような変更は無さそうです。

  • 1.6.0から2.8.2へのバージョンアップ
  • GKE環境の更新
  • テストシナリオの修正
  • 最後に
続きを読む