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にコマンドをまとめています。

github.com

実装

コントローラ

島の新規登録や変更等、基本的なCRUDについてはコントローラをそれぞれ作成し実装しました。

特に変わったことはしていない...と思います。

以下は島新規登録画面のコントローラです。

github.com

ゲーム自体の処理

LaravelのModelはORマッパーでよく使われるため、ゲームのロジックを入れてしまうとクエリと混在して大変なことになりそう...ということで、Entityディレクトリ以下にゲームロジックを分離して、Modelとの相互変換だけをModelに書くようにしました。(いつもはロジックはサービスクラスに書いているのですが、肥大化しそうだったので...)

以下は森セルの実装ですが、毎ターン木が増えたり外の島から見た時に木かミサイル基地か違いが分からないようにしたりといった処理が入っています。

github.com

ターンの更新処理は1つのartisanコマンドにまとめました。超長くなってしまったのですが、処理速度は許容範囲内だったので同時登録島数を制限する方針でヨシとしました。

cronで定期実行するイメージです。

github.com

怪獣や災害とかの仕様はwikiと記憶を頼りに大体それっぽくなるように実装しています。

DBへの保存と読み込み

前述のとおり、ユーザーからの入力コマンドや島の地形情報などは全部jsonにして保存しています。 nullableのカラムを大量に用意することも考えたのですが、アップデートの度にalterするのは嫌だなと思ったので...

あと普通にゲームであればシリアライズなどもありなのかな?と思ったのですが、あまりWebサービスでデータをシリアライズして保存...とかすると脆弱性になりそうなのでやめました。

DBのjson ↔ Modelクラス ↔ Entity となる形で、変換を集約してなんとかしました。

よく見るとjson_decode()で取得したobjectを...get_object_vars()で展開してコンストラクタに突っ込んでます。なんとかなってなさそう。

github.com

フロントの処理

基本はバックエンドから情報を渡し、Vueで島のステータスやセルや開発計画などを整形して表示しています。フロント部分は全部友人が作ってくれたのであまり詳しく書けません...

github.com

作った機能

OAuthログイン

従来の箱庭ではユーザー名とPWを入れてログインという形式が主ですが、セキュリティを考えるのがやや面倒だったのでGoogle様のOAuthログインを使いました。 メールアドレスなどは保持せず、ユーザーの識別子だけ保存・利用しています。

github.com

あとから友人にYahooのログインも追加してもらいました。

ほかの島のプレビュー

ミサイルをほかの島に打つ際、座標指定が楽になるようにウィンドウ内で開けます。(フロントの表示は友人が超頑張ってくれました)

他の島名は一応ぼかしています(身内だけど)

実績

ターン賞や繁栄賞といった実績がもらえるようにしています。

実績は6種類くらいあります

ログ

島に起こった出来事をログとして保存して表示するようにしています。

jsonで色やリンクなどを保存し、フロントでパースしてます。ちょっとイケてないのですが、v-htmlを使うとxssの危険があるので...

巨大隕石と噴火が1ターンにきた島のログ

掲示

掲示板です。島を持っている人しか投稿できないようにしており、極秘通信は資金が必要にしています。

テーマ切り替え

友人が作ってくれました。ダークテーマとホワイトテーマで切り替えができます。

github.com

オタクなのでダークテーマにしています。

レスポンシブ対応

これも友人が作ってくれました。別件で私も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の料金が意外とお高かったのでやめました。

github.com

UT実行環境の整備

ここまでほぼUTを書いておらず、さすがにまずいと思ったので手始めにGithub Actionsをつかってテストが実行されるようにします。

ほぼテンプレートのまま動いたのでありがたいです。README.mdにバッチも付けてそれっぽくなりました。

github.com

現在もUTは書けていません...

完成

大体実装できて動いたので完成です。公開するならドメインをとってとるなどしたいのですが、とりあえず身内だけで遊んでいます。

おわりに

詰まったところ

Cloud Run

Cloud Runを使い始めたころ、特定のページにアクセスすると504エラーとなりインスタンスが応答しなくなってしまう現象に悩まされました。

1000文字くらいのJSONをBladeに渡すところで落ちていることがわかり、結局のところCloud Run第一世代だったのが原因だったのですが、これに気が付けずすごい時間がかかってしまいました...。

感想

モダンな環境でCGIゲームを再現でき、またインフラからフロントまで一貫でつくる経験がなかったのでいい経験になりました。

サ終したサービスを再現するという1つの目標が達成できたので個人的には満足しています。

ぞぎなど一部機能がないですが、気が向いたらまた作ります。

最後になりますが、フロントの実装&技術相談にのってくれたF氏、潜水艦など一部ドット絵を打ってくれたM氏、ありがとう。好きなだけ遊んでください。