セカンドライフで被らないチャンネルの作り方

セカンドライフ制作
スポンサーリンク

セカンドライフで物づくりしてますと、
llSay / llRegionSayTollListen 使ってコマンドで物を操作することありますよね。
そんなときに、どの出力チャネル使おうか迷うことありませんか。

例えばですが、適当に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こ

コメント

タイトルとURLをコピーしました