simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

Unity:非VRからVRモード切替機能とその逆切り替えをするScript記述方法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Management;

namespace CubeWalk
{
    /// <summary>
    /// VRモード切替器
    /// </summary>
    public class VRSwitcher : MonoBehaviour
    {
        /// <summary>
        /// 非VR時にのみアクティブになるオブジェクト群
        /// </summary>
        [SerializeField] List<GameObject> keyMouseObjects;
        /// <summary>
        /// VR時にアクティブになるオブジェクト群
        /// </summary>
        [SerializeField] List<GameObject> vrObjects;
        /// <summary>
        /// VR アセット位置調整用
        /// </summary>
        [SerializeField] Transform vrAssets;
        /// <summary>
        /// プレイヤーオブジェクト発見用
        /// </summary>
        [SerializeField] Transform players;

        public void EnableVR()
        {
            StartCoroutine(this.CoEnableVR());
        }

        public void Enable2D()
        {
            XRGeneralSettings.Instance.Manager.StopSubsystems();
            XRGeneralSettings.Instance.Manager.DeinitializeLoader();
            foreach (var item in this.vrObjects)
            {
                item.SetActive(false);
            }
            foreach (var item in this.keyMouseObjects)
            {
                item.SetActive(true);
            }
        }
        
        IEnumerator CoEnableVR()
        {
            yield return XRGeneralSettings.Instance.Manager.InitializeLoader();
            while (!XRGeneralSettings.Instance.Manager.isInitializationComplete)
            {
                yield return null;
            }
            XRGeneralSettings.Instance.Manager.StartSubsystems();

            // VR開始位置をプレイヤー位置に調整
            foreach (Transform vrm in this.players)
            {
                if("Player" == vrm.tag)
                {
                    this.vrAssets.position = vrm.position;
                    break;
                }
            }

            foreach (var item in this.keyMouseObjects)
            {
                item.SetActive(false);
            }
            foreach (var item in this.vrObjects)
            {
                item.SetActive(true);
            }
        }
    }
}

CubeArtWorld:爆発するキューブと四散する仕組み

爆発すると周囲のキューブをごっそり削り取り、削り取られたキューブが四散するというものが可能なのか調べていきます

f:id:simplestar_tech:20210319173631p:plain
ラクガキキングダムのインポートできます

できましたー

CubeArtWorld:過去へさかのぼるタイムトラベル

f:id:simplestar_tech:20210223170337p:plain
過去の世界とは

実はこの半年、ゲーム内のワールドデータのバックアップを毎日取ってきているため、これを時刻指定で参照して拾ってこれさえすれば、過去を再現した世界を見ることができるようになります!!

さっそく作れるのか技術課題を明らかにしてみましょう。

## 過去データ形式

日付ごとにファイルが作られています。
ワールドごとにとびとびなので、日付を決めただけでは、正しい過去の日付を見つけることができない

リストを日々更新することになるが、ファイル一覧をなめるのは無駄が多い
そこで、バックアップ処理で追記していく json を Title Internal に置くことにしてみましょう

また、TitleInternalData の規則に変な奴がいるので、CubeDataLife としてリネームしておくことにします。
関連処理の lambda 実装を追って、移行計画も立ててみましょう

キー名を統一するだけに見えたので、いきなり試行 朝3時に定期実行が来るので、様子を見ます→キー使用コードの差し替え漏れでバックアップが機能しませんでした。
修正したので次の日こそ!

構想では 2020年7月21日が紀元とされていて、そこから暦を一日ずつ刻むことにして
[1, 2, 3, 4, 5, 9, 100, 101, 200, 209]
といった形で配列を刻みます

TimeSpan span = DateTime.UtcNow- new DateTime(2020/7/21) ;
span.Days //差の日数

時刻から日数、日数よりさかのぼって発見する記録日数が見つけられたなら
日数を紀元に足して記録日を求める
DateTime today = new DateTime(2020/7/21).AddDays(span.Days);

var suffix = date.ToString("_yyyy-MM-dd");
で求めたい過去のデータを見つけられる気がする

まずは一度 s3 のファイルリストを取得するところからですね

ファイルリストは取れました。求めていたファイル名にも復元できます。

続いてクライアントからどうやって取り出すのか

## タイムドアアイテム

