Laravelで作る箱庭諸島
はじめに
この記事について
2023年に箱庭諸島っぽいゲームを作ったので記録します。
箱庭諸島について知らない人はググってください。20年以上昔に開発されたCGIゲームですが、まだ運営されているサーバーが結構あります。
元はperlスクリプトで書かれたものを、PHP(Laravel)とVue、GCPなど使えるもの使ってフルスクラッチで作るとこうなった、というものです。
いいからソースコード見せろって人はGithubにあるのでそちらをどうぞ。
つくるもの
冒頭で箱庭諸島っぽいものと書いた通り、箱庭諸島とはいくつか仕様が違うものを作りました。
というのも、2007年頃にサービス終了した「しまにてぃ」というSNSがあり、これに乗っかっていた箱庭諸島ライクなゲームがすこぶる楽しかったので、これを再現したいなーと思ったのが事の発端だからです。
※今調べると、これはみんなのあいらんど*1というシェアウェアであったようで、この開発元は箱庭諸島の開発者でもあるとくおかさんとのこと。
使ったもの
ざっくり以下のフレームワーク/ライブラリを使いました。
バックエンド
フロントエンド
- Node 18.15
- Vite 4.3
- Vue 3.3
- Tailwind CSS 3.3
CSSフレームワークは最初はBulmaを使っていたのですが、途中から友人がTailwindにリプレイス&いろいろ実装してくれました。ありがたい...
インフラ
開発
コードを書き出すとキリがないので、かいつまみつつ書いていきます。
設計
実際は開発と設計を行き来しながら作業してます...
リソース設計
あまりRESTからはみ出ないように...と意識して画面や遷移を書くだけ書きました。後から結構変わってしまいましたが...
テーブル設計
設計するならとER図を作ろうとしたのですが、途中で面倒になったのでテーブルだけ書いて終わりました。
島の地形など、細かいデータはあまりSQLで検索するものでもないかな...ということjsonカラムにしました。
開発環境整備
php-fpmとnodeのDockerコンテナをそれぞれ用意し、それぞれバックエンドとフロントエンドの開発サーバーを動かすようにしました。
Makefileにコマンドをまとめています。
実装
コントローラ
島の新規登録や変更等、基本的なCRUDについてはコントローラをそれぞれ作成し実装しました。
特に変わったことはしていない...と思います。
以下は島新規登録画面のコントローラです。
ゲーム自体の処理
LaravelのModelはORマッパーでよく使われるため、ゲームのロジックを入れてしまうとクエリと混在して大変なことになりそう...ということで、Entityディレクトリ以下にゲームロジックを分離して、Modelとの相互変換だけをModelに書くようにしました。(いつもはロジックはサービスクラスに書いているのですが、肥大化しそうだったので...)
以下は森セルの実装ですが、毎ターン木が増えたり外の島から見た時に木かミサイル基地か違いが分からないようにしたりといった処理が入っています。
ターンの更新処理は1つのartisanコマンドにまとめました。超長くなってしまったのですが、処理速度は許容範囲内だったので同時登録島数を制限する方針でヨシとしました。
cronで定期実行するイメージです。
怪獣や災害とかの仕様はwikiと記憶を頼りに大体それっぽくなるように実装しています。
DBへの保存と読み込み
前述のとおり、ユーザーからの入力コマンドや島の地形情報などは全部jsonにして保存しています。 nullableのカラムを大量に用意することも考えたのですが、アップデートの度にalterするのは嫌だなと思ったので...
あと普通にゲームであればシリアライズなどもありなのかな?と思ったのですが、あまりWebサービスでデータをシリアライズして保存...とかすると脆弱性になりそうなのでやめました。
DBのjson ↔ Modelクラス ↔ Entity となる形で、変換を集約してなんとかしました。
よく見るとjson_decode()
で取得したobjectを...get_object_vars()
で展開してコンストラクタに突っ込んでます。なんとかなってなさそう。
フロントの処理
基本はバックエンドから情報を渡し、Vueで島のステータスやセルや開発計画などを整形して表示しています。フロント部分は全部友人が作ってくれたのであまり詳しく書けません...
作った機能
OAuthログイン
従来の箱庭ではユーザー名とPWを入れてログインという形式が主ですが、セキュリティを考えるのがやや面倒だったのでGoogle様のOAuthログインを使いました。 メールアドレスなどは保持せず、ユーザーの識別子だけ保存・利用しています。
あとから友人にYahooのログインも追加してもらいました。
ほかの島のプレビュー
ミサイルをほかの島に打つ際、座標指定が楽になるようにウィンドウ内で開けます。(フロントの表示は友人が超頑張ってくれました)
実績
ターン賞や繁栄賞といった実績がもらえるようにしています。
ログ
島に起こった出来事をログとして保存して表示するようにしています。
jsonで色やリンクなどを保存し、フロントでパースしてます。ちょっとイケてないのですが、v-htmlを使うとxssの危険があるので...
掲示板
掲示板です。島を持っている人しか投稿できないようにしており、極秘通信は資金が必要にしています。
テーマ切り替え
友人が作ってくれました。ダークテーマとホワイトテーマで切り替えができます。
レスポンシブ対応
これも友人が作ってくれました。別件で私もTailwind CSSを使いましたが、レスポンシブ対応が超楽です。
デプロイ
GCPの用意
ローカル環境で一通り作ったところでデプロイをしていきます。GCPのプロジェクトを作り、APIを有効化していきます。 最終的に使ったのは、
- Cloud Run
- Cloud Run Jobs
- Cloud Schedular
- Cloud Run Jobsとあわせて定時のターン処理で使います
- Cloud Build
- Artifact Registry
- Secret Manager
- Cloud SQL
- のちに費用が高かったので適当なレンタルサーバーに乗せ換えました
- Cloud Function(第一世代)とCloud Pub/Sub
- ビルド結果をDiscordへ通知するのに使いました
あたりです。Cloud Runは512MB RAMでも十分動いたのでそれでよさそうです。無料枠からは足が出てしまいますが、900円くらいです。
クラウド破産は怖いので念のため請求アラートも設定しました。(これくらいなら財布がやられる前にサーバーが落ちそうではあります)
当時の箱庭諸島の記事を見ると、重いのでレンタルサーバーへの設置は禁止といろいろなサイトに書かれており、サーバー性能のインフレを感じます。
CI/CDパイプラインの整備
Githubへの特定ブランチへのpushをトリガーに、CloudBuildが走るよう設定します。
CloudBuild構成ファイルを作成し、ビルドとmigrate、イメージのpushとリビジョンの切り替えなどを書いていきます。
イメージのキャッシュはビルド時間が早くなりそうだったのですが、Artifact Registryの料金が意外とお高かったのでやめました。
UT実行環境の整備
ここまでほぼUTを書いておらず、さすがにまずいと思ったので手始めにGithub Actionsをつかってテストが実行されるようにします。
ほぼテンプレートのまま動いたのでありがたいです。README.mdにバッチも付けてそれっぽくなりました。
現在もUTは書けていません...
完成
大体実装できて動いたので完成です。公開するならドメインをとってとるなどしたいのですが、とりあえず身内だけで遊んでいます。
おわりに
詰まったところ
Cloud Run
Cloud Runを使い始めたころ、特定のページにアクセスすると504エラーとなりインスタンスが応答しなくなってしまう現象に悩まされました。
1000文字くらいのJSONをBladeに渡すところで落ちていることがわかり、結局のところCloud Run第一世代だったのが原因だったのですが、これに気が付けずすごい時間がかかってしまいました...。
感想
モダンな環境でCGIゲームを再現でき、またインフラからフロントまで一貫でつくる経験がなかったのでいい経験になりました。
サ終したサービスを再現するという1つの目標が達成できたので個人的には満足しています。
ぞぎなど一部機能がないですが、気が向いたらまた作ります。
最後になりますが、フロントの実装&技術相談にのってくれたF氏、潜水艦など一部ドット絵を打ってくれたM氏、ありがとう。好きなだけ遊んでください。