CreationCollege-TOP
Now Printing..
▲ C.College TOP▲ WebAP-Labo Top.情報局2号館

∇ 記事難度
 中級。ある程度のプログラミング言語の知識を知っている事が望ましい。また、今回の記事は思いっきりDelphiに特化しているため、他言語の人は各自移植する必要あり。


∇ 感想はこちらに
クリエイターズボード(掲示板)

- Article: ネットワークゲームへの道(2)

Update: 2000/9/1


データ取得2つの方法

 前回の続きです。前は概念や流れを書いてしまったので、今回はその実装例を示す事になります。
 最小構成のネットワークアプリ(ゲーム)を作るために必要な要件として、アプリケーション上で、ISP(Internet Service Provider)のWebページの任意のアドレスにある情報を取り出す手段を探さなければいけません。出来そうに見えて、このTipsは単独ではなかなか見つかりません。もっとも、大方のプログラミング言語はHTTPのプロトコルのサポートをしているので、その仕様さえ知っていればもちろん取得できるはずですが、鷹月も詳しくは知らないという状態です(^^;)。それはともかく、Delphi3.1Jを使用した場合、簡単に上記目的を満たす方法が2つあります。

[1] IEのエンジンとなっているWebBrowser-ActiveXオブジェクトを使用し、任意ページのテキストデータを取得する
[2] Delphi拡張コンポーネントURLMONを使い、指定アドレスのファイルをダウンロードする

 任意のファイルを自動でDLしてHDDに保存する機能があれば、次のような事が実現できるでしょう。(あくまで一例です)

・Web上に集計されたハイスコア情報の取得
・RPGマップの自動拡張
・パズルゲーム追加面のDL
・最新情報の類の取得
・アクセサリの、最新のスキン画像DL
・etc..ようはアイデア次第でどうにでも活用できます。

WebBrowserオブジェクト

 WindowsのInternetExplorer4.x以降を導入しているマシンは、そのブラウザのエンジンとして、WebBrowserというActiveX(OCX)を必ず持っています。アプリケーションからこのActiveXを操作することで、理論上IEが備えている機能を全て借りることができるのです。
 Delphiで使う場合ですが、最初からこのオブジェクトは登録されていないので、「ActiveXコントロールの取り込み」を行う必要があります。(この操作は、Delphi3.xのものです。それ以降のDelphiは未所有なので分かりません)選択するコントロール名は、「MicroSoft internet Controls(version1.1)」です。その結果、2つほどコンポーネントパレットに登録されますが、実際に使うのは「TWebBrowser」のみです。TWebBrowser_V1というのはどうもIE3.x互換になるよう作られているもののようで、とりあえず無視して構いません。TWebBrowserは普通のDelphiコンポーネント同様、フォームに貼り付けるだけで使えるようになります。
左のようにTWebBrowserと、Buttonを一つずつ貼り付けてください。それで、ButtonのOnClickイベントには次のコードを書きます。
procedure TForm1.Button1Click(Sender: TObject);
begin
  WebBrowser1.GoHome; // これだけ
end;
このソースをコンパイル実行し、ボタンを押してみましょう。普段使っているIEの「ホーム」を押した時に出てくる画面がこのように表示されます(会社でこの画面を作ったから、msnのページが出てしまいましたね)。ようするに、このTWebBrowserは、ブラウザそのものなんだと思ってください。

 なお余談ですが、TWebBrowserの中には、GIFイメージを読み取る機能が付いていることから、このインスタンスを利用する製品には、あのUNISYSのLZW著作権問題が関わってくる可能性があります。が、MSや識者たちの見解によると、TWebBrowserを使うという事は、結局はIEを扱うのと同じなわけで、この問題には抵触しない…とされています。が、UNISYSはこれに関する正式な見解を一切出していないために、誰も100%問題ないとは言えないようです。てか、LZWなんて滅びなさい。バックビットだってあるし、PNGで十分。Burn All gifs Day〜♪

 まあ準備はこれくらいにしまして、本題に。いまやろうとしていることは、別にWebブラウザをクローンすることではなくて、ISP上のデータを持ってくることです。さて、上でページが表示できたという事は、WebBrowserオブジェクト内には、これらのデータがすでに保持されているというわけで、それを引っこ抜いてやればよいわけですね。先のフォームに、抜き出したテキストを貼り付ける先となる、RichEditコントロールを一つ追加してあげて、そこにデータを出力する事にしましょう。以下コードと実行例です。

// openボタンをクリックすると、Editコントロールに設定したアドレスを読み、
// そのページを画面に表示する。
procedure TForm1.Button1Click(Sender: TObject);
var le: OLEVariant;
begin
  WebBrowser1.Navigate(Edit1.Text,le,le,le,le);