インスタントアイテムとして、ガフの扉を配置すると、日時を指定できるようになり (2020/7/21 ~ 今日まで)
選ぶと同時に扉が上昇しながら大きくなり 天井で巨大化
周囲のキューブが崩れ、自身と共に扉に向かって浮き上がっていき ゆっくりとホワイトアウトすると

過去世界データを引いて、世界が再構築され、その世界に扉から今度は落下して着地する
以後はもう一度ガフの扉を開くまで、過去世界でアイテムを増やすことも減らすこともできない世界に取り残されて、残らない過去への干渉を続けるだけになる

ガフの扉で最大の今日の日付を選ぶと、元の世界に戻ってくる

というの作るとしたら

## サーバー側の仕事

戻りたい過去の日付をサーバーに送り、過去の世界のデータダウンロード url を発行して返す という仕組みが世界データロードで差しかわれば
キャッシュを残さずに動けるかも

ということで、s3 の一次的なダウンロード url の発行というものを行います

もろもろできましたね
リリースしました

CubeArtWorld:ユーザーショップ機能

長らく作ってる姿をお見せしてきたゲームは、ついに昨年クリスマスにリリースしました!
booth.pm

ひそかにブログを見守ってくれたそこのあなたに、ぜひ買って遊んで、口コミで世に広めてもらいたいです。ご協力お願いします。

今回は、そのゲームタイトル「 Cube Art World 」に入る大型アップデート:ユーザーショップ機能について構想から記していきます。

ユーザーショップデータ

プレイヤーがキャンバスキューブと呼ばれる、所有権を有するキューブにアイテムと価格設定を加えて在庫とともに陳列するというデータをサーバー側にて刻むとすると
さて、このデータをどこに置くか どうやって他プレイヤーがアクセスするかについて考えていきたいと思います

現在のキャンバスキューブのメタデータにショップアイテム配列を追加する というのもありかもしれません。
別に用意して、データを取りに行ってあるかどうか確かめる方法もあるのですが、これは UI について決めていくと、そのハマり具合がわかり、決まりそうです。

ということで、現在のキャンバス UI から見ていきましょう。

f:id:simplestar_tech:20210214135022p:plain
キャンバス閲覧時

こちら、編集することができる者だけ Edit ボタンを押して次の編集UI に移行することができます。

f:id:simplestar_tech:20210214135206p:plain
キャンバス所有者のみが開ける編集画面

既存の UI はそのままに、ここにユーザーアイテムショップ機能をつけるとして、発想としてはショップを開くボタンを置いてみるとします。

メインの Cube Type を Canvas として、これをドロップダウンリストで Shop に切り替えられるとしましょう
この考え方の良いところは、既存の UI 設計を崩さず、新しい UI に切り替えて提供できる点です。

今の状態をそのままに、リストディフォルトを Canvas とします。データに無い場合はこれ
そして、Shop に切り替えたときに、既存の UI はすべて消え、新しいショップ用 UI に切り替わります。

そこにはアイテム選択と価格、在庫数を入れるフォームを用意し、Add ボタンでリストに追加
既存リスト項目の編集、削除が行えることにします。前後移動も可能な左右やじるしボタンも用意

項目はサムネイルとアイテム名 キャンバスの場合はタイトル 簡易説明が付く 価格 GD と、販売予定個数

あと、今後購入履歴を過去 5件ほど記録して、これをプレイヤーが認識できることとします

保存する情報はどこか
メタデータには ドロップダウンの選択項目のみ記録して、配置パスは分けます
キャンバスIDで管理はされますので、キャンバスから参照、編集には困らない

タイプがキャンバスの場合は既存のフロー
ショップの場合は先行してショップ情報を引き出して、これを使って、ショップ用の UI を表示することにします。

ドロップダウンはこの通り

f:id:simplestar_tech:20210214151853p:plain
CubeType選択

UI の行き来を作ってみましょう

販売項目追加において、いったいプレイヤーは何のアイテムを売ることができるのか?
これについて、インベントリ内の運営規定IDのキャンバスと、自身が所有権を持つキャンバスに値を付けて販売できるものとします。

インベントリアイテムリストを絞り込めるかチェックしてみます

リストを選ぶ UI を先に作っていきました

f:id:simplestar_tech:20210214181904p:plain
陳列項目追加 UI

UI の切り替えがうまくいったところで、まずはキャンバスのキューブタイプ情報を既存のメタデータに追加します。
これを保存できるようにして、さらに読み込んでから UI をそのタイプで初期化して表示するように作り替えてみましょう。

