llRequestAgentDataのタイムアウトは何秒が妥当?
導入:llRequestAgentData()とは?
llRequestAgentData(key id, integer data)
この関数は、アバターの名前や生年月日など、エージェントデータを問い合わせるもので、2つの引数には次のものを指定します。
- key id:アバターのUUID
- integer data:取得したいデータの種類(以下より1つ選択)
- DATA_ONLINE … オンラインかどうか
- DATA_NAME … アバター名
- DATA_BORN … 生年月日
- DATA_RATING … アバターのレーティング情報
- DATA_PAYINFO … 支払い情報
主に、オンライン判定などに使われている関数ですが、
llKey2Name(key id)
という、UUIDからアバター名を取得する関数の代わりに使われることがあります。
というのも、llKey2Name()は、同一SIM内(もしくは隣接SIM内)にアバターがいる場合でないとアバター名が取得できないのに対し、llRequestAgentData()はアバターが同一SIM内にいない場合でも、さらに、たとえログアウトしていてもアバター名が取得できるという利点があるので、必ず取得しないといけないような場合にはコチラの関数を使うことになると思います。
ただし、直接取得できるわけではなく、ちょっと回りくどい取得方法になってしまいます。
llKey2Name()の場合
string avatar_name = llKey2Name(id);
この1行で、変数「avatar_name」内に取得したアバター名が入るのでとても簡単です!
llRequestAgentData()の場合
1. まずはリクエストする
key queryid = llRequestAgentData(id, DATA_NAME);
2. 次にイベントで値を取得する
dataserver(id, data){ if (id == queryid){ string avatar_name = data; } }
という風に、dataserverイベント内でやっと変数に値を格納できるわけです
序論:llRequestAgentData()の欠点
llRequestAgentData()には、無視できない大きな欠点があります。それは、引数(key id)にアバター以外のUUIDを指定してしまうと、エラーも出ずdataserverイベントも発生しないというものです。通常、llRequestAgentData()でリクエストした後、dataserverイベント内でデータを取得し、そのイベント内に次の処理に進むよう指示を書いておくものですが、イベントが発生しないとなると、そこでスクリプトが沈黙してしまうことになってしまいます。
いやいや、idにアバター以外のUUIDを指定するようなことってあり得ないんじゃない?
そう思われる方もいらっしゃるかもしれませんが、例えば「プリムカウンター」などでこの問題が発生することがあります。
間違いだらけのプリムカウンタースクリプトw
ちと即興で書いてみますので間違いがあったらごめんなさい…
// Get Raw Data list raw_data; list av_keys; list prims; integer av_num; uuGetRawData(){ raw_data = llGetParcelPrimOwners(llGetPos()); av_num = llGetListLength(raw_data) / 2; av_keys = llList2ListStrided(raw_data, 0, -1, 2); prims = llList2ListStrided(llList2List(raw_data, 1, -1), 0, -1, 2); } // Request Agent Data; list av_names; integer request_time = 0; key queryid; uuRequest(){ key temp_av_key = llList2Key(av_keys, request_time); queryid = llRequestAgentData(temp_av_key, DATA_NAME); } uuAnswer(string name){ av_names += name; } // Display uuDisplay(integer flug){ string result; if (flug){ integer i; for (i=0; i<av_num; i++){ result += llList2String(av_names, i) + " : " + (string)llList2Integer(prims, i) + "prims \n"; } } llSetText(result, <1.0, 1.0, 1.0>, 1.0); } // Flow default{ state_entry(){ uuDisplay(FALSE); } touch_start(integer total_number){ request_time = 0; av_names = []; uuGetRawData(); uuDisplay(FALSE); uuRequest(); } dataserver(key id, string data){ if (id == queryid){ uuAnswer(data); request_time ++; if (request_time < av_num){ uuRequest(); }else{ uuDisplay(TRUE); } } } }
(動くんだろうかこのスクリww)
上記のようなスクリを作ったとします。タッチで始動し、データを取得した後にフローティングテキストとして表示させるだけのものです。
さてさて、このスクリを沈黙させるためのイタズラ(w)は、ほんの数秒でできてしまいます。それは、その土地に「グループに譲渡したオブジェクトを1つ置く」だけです^^
そうすると…
- グループ所有のオブジェクトがヒット → 持ち主はグループなので、そのグループUUIDを取得
- グループUUIDでllRequestAgentData()問い合わせ → アバター以外のUUIDが送られて沈黙…
ということになってしまいます。
間違っても売り物にはできないですね…
というわけで、ようやく主題「llRequestAgentData()のタイムアウト」の話に突入です(うぅ、長かった…)
本論:タイムアウト処理について考える
いきなり答えから書いてしまいますが…
多少マシになったかもしれないプリムカウンタースクリプトw
// Get Raw Data list raw_data; list av_keys; list prims; integer av_num; uuGetRawData(){ raw_data = llGetParcelPrimOwners(llGetPos()); av_num = llGetListLength(raw_data) / 2; av_keys = llList2ListStrided(raw_data, 0, -1, 2); prims = llList2ListStrided(llList2List(raw_data, 1, -1), 0, -1, 2); } // Request Agent Data; float timeout = 1.0; // <--- 今回の主題「タイムアウト」はコレ!! list av_names; integer request_time = 0; key queryid; uuRequest(){ key temp_av_key = llList2Key(av_keys, request_time); llSetTimerEvent(timeout); // <--- リクエストの直前にタイマー始動 queryid = llRequestAgentData(temp_av_key, DATA_NAME); } uuAnswer(string name){ av_names += name; } // Display uuDisplay(integer flug){ string result; if (flug){ integer i; for (i=0; i<av_num; i++){ result += llList2String(av_names, i) + " : " + (string)llList2Integer(prims, i) + "prims \n"; } } llSetText(result, <1.0, 1.0, 1.0>, 1.0); } // Flow default{ state_entry(){ uuDisplay(FALSE); } touch_start(integer total_number){ request_time = 0; av_names = []; uuGetRawData(); uuDisplay(FALSE); uuRequest(); } dataserver(key id, string data){ llSetTimerEvent(0.0); // <--- タイマー解除 if (id == queryid){ uuAnswer(data); request_time ++; if (request_time < av_num){ uuRequest(); }else{ uuDisplay(TRUE); } } } timer(){ // ココからタイムアウト処理 llSetTimerEvent(0.0); // <--- タイマー解除 uuAnswer("Group Owned"); // <--- 「グループ所有だよ」という情報を格納 request_time ++; if (request_time < av_num){ uuRequest(); }else{ uuDisplay(TRUE); } } }
(↑同じく即興のため動かない可能性アリです…)
…とまあ、こんな感じで、データの問い合わせの直前にタイマーをかませてあげればいいわけなんですが、そのタイマーは何秒にセットするのが妥当なのか?? というテーマなわけですw(長くてゴメンナサイ…)
このタイマーを短くしすぎると、dataserverイベントが発生する前にグループ所有だと判断してエラいことになりそうですし、逆に長すぎても、処理のたびに待ち時間にイライラさせられる(え? あたしだけ??)ことになります…。
llRequestAgentDataの所要時間を計測してみた
次のようなスクリでデータを取ってみました。
ソースコード
// Time float start_time; float end_time; // Request key queryid; integer i; uuRequest(){ if (i < 100){ queryid = llRequestAgentData(llGetOwner(), DATA_NAME); }else{ end_time = llGetTime(); float total_time = end_time - start_time; llOwnerSay("Time: " + (string)total_time); llResetScript(); } } default{ state_entry(){ llOwnerSay("Initialize complete. Touch me!"); } touch_start(integer total_number){ llOwnerSay("Start!"); state active; } } state active{ state_entry(){ start_time = llGetTime(); i = 0; uuRequest(); } dataserver(key id, string data){ if (id == queryid){ i++; uuRequest(); } } }
単純ですねw
タッチで始動。100回llRequestAgentData()を繰り返し、その処理にかかった時間を返すスクリプトです。
結果
100回あたりの所要時間は…
| 1回目 | 15.959760 |
| 2回目 | 15.965090 |
| 3回目 | 15.891180 |
| 4回目 | 15.892800 |
| 5回目 | 15.892130 |
| 6回目 | 15.890680 |
| 7回目 | 15.891290 |
| 8回目 | 15.898130 |
| 9回目 | 15.965660 |
| 10回目 | 15.891080 |
| 11回目 | 15.889950 |
| 12回目 | 15.902240 |
| 13回目 | 15.891260 |
| 14回目 | 16.250990 |
| 15回目 | 15.895420 |
| 16回目 | 15.885280 |
| 17回目 | 15.891570 |
| 18回目 | 16.408150 |
| 19回目 | 18.385250 |
| 20回目 | 15.896830 |
(単位:秒/100回の問い合わせ)
となりました。ほぼ、1回の問い合わせにかかる所要時間は「0.16秒」と思っていいようです^^
ま〜、SIMによっては違ってくるのかもしれませんので余裕を持たせて…
結論
llRequestAgentData()のタイムアウトは「0.5秒」が妥当!!…かもw