通信部分の抱える問題

前回の続きで通信部分の抱える問題を解説しておきたい。「(サーバを利用しないと)接続するのに手間がかかる」「プライベートIP同士では(サーバを利用しないと)対戦できない」「サーバを利用してもプライベートIP同士の対戦は観戦できない」という話は前々からあるのだが、ネットワークの構造上どうにもならない話もあったりするので、ちょっと今はおいておく。3つ目は私の設計の問題なんだが…
デュエルオンラインの開発当初、通信に関してまず問題になったのは「不一致の発生」であった。当時、私個人はブロードバンドではなかったし、世間一般としてもブロードバンドが使えるのは都会だけという時代であった。なので、私は通信は極力軽くしたくて、送受信するデータは必要最小限となるように設計しようとしていた。なので、「何か操作したときに、その操作した内容を相手に送る」というのが基本的な仕様であった。例えば、フィールドにカードを置いたときには、そのカード名と置いた場所のみ相手に送るという感じ。この方式だと通信量は少なくて済むが、一度不一致が発生すると修正が不可能という問題を抱えていた。
設計当初は「ネットワークの通信状態は不一致が発生するほど悪くはならないだろう。そういう状態なら既にデュエルできる状態じゃないだろう」と思っていた。ところが、実際には不一致は結構な頻度で発生した。ちょっと驚いた。
他の問題としては「データが途中で切れる」というのもあった。プログラムとしては1電文としてまとめて送ったつもりの物が、相手には途中までしか届いてなく、それを正常と思って処理するものだから、わけのわからないことが発生した。TCP/IPパケット通信なので、原理的にそういうことは有りえるとは思っていたが、これも頻発するとは思ってなかったことである。
リリースしてから1ヶ月もたたないうちにこの2つは問題として浮上した。「TCP/IPは便利だけど、さほど信頼できるものではないんだ」というのが私が得た教訓である。今までつくったプログラムは実験室で動かすようなものばかりだったからなあ。
しょうがないので、独自のプロトコルを入れることにした。不一致対策のために「ACK」を導入した。これは、データを相手に送った後、相手からの応答があるまで次の操作はできないようにするというもの。もちろんデータを受け取った相手は、その応答を逐一送信する。通信の回数は単純に倍になるし、面倒だし、プレイヤーからすれば操作はしばらくロックされたりするし、悪いことが多いんだけど、他の解決策を思いつかなかった。何か操作をするたびに、ボタンやメニューが反転して一時的に操作できない状態になるのはこの機能のせいである。データを逐一、お互いに確認を取りながら処理することで不一致を無くそうという発想である。ちなみに応答としては、本当に「ACK」という文字列を返しているだけだったりする。本当は受信したデータをオウム返しで返すとかするといいんだけど、そこで不一致があったところでどうしようもなさそうなのでやめた(単に面倒という話もある)。
2つ目の対策として「</END>」の導入というのがある。これはデータの最後を意味する文字列であり、これが来るまで待ってから、処理を始める。したがって、データが途中で切れていても、その時点では処理は始めず「</END>」が来てから処理を始めることになる。これで、確実のデータの処理を始めることができる。ずっと「</END>」が来なかったら?というのは気にしないことにした(をぃ)。
とりあえず、この2つの対策を入れたことで、通信の安定度は格段に上昇した。しかし、問題がなくなったわけではない。
現状、やりたくてもできてない対策として「再接続」がある。ネットワークの不調により接続が切れたとき再接続して、デュエルを途中からでも再開できるようにしたいと思っているのだが、これがうまくいかない。切断されたことがうまく検知できないのである。異常な切れ方をするせいか、うまくキャッチできない。私の知識の問題なのか、Delphiコンポーネントの問題なのか、そもそもそういうものなのかはよくわからないが。そして、再接続ができたとしてもお互いの状態に不一致が発生している可能性は否定できない。異常な切れ方をしているのだから、ACKもどうなったのかわからないし、互いの状態が一致している保証はない。強制的に一致させることも考えたのだが、今の仕様では、例えば相手のデッキの中の情報は持ってないので、一方の持っている状態に強制的に一致させることはできない。互いの情報を合わせて一致させるとしても不一致をどうしょりすれば正しいのかよくわからないし。
これは一言で言うなら私の設計ミスである。最初から、通信は「操作の内容」ではなく「そのときの全情報」を送るように設計すれば良かったのだ(フィールドとか手札とか墓地とかデッキとかとにかくすべての情報を)。そうしておけば不一致が発生してもすぐにどちらかに一致させることができ、再接続も実現は簡単だっただろう(ACKは結局は必要だったんじゃないかと思うけど)。事実、観戦機能は「全情報を送る」というやり方で実装している(観戦に必要ないのは省いているけど)。デュエルリプレイ機能もその応用だったりする(観戦者に送るデータをほぼそのままファイルに落としているだけ)。「絶対にそっちの方が楽だった!」とかなり後悔している部分である。ブロードバンドがもっと普及していれば、絶対にそうしたのだが… 先見の明がなかったかなあ。