続いて、自身の所持金額を左上に表示します
もろもろ初期表示まわりがそろいましたので、続いて、 Add ボタンを押したときに 未設定の陳列アイテム 選びなさい UI が現れるようにします。
ここでSelect を押したとき、インベントリのようで、そうではない所持アイテム一覧のうちフィルターされた ショップ陳列可能なアイテムが並ぶようにしてみます

陳列項目を追加、編集するという UI だけできてきました。

f:id:simplestar_tech:20210216000941p:plain
追加操作を提供 不正があれば理由を示します。

こちらの機能は
simplestar-game.booth.pm

リリースされました!!
経済という概念、プレイヤー同士が金銭でアイテムをやり取りし出すことがはじまります

CubeWalk:ワールドチケットの仕組み

実装メモです
整理出来たら消します。

ワールドチケット?

世界を編集できる権限をユーザーに付与したいという要求に対して、出した答え

ユーザーのインベントリに ticket カタログに所属するワールドチケットアイテムを買ってもらい
そのチケットには有効期限が設定されていて、購入から時間が経過すると消えていくしくみ

スタックできるので 2個買うと、1チケット消費され、さらに有効期限が続くという考え方から始まります。

実装試験してみると、購入からの有効期限となっており、2チケット買っても、消費される瞬間はほぼ同じ
残念ながらスタックすることはできないため、出端から計画は崩れました。

既存のキューブとのバッティング

PlayFab のアイテムはカタログに所属しています。
いままでは cube カタログしか使ってきませんでした。

チケットは ticket カタログ
キューブは cube カタログ

にそれぞれ所属するとします。

カタログを分けて扱うようにするので、cube カタログのものと ID がバッティングするものを作らない限りは影響なしと思いたい
既存の各種クライアントコードにてカタログバージョンチェックを入れる必要がありそうだ

実際 ID 重複が許されるのかも見ると→重複は可能でした

ので、ID決定で困ることはないですが、クライアントのシステムにてカタログ所属考慮が抜けているとロジック破壊が起きるので
インベントリの情報を扱っているところはすべてカタログで分岐を書かなくてはならない 書いてなかったら直さなければならないことになりました。

準備として修正を進めます。→修正完了

いきなり編集権利を買うのではなく、交換チケットを買ってほしいときに権利と交換する案

チケットの種類は worldチケット 1つ、買うときにカウント消費アイテムとして、永続化して保持してもらう
新たに、ワールドごとの編集権利アイテムというものを購入不可能なものとして、world チケットと交換可能で作る
ワールド編集時に権利アイテムが無ければチケットと交換、これをユーザーに確認してサーバー処理で安全に交換する

良い点:
チケット購入時にワールドを確認する必要がなくなる
買ってすぐに使わなくても価値は下がらない
編集開始時にチケット交換をたずねるだけになれる
権利の重複フローをシステムで消せる

どうやって購入?

ストアという概念を定義し、システムメニューから選べるようにします
できれば、ユーザー特定情報がユーザーから申告できるように、プロフィール画面を作る→名前で検索できるのでしばらくはいらない

ストアについて記事を探して実装イメージを固めてみます

ストアとリアルマネー

なるほどストア機能は仮想通貨による価格設定などが良さそうですね。
ただ、仮想通貨とリアルマネーのトレードをして管理するようになると、仮想通貨でゲーム内でいろいろなアイテムを購入できるものにした場合
これは日本国では資金決済法の適用が義務付けられるので、例えば同人誌即売のようにアイテムを一意に決めて売るとは勝手が違うため、事業者登録が必要になります。
なので、このゲームは複雑にならないよう、仮想通貨によるアイテムの販売、そのための仮想通貨の購入は避けます。

そこで、直接ワールド編集権利のみを、このゲームのサーバー運営代として支払ってもらう仕組みにします。
権利だけを得るチケットをリアルマネーと交換するだけ
simplestar-tech.hatenablog.com
こちらの記事が参考になりそう。

メニューにストアを作り、そこから購入したチケット一覧を並べて、所持数を表示し
チケットと枚数を指定したら購入 url が開き、購入後に購入しましたボタンを押したら
成功→チケット数が増える
のフローを経て、ストア画面が更新表示される

という UI とロジック動くところを作ってみます。

