2011年04月20日

キャラクタに対する「ガベージコレクション」

Simple dungeonでは、戦闘で倒されたNPCなど、セッション一覧から除外したいキャラクタが定期的に出るわけですが、その一方で、別のキャラクタにしてみれば「一定距離内にいるキャラクタ全員にメッセージ送信」という事象も絶えず発生します。

このとき、マップからキャラクタを除外するタイミングを誤ると、計算対象としてリストアップされてるのにいざ計算しようとするとDBからselectできずにエラー、というような事態が起きます。

キャラクタのマップからの除去(simple dungeonではsessionsテーブルからの除去)はよく考える必要があります。



・・・なんて、他人事のように書いちゃいけませんね。

現にrev.126がテストを通りません。

現在、対応を検討中です。


とほー。
posted by へろ at 01:52| Comment(0) | TrackBack(0) | 日記

2011年04月18日

erlシェルからの起動と、-detached指定でデーモン起動したときの動作が違う(デタッチするとサービスが落ちる)

Simpledungeonをスーパバイザ挙動を使って動かす作業中です。

erlシェルからの起動と、-detached指定でデーモン起動したときの動作が違う(デタッチするとサービスが落ちる)という現象に出くわしました。

コマンドラインで
  erl -pa /opt/yaws/lib/yaws/ebin
と起動して、erlang shellから
  simpledungeon:start().
と入力すると、サービスはちゃんと動く。

でも、同じバイナリで
  erl -pa /opt/yaws/lib/yaws/ebin -noinput -noshell -run simpledungeon start -detached &
というふうに、デーモン起動すると、なぜかサービスが動いてないわけです。

いろいろ見て回って、最終的に檜山さんのまとめた"helloちゃんと作るシリーズ"の記述に答えがありました。

http://erlang.g.hatena.ne.jp/m-hiyama/20090114/1231891538

ここ。

要するに、デーモン起動したときだけサービスが動かないというのは、start_linkで起動してるのに親シェルがデタッチしたためにサービスが止まった、ということのようです。

で、檜山さんのスケルトンを参考に、unlinkの1行を追加したところ、Erlangシェルでスーパバイザを起動したときと同じように、サービスが動き続けてくれました。

=======================
修正前:
  {ok, Pid} = supervisor:start_link({local,?MODULE},?MODULE,[]),
=======================
修正後:
  {ok, Pid} = supervisor:start_link({local,?MODULE},?MODULE,[]),
  unlink(Pid),
=======================

うー、つかれた。

start_linkしてunlinkするんだったら、素直に「start」させてくれよ、という気もしますが、そこはOTPの長年の熟成の中で入ってないわけですから、ひとまずこの作法に従うことにします。まあ、unlinkがいるのは、シェルから起動するアプリケーション最上位のsupervisorだけ、それ以外は全部linkしててほしいので、この構成は正しいと考えるのが妥当でしょうね。
posted by へろ at 01:39| Comment(0) | TrackBack(0) | Erlang

supervisorでハマッた

分かってしまえば、ものすごくシンプルなんだけど、simpledungeon開発中にハマッた件。

Erlang/OTPの、supervisor ビヘイビアで、start_linkに渡すスーパバイザ名と、start_childに渡すスーパバイザ名の書き方に違いがあるということに気づかず、謎のエラーが出続けて1週間ほどタイムロスしてました。

●スーパバイザの起動

supervisor:start_link({local,?MODULE},?MODULE,[])

のように書いたわけです。この記述には問題はありません。

●子プロセスであるyawsを起動するところ

子プロセスの起動側に問題がありました。

誤:[supervisor:start_child({local,?MODULE}, Ch) || Ch <- ChildSpecs]
正:[supervisor:start_child(?MODULE, Ch) || Ch <- ChildSpecs]

ずっと、誤の書き方をしていて、下のようなエラーメッセージが出続けていました。なんでnodedownやねん、と。このエラーメッセージの原因が分からなかったわけです(相対ディレクトリ指定ができないのかなとか、どっかがリストになってないのかな、とか)。でも、全然原因は違ってました。。。

=エラーメッセージ:=============
1> simpledungeon:start().
** exception exit: {{nodedown,simpledungeon},
{gen_server,call,
[{local,simpledungeon},
{start_child,
{yaws_log,
{yaws_log,start_link,[]},
permanent,5000,worker,
[yaws_log]}},
infinity]}}
in function gen_server:call/3
in call from simpledungeon:'-start_yaws/0-lc$^0/1-0-'/1
in call from simpledungeon:start_yaws/0
in call from simpledungeon:start/0
2>
===================

原因はシンプルで、start_childの第1引数には、start_linkのときに指定していた{local, 名称}タプルではなく、名前のアトムを渡さないといけなかったのですね。

http://www.erlang.org/doc/man/supervisor.html

にある、supervisor:start_child/2の定義。
=======================
start_child(SupRef, ChildSpec) -> Result

Types:
SupRef = Name | {Name,Node} | {global,Name} | pid()
Name = Node = atom()
ChildSpec = child_spec() | [term()]
Result = {ok,Child} | {ok,Child,Info} | {error,Error}
Child = pid() | undefined
Info = term()
Error = already_present | {already_started,Child} | term()
=======================

SupRefが、「どのスーパバイザに子プロセスを起動させるか」の参照なわけですが、{global,Name}がある一方で、{local,Name}はなく、そのかわりにNameがあるわけです。

というわけで、スーパバイザとスーパバイズ対象の子プロセスをどちらもローカルで立ち上げるのであれば、start_linkでは{local, Name}を指定し、start_childではNameを渡すという起動方法になる、、、のですよ。

はまった。
posted by へろ at 01:20| Comment(3) | TrackBack(0) | Erlang

2011年04月17日

Supervisor配下で起動するためのyaws_api:embedded_start_conf/1,2,3,4

Yawsの本家にて、yawsを別のスーパーバイザ管理下で起動するための方法の説明記事として、Running yaws embedded in another applicationが掲載されています。

Simple dungeonでこれを使おうと思ったら、当初「yaws_api:embedded_start_conf/1,2,3,4」の関数がないというエラーが発生。

Yaws1.87とやや古かったのが原因でした。最新のYaws 1.89に上げるとこの関数が利用可能でした。
posted by へろ at 12:19| Comment(0) | TrackBack(0) | Yaws

2011年04月07日

Simpledungeonサーバーに戦闘処理を追加中

というわけで、Simpledungeonサーバーに戦闘処理を追加中です。



ひとまずPerl版サンプルクライアントで実装していますが、
プレイヤーキャラクタは、NPCを攻撃できます。

続きを読む
posted by へろ at 03:18| Comment(0) | TrackBack(0) | Erlang