ソラマメブログ

2007年04月11日

聞き耳を立てる(初級スクリプト第七回)

照明スクリプトの続きです。
前回作ったスクリプトの改良になりますので、実習する際には前回分からやってみて下さい。

前回の問題点

前回はタッチするたびに赤、緑、青、消灯と変化する照明を作りましたが、この照明は好きな色にする際に何度もタッチしなければならないという問題点があります。
いきなり青い光をつけたいとか、赤い光で点けていたが消灯したいというようなとき、数回クリックするのは面倒です。
ラグが発生しているときなどには、誤って二度、三度とタッチしてしまい、一気に色が変わってしまうようなこともあります。

聞き耳を立てる(初級スクリプト第七回)

何度もタッチすることなく好みの色にしたり、消灯するようにしたいところです。
例えば、タッチのたびに色を変えるのではなく、タッチしたあと、チャットで色を指定するとその色に変わるような仕組みはどうでしょうか。
"red"と言えば赤く光り、"blue"と言えば青、"off"で消えるような照明です。
lslでは、チャットの発言をチェックするための機能が用意されています。
これをリッスン(listen)機能と言います。
スクリプトが発言を「聴く」わけです。

リッスンはイベント(きっかけ)になります。
「発言があったときに」というイベントですね。
ただし、このイベントは常に発動するわけではなく、あらかじめスクリプトのほうで、
「これから発言に耳を傾けるようにします」
と設定しておかなければいけません。
マイクのスイッチを入れるようなものだと思ってください。

ですので今回の改良は、
(1)タッチしたときにlistenをONにする
(2)チャットで色名が発言されたら照明の色を変える
この2点になります。

さっそくlistenの使い方から見ていきましょう。

listenのON

まずはlistenをONにする命令からです。

  integer llListen(integer channel, string name, key id, string msg)