ショップUIを新規作成

現在は Esc キーでシステムメニューが出る 今はそこからフライトカメラモードとゲーム終了・再開を押せる状況
このメニューにタブ切り替え機能を用意して、そこで新しいコンテンツビューを表示できるようにします

アイテムの表示

やり方としては catalog と store の両方の情報を取り出し、インベントリと合わせて
各種アイテム情報と所持数、購入ボタンを配置することになる

所持数は…購入画面にて + ボタンを押すと、所持数+ いくつ という表示で行おうと思います。
ひとまずショップ内容の表示まで手を動かしました。


アイテム購入画面

新しくダイアログを表示して、既存の所持数 + ボタンで購入するアイテム数を制御し
そのうえで購入ボタンを押すと、アイテム数が増える

なんと、キューブアイテムは 2 個で 1キューブなので、価格を倍額で表示しておこう

現在の所持金も表示しなければならないことにも作っていて気が付けました

f:id:simplestar_tech:20200724222139p:plain

アイテムの購入ボタンを押すと、所持金、個数、可能な購入個数などの制御が正しく動くところまでできました。
あとはキックされた処理でアイテム購入を行う部分を実装することになりそうです。

仮想通貨でのアイテムの購入処理

リアルマネーとは異なり、こちらは簡易な手順なので、試験することろまで進めます。

実はアイテムは個数指定で購入することができないのが PlayFab の作りでした。
ワークアラウンドとしては、アイテム数を増やしたりして、サーバー側でうまいこと仮想通貨を減らしてほしいとのこと

これは AzureFunction を増やして対処する

同じように、現実のお金の場合もアイテムは一つずつしか購入できないのか調査してみます。

PlayFabClientAPI.StartPurchase

リクエストに Quantity を指定できるのでうまくいっている様子
しかし?

RM 以外の仮想通貨も使えるのだろうか?

はい、その通り!

仮想通貨での動作を確認したときの記録がこちら

スロットを持たない新規アイテムの購入フロー

アイテムのうち、インベントリのスロット番号を持つものがありますが
新規購入をするとこの値が設定されていないので、いつ設定するか考える必要があります。

わざと所持していないアイテムを買ってみて動作をみてみましょう。

この考えは正しく、現在スロット操作が不能になる
理由 inventory item has no custom data slot

アプリの処理として、これを生成しているコードは AzureFunction
処理としては既存のインベントリから取り出したアイテムが Slot 指定を持たないことに疑問を持ち、不正リクエストとして返している
ただ、それは不正なリクエストではなく、サーバー側の不正状態といえる

今後、スロットに所属しないアイテムを許可することを考えると、ロジックを見直してみる必要がある
無視してもよさそうなので、無視

ここでスロットを持たないアイテムにスロットを追加するのはやめた。
逆にリクエストに問題が無ければ受け付ける仕組みとなっていたため、インベントリ情報をローカルで表示する際、アイテムにスロット番号が指定されていない場合は
ローカルにてスロット番号をリクエストすることで解決できるゴールが計算できる

この計算が正しいか確認する

残念ながら、クライアントからのリクエストは受け付けられなかった。
カスタムデータを持たないアイテムに対するスロット指定はもともと受け付けないようにしていたからである。

ただ、これからはスロット指定を持っていないアイテムについてもスロットを指定できるようにするため、この問題は既存ロジックの書き換えで対処できることになる
修正後

破壊的変更なのでメンテナンスして調べる
そろそろ staging 環境など用意した方が良さそう

アイテムは置けるので、スロットの反映は許可されることになった
f:id:simplestar_tech:20200725133958p:plain

スロットをはじめから持たないキューブが得られた時、これをクラアントから空いている箇所に設定したはずが
どういうわけか重複するエラーを作ることができた。
あとから設定されたものでスロットが上書きされるため、ローカルの見た目はアイテムが消えたように見える

再現しなくなってしまったが
サーバー側操作でわざとスロットを重複して与えることができるので、その場合に空いている場所へ退避してからリクエストするクライアントになるよう処理をくわえることにした。

動作に問題はない

リアルマネーによるアイテムの購入

仮想通貨に関しては現実資金の決済なしにアイテムは仮想通貨によって購入することができました。
実際のお金を必要とする場合は、確か、購入用の Web ページを表示するところからです。

実際に資金の移動と購入が行えるかチェックしてみます。

