セカンドライフで物づくりしてますと、
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こ
コメント