ちょっと難しそうです(^^;
ですが、この命令はスクリプトでは非常に使用頻度が高い命令ですので、きっちり使い方を覚えておくべきです。

引数が4つありますので、順番に説明していきます。

integer channel

聞き耳を立てるチャットチャンネルを指定します。

チャットチャンネルというのは、チャットの周波数みたいなものです。
普段使っているチャットは0チャンネルで送受信されています。
それ以外にもチャンネルは無数にあり、例えば2147483647チャンネル(驚)なんかはスクリプトのエラー情報が飛び交っているチャンネルです。
2チャンネルでは常に最新のネタが受信可能です(違

チャット欄から0以外のチャットチャンネルで発言するには、
  /1 メッセージ
のように入力します。
/の後ろに書いてある数字がチャンネルです。

例えば、色名の入力を7チャンネルで行うと決めたら、llListen()に指定するチャンネルを7にするわけです。
その場合はユーザーは「/7 red」とか「/7 off」のように発言することでスクリプトに命令することができます。

0チャンネル以外のメッセージは、通常チャットログには表示されません。
「red」「off」のような発言がログに流れるのはうっとおしいですし、不特定多数の人がいるような場所ではスパム扱いされる可能性もあります。
ですので、スクリプトに対するコマンドは0チャンネル以外を使うのが望ましいでしょう。

string name

誰の発言に耳を傾けるかです。
アバターに限らず、オブジェクトの名前を指定することもあります。

string型というのは文字列を扱う型で、スクリプトの中で文字列を書くときには""で囲みます。
例えば私の名前であれば、
  "Miz Cremorne"
のように書きます。

全ての人の発言に耳を傾けるときは、空文字を指定します。
空文字とは、
  ""
のことです。

key id

これも誰の発言に耳を傾けるか、です。
ただしこちらは名前ではなくUUIDを指定します。

UUIDというのは、SLの世界に存在するもの全てに割り振られているユニークなIDのことです。
認識番号みたいなものですね。
この番号は"66864f3c-e095-d9c8-058d-d6575e6ed1b8"のような形式になっており、スクリプトではアバターやアイテムを特定するためにしばしばこのIDを使います。
ですが、キーの実際の中身("66864f3c-e095-d9c8-058d-d6575e6ed1b8"のような)を、具体的に調べなければならないことはそれほど多くはありません。
ほとんどの場合は、key型の変数を用意し、その中にこの英数字を格納して使いますので、変数名をやり取りするだけになります。

ついでに説明しておきますが、key型の変数の初期値は、"00000000-0000-0000-0000-000000000000"です。
この値はNULL_KEYという特別な定数名で扱うことができます。
NULL_KEYというのが出てきたら、それは"00000000-0000-0000-0000-000000000000"のことであり、key型の初期値なんだということだけ分かっていればOKです。
この値は「空っぽ」「存在しないもの」と同義になりますので、NULL_KEYで示されるアバターやアイテムは存在しません。

llListen()でNULL_KEYを使った場合は、全ての人/物の発言に聞き耳を立てるようになります。
「存在しないもの」の発言を聴く=誰のものでもない発言を聴く=誰のであろうが発言全部を聴く、ということです。

string msg

何という言葉に耳を傾けるかです。
例えばここに"red"と指定すると、"red"という発言以外は一切聴いてくれません。
"I love you"と指定したら、"I love you"以外は完全無視する恋は盲目的な厄介なスクリプトになります。

"red"も"green"も"blue"も"off"も聴いてくれよ~という場合は、空文字""を指定します。
空文字""は「何であろうと発言を聴く」ことになりますので、想定外の言葉も全部聴こえてきます。
聴きたくないことも中にはあるでしょうが、こればっかりはどうしようもありません。
従って、想定外の言葉に対する反応は別途考えておく必要があります。

以上4つの引数を使ってllListen()命令を実行すると、integer型の数値が戻ってきます。
この数値は「ハンドル」と呼ばれます。
ONにしたマイクの番号みたいなものだと思ってください。

例えば、llListen()を二度実行したりすると、このハンドルはそれぞれ違う値が割り当てられます。
マイク1とマイク2がONになるようなものですね。

具体的にllListen()の書き方を例示しておきましょう。

  integer handle = llListen(7, "", llGetOwner(), "");

この例では、チャットチャンネル7での発言を聞き取ります。
発言者の名前にはこだわりません。
ですが、llGetOwner()でUUIDを指定していますので、実際はただ一人の人物の言葉だけを聴きます。

llGetOwner()は初登場ですが、オブジェクトのオーナーのUUIDを取得する関数です。
従って上記の例はオーナーの発言しか聞き取りません。

最後の引数が空文字ですので、発言内容に関わらず聞き取ります。
罵りであろうが、侮蔑であろうが、愚痴であろうが、何でも聞いてくれる健気なスクリプトです。

このllListen()で有効になったハンドルが、変数handleに入ります。
後述しますが、この番号は使い終わったlistenをOFFにするときに使います。

さて、これでlistenをONにする方法はわかりました。
listenがONになると、listenイベントが発生するようになります。
続けてlistenイベントの処理の仕方を見てみましょう。

listenイベント

llListen()で指定した条件に当てはまる発言があると、listenイベントが発生します。
listenイベントは以下のようになります。

  listen(integer channel, string name, key id, string message){
    //処理
  }

llListen()と同じような引数があります。
簡単に説明すると、

integer channel

発言があったチャンネルです。
llListen()で指定したチャンネル以外はあり得ませんが、場合によってはllListen()を二度使って複数のチャンネルに聞き耳を立てるような使い方をすることがあります。
その際、この引数の値を見て、どこのチャンネルの発言なのかを判断します。

string name

発言者の名前です。
llListen()で発言者を""にしたような場合はあらゆる人・物の発言が聴こえてきます。
例えば私の発言だった場合は、"Miz Cremorne"という名前がこのname変数に入ってきます。

key id

発言者のUUIDです。
llListen()で発言者のUUIDにNULL_KEYを指定するとあらゆる人・物の発言が聴こえてきます。
そのような場合にはこの引数の値を見て、誰の発言なのかを判定するのに使います。

string message

発言内容です。
"red"や"green"や"blue"や"off"など、発言された内容が入ってきます。
今回はこの引数の内容を判定し、照明の色を変化させることになります。

なお、llListen()でチャンネルを0以外に設定している場合、チャット欄からの入力は「/7 red」のようになりますが、このmessageに入ってくるのはあくまでも"red"だけです。
チャンネル指定の「/7」は含まれません。

ここで一度、今回作ろうとしている照明スクリプトのlistenイベントの部分を書いてみましょう。
照明の色を変化させる処理は、前回作ったユーザー関数をそのまま利用します。


  listen(integer ch, string name, key id, string message){
    if (message == "red"){
      light(<1.0, 0.0, 0.0>);
    }else if(message == "green"){
      light(<0.0, 1.0, 0.0>);
    }else if(message == "blue"){
      light(<0.0, 0.0, 1.0>);
    }else if(message == "off"){
      light(ZERO_VECTOR);
    }else{
      llSay(0, "You can use red, green, blue, and off only.");
    }
  }


messageの内容をif文で判定しています。
そしてred,green,blue,offそれぞれに応じてユーザー関数light()を呼び出します。
それ以外の発言だった場合は、red,green,blue,offしか使えない旨のメッセージを表示するようになっています。

このようにlistenイベントを組み込めば、発言に応じて色が変わる照明を作ることができるはずです。

listen使用上の注意

実際にスクリプトにlistenイベントを組み込む前に、注意しなければならないことがもう一点あります。
それはllListen()によってONにした「聞き耳」をOFFにする処理です。

SLでは、常時多くのチャットが飛び交っています。
listenをONにすると、チャットで何か発言があるたびに、スクリプトが動くことになります。
発言者の名前やUUIDを指定していても、発言を処理すべきかどうかの判断が内部的になされますので、どうしてもサーバーの負荷になってしまいます。
サーバー上では複数のSIMが動いていますので、周囲に誰も居ないから・・・と思っても、実は他のSIMで多くの人が遊んでいて、全体的にラグが発生するようなことにもなりかねません。

ですので、listenの使用は最小限に留めるべきでしょう。
使い終わったらきちんとOFFにするのがスクリプトのマナーになります。

OFFにするにはllListenRemove(integer number)という命令を使います。

引数numberには、ハンドルを指定します。
ハンドルとはllListen()を実行したときに返ってくる番号です。

具体的に書いておきます。

  handle = llListen(7, "", llGetOwner(), ""); // listen ON
  llListenRemove(handle); // listen OFF

今回のスクリプトでもきちんとlistenのOFFを書くべきですが、どこに書くのが良いでしょうか?
使い終わったあとにOFFしますので、listenイベントで発言を受け取ったときにOFFにするのが良さそうです。
listenイベントの中で、
  llListenRemove(handle);
の一行を追加しておくと良いでしょう。

しかし・・・。
発言が無かったらどうでしょうか。
listenをONにしたものの、ユーザーが色名を発言せず、無言でどこかに行ってしまったら、listenは健気にいつまでも待ち続けることになってしまいます。
あまりにも哀れで涙がこぼれそうです。

いつまでも待ち続けることがないよう、timerイベントを使いましょう。
例えば、30秒間発言を待ち、発言が無ければlistenをOFFにするような仕組みです。

そこまでやっておけばlistenの負荷対策は十分でしょう。

照明スクリプト改

では、前回のスクリプトにlistenを組み込んでみましょう。
太字が追加部分になります。


integer handle;

light(vector color){
  if (color == ZERO_VECTOR) {
    llSetPrimitiveParams(
      [PRIM_POINT_LIGHT, FALSE, ZERO_VECTOR, 0.5, 3.0, 0.75]
    );
  }else{  
    llSetPrimitiveParams(
      [PRIM_POINT_LIGHT, TRUE, color, 0.5, 3.0, 0.75]
    );
  }
}

default {
  state_entry(){
    light(ZERO_VECTOR);
  }

  touch_start(integer detected){
    if (handle) {
      llListenRemove(handle);
    }

    handle = llListen(7, "", llDetectedKey(0), "");
    llSetTimerEvent(30.0);
  }

  listen(integer ch, string name, key id, string message){
    if (message == "red"){
      light(<1.0, 0.0, 0.0>);
    }else if(message == "green"){
      light(<0.0, 1.0, 0.0>);
    }else if(message == "blue"){
      light(<0.0, 0.0, 1.0>);
    }else if(message == "off"){
      light(ZERO_VECTOR);
    }else{
      llSay(0, "You can use red, green, blue, and off only.");
    }
    llListenRemove(handle);
    llSetTimerEvent(0.0);
    handle = 0;
  }

  timer(){
    llListenRemove(handle);
    llSetTimerEvent(0.0);
    handle = 0;
  }

}


タッチイベントを大幅に改造しています。
誰かがタッチすると、listenをONにしますが、llListen()にUUIDとしてllDetectedKey(0)を指定しています。
llDetectedKey(0)はタッチした人のUUIDを取得する関数です。
つまり、タッチした人の発言に耳を傾けるようになります。
そしてllSetTImerEvent()で30秒間のタイマーをセットしています。

llListen関数を実行する前にif文がありますが、これはlistenの複数起動を防ぐためのものです。
llListenは異なる引数の組み合わせを使って実行すると、新たに「聞き耳」を立てます。
もしも二人の人が立て続けにタッチした場合、二つの「耳」が動き出すことになってしまいます。
ですので、もしもhandleが0でなかったら(=すでにllListenが実行されていたら)llListenRemove()でlistenを閉じています。

タッチした人が発言するとlistenイベントが発生します。
ここで発言に応じてlight()ユーザー関数を呼び出します。
発言を処理したあとは、llListenRemove()でlistenの後片付けをしています。
また、llSetTimerEvent(0.0)でタイマーもOFFにします。

発言のないまま30秒が過ぎるとtimerイベントが起こります。
llListenRemove()でlistenをOFFにし、llSetTimerEvent(0.0)タイマー自体もOFFにします。

今回のポイント

listenのON/OFF:
  handle = llListen(7, "", llGetOwner(), ""); // listen ON
  llListenRemove(handle); // listen OFF

listenイベント:
  listen(integer channel, string name, key id, string message){
    //処理
  }

UUID:
SL内の全アバター・全オブジェクトに割り振られているユニークなID。
テクスチャーやサウンドなどのデータにもUUIDが付けられている。
「存在しないもの」はNULL_KEY(="00000000-0000-0000-0000-000000000000")。

チャットチャンネル:
通常チャットは0チャンネル。
0以外のチャンネルで発言する際には「/7 red」のように発言する。

ぜひマスターしていただきたいのはlistenの使い方です。
タッチイベントと並んで最も使用頻度の高いイベントですので、使いこなせるようにしておきましょう。

さて、これで何度もタッチすることなく色を変えられる照明ができました。
ですが今度は、いちいちチャットで発言しなければならないのが面倒に思えてきますw
もっと簡単なオペレーションで、照明の色を自由に変えられるようにはできないでしょうか?

・・・それはまた次回の課題にしたいと思います。


同じカテゴリー(初級スクリプト)の記事画像
衝突判定(スクリプト初級第二十三回)
カメラ制御(スクリプト初級第二十二回)
センサーを使おう(スクリプト初級第二十回)
HUDを作ろう(スクリプト初級第十六回)
prim間通信(スクリプト初級第十五回)
アニメさせよう(スクリプト初級第十三回)
同じカテゴリー(初級スクリプト)の記事
 衝突判定(スクリプト初級第二十三回) (2007-05-08 12:15)
 カメラ制御(スクリプト初級第二十二回) (2007-05-07 14:36)
 デモ商品を作ろう(スクリプト初級第二十一回) (2007-05-02 12:15)
 センサーを使おう(スクリプト初級第二十回) (2007-05-01 12:15)
 ステートのこと(スクリプト初級第十九回) (2007-04-27 12:15)
 rez!(スクリプト初級第十八回) (2007-04-26 12:15)
この記事へのコメント
むちゃくちゃわかりやすい記事でした。
これまでに読んだ listen の使い方解説の中で一番です。
これだけの文章を書くのはかなりの時間がかかると思いますので本当に感謝します。
Posted by Tak Nishi at 2007年04月11日 12:30
ありがとうございます。励みになります(><;

lslに用意されているイベントの中で、最も柔軟性があるのがlistenイベントですので、どうしても使用頻度が高いイベントです。
これが使えるかどうかでスクリプトの幅が格段に違ってくるので、敢えてダラダラと説明してみましたw
Posted by Miz at 2007年04月11日 13:30
はじめまして。
本当に凄いですね〜
負荷の事まで考慮されている事に感動です。
勉強になるなぁ。
Posted by Nitaro at 2007年04月11日 13:50
初めまして、Nitaroさん。

負荷についてはSecondLife Wiki JPのスクリプト入門(http://secondlife-wiki.main.jp/modules/pukiwiki/?%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8%C6%FE%CC%E7)の受け売りではあります(^^;
私もlslを勉強し始めたときに、SL Wiki JPの記述を読んで、
「便利なイベントだけど負荷になるのは厄介だなぁ」
と印象的だったのを覚えています。

listenイベントについて解説する以上、読んだ人が実際に使うことを想定してますので、仮に負荷についての記述を省略してしまうと、多くの人が負荷を考えずにlistenを多用するかもしれないと心配しました。

個人的に使うだけならまだ良いですが、商品として高負荷なスクリプトが出回ってしまうと、SL全体にとって問題になりますので、どうしても避けては通れない話題ですねぇ・・・(^^;
Posted by Miz at 2007年04月11日 14:12
第1回から読み始めてここまで着ました。
分かりやすい解説ありがとうございます。
Posted by もに at 2007年04月14日 21:22
>もにさん

listenは最初の山場ですね。
疑問点などあればご遠慮なくコメントでもメールでも質問して下さいね。
Posted by Miz at 2007年04月16日 11:46
script初心者として、いつもありがたく読ませていただいてます
一つ教えて欲しいのですが、IMにより起動するスクリプトを作りたいと考えているのですがListenのチャンネルでそのような設定はあるのでしょうか?もしご存知でしたら教えてください
Posted by nyagos kidd at 2007年05月22日 00:46
>nyagos kiddさん

残念ながらオブジェクト(スクリプト)はIMを受信できません。
従ってIMに反応するようなスクリプトは作ることができません。

IMを使いたいのは何故でしょうか。
理由によっては、以下の方法で代替は可能かと思います。

*他の人にコマンドを見せたくない、という理由の場合
listenのチャンネルを他の人にわからないようなチャンネル(例えば1342とか)に設定する方法があります。
スクリプトにコマンドを送るときは、「/1342 command」のように発言すればOKです。この発言は通常他の人には見えません。

*遠く離れたところから操作したい、という理由の場合
IMに似た機能でEmailというのがあります。
これはズバリ、Emailの送受信をする機能なのですが、セカンドライフのオブジェクトは何と一つ一つがEmailアドレスを持っていますw
オブジェクトに対してEmailを送信し、それを受信して動き出すスクリプトであれば、遠隔操作が可能です。
ただしEmailは速効性はありません(例えば送信には20秒かかります)。

Emailの詳細については別途記事にしてみようと思います。
Posted by Miz at 2007年05月22日 09:31
大変参考になる記事をありがとうございます。
listenイベントの中の llListenRemove(handle);
をコメントアウトしないと、コマンドを入れる前に オブジェクトをタッチしないといけないようなのですが、コメントアウト or 削除した場合、サーバーに負荷がかかってしまいますか?
Posted by Munemitsu Nishi at 2007年06月19日 01:53
>Munemitsu Nishiさん

そうですね、llListenRemoveを外した場合は、リッスンが動きっぱなしになるので、多少サーバーの負荷にはなります。
まあ、一つくらいリッスンが動きっぱなしでも問題はないのですが、できれば使わないときは止めておいたほうが世のため人のためではあります。

タッチしてはじめてリッスンを有効にしているもう一つの理由は、近くに同じコマンドを処理するオブジェクトが存在したいた場合を考慮してです。
例えば、このランプが二つ並んでいたらどうなるでしょうか。
もしもどちらのランプも常にリッスンが動いていたなら、redと発言したタイミングで両方のランプの色が赤に変わってしまいます。
タッチしてからリッスンを有効にすれば、色を変えたいほうだけを正しく変えることができます。
Posted by Miz at 2007年06月20日 19:34
なるほど、2つ存在した場合というのは、想像していませんでした。
どうもありがとうございました。
Posted by Munemitsu Nishi at 2007年06月21日 02:06
とても分かりやすく教科書みたいですので、初回から順番にレッスンをさせて頂いています。
ところで今回、同じようにスクリプトを書いて試したのですが、どうも動きが変なので、お尋ねしようと思いました。

1.タッチするときに、クリックしただけでは認識せず、パイメニューを出した瞬間に認識します。(ちなみに私はMacです)

2.上記のときに、まずオブジェクトに発声させて、その状態で、7チャンネルで色名を言うと、言ったときには全く変化せず、その次にクリック(パイメニュー出し)した瞬間に、色が変わります。
touch_start の中で lightを呼び出しているわけではないのに、なぜかこのような動作になるので不思議です。

上記のように、一応の動作はするのですが、なんかタイミングが先生のものと違うようなので何か心あたりがありましたら、アドバイス頂けませんでしょうか?
よろしくお願いします。
Posted by わた at 2007年07月07日 22:37
>わたさん

むむ・・・これまた不可解な動きですね(^^;
ラグのせいにしてしまえば全て片付きそうですが、上記現象は常に起こるのですよね?

あとは念のため、Editモードを抜けてテストしてみて下さい。
Editモードでオブジェクトを選択している間は、ただクリックしただけではタッチイベントが発生しなかったと思います。
Posted by Miz at 2007年07月09日 11:56
お世話になります。
やっぱりだめです。
Editモードは抜けています。
スクリプトを再度見比べたのですが、間違っているわけではなさそうです。
ラグではないと思います。待ってても変化しないし、だけど、パイメニューを出したらすぐに変わります。

ひょっとするとMac特有の何かがあるのかなー?なんて思い始めています。
再度現象を正確に整理しておきますと、

(1) タッチする(ワンクリック)  >  Listenに入る (私の場合、ガイダンスをしゃべるようにしてます)

(2) 7チャンネルで命令をする。

(3) コマンドキー + ワンクリック (パイメニュー)  >  色が変わる

不思議なことに、最初のタッチは、パイメニューではダメで、
後のタッチは、普通のクリックではダメ、なんです。

とりあえず、次の課題にチャレンジしたいと思います。また何か気がついたら報告させて頂きます。
Posted by わた at 2007年07月09日 21:45
上記の不具合の原因らしきものが分かりました。

オブジェクトの「リンク」が悪さをしているのではないかと思い、試しに、1プリムのボールを作って、それにスクリプトを書いたところ、見事に動作しました。

リンクされたオブジェクトで、次の課題にあるダイアログで試した場合、
1つのプリムにスクリプトを書いて、そのプリムのオブジェクト名は名無し、全体のオブジェクト名だけ付けていると、ダイアログには Object と表示され、上記のような変な動作をしました。

検証してませんが、たぶんオブジェクトのリンクの順番に関係するのかなあと思っています。
Posted by わた at 2007年07月12日 21:32
こんにちは、初めましてSLを初めて2ヶ月ほどの新人のpyonです。

今、スイッチで3つのライトがオン・オフ出来るようなLSLを組んでる最中なのですが、上手く組めません。
仕組みは、スイッチをタッチする事にライトにメッセージを送って、それを
聞いたライトがオン・オフするという感じかな・・・とまでは来たのですが
それを、なかなか形に出来ません。

いい案等、ありましたらお力添えお願いします。
Posted by pyon at 2007年07月30日 16:01
>pyonさん

おっしゃるとおりの仕組みで実現できるかと思います。
方向性は間違っていませんので、あとはどう実現するかですね。
うまくいかないのはどの部分でしょうか。

メッセージが正しく送れないのか、それともメッセージは送れているけど、ライトのオン・オフを切り替えるのがうまくいかないのか・・・。

どこに問題があるのか、まずそれを見極めるといいかと思います(^^
Posted by Miz at 2007年07月30日 18:35
返信ありがとうございます。
メッセージは送れているので、問題はライト側かなと思います。
リッスンの使い方が把握しきれていないのか、エラーが出て上手くいきません。
難しいですww
Posted by pyon at 2007年07月31日 00:58
>pyonさん

エラーはスクリプトを保存したときに出ますか?
保存のときに出るようなら、エラーになっている行番号が表示されるかと思います。
そのあたりを重点的に見直してみて下さい。
何が間違っているかはエラーのメッセージでわかります(英語ですが)。
例えば「シンタックスエラー」であれば文法的な間違いです。
文末の;が無いとか、{}や()の対応がうまくいってないとか、,と.の違いとか、ささいな見落としの可能性があります。
変数の型が違っていてエラーになっていることもあり得ます。

保存のときにはエラーが出ず、実行中に出るエラーの場合は、メッセージを教えてください。
照明のスクリプトに関して言えば、実行中のエラーってあんまり考えにくいですけども(^^;
Posted by Miz at 2007年07月31日 11:47
いつも参考にさせていただいてます。

 上記発言に見られるサーバ負荷について、いかなる条件下で使用した場合に
どのような負荷がかかるのか等、計測しての発言ではなさそうですが
Listen0がかける負荷とはどの程度のものなのでしょうか、漠然とした広義の言葉はいらないので、
サーバ負荷の根拠を示していただけないでしょうか。

ch0で受ける必要の無いものに使わない、listenチャンネルの開放処理を
スクリプトに入れるのはマナーとして、どうしても常にch0を利用する
スクリプトを記述せざるを得ない場合もあります。
ウインドウ内で、可視発言をひらう他の方法は、知りうる限り無いようですが、
何かありますか?

PS.
上記発言にみられるように、根拠ない発言から、危険、問題、
などの負の言葉だけが一人歩きするほうが怖いです。
Posted by mi at 2007年09月15日 18:53
追記
Listen0がかける負荷とありますが、Listenがかける負荷としてください。
よろしくお願いします。
Posted by mi at 2007年09月15日 19:08
>miさん

不用意なlistenの多用でSIMが不安定になったり、落ちてしまったりという経験は何度かありますので、経験に基づいて、安易な多用は負荷となると書いています。

具体的な負荷数値についてはテスト可能な環境がありませんので、お答えしかねます(個人所有のSIMは持っていないため、過度な負荷をかけることはできません)。

miさんがテスト可能な環境をお持ちでしたら、クライアントの統計ビューで各種負荷状況がモニタできますので、ぜひテストしていただけたらと思います。
Posted by MizMiz at 2007年09月15日 22:41
その不用意なListenの多用で落ちたという経験ですが、
どのような環境でどう多用してSIMが不安定になったのですか?
Posted by mi at 2007年09月16日 02:48
>miさん

私に何を求めていらっしゃるのでしょうか。

ラグやSIMの不安定と、listenとの間に直接的な因果関係があることを明確な証拠を挙げて証明せよということでしたら、私には出来ません。

miさんはlistenが負荷にはならないと仰りたいのですか?
それとも、どの程度負荷になるのかが知りたいだけでしょうか。

後者であれば、先に回答した通り、私には測定する手段がありませんので、ご自分で調べていただければと思います。
Posted by Miz at 2007年09月16日 21:49
 ここに書いてある通り、Listenが果たして本当にSIMを不安定にさせたり、
また重たいSIMでListenを使用した場合、SIMを落とすに至るのか、
それがどのように影響するのか、これからListenを使用したオブジェクトを
作成しなければならないため、listenがかける負荷、程度を知る必要がありました。
本当であれば早急に回避策を考えなければいけませんので。

 miz殿は、不用意なListenの多用が原因でSIMが不安定になったり落ちたという
経験に基づく発言をされています。断言するに至る根拠を示していただけると期待
しましたが、どうやら私の期待が大き過ぎたようです。

ちなみに、負荷調査については、私も環境を持っておりませんので、
今回はそのまま作成します。しかし、アンテナは張りつつ
不具合を起こすのであれば、回避策を講じて作成したスクリプトを
修正ようと思います。

それでは大変失礼致しました。
失礼な質問に関わらず、お付き合い頂き、ありがとうございます。
本当は、こんな形でお知り合いになりたくはありませんでしたが、
仕方ないです。心象を害されたかもしれませんが、お許しください。
それでは。
Posted by mi at 2007年09月17日 00:19
>miさん

いろいろと誤解があるのかもしれませんが、改めて書いておきます。

負荷になるという点では、listen以外にも短い間隔でのタイマー、繰り返し探知を行うセンサーなども同様です。
要するに常に「稼動状態」になるスクリプトは全て、サーバーのリソースを消費し続けることになりますので、当然ながら負荷になります。
その「負荷」というのがどの程度のものなのかについては、前述した通り、私には詳細なデータを入手する手段がありません。
listen一個でサーバーリソースの1%を使うのか、それとも10%を使うのか、具体的なところはわかりませんが、確実なのはリソースを消費する、ということです。

SIMが稼動しているサーバーのスペックは様々ですし、ユーザーの接続数、周囲で稼動している他のスクリプトなどによってもトータルの負荷状況は大きく変わってきます。
ですので、例えばlistenを100個動かすとSIMが落ちるとか、そういう閾値を示すことは困難です。

「listenは負荷になるから使わないほうが良い」と受け取られているのでしたら、大いに誤解されていると言わざるを得ません。
現実問題として、例えばlistenが2,3個動きっぱなしになっていたところで、SIMへの影響度合いはたいしたものではないと思われます(その程度の稼動は想定したサーバースペックになっているはずです)。
だからと言って、皆が「動かしっぱなしで大丈夫だろう」と思って使い始めたら、トータルの負荷はどれだけ増大するかわかりません。

仮に私が自分の土地で調査を行い、「listenは100個まで動かしても大丈夫でした」と報告したとして、それを鵜呑みにして皆が100個のlistenを動かし始めたら、それこそサーバーリソースを食いつぶしかねません。
「listenは負荷になるから怖いな」と思っていただいたほうが安全ではないでしょうか。
ですので、使う立場から考えるべきことは、必要以上にlistenを動かしっぱなしにはしないことだと書きました。

根拠の無い発言でもなければ、必要以上に危機感を煽っているわけでもないということをご理解いただければ幸いです。
Posted by MizMiz at 2007年09月18日 11:20
つまり、Miz殿は、チャット発言で半径20m以内にある待機状態にあるListenイベントが
一斉に反応するので、範囲内に多数設置していると、リソース消費に繋がるから、
極力書くな!書くなら知って書け!ということですか?
負荷が上がることを知って書く人が100人いて皆が100個置けば、サーバリソースを
食いつぶしてしまうわけですか?そうなら一人がリソース消費に気をつけていても
意味無いですよ。

>「listenは負荷になるから使わないほうが良い」と受け取られている・・・以下略。
>「listenは負荷になるから怖いな」と思っていただいたほうが安全ではないでしょうか。
>必要以上にlistenを動かしっぱなしにはしないことだと書きました。

 現にいらっしゃいますね。根拠もなく意味不明なことおっしゃる方。

>根拠の無い発言でもなければ、必要以上に危機感を煽っているわけでもない

 失礼ながら申し上げますと、経験上の推測は、あたりをつけることはできても
根拠になり得ません。
それだけ経験上の憶測が根拠の無い発言ではないと自信をもって主張できるのであれば、
是非リンデンラボに報告してください。
仕組みを作って100%SIMを落とすことができれば、リンデンラボも腰を上げるのではないでしょうか?
なんらかの公式発表があるかもしれませんし、なによりわれわれスクリプターの指針が示されます。
そのほうがスクリプターも堂々と回避策を講じることなく安心してスクリプトを書けます。
ここで回避策や安全策を主張されるより、よほど有意義ですし、Miz殿らしいと思いますよ。
Posted by mi at 2007年09月18日 21:27
>miさん

やはり誤解があるようですね(^^;
いや、本当はわかっていただけているのかもしれませんが。

miさんの仰るように、

>アンテナは張りつつ
>不具合を起こすのであれば、回避策を講じて作成したスクリプトを
>修正ようと思います。

それで良いと思っています。
Posted by Miz at 2007年09月18日 22:28
はじめまして、
最近SLを初めてLSLの勉強で参考にさせていただいています。

listenを使っていて気になったのですが、
touchした際にhandleを取得して使ったら解放していますが
何もしゃべらず時間もたたないうちに再度touchしたら解放しないまま、
またhandleを取得していますよね。

listenが終了しなければずっと待機しているのであれば、
ダブルクリックなど連続で押した場合に最初のlistenがずっとONのままな気がするのですがどうでしょうか?
Posted by 鸞鳳 at 2007年10月04日 12:47
> 鸞鳳さん
 クリックイベントが意図せず数回発生してたとか、スクリプト記述ミスか、操作ミスか、LSLのバグなのか、テストは何時行ったものなのか、周りに同様のスクリプトが稼動していたのか、稼動してるならそのスクリプトが反応しただけなのか、テスト条件の記載など情報が全く無いため、判断できないですし、おそらく誰も答えようがありません。

まずは、きちんと条件を書くことですが、今回、想像するに、クリックイベントが意図せず数回発生してたと思いますので、右クリックしてタッチし、確実に1回ずつクリックイベントを発生させ、Listenオンオフの状態を確認し、llListenRemoveが効いた時に、スクリプトの動作確認することをお勧めします。
Posted by mi at 2007年10月04日 20:01
>鸞鳳さん

確認してみました。
私の認識では、同一チャンネルのllListenについては新しいlistenは生成されないと思っていたのですが、違いました。

まったく同一の引数を使ってllListenを複数回実行した場合は新規listenは生成されませんが、引数が一部で違うと新たなlistenが作られてハンドルが返ります。

ということは、鸞鳳さんがご懸念されている通り、不特定な相手を指定してlistenするような場合(llDetectedKeyのような場合)、llListenRemoveする前に再びllListenを実行すると、そのたびに稼動するlistenが増えていく可能性があります。
これは要注意ですね。
この記事に掲載している私のコードも修正する必要があります。

もし常にlistenが一つだけ稼動するようにするのであれば、llListenを使う前に必ずhandleがオープンされているかどうかを確認し、llListenRemoveすべきです。

 if (handle) {
  llListenRemove(handle);
 }
 handle = llListen(7, "", llDetectedKey(0), "");

鸞鳳さんのご質問がなければ誤解したままでした。
貴重なご質問ありがとうございました。
Posted by MizMiz at 2007年10月05日 00:17
> Miz殿
 そのコードだと、Listenが生成されて当然です。
 サンプルコードに問題がありますね。

> 鸞鳳殿
 リッスンオフオンのサンプルコード記載します。
このコードで、動作きちんとしますし、解放もできてます。
サンプルは、チャットウインドウ上で、「/7 文字」と打つと、
historyに「文字」が出てきます。動作検証済みなので、ご自由にどうぞ。

--- コードここから

integer handle;
integer SW;

default
{
state_entry()
{
SW = FALSE;
handle = llListen( 7, "", llDetectedKey( 0 ), "" );
}

touch(integer total_number)
{
if ( SW )
{
SW = FALSE;
handle = llListen( 7, "", llDetectedKey( 0 ), "" );
}
else
{
SW = TRUE;
llListenRemove( handle );
}
}

listen( integer ch,string name,key uid,string msg )
{
llSay( 0,msg );
}
}

--- コードここまで
Posted by mi at 2007年10月05日 03:26
> Miz殿
読み返して理解しました。先に動作してる可能性のあるListenを
止めるという意味でのコードですね。申し訳ございませんでした。

>サンプルコードに問題がありますね。

と、発言しましたが、私の発言に問題がありました。
謹んで、訂正させていただきます。
Posted by mi at 2007年10月05日 03:39
追記:
連続投稿お許し。
これ落とし穴。
if( handle ) { ~
handleの番号は、0から始まり同スクリプト上でリセットされるまで、
Listenの起動停止によってインクリメントされる。
handleは、"先のListen起動で付与された番号を指定し解放" すること。
以上で、2重起動を回避すれば、待機状態でListen待ち受けることは無い。
Posted by mi at 2007年10月05日 10:15
>Miz様、mi様
返信ありがとうございます。
自分の質問文がわかりにくくて申し訳ありませんでした。

頂いた情報をコードに追加してみます。
ありがとうございました。
Posted by 鸞鳳 at 2007年10月05日 11:00
ちょっと良く分からなくなってしまいました。
コレまでのやり取りを見てると。
そもそも、タッチしたユーザーごとにLISTEN関数を開く意味が分かりません。
むしろ、これはNULL_KEYで関数を実行して
LISTEN関数の中で応答するべきAGENTを選別するほうが、引数が変わる可能性もないわけで、より安全ではないでしょうか?

また、
if (handle!=0) {
llListenRemove(handle);
}
handle = llListen(7, "", llDetectedKey(0), "");
llSetTimerEvent(30.0);
}

さらにその次善の策として示されたコードそのどちらも。
過去の履歴に対しては殆ど検査していないように見受けます。

つまり、すでに開かれたハンドルが5であろうと100であろうと最新の1つの関数に対してしか終了処理を行っていない。

・・・という点を考えましてやるのであれば

integer handle=-1;
key touch_agent;
open_listen_handle(integer handle,key touch_agent){
if (handle==-1){
handle = llListen( 7, "", touch_agent, "" );
}
else {
while (handle!=-1){
llListenRemove(handle);
handle--;
}
handle = llListen( 7, "", touch_agent, "" );
}
}
default
{
state_entry(){
}
touch(integer total_number)
{
touch_agent=llDetectedKey( 0 )
open_listen(handle,touch_agent);
}
}

多少、コードに無駄はあるかもしれませんがこのような形のコードではだめなのでしょうか?

まぁ、要はLISTEN関数を開く必要があるときに
1.LISTENN関数を開く前にhandleの累積番号を調べる。
2.累積番号を見て-1つまり、次にLISTEN関数を呼び出したときにはハンドルに0が返されることを確認できた場合は無条件でLISTEN関数を開く。
3.累積番号がー1以外の場合、ハンドル番号がー1になるまでハンドルを順々に無差別に解放する。
4.3.の処理が終わったらLISTEN関数を呼び出す。

という手順を踏めば良いのではないかということですが。
どうなのでしょうか?


あと、前述したように何故AGENT_KEYを別々のLISTENNを開く必要があるのかがイマイチ理解できません。
NULL_KEYで開いてしまって、あとはLISTENイベントハンドらの中でエージェントを選別するのではダメなんでしょうか?

また、その場合、touchイベント内で書くAGENTごとにLISTENハンドルを開く場合、と比べてどれだけの負荷が期待されるのかなども教えていただけるとうれしいです。

まぁ、リンデン自身がWIKIの中で相当LISTENを嫌っているということを考えてもLISTEN関数の処理が相当重い(もしくは重くなる可能性をたぶんに秘めている)のというイメージはつかめますが、具体的ナ原因とか負荷の具体的な掛かり方。そうしたものが分からないと、確かに「注意しろ」→「何を?」→「分からないけど、やばそうだから使うな」→「当SIMではLISTEN関数を含んだオブジェクトの使用はBAN対象」みたいな潮流ができてもおかしくないと思います。
Posted by うぐぅーー at 2007年10月16日 15:37
>うぐぅーーさん

まず以下の私のコードですが、

touch_start(integer detected){
 if (handle!=0) {
  llListenRemove(handle);
 }
 handle = llListen(7, "", llDetectedKey(0), "");
}

2度目のタッチで最初のlistenが閉じられるので、複数のlistenが同時に動くことはありえません。
ですので過去の履歴を意識する必要はないです。
もしも複数のlistenが同時に動くようなコードであれば、仰るとおり、while等を使って全listenを閉じるような処理は非常に有効だと思います。

listenが開きっぱなしにならないようにする方法として、
1、複数のlistenが動かないようにする
2、現在動いているlistenを全て閉じる
どちらにしても目的は達せられるはずです。
状況に応じて使い分ければ良いと思います。

AGENT_KEYを一々指定しているのは、LSL-WIKI等で「可能な限りフィルタをかける」ことが推奨されているためです。
それがどれだけ負荷を減らすかは私には明確な答えが出せませんが。

何度も書きますが、listenが負荷になるのは間違いないとして、だからと言って「使うな」という話は一度もした覚えがありません。
ましてやlistenがBAN対象というのはあまりに極端な取られ方であると言わざるを得ません。
しかしながら、もしも誰もがlistenを注意して使わず、結果としてSIM全体が重くなっているという因果関係が立証されたとしたら、「止むを得ないのでlistenの利用は制限しよう」という何らかの動きが出てくる可能性は確かに否定できません。

たとえば包丁は便利な道具ですが、使い方を誤れば危険です。
誰もが使い方を誤り、便利さよりも危険性のほうが高くなるようなら、いっそ包丁というものをこの世から無くしたほうが良い、という論理です。
そうならないよう、包丁は正しく使いましょう、と訴えているのですが・・・「人を刺しちゃ駄目」というくらい明確なしきい値を、お求めなのですよね?(^^;
Posted by MizMiz at 2007年10月17日 13:19
いつも拝見しております

llListenを使いリストから要素を削除する場合について
関数はlDeleteSubListとllListFindListを使用するのは分かったのですが
組み合わせかたがいまいち分かりません。
list my = ["a","b","c","d"]
llSay(123, result); ←このチャンネルの言葉をリストから繰り返しはずしていきたい場合
}

listen(integer ch, string name, key id, string message)
{
integer handle = llListFindList(my, [message]);

if(handle>=0){

if (handle != -1){

my = llDeleteSubList(my, handle, handle);

}
else
{
llResetScript();

こんな感じなのですが伝わりますでしょうか?
何回もllSayするたびにリストからはずして、リストがなくなったら
リセットをかけるようにしたいのですが。
初歩的な質問でしたらもうしわけございません。
Posted by マインド at 2008年07月09日 17:22
http://www.mp3dj.eu
an extremely nice post. Taking the time and actual effort to produce a good article… but what can I say… I procrastinate a lot and don't seem to get nearly anything done.|
Wow that was unusual. I just wrote an extremely long comment but after I clicked submit my comment didn't appear. Grrrr... well I'm not writing all that over again. Anyhow, just wanted to say great blog!|
Posted by Kaspircog at 2016年03月22日 21:45
 
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。