行えました。
返金は 180 日以内なら可能なので、試験的にお金を使ってもすべて取引は無かったことにできる様子
少しテストの障壁が低くなった

PayPal を選んでみたが、手数料は約 13% ほどとられるみたい

権利アイテムの定義

お金で買ったのはチケットなので、これは回数によって消費する券

これとは別で、編集したいワールドにて編集操作を行ったら、サーバー側で権利アイテムを持っているかチェックし
これを持たない場合にチケットを利用するか聞くフローが作られ
もしチケットも持っていない場合はチケット購入画面を開くフローが作られる

というのをやります。

最初はワールドごとの権利アイテムを作り、続いて、サーバー側で編集したいワールドの権利アイテムを所持しているかを見つける処理から進めてみましょう。

world カタログの、リクエストされている worldName と一致する ItemId のアイテムをインベントリに持っている場合は、権利アリ
そうでない場合は権利なしを返すことで作成できました。

権利なしを受け取ったら、権利購入へ進める

キューブ操作の戻り値を正しく処理することを今まで行っていないはずなので、その部分を見ていく
この点を確認すると、権利を持たない場合の戻り値を正しく受け止めることができるようになるだろう

加えて、ALB (Amazon のアプリケーションロードバランサー)の拒否戻り値を使って、ユーザーにメンテ中のサーバーであることを知らせるなど
はじめて、ユーザに理由を添えて示すことができるようになるはずである

できますね。
ステータスコードで分離してみます。

キューブ編集中の状態でエラーが起きるので、操作不能にしてからメッセージを表示し、閉じたら元に戻すようにしたいところ

操作不能、復帰処理

入力ステート管理があり、このステートを切り替えるで完了
今までシステム操作ステートに入ったらシステムメニューが表示されるような仕組みだったが、明示的に Esc キーを押したときだけ表示に切り替えた

権利がないときの UI

新しい独立パネルを作り、現在の保有チケット数を表示
権利を取得したい、チケットを使いたいワールド名を示して、一日編集権に交換することを確認する

確認できたら、編集権を得た画面になり、パネルと閉じた後は 1日だけ利用できるようになる
アイテムは勝手に 1日経つと消費される

新規実装ですね、パネルから作っていきます。

持っているチケットアイテムが列挙されるパネルが、編集権を持たないワールドを編集したときに現れるようになりました。(試験目的で同じチケットを3つ並べてます)
f:id:simplestar_tech:20200726220433p:plain

チケットアイテムを一つも持たない場合は、ショップへ移動するフローが構築されることになってます。
f:id:simplestar_tech:20200726220827p:plain

チケット購入ページへの遷移

無いので作ってみます。

UI操作をコード化して、ショップのチケットカタログを開くところまでは作れました。

チケットを消費してワールド編集権利を得る

バンドル化するのも良いと思いましたが、チケットとワールドごとの権利は 1対多なので
新しく AzureFunction を作ろうと思います。

構想したものが連休中に終らず…タイムアップです

これは新しい Azure Function ですね

ゴリゴリ実装してコード整理、動作確認完了!

決済操作した後、オーダー番号で確認フローを進めない限り、クラアントからの支払いは完了しないことがわかった。

あとは権利を手にしたときに、サーバーが眠ってたら起こす仕組みを作れば、課金まわりの一通りの機能がそろう

バックアップ時に残り日数を減らし 0 になったら寝る

残り日数とは?というものですね。
概念というよりは、ワールドデータサーバーごとに参照、変更できるゲーム内で一意の値のことですね。

一つのキーに複数のワールドデータを詰めると、競合で消えてしまうデータができてしまうので
一つのキーに一つのワールドの活動限界日数を記録することにします。


000 : 1

Set で null 文字を渡すと消せることから、バックアップで 1 → 0
0 → null としてカウントダウンした値を Set する仕組みにし、 null のときはバックアップしない(ワールド寝てるから)

ユーザーが編集権利を購入したとき、現在のワールドチケットの有効稼働日数を Set する
セット前に null であることを確認したら、ワールドを呼び覚ます AzureFunction を実行する

で、いけると思っている

作ってみます。。。。

ワールドを寝かしたり、呼び起こす AzureFunction はできた。
これを他の AzureFunction 実行時にも呼び出せるか調べて、書き換えてみます。

タイトルデータ internal が PlayFab にはあってそうですが、その特性を調べていきます。