end;

// transボタンをクリックすると、先にロードした画面から、タグ等の情報
// をすべて無視、純粋にテキスト部分だけの情報を取ってきて、その内容を
// RichEditコントロールに出力する。
procedure TForm1.Button2Click(Sender: TObject);
var wDoc,wTex: OLEVariant;
begin
  wDoc := WebBrowser1.Document;
  wTex := wDoc.body.createTextRange;
  RichEdit1.Text := wTex.text;
end;

 この通り、右側のRichEditに、ページのソースだけを持ってくることができました。もう完全にDelphi内部の世界になりましたね。あとはこのデータをどう調理しようが勝手、というわけです。
 ActiveXは独立したコントロール(DLL-OCX)であるため、Delphiに限らず、VB(まさにActiveXはVBのためにあるわけですが)、VC++においても、上記は実現可能です。各人移植して試してみてください。なお、HSPに関しては、Win32DLLは呼べるんですが、ActiveX-DLLファイルは現状呼べないはずなので、この機能は使えないんじゃないかな〜と思います。(ActiveX-DLLを呼ぶWin32-DLLでも用意すればどうだか知りませんが…)。  なお、WebBrowserコントロールについてのリファレンスは、由木尾さんの「蛸薬師倶楽部」にありますので、ありがたく参考にしましょう。

指定ファイルのダウンロード

 二つ目の機能の実装にいきましょう。Delphiには、URLMONという非ビジュアルコンポーネントの中に、URLDownLoadToFileという、正にそのまんまの機能を持つメソッドが用意されています。Delphi凄いと思いきや、実はこれもコントロールの正体はMSが提供しているコントロール「URL Monikers」であり、要するにDelphi用にラッパーしてただけなんですね。この記事を書いている最中に知りました(^^;)。ということでこれも、言語非依存で使える機能だと思います。さっさとソースを紹介しちゃいましょう。

// uses節に、URLMONを加える必要があります。

// ボタンを押したときに発生するイベント
procedure TForm1.Button1Click(Sender: TObject);
var p1: IUnknown;
    p2,p3: PAnsiChar;
    p4: DWORD;
    p5: IBindStatusCallback;
begin
  p1 : = nil;
  p2 : = PCHAR('http://domain/directory/targetfile'); // データ置き場
  p3 : = PCHAR('test.bmp'); // 保存先
  p4 : = 0; // ここは何があっても常に0
  p5 : = nil; // DL中にDialogを出すかどうか。大抵は邪魔なのでnil設定。
  URLDownLoadToFile(p1,p2,p3,p4,p5);
end;
 p2に、ダウンロードしたいファイル(htmlでもlzhでもjpgでも構いません。とにかくファイルであれば良いです)、p3には保存先のファイルの場所を設定します。p3はそうですね…Delphiの場合、FormのonCreateメソッドの最初に、exePath := ExtractFilePath(Application.ExeName) と書いて実行ファイルのカレントパスを取得、大抵はこれを基準に指定するから、 p3 := PCHAR(exePath+targetFile)という形になるんでしょう。
 ともかく、ボタンを押すと、p2に指定したアドレスのファイルをp3にダウンロード&保存してくれます。内部ではおそらく、HTTPプロトコルの、「GET http://〜 HTTP/1.0」あたりのメソッドが走っているのでしょうが、気にする必要なし。WebBrowserも含めて、ほとんど技巧的な事をやらずに目的を達成できました。

 あ、失敗した場合の事くらいは考えなくてはいけませんね(汗)。ページ取得要求、ファイル取得要求はすぐには帰ってこない上に、対象のファイルがなかった場合は、エラーメッセージという形でクライアントに帰ってきます。成功か失敗か、URLDownLoadToFileの返り値(HRESULT型)で分かります。

var result: HRESULT;
しておいて、

result := URLDownLoadToFile(p1,p2,p3,p4,p5);
if (result=s_ok) then 成功 else 失敗;
 こんな感じで多分大丈夫なはずです。ファイルを自由にDLできるようになってくると、格段にできる事が増えてきますね。
 なお、この2種類の方法の使い分けですが、ファイルそのものを取得したい場合は明らかにURLDownLoadToFileなのですが、そうでない場合はどうしましょう。前者を使うと、IEを一つ立ち上げるのと同じメモリを消費するので、ゲームなどマシンパワーを必要とするアプリにはあまりよい方法ではありません。実際、URLDownLoadToFileを行い、そのデータを改めて読み込むという2ステップを踏めば、全て後者で事足ります。じゃあ後者で良いのかと言うとそうでもなくて、サーバが異常に重くて、レスポンスしてくれなくなった場合なんですが、前者はサーバエラーを返してくれるのに対してこちらは、下手をすると止まりつづけるか、数分間待たされた後に失敗という応答を返す事になります。おまけにプログラム的には、前者は非同期(プログラムの実行を止めない)のに対して、どうも後者は同期っぽいのです(すみません、これちゃんと確かめてません)。
 状況に応じて使い分ける必要がありますね、ということだけ認識しておいてください。

