ここ何ヶ月か合間の時間を見繕って勉強がてらgRPCとgoでオンラインポーカーを作っていたのでご紹介してみる(ソースがあれですみません。。
- proto
https://github.com/admogh/online-porker-protos - server
https://github.com/admogh/online-porker-server - client(go)
https://github.com/admogh/online-porker-clients
Dockernizeしていないのでそのままですが多分諸々入れればローカルで動作するようになっています。
仕様
- ポーカーのルール的には古いクローズドなファイブカードドローで(ただし現状ベッティング要素なし)、4人プレイ専用で、単純に1人ずつカード交換をします。
- 他のクライアントからの接続がない場合Wait後残りのプレイヤーをNPCとしてゲームが開始されます。
- 基本的にクライアントが何もしないとサーバ側で自動的にタイムアウトしたとみなして処理を進めていきます。
- リクエスト的には以下があります(詳細は proto を御覧ください)
rpc | 概要 | サーバ | クライアント |
---|---|---|---|
SignUp | 登録 | 新規登録時に名前を設定しないと自動的に名前が決められてレスポンスに返します | レスポンスの名前をデータベースに保存します。 |
ChangeName | 名前変更 | 二回目以降で違う名前で実行した時にサーバへ変更のリクエストがされます。 | |
SignIn | ログイン | ||
Join | 部屋に参加 | 部屋のIDと名前を返します。 | |
Start | ゲームスタート | game_id(一ゲームを一意に表すID)、クライアントのIDおよび他プレイヤーのIDと名前を返します。また最初の手番となるプレイヤーのIDとクライアント用の5枚のカードを返します。 | |
Change | カード交換(自分用) | ||
Draw | カード交換(他人用) | 他のプレイヤーのカード交換をリクエストします。順番が違う場合エラーを返します。また、他のプレイヤーの入力待ちの場合一定時間Wait後に結果を返します。 | |
Open | カードオープン | すべてのプレイヤーの交換後のカードをレスポンスとして返します。またポーカーの役と結果としてのランクを返します。 | すべてのプレイヤーの交換が終わった段階でリクエストします。各プレイヤーのカードの役とランクを表示し、誰が勝者かを表示します。 |
End | ゲーム終了 | プレイヤーの入力に応じてゲームを続けるか終えるかをリクエストします。サーバはリクエストに応じて次のゲームのためのリフレッシュ処理、または終了処理を行います。すべてのプレイヤーがOpenResponseを受け取るまでWaitします。 |
構成/アーキテクチャ
- クライアントはsqliteにユーザデータ(現状名前とID)を保存します。
- サーバはSignUpしたユーザ情報(現状名前とID)をMySQLに保存します。
- サーバはredisにゲームに利用する情報を保存して使います。
https://github.com/admogh/online-porker-server/wiki/Redis-Keys
デモ
上がサーバで下がクライアントになります(たまたま最後にストレートがでた。
gRPCについて
gRPCを使った実装ははじめてではなかったですが、今回改めて一から設計・実装をやってみて、gRPCが担保してくれるところはあるにしても、サーバとクライアントとのインタフェースを考えるのはやはりいつも大変だなと感じました(よりリアルタイムなオンラインゲームを作ろうとした時には、protoで表現できる領域が少なくなって、もっと実感することが多いかもしれないです)。
実際今も完成形ではありませんが、最後までprotoをフィックスさせることができませんでした。
今回は結局使いませんでしたが、streamingを使ったprotoも検討していました(途中まで実装していた)。今回は1ターンで終わりなので必要性がないという判断に至ったのですが、本質的にストリーム処理として必要なものなのかということを考えるのが判断のポイントになるように思いました。
サーバとクライアントとのインタフェースを考えていると悩む場面も少なからずありましたが、protoについて、リクエストがサーバに必要なものであり、レスポンスがクライアントにとって必要なものであると意識すること、protocで生成される利点を活かして共通に使われるような機能を組み込むこと(今回であればポーカーの役など)がよいように思いました。
その他
- NPCの機能は必須項目として実装しましたが、やはり考えることは増えます。設計次第なところはあると思いますが、NPCを完全にPCと同じものとして扱うのは他にかかるコストが増えていく気がしてバランスが求められるように思いました。
- 正直現状色々足りてないし、バグもあります。gRPC的にちゃんとエラーコードを適切なものとして返すことも必要です。
https://github.com/admogh/online-porker-server/issues
最後に
ここまで読んでいただいてありがとうございました。
最近ボードゲームをやっていてアプリ化して遊べないかなとかぼんやりと考えることがあるのですが、このようなことに興味ありましたらお気軽にご連絡ください。一緒に何かを考えましょう。
https://twitter.com/admonow