セカンドライフで物づくりしてますと、
llSay / llRegionSayTo と llListen 使ってコマンドで物を操作することありますよね。
そんなときに、どの出力チャネル使おうか迷うことありませんか。
例えばですが、適当に25135034 という数値を使ったとします。
これは恐らく他のものと被ることはないと思われます。
しかしですよ、このチャネルを使用した物を配るとします。
すると、もらった誰かがそのオブジェクトを操作しようとすると、
他にもらった誰かも、そのチャネルを受信してしまいます。
llListen でフィルターもかけられますが、
今回はチャネル番号をアバター固有の情報から作成する方法を紹介します。
つ「ハッシュ関数」 パンパカパーン
はい。以上です。
文字列から数値を作り出すあれです。
え?
SLって、16進数の文字列から数値へ変換する関数なんてないんじゃないの?
と思う方もいるかもしれませんが、実は (integer)”0x1234″ とすれば、
文字列を16進数の数値とみなして変換できるわけです。
とりあえず、keyから整数を変換できる関数を2種類作ってみました。
integer i;
/* 線形合同法によるハッシュ関数 */
integer getKey2HashLCGs(key src) {
integer start_pos = 0;
integer seed = 0x32123;
integer hash = seed;
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// 32ビットずつ取り出してハッシュを作成
for(i = 0; i < 8; i++) {
hash = hash * 0x1234321 + (integer)("0x" + llGetSubString(src, start_pos, start_pos + 3));
start_pos += 4;
if( (start_pos == 8) ||
(start_pos == 13) ||
(start_pos == 18) ||
(start_pos == 23) ) {
start_pos++;
}
}
return(hash);
}
/* SHA1+線形合同法によるハッシュ関数 */
integer getKey2HashSHA(key src) {
integer start_pos = 0;
integer seed = 0x32123;
integer hash = seed;
string buffer = llSHA1String((string)src);
for(i = 0; i < 40; i += 4) {
hash = hash * 0x1234321 + (integer)("0x" + llGetSubString(buffer, i, i + 3));
}
return(hash);
}
default {
state_entry() {
}
touch_start(integer total_number) {
key x;
x = llGetOwner();
llSay(0, (string)x
+ "\n-> " + (string)getKey2HashLCGs( x )
+ "\n-> " + (string)getKey2HashSHA( x )
);
x = llGenerateKey();
llSay(0, (string)x
+ "\n-> " + (string)getKey2HashLCGs( x )
+ "\n-> " + (string)getKey2HashSHA( x )
);
}
}
恐らく上のgetKey2HashLCGsの方が、llSHA1Stringを使わない分、速いです。
お好きな方をどうぞ。
SHA1で作った関数は、引数の key を string に型を変更すれば、
何でも好きな文字列から32ビットの整数を取得できるようになります。
それと、両方の関数とも seed を引数に追加するこでとることで、
色々なアプリケーションでアバターごとに被らないチャンネル番号の作成を
利用できるようになると思います。
以上!
あ!
注意点として、
運が悪いと取得したチャンネル番号が「0」になることもあるかもしれませんので、
その点だけはお気を付け下さい・・・。
それを踏まえてこんな感じに使いましょう。
integer i;
string MY_PROJECT_NAME = "test tool";
integer getOriginalKey(string src) {
integer start_pos = 0;
integer seed = 0x32123;
integer hash = seed;
string buffer = llSHA1String((string)llGetOwner() + (string)src);
for(i = 0; i < 40; i += 4) {
hash = hash * 0x12344321 + (integer)("0x" + llGetSubString(buffer, i, i + 3));
}
if(hash == 0) {
hash = 0x43019768;
}
return(hash);
}
default {
state_entry() {
}
touch_start(integer total_number) {
llSay(0, (string)MY_PROJECT_NAME
+ "\n-> " + (string)getOriginalKey( MY_PROJECT_NAME )
);
}
}
オマケというメモ
線形合同法の乱数は、
X_n+1 = ( A * X_n + B ) mod M
という式から作成できます。
ここで、数値は32ビットの整数なので、
M = 0x100000000 = 4294967296 となります。
M の素因数分解は、2 ^ 32 です。
ここで A-1 が、Mの素因数分解の全て割り切れると、
この乱数の周期性が最大となることが知られています。
2 ^ 15 + 1 = 0x8001 = 32769 とかを設定すると、周期性が最大となります。
ですが、今回はハッシュ値を求めるためなので、
好きな値を入れても構わないです。
Bの値なんてどんな値がくるか分からないのですから。
追記 2015/3/25
listenに入ってくるチャンネル番号は
オブジェクトのUUIDなので、人でフィルターができないと思っていました。
ところが実はいい方法がありまして、これをある方から教えていただきました。
所有者を調べる llGetOwnerKey という関数があるのです。
たしかにこれを使えば、オブジェクトのUUIDだとしても誰が持っている
オブジェクトが発言したのか分かりますね!すっすごい!
&nbsLlGetOwnerKeこ



コメント