ISP活用の基本モデル

 これらの2つの仕組みをどう活用するかは自由ですが、基本的なモデルを一つだけ紹介しておきましょう。「最新版のデータに自動でダウンロードする」ことを想定します。更新するデータは複数あるとします。しかし、直接そのファイルのアドレスを書く事はしません。これは何故なら、すでにクライアントが最新版状態になっているのにまたDLする……なんて事を防ぐために、データの日付などを埋め込んだ情報を最初に調べておく必要があるからです。また、アドレスをアプリ側で固定してしまっては、あとで変更ができず、非常に面倒なことになります。
 そこで、まず読むべきファイルは、欲しいファイルの情報だけを書いてあるインデックスファイル、という事になります。記憶メディアにおいて「TOC」とか「ヘッダ」とか呼ばれるものですね。取得するべきアドレスなどもここに書いておきます。
 それから、インデックスデータに書かれたアドレスを、DLしてよいとアプリが判断したらようやく取得していきます。

 モデルを図にしてみました。このようにしておく事で、データ配給側も柔軟に対応できるだけでなく、指定ファイルがない事によるレスポンス遅延というエラー等も起きにくくなるはずです。

APPENDIX

 今回のTechnologyをそのまま、或いは多少捻る事で、ウェブに関連するいくつものアプリ・アクセサリを作る事ができます。たとえば、指定ホームページ内の文書検索とか、ローカルにホームページ構造をDLするとか。おまけということで、「ドキュメントオブジェクトの中から、リンクアドレスを取得する」ソースを紹介しておきます。
 まず前提として、対象のページを読み込む必要があります。TWebBrowserのNavagateメソッドの叩き方については既に触れましたので書きません。で、その後ということで。

procedure TForm1.Button2Click(Sender: TObject);
var vDoc: OLEVariant;
    cnt: integer;
begin
  vDoc := WebBrowser1.Document;
  for cnt:=0 to vDoc.links.tags('a').length-1 do
    RichEdit1.Lines.Add(vDoc.links.tags('a').Item(cnt).href);
end;

 実行結果はこの通り。CreationCollegeの扉をサンプルとして使ったわけですが、さすがインデックスページ、出るわ出るわ。で、この取得したアドレス情報を、URLDownLoadToFileに入れてしまえば、1階層下のファイルを全部ローカルにコピーできちゃうというわけですね。

インタラクティブへの取り組み

 何かバイナリを載せようと思いましたが、ファイルも長くなったので今回は無しでも良いですね。さて、データの取得(および自動化)の方はこれでバッチリだと思いますが、ネットワークゲームに必要なのはあとはクライアント側から情報を送ること。というわけで、次回はCGIとの接続について触れる事になると思います。それが終われば、この単純な構造でのネットゲーム製作に必要な技術はコンプリートということになります。ただ、鷹月自身Perlはあまり詳しくないし、ちと忙しいので次回はしばらくお待ち下さいな。

 ちなみに、私のこのホームページ、正式名称は「鷹月ぐみな情報局2号館」です。1号館が存在しないのに「あれれ?」と思っている人もいることでしょう。3年前にホームページを大学サーバからプロバイダに移した時に、「1号館」→「2号館」への本館移設が行なわれ、1号館はほどなく閉鎖しました。でもあえて、こちらを2号館と名乗りつづけているのには、そのうち1号館を復活させようと言う気があるからです。
 では中身は?こんなコンテンツでわざわざネタを出した時点で気が付いた方も居ると思いますが、ネットゲーム専用のホームページになる予定なのです。とある常時稼動のネットゲーの製作を目指しています。いつの事になるかは分かりませんが、そういう夢を持ってこの3年間、2号館を運営してきたというわけですね。

 今回は、Delphiに特化し、おまけに英語じゃらじゃらのmsdnページにリンク張ってみたりと、少々専門的な内容になったかもしれませんが、いかがでしたでしょうか。感想などお待ちしています。  

- 鷹月ぐみな


.
Copyright- 鷹月ぐみな(gumina)たかつきCOMPANY 1997-2000.▲ C.College TOP