はじめに
作成したのは、マップ上をボタンコンポーネントで移動し、宝石を探すゲームです。
最近IEOなどで話題になったブリリアントクリプト(Brilliantcrypto)というゲームのDiscordコミュニティに、発表当初から参加しています。
2023年末のβテスト後、リリースや各種情報解禁までの間、コミュニティが閑散とした時があったのですが、その時にネタ的に作り始めたファンゲームです。
Brilliantcryptoは"Play to Earn"の側面が注目されていますが、ゲーム自体もなかなか楽しめます。その中でもレーダーの反応から宝石の場所を探る要素が気に入っていたので、レーダーを使った宝探しゲームにしてみました。
開発も一区切りしていて、興味を持ってくださった方から質問をいただくこともあり、技術項目について記事にすることにしました。今回はその中でも概要的な内容になります。
なお、私はβテストに参加しましたが、Brilliantcrypto運営でも関係者でもありませんので、その点はご注意ください。(運営さんに迷惑がかかるといけないので)
また、一応書いておきますが、本前書き?はBrilliantcryptoに関連したNFTや仮想通貨の投資を推奨するためのものではありません。
主なリソース
主に3つのリソースから構成されています。
- 1段目のLambda関数(関数URL有)
- 2段目のLambda関数
- DynamoDB
上記に伴い、暗黙的に作成されるリソースに加え
- 1段目のLambdaが2段目のLambdaを呼び出すポリシー
- 2段目のLambdaがDynamoDBを操作するためのポリシー
も作成しています。
上記をソースコードと共に、AWS SAMを使って同一リポジトリで管理しています。
なお、開発言語はNode.jsで、Discord.jsなどの定番ライブラリは利用していません。
構成図
図を書くほどのものでもないですが、せっかくなのでChatGPTに作成してもらいました。
色々な種類の図を書いてもらったのですが、あまりしっくりするものが出せず、比較的わかりやすかったシーケンス図を掲載して起きます。
処理概要
- Discord上のスラッシュコマンド、ボタンコンポーネント操作などをトリガーにAPIが呼び出されます。
- 1段目のLambda関数は"INTERACTIONS ENDPOINT URL"になります。
- 1段目のLambda関数はリクエスト内のシグネチャを検証し、正当なアクセスか検証を行います。
- 検証に成功すると2段目のLambda関数を呼び出し、Discord側には仮のメッセージを返します。
(DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE) - 2段目のLambda関数は1段目のLambda関数から呼び出されます。
アプリケーションの本処理を受け持ち、処理が完了すると仮メッセージを更新します。
特徴
サーバーレス構成、SAMを選んだ理由
個人開発のため固定費をかけたくないのが理由です。
本業の隙間時間になるので、開発時間を安定かつ継続的に確保できません。
無駄な費用を抑えるためにサーバーを休止させると、開発を再開する際の心理的なハードルが意外と高くなります。
気持ちの問題、熱意があれば苦にならない!と言われればそれまでですが、いつでも動かせるものがあり、思い立ったらすぐ作業できるというのは、モチベーションの維持に役立ちました。
また同様の観点で記憶の問題があります。
仕様や運用手順を忘れると、せっかく確保できた時間が調査に費やされます。
対策としてドキュメントを作成するのも面倒です。(続かない)
そこでSAMを使ってコードとリソースの管理を集約しました。
その他簡単なドキュメントはREADMEかコードにコメントで書いて済ませています。
そして、個人開発をしていると不要なリソースが溜まりがちです。
定期的に棚卸しをする際の調査も面倒ですが、SAMならプロジェクト単位で一括して削除できます。
Discord.jsなど著名なライブラリを使わなかった理由
1つ目はDiscord公式のサンプルコードとチュートリアルを応用するだけで、大枠が作成できてしまった点です。
どんなプラットフォームを使うにしても、運用後のトラブルシューティングのことを考えると、できるだけ公式ドキュメント(一次資料)に馴染みたいと常々考えています。
チュートリアルはわかりやすいのですが、Discordの公式資料は自分には難解で、ライブラリのドキュメントと両方読み込んでいく気力がなくなってしまったのも正直理由としてはあります。
2つ目はライブラリのサンプルコードや、検索時に見つかる用例のほとんどが Gateway API(WebSocket)を使った実装ばかりだった点です。
(きちんとドキュメント読めば見つかるのでしょうが)
API Gatewayを使えば、WebSocketは使えますが、自分の作成するアプリでは不要なため見送りました。
Lambda関数が2段階に分かれている理由
・Discord側は必ず3秒以内にレスポンスを返す制限を課しています。
さほど処理時間を要する実装はないのですが、Lambda関数のコールドスタートや、Discordサーバーの設置場所に起因するレイテンシ
(今回は@edgeまでは使っていないため)、Discord側API呼び出しなど(これが一番時間を消費する)考慮すると3秒を超えるケースが割と発生しました。安定稼働させるため2段階にしています。
関数URLを選んだ理由
APIの呼び出し元がDiscord限定かつ、APIとしての呼び出しのみなのでAPI Gatewayは省きました。
その他
開発サイクル、デプロイ、バックアップ、負荷監視など記事にしたい内容はたくさんありますが、また別の機会に書きたいと思います。