最大で 15分ほど遅れがあるそうですが、全然大丈夫
一日ごとのバックアップ時に 1ずつ減らして 0 → null のときに、サーバーを眠らせるようにして、動作確認とれました。

権利購入成功時に、サーバーが寝てたら起こす

権利購入時には TitleInternalData で、ワールドの稼働日数を確認し null だったときに
start インスタンスでサーバーを起こす

その後 TitleInternalData にはワールド名をキーにチケットの日数を与える

動きました!!
ワールドチケットの仕組み、最初に思い描いた仕様を形にすることができました。

とまぁ、ゲームの一機能を作るって、だいたいこんな感じですね。
めっちゃつまらない&苦痛の連続でした

AWS:LambdaにAuthorizerを設置してからAPIGatewayを公開する

前書き

インスタンスを start するだけで docker コンテナが起動して、機能し始めるというのは用意できてます。
あとはこれを PlayFab から HTTP リクエストで呼び出すだけ、なのですが、ここで公開 API でありつつも
認可の仕組みありきという API Gateway と接続した Lambda のお仕事にするので、そのあたりの具体的な手順と概念について記録します。

API Gateway

Lambda の試験を終えて、外から呼び出そうとトリガーに API Gateway を追加します。(ウィザードで新規作成)
f:id:simplestar_tech:20200719123820p:plain

プロトコルを HTTP にして、Authorizer 作成をします。

発行者URLに AWS Cognito の次の形式の url を書いて渡します
https://cognito-idp.us-west-2.amazonaws.com/us-west-2_XXXXXXX

一緒に Client ID を求められるので、 AWS Cognito でアプリクライアントを新規登録して、それの ID を打ち込みます。

これで Authorizer 設定完了

AWS Cognito

作るだけです。
昔やった
simplestar-tech.hatenablog.com

で、ユーザーのサインアップを許可しますか? 管理者のみがユーザーを作成できます として、勝手にユーザー追加されないようにします。

で、管理者としてユーザーを追加した後
Amazon Cognito ドメイン名を空いている prefix で取得し、次の通り、リダイレクトURL付で URL を作って
docs.aws.amazon.com

ログインの画面を開きます

あー実は 有効な ID プロバイダを Cognito で作らないとダメ(次の画面の設定にする)

f:id:simplestar_tech:20200719124633p:plain

管理者が追加した e-mail ユーザーに初回パスワードが送られてくるので、このログイン画面で初回パスワードを入れログインし
すぐに新しいパスワードに切り替えて、認証成功させる

上記画像のリダイレクト url とリクエストする リダイレクト url が一致しないとエラー画面になるので、しっかり合わせること
一つでも間違えると、理由なきエラー画面なので、なんとかネット上のやりとりで、url に違いがあるとダメということにたどり着けた

これでログイン画面の表示と、正しい email と パスワードでログイン成功となる
成功すると、リダイレクト後の url に AuthorizationCode つまり code=~~のクエリ文字列があるので記録する

Authorization Code

さっきリダイレクト url で code= で得たものを
/login
と同様に
/oauth2/token
のcode=AUTHORIZATION_CODEに設定して、次の通り POST リクエストを送る

docs.aws.amazon.com

サンプルでいうところのこれ

POST https://mydomain.auth.us-east-1.amazoncognito.com/oauth2/token&
Content-Type='application/x-www-form-urlencoded'&
Authorization=Basic aSdxd892iujendek328uedj

grant_type=authorization_code&
client_id=djc98u3jiedmi283eu928&
code=AUTHORIZATION_CODE&
redirect_uri=com.myclientapp://myclient/redirect

ああ、 Basic の次に続くやつは Baes64 文字列で、変換元は "client_id:client_secret" これは Cognito の管理画面で手に入る

得られる json を見ると長ーい文字列で

"id_token" が得られるので、これを記録
あと、Cognito でトークンの有効期間が設定できて、最長で 10年設定できる(今日から10年に設定した)

API Gateway を呼ぶ

これで設定した Authorizer にマッチする管理ユーザーの 10年有効な id_token を得られたので、Authorizer 設定で指定した通り Authorization ヘッダーに仕込む

自分はこれで認可されて APIGateway の先の Lambda をたたくことができた

追記

実は id_token は 3600秒後にかならず有効期限が来て使えなくなります。
一緒に refresh token が得られていると思うので、

