通信部分の抱える問題

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

まずは通信部分から

というわけで、ようやくプログラムに関する話の始まりである。用語の細かい説明は省略することが多いので、不明な言葉があれば、調べるなり質問するなりして欲しい。
私は「プログラムの設計」というものをちゃんとやらないというか、頭の中だけでやってしまう人間である(この話については後でもうちょっと詳しくしたい。覚えてれば)。というわけで、とりあえず、いきなりプログラムを書き始めた。
最初に手をつけたのは根幹とでも言うべき通信部分。当然TCP/IPである。最初、便利なコンポーネントは無いか探してみたのだが、あまりいいのが見つからず断念。そういうコンポーネントは結局のところ細かいところが思うようにいかないことが多く、最終的には自分で一からつくった方が楽だったりするという経験もあるし。と言いながら、Delphiに標準でついているコンポーネントを使ったのだが。別にWinSockで組んでも良かったんだけど、せっかくだし、慣れていこうかなあとか思って。
と書きながら「そもそもコンポーネントって何?」という人がいそうな気がしたので、ちょっと解説を。Delphi以外の環境でどうなのかは知らないが、Delphiでは単純に言えば「部品」である。ボタンとかメニューとかも部品になっていて、それをちょっと選んで置けば使えるという感じ。テキストエディタとして使えるものもあったり、リスト表示に使えるものもあったり色々ある。それらを使いこなすのが1つのポイントとも言えるくらい。で、TCP/IPについてもそういう便利なコンポーネントが既に用意されていたというわけ。
TCP/IPそのものは仕事で何回か使ったことがあり、基礎はわかっていた。簡単に解説しておくと、私が使っているのは普通のサーバ・クライアントモデルという奴。接続するときに予め役割を決めておき、一方がサーバとなり、他方がクライアントとなって、サーバが接続待ちになっている状態でクライアントが接続して通信を確立するという方式。で、Delphiにはそれぞれサーバ用のコンポーネントとクライアント用のコンポーネントが用意されていて、サーバの方はポート番号を指定してOpenするだけ、クライアントの方はアドレスとポート番号を指定してOpenするだけで接続完了。簡単簡単。最も接続するだけならWinSockを使ってもさほど難しくはないのだが妙な呪文というか、約束があったりして、昔苦労した記憶があったりするし、これからプログラムをしようという人には「使えるコンポーネントがあれば、それを使っておきなさい」と私からは言っておきたい。
接続ができることを確認してから、とりあえず、チャット機能をつくってみる。通信ができていることを確認するにはわかりやすく、実際に使う機能だから。これはあっさりと成功。通信さえできることが確認できれば、後はコツコツつくっていくだけ。昔、通信関係で苦労したことがあるので一安心。
しかし、この通信関係は後々あれこれ修正することになる。正直なところ、今でも通信部分は完璧とは言えない。ときどき止まってしまうことを経験したことは多いだろう。通信部分の修正内容と、未だに抱える問題については次回に説明することにしたい。

開発言語の選択

多少、時系列と合わない部分もあるが、デュエルオンラインの細かい話を含め、開発の詳細について話していこうと思う。
開発言語はDelphiを選択。理由はあまりなくて、一番は「使ってみたかったから」だろうな。実はプログラム歴が長い割にはWindowsGUIのプログラムは書いたことがなくて、よくわからなかったのよ。当然、VC、VBは候補に上がったんだけど、VBは「簡単だけど、かゆいところに手が届かない」という話をよく聞いていたので却下。VCは色々できる分良さそうだったんだけど、どうもきれいにコードを書けそうにない(特に自動的に挿入されるスケルトンが気に入らない)という理由で却下(何のことかわからない人のためにちょっと解説しておくと、VCでは関数の追加とかしたときにテンプレートみたいな感じで、決まり文句は自動挿入されて「この部分は消さないように」なんてコメントがついていたりする。「だったら最初から見えないところに置いておけ!」と言いたくなってしまう)。その点Delphiは簡単そうな割には機能は豊富でたいていのことはできそうで、コードもきれに書けそうな気がしたので決めた。まあ、コードがきれいになるかどうかはプログラマの能力次第なのだが…
今は仕事でも使っていたりして、結構Delphiは気に入っている。

カードデータの話

