読者です 読者をやめる 読者になる 読者になる

アーキテクチャをスマートに。

株式会社ネオジニア代表。ITアーキテクトとしてのお仕事や考えていることなどをたまに綴っています。(記事の内容は個人の見解に基づくものであり、所属組織を代表するものではありません)

ネオ富豪の開催にあたって(4)追加機能

考え方 プログラミング

再接続対応

参加者のプログラムは、ネット経由なので通信途絶えたり、プログラムバグで強制終了してしまったりすることも想定されます。
その場合にどう対応するべきかを考えました。
当初は、接続が切れないように維持することも課題の内だと考え、あえて再接続の対応はしない方針でした。ところがいざ自分がプレイヤープログラムの開発に着手すると、(C++で組んだこともあって)バグが取れるまでは"落ちたり"することもあり、「接続が切れたあと再接続すればプレイ継続させてほしいな」と思うようになりました。
そういった参加者目線での考え方に立脚し、どう対応すべきかを再考しました。
接続が切れてしまった場合は、途中でゲームからプレイヤーがいなくなるとおかしくなるので、ゾンビ状態でプレイヤーを残しておき、ゾンビは強制的にパスするようにし、ゲーム進行を妨げないようにしたいと考えました。そして再接続したときに、ゾンビから復帰できるようにしたいと考えました。認証の仕組みがないため、何をもって再接続とみなすかは、接続元IPアドレスと、プレイヤー名、ユーザエージェントをプレイヤーキーとして、キーが一致するかどうかで判定するようにしました。

内部の構造としては、WebSocketのハンドラとプレイヤークラスを直接結びつけるのではなく、間にもう一層かますような構造にしています。早い話が、GoF本でいうアダプタパターンです。
つまり、プレイルームに参加しているプレイヤーオブジェクトは、WebSocket ハンドラと直接つながっているのではなく、プレイヤークラスのアダプターとなっています。接続が確立されている場合は、アダプターが WebSocket を経由しているプレイヤークラスを呼び出し、流れてくるデータをパススルーします。接続が切れた場合は、プレイヤークラスを呼び出せないので、アダプターが代打ちするようになっています。(ゾンビ状態)そして再接続されたら、プレイヤーキーが一致するアダプターがすでに居れば、そのアダプターに接続するようにしました。

この再接続機能が実装されたのが12/27のことです。
実はこの再接続の仕組み、とても重要なものとなりました。大会開催日当日、競技中に特定のメッセージを受信したら落ちてしまうプレイヤープログラムがあったのです。もしこの機能がなかったら、そのプログラムはまったく参加出来ない状態になってしまうところでした。

観戦機能

当初から構想していた、第三者が観戦できるようにするための機能です。クイックプレイまでには必要ない機能だったので、ずっと実装を見送っていたのですが、後述のテストフレームワークの構想をが深まるにつれ、実現にはなくてはならない機能となりました。

Webとしての機能ではなく、もちろんゲームマスタに組み込む形で実装しています。すなわち、Webインタフェースを介さなくても、ゲームマスタ、プレイヤー、観戦者、という役割を実装できるようになっています。
また、プレイヤーと観戦者はインターフェスが同じで、受け取るイベントとデータが事なるだけ、というクラス設計にしました。
具体的には、IGameEventListener というインタフェースを抽出し、型パラメータを使ってデータコンテキストクラスを与えて継承する、という構造です。

この辺のクラス設計が一番難しかったです。今の形になるまで何度もリファクタリングしなおしました*1。観戦機能が完成したのが1/5でした。

テストフレームワーク

これは当初の予定にはなかったのですが、自分のプレイヤープログラムのことを考え始めるにつれて、簡単にテストが出来る環境がないとつらいだろうな、と思い始めたのがきっかけでした。
そこで、改めて参加者目線で考えなおし、どんなことに困るだろうか、どうしたら挫折することなく面白さを維持しながらプログラミングできるだろうか、と考えました。
おそらく、WebSocket 自体さわったことがない人ばかりなので、まずはちゃんと接続できてるかどうかのチェックかなと思いました。JavaScript でない人はさらに JSON デコードも通信制御の基礎としてチェックが必要です。
次は、プロトコル解釈、でルールの検証、と進んでいって、あとはより強い思考ルーチンの開発へと没頭していく、という流れかなと。

というぐらいまで考えがまとまった時点で、もう作り始めました。
最初の接続チェックとJSONデコードまでを TEST-1、最後のより強い思考ルーチンを作る段階を TEST-3 として、残りの部分を TEST-2 として段階設定し、テストシナリオを作成しました。
TEST-2 ではちょっと遊び心を取り入れて、参加者を色々楽しませる工夫をしたつもりです。

最終的には、当初の想定になかったためちょっと駆け込み気味で洗練されていない感じは否めないですが、あまり他では類を見ないテスト支援のための仕組みと、参加者を楽しませるための仕組みとしてもよく出来たと思っています。

テストの仕組みが完成したのが1月7日でした。ここでマスタサーバの機能はほぼ完成。あとは不具合修正のフェーズとなりました。

ツイート機能

ツイート機能はおまけだったので、最後の最後まで実装を遅らせました。
面倒だったのでこのメッセージだけプロトコルが違い、局面データを持たないメッセージとしました。プロトコル仕様のページにはそのように書いてあったし、テスト実装してないけど大丈夫だろうと高をくくっていましたが、再接続の説明で書いた通り、それが仇になりました*2。この点は反省しないといけないです。
おそらく誰もこの機能を使ってくれないだろう、完全に自己満足のための機能で終わってしまうのだろうと思っていましたが、一人だけ使ってくれた参加者がいました。感謝。

*1:詳細は Mercurialリポジトリを見れば全部残っています

*2:あるプレイヤープログラムは、ツイートメッセージを受信すると他のメッセージのフォーマットと違うため、型が一致しないというエラーになって"落ちて"しまうのです