grant_type = refresh_token
refresh_token = それ

として oauth2/token API をたたきます

更新トークンは 30~最大設定で 3650 日、有効なので

また id_token を 1時間で有効期限付きで取得できます。

自動化したいなら更新トークンで id_token を得て、その id_token で API Gateway の Lambda を実行する、といった感じですね


参考:こちらはトークンオーソライザー使ってますね(過去の自分)
simplestar-tech.hatenablog.com

課金するとデータサーバーが起動して、お金が尽きたら勝手にデータサーバーを止める仕組みづくり

前書き

長いことはじまりの世界のデータサーバーは一つ
AWS Elastic Beanstalk で環境を用意して運用して半年~1年が過ぎようとしてます

ここでモチベーションがあがる機能追加案がありまして、世界を広げるという操作をプレイヤーが行えるようにする
具体的にはプレイヤーがお金を払い、サーバー運用費を越える額がたまった後、それをトリガーに一つとなりのワールドのデータサーバーが立ち上がり
これを編集できるようになるというものです。

ec2 インスタンスに docker 環境を作る

こちらの記事を参考に手を動かしてみます。
Amazon Linux2にDockerをインストールする - Qiita

AWS ec2 インスタンスを作る
ssh 接続はセキュリティーグループで自宅の IP アドレスからのアクセスからのみ許可
秘密鍵を自身の .ssh/aws フォルダに配置して、これを指定して ec2-user というディフォルトユーザー名で、出来上がった ec2 インスタンスDNS アドレスにたいして ssh 接続を成功させます。

この時点で、自宅と同じ IP アドレスであり、インスタンス作成時に作った自分しか知らない秘密鍵を持ち、これを指定した者だけが
この ec2 インスタンスに入って自由に操作できるようになります。

とりあえず最新状態にして docker エンジンのコマンドを打てるようにしてみましょう

sudo yum update

で y 押す

docker をインストール
sudo yum install -y docker
インストールした docker をスタート
sudo service docker start
sudo 打たなくて良いように ec2-user アカウントを docker グループに追加(※有効化されるのは次のログインからだから注意)
sudo usermod -a -G docker ec2-user
インスタンス再起動時に docker コマンドが打てるよう自動起動を有効
sudo systemctl enable docker
docker-compose 実行ファイルをダウンロードして、実行権限付与
sudo curl -L https://github.com/docker/compose/releases/download/1.26.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

動作確認
docker --version
docker-compose -v

どちらもバージョン番号が出力されれば ec2 インスタンスを起動して Amazon Linux 2 にて docker 環境整えるはクリア

docker 環境に ECR から pull してアプリの動作確認

まずローカルで docker image を作成します。

FROM golang:1.14 AS build
RUN apt-get update && apt-get install -y \
    python3-dev \
    python3-pip
RUN go get -u github.com/aws/aws-sdk-go
WORKDIR /app
COPY . .
RUN GOOS=linux CGO_ENABLED=0 go build -o /tmp/cubedataserver application.go

FROM alpine:3.12
COPY --from=build /tmp/cubedataserver /bin/cubedataserver
ENTRYPOINT ["cubedataserver"]

この Dockerfile には go build した成果物をビルドに必要なライブラリのないスリムな alpine image 上で実行 という
コンテナを小さくするアイディアが記載されています。

ecr にログインして、docker push して、IAM で ecr リソース指定アクセス権限ポリシーをつけておけば docker pull できました。
いざ、ローカルテストと同じように docker コンテナが機能し、セキュリティーグループで自宅から指定ポートのインバウンド設定すれば
自宅からキューブ情報の操作ができること確認しました。(アプリ層でトークンが正しいこと確認してます)

lambda でインスタンスを stop, start する

できれば ALB のルールもつけたり、消したりしてほしい

参考にした記事はこちら
dev.classmethod.jp

docker run するときに --restart=always オプション付けていれば、インスタンスの起動完了と同時にサーバーとして機能をはじめました。

IAM ロールで、指定リージョンの指定インスタンスIDに関してのみの start, stop ポリシーを付けたので、暴走する関数にはならない
lambda を実行すれば機能したので、これを PlayFab から呼び出せるので、すべての技術課題は解決した

完成はしてないが、あとは調査いらずの作業だけになったぞ

参考
PlayFab Cloud Script → API Gateway → Lambda(python) → EC2 start, stop