開発の詳細に入る前にカードデータにまつわる話をしておこうと思う。あのCard.txtという奴だ。なぜ、テキストファイルなのか、あのフォーマットはどう決まったのか等々話しておきたい。
結論から言おう。「既にCard.txtというものはデュエルオンライン開発前から存在し、それを流用しただけである」と。デュエルオンライン用に作成したものではない。では、何のために使われていたものなのか。
順に話をしよう。私が参考にしたApprenticeは英語バージョンしかなく、日本語が使えなかった。「メニューが英語」というレベルではなく、カード名が英語で、チャットも日本語が通らないという有様であった。そこで、Apprenticeを日本語化するツールというものが開発されていた。これはチャットで日本語が使えるようにし、カードもフィールド上は英語のままだが、その日本語での説明を別ウィンドウで表示できるようにするものであった。そして、その日本語での説明のデータとして使われていたファイルがCard.txtである。ApprenticeMTG用のソフトであるから、当然その日本語化ツールに含まれていたCart.txtはMTGのものである。それをS氏はCard.txtに遊戯王のカードのデータを入れて、Apprentice遊戯王のカードを使えるようにしていたというわけである。それを使い回したに過ぎない。元々ソフトが読むファイルなわけで、フォーマットが決まっており使いやすかったというのが主な理由であるが、他に適当なものが見つからなかったというのも本音。あと、個人的にデータはテキストの方が更新も管理もしやすいという思いもあった。
当時はCard.txtしかなかった。デッキに入れるカードだけデータ化しておけば良かったからだ。そこに融合カードだのトークンだの、制限カードだのデータを色々と追加してもらって今に至る。私自身はカードデータの更新はしたことがなく、更新して下さっている方々には頭が下がる思いである。感謝している。
UserCard.txtは開発当初からあった。オリカやデータの更新が間に合わない場合に自分でデータを追加できるようにという思いでつくったファイルである。Card.txtに追加したのでは更新するときにわけがわからなくなるので。実際、どの程度活用されているのかは知らないが…
SameCard.txtはあんまりスマートな解決策ではないが、デュエルオンラインの欠点を解消するためにつくったものである。カードデータの更新の手間が増えただけじゃないかという気がしないでもないが。これについては後々説明することにしたい(忘れなければ)。
ちょっと、実装上のぶっちゃけた話をしておくと、実はCard.txt、FusionCard.txt、Token.txt、UserCard.txtのファイルはプログラム的には区別がない。どのファイルに書いてもデータはすべてプログラムでは同じように扱われる。「管理の便宜上はわけた方がいいかな?」と思って、私の趣味でわけただけの話である。Card.txtにトークンを書いても一向に構わない。あと、カードデータが増えると、デッキ作成画面が開くのが遅くなるのだが、これも後々説明することにしたい(忘れなければ)。
デュエルオンラインのようなソフトではデータは命とも言えるものである。データなしでは何もできないのだから。そういう意味でデータ更新してくれている人には素直に感謝している。当初、カードデータの作成を支援するツールをつくろうかと本気で思ったことがあるのだが、そんなに使い勝手が良いツールになりそうになかったのでやめた。「こんなツールが欲しい」という意見があればぜひ遠慮なく要望として出して欲しい。

始めた理由は忘れた

なぜ、遊戯王に興味を持ち、OCGをやり始めたのか、そのきっかけは忘れた(←おぃ)。何かのゲームの攻略本についてきたカードがきれいだったので興味を持ったのがきっかけだったような気もするが。まあ、元々漫画は読んで知っていたし。
で、遊戯王を始めようと思って最初に思ったことは「対戦相手は?」ということ。1人で遊べるゲームじゃないし。少なくとも身の回りで聞いたことはない。
で、ちょっと考えて「この時代、きっとネット対戦の環境があるに違いない!」と思い、キーワードを色々考えながら検索してみる。当時調べたところでは、どうやらネット上ではチャットデュエルが基本だったらしい。これにはガッカリ。そんなものじゃ楽しくない。当時はブロードバンドでなかったこともあって、負荷の軽い専用のソフトとか、そういうのを期待していたのだが。
そして、さらに調べてみると、MTGのネット対戦のフリーソフトApprenticeを使って、無理矢理遊戯王のネット対戦をしている人達のサイト(その中心はS氏であった)を見つける。本当に無理矢理だったので、試しに使ってみたところ、かなり使いにくい。「裏守備がしにくい」とかいうレベルのもあれば、「日本語が通りにくい」「カード名がフィールドでは日本語で出ない」というレベルまで、どうみても使いにくいものだった。
そこで私はApprentice遊戯王バージョンを作ればいいのではないか」と思い立つ。「きっとS氏達もApprenticeが使いやすいと思っているわけではあるまい。遊戯王バージョンをつくれば、きっと使ってくれる。そうすれば少なくともS氏の仲間をネット対戦の相手として確保できる」という計算もあった。こうして私は2001年のゴールデンウィーク前に開発環境を整え、ゴールデンウィークガリガリと開発を始めるのでった。

デュエルオンライン開発日誌ということで

デュエルオンラインの画面

今まで書いてきた「管理人の一人言」は停止して(直リンでは動かないのでリンクはないです)、Blogで「デュエルオンライ開発日誌」と題して、過去の開発について私の記憶を元に、様々なエピソード、技術的な問題などつらつらと書いてみたい。例によって不定期連載であるが。単に「Blogを使ってみたい」というだけの話なのだが、まあ、デュエルオンラインの開発の経緯に興味のある人や技術的な話に興味ある人もいるんじゃないかなあと。「そんな暇があれば、バージョンアップしてよ」という声も聞こえてきそうだけど、私はまとまった時間がないとバージョンアップできない人なので、その点はご理解頂きたい。
「デュエルオンライン」を知らない人のためにちょっと書いておくと、有名なトレーディングカードゲームである遊戯王をネット上でできるように私が作成したフリーウェアである。ネット対戦型のゲームをつくる上ので技術的な問題も話題として出てくるかもしれないので、興味のある人はぜひ見て欲しい。
ちなみにデュエルオンラインはここに置いてある。もっと情報が欲しい人はその上のページを見ればだいたいわかるんじゃないかと思う。