Flashでリアルタイム通信するための Union Server をUnixにインストールする方法について。

僕はレンタルサーバ( DreamHost の Virtual Private Server )を借りてそこにインストールしました。

Unixコマンドのことは良く分かってないですが、とりあえず下記の手順でいけます。

▼インストール
sshでログインして下記コマンドを入力。
wget http://www.unionplatform.com/releases/union/union_1.0.0_alpha6.tar.gz
tar xzvf union_1.0.0_alpha6.tar.gz union
rm union_1.0.0_alpha6.tar.gz
cd union
chmod 775 *
やってることは、ダウンロードして、unionディレクトリに解凍して、不要になった圧縮ファイル削除して、unionディレクトリに移動して、パーミッションを変更です。

▼設定
union.xmlを開いて、管理パスワードを変更。
念のため<charset>UTF-8</charset>を追記。

▼起動
java -Djava.compiler=NONE -Dfile.encoding=UTF-8 -cp lib/union.jar:lib/stax-api-1.0.1.jar:lib/wstx-asl-3.2.6.jar net.user1.union.core.UnionMain start &
日本語を送受信する場合は、上記のように文字コードを指定する必要があります。

▼停止
「ps -x」でプロセス一覧を表示して
「kill プロセス番号」で停止させる。
管理ツール(UnionAdmin_1.0.0.84_Alpha6.swf)からログインして止めることもできます。

▼死活監視
サーバを起動し続けていると10日ぐらいで落ちることがあったので、
止まった時は自動で再起動するようにshellスクリプトを書きました。
#!/bin/sh
while true
do
    isAlive=`ps x | grep union | grep -v grep | wc -l`
    if [ $[isAlive] = 0 ]; then 
		java -Djava.compiler=NONE -Dfile.encoding=UTF-8 -cp lib/union.jar:lib/stax-api-1.0.1.jar:lib/wstx-asl-3.2.6.jar net.user1.union.core.UnionMain start &
    fi
    sleep 20
done
上記をobserve.shというファイル名で保存。 下記のコマンドでバックグラウンドモードで常駐します。
sh observe.sh &

Flashでリアルタイム通信するためのソリューションは色々あるけど、
Unionはかなり簡単にインストールできるし、機能も十分なのでおすすめ。
Let's enjoy Union Platform !

とても参考になるサイト:
fla.la » Union Platform
Reactor API 日本語リファレンス
テーマに基づいて48時間以内にゲームを移植する企画「むりげー」。
これまではただの飲み会でしたが、
2月7日に無茶なイベントを開催するそうです。

超むりげー

2月5日にテーマが発表されるので
Flashでゲーム作って参加すると面白いよ。

RTMPとBitmapData.draw

| コメント(0) | トラックバック(0)
RTMPストリーミングビデオをBitmapData.drawするには
ストリーミングサーバ側の設定で下記の設定をしないといけないわけですが
<AudioSampleAccess enabled="true" />

設定しているにもかかわらずエラーが出続けてdrawできない時がある。
10回接続して1回くらい起こる。
その時はonMetaDataイベントも受け取れていない。

よくわからないけどタイミングの問題らしく
"NetConnection.Connect.Success"が届いた直後にstream.play()するとダメみたい。
下記のような感じで0.1秒くらい待ってからplay()するとエラーが出なくなった。

var _video : Video = new Video(320, 240);
var _stream : NetStream;
var _connection:NetConnection = new NetConnection();

function connect(){
    _connection.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
    _connection.connect(rtmp_app);
}
function onNetStatus( event : NetStatusEvent ){
    switch(event.info.code){
        case "NetConnection.Connect.Success":
            _stream = new NetStream(_connection);
            _video.attachNetStream(_stream);
            var _timer:Timer = new Timer(100, 1);
            _timer.addEventListener(TimerEvent.TIMER, onTimer);
            _timer.start();
            break;
    }
}
function onTimer(e:TimerEvent){
    _stream.play(rtmp_file);
}

buffer時間と関係あるのかな。
ほとんど問題ないのに、特定のストリーミングflvだけ表示できなくて、なんでかな~と思ったらFLVPlaybackコンポーネントが古いせいでした。
たぶんCS3の頃のFLVPlaybackを使い回してたから。CS4付属のに変えたら解決。

FLVPlayback.VERSIONが"2.1.0.14"だとダメ、"2.1.0.19"ならOK。
具体的な原因はよくわかんない。

下記のJUV RTMP Researcherというプロキシを使うとRTMPパケットをモニタリングできる。
http://www.smaxe.com/product.jsf?id=juv-rtmp-researcher

Windows7をインストールしたんですが、ガジェット表示のためのサイドバー領域がなくなってるじゃないですか。
それだとウィンドウを最大化した時にウィンドウとガジェットが重なってしまう。

twigadgeやCPU meterを常に表示しておきたい僕としては、
ガジェットを前面にとかじゃなくて、Vistaみたいに最大化時に重ならないようにしたい!
ということで探したらよいソフトがありました。

MaxMaxというツールをインストールすると解決です。
こちらからダウンロードできます。

サイドバーってそんなに不人気だったの?
選択肢として残しておいてくれても良いのに。

Director Lingoの話。
サーバを変えたら、getNetText()、netTextReuslt()でテキストを取得できなくなった。
IEでは問題なくてFirefoxでのみ空の文字が返ってくる。
原因はWebサーバの設定でgzip圧縮転送が有効になっていたため。
圧縮されているとShockwaveで正しくデータを受け取れない。

この辺を参考に圧縮を無効にするとよい。
mod_deflate - Apache HTTP サーバ
mod_deflateによるコンテンツの圧縮転送
mod_deflate、zlib.output_compression、コンテンツをgzip圧縮して返す。

現時点で3000件以上投稿されています。
いろんな作品があって楽しいです。
特に上手な作品をピックアップしてみました。

作者別:
POKEMONさん

NAOSUKEさん

HEEさん

WEEPYMOONさん

TOADさん

景色、建物:

キャラクター:

ゲーム機、コンピュータ:

乗り物:

その他:

立体ブロックを使ってお絵描きできるサイト「Q-BLOCK」を公開しました。
ぜひ遊んでください。

メモリを大量に消費しちゃいます。動かない人は新しいPC買うと良いよ。
短期間でサクッと作れたので割と満足してます。
Papervisionのおかげ。

SWF以外の部分をクリックしたらポーズさせるための小技をご紹介します。
デスクトップなどブラウザウィンドウ外をクリックした場合も含みます。
下記のようにTextFieldのFocusEventを使うところがポイントです。

package {
	import flash.display.Sprite;
	import flash.events.FocusEvent;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	public class DocumentClass extends Sprite {
		private var debugTF:TextField ;
		public function DocumentClass() {
			//デバッグ用のテキスト
			debugTF = createTextField(600, 20, 0, 0, "はじめにここをクリックしてSWFにフォーカスを移してください。");
			addChild(debugTF);
			//フォーカス検知用テキストフィールド
			var focusTF:TextField = new TextField();
			//ステージ全体を覆うサイズにする
			focusTF.width = stage.stageWidth;
			focusTF.height = stage.stageHeight;
			focusTF.selectable = false;
			focusTF.addEventListener(FocusEvent.FOCUS_IN, onFocusIn);
			focusTF.addEventListener(FocusEvent.FOCUS_OUT, onFocusOut);
			//必ず最上位レイヤーに配置
			addChildAt(focusTF, this.numChildren);
		}
		//ポーズ処理
		private function onFocusOut(e:FocusEvent):void {
			debugTF.text = "SWFからフォーカスが外れたのでポーズ中。灰色のエリアをクリックすると再開します。";
		}
		//再開処理
		private function onFocusIn(e:FocusEvent):void {
			debugTF.text = "SWFにフォーカス中。画面外をクリックするとポーズ状態になります。";
		}
		private function createTextField(w, h, x, y, str):TextField {
			var tf:TextField = new TextField;
			tf.width = w;
			tf.height = h;
			tf.x = x;
			tf.y = y;
			tf.text = str;
			tf.selectable = false;
			return tf;
		}
	}
}

・サンプルSWF
・ソースファイル

2009/09/28追記:
わざわざTextFieldなんか使わなくても、Event.ACTIVATE と Event.DEACTIVATE 使えば済むね。

例えば、ゲームのセーブデータをサーバで保存していて
「アイテム1個持ってます」という情報をSWFで受け取る場合、
SWFに届く前に「アイテム100個持ってます」に改竄されるとまずいということで暗号化することにしました。

RSA暗号は「暗号」と「署名」の2つの使い方ができます。
▼暗号
公開鍵を使って誰でも暗号化できるが、復号できるのは秘密鍵を持っている人だけ。
SWFからサーバに送るデータを途中で読み取られないようにする場合などに使うと思います。
SWFに埋め込んだ公開鍵で暗号化して送り、サーバ側にある秘密鍵で解読というような使い方。
こちらが参考になります:suz-lab - blog: AS3で暗号/復号化(RSA版)
▼署名
秘密鍵を使って暗号化したデータを、公開鍵で復号する。
暗号文を復号した結果が平文と同じなら、正しい送り主からのデータであり改竄されていないということになる。
今回はこちらを使います。
サーバ側で秘密鍵を使って暗号化し、SWF側で公開鍵を使って復号するという流れ。

暗号化ライブラリはas3cryptoを使います。
下記がサンプルソースですが、
実際には【2】の処理はサーバ側で行い、SWF側では【1】と【3】を実装することになります。

import flash.utils.ByteArray;
import com.hurlant.crypto.rsa.RSAKey;
import com.hurlant.util.Base64;
import com.hurlant.util.Hex;
public function CryptTest() {
	// 暗号化の対象となる文字列
	var original_str:String = "アイテム1個持ってます"; 
	//------------------------
	//【1】復号用の公開鍵
	var public_modulus:String= 
"f0bc13f68e0c02397af4aeaf2edc94f92e94945eea1f745235ff05ff16e9b6490267b9"
+"82b22c6aff4b6887fc89e7d92d8a2254c7f4c2fb7a116478f875dc8da5";
	var public_exponent:String = "10001";
	//------------------------
	//【2】↓ここからは、サーバ側でやる処理ですがサンプルということで載せてます。
	//暗号化用の秘密鍵
	var private_exponent:String= 
"de6f3a16e7bb4ad6e7b86c2bec25def4bb48882b8732971d5b4d0fb25aee8a00f2fd"
+"6987d1ca990846b50e70be386867be09b64840157c0d7d451d91ccc92e21";
	var rsa_sign:RSAKey = RSAKey.parsePrivateKey(public_modulus, public_exponent, private_exponent);
	//ByteArrayに
	var srcEncryptBA:ByteArray = Base64.decodeToByteArray(Base64.encode(original_str)); 
	//暗号化したデータを格納するためのByteArray
	var dstEncryptBA:ByteArray = new ByteArray();
	//暗号化実行
	rsa_sign.sign(srcEncryptBA, dstEncryptBA, srcEncryptBA.length);
	//暗号化されたデータをBase64エンコード
	var encrypted_str:String = Base64.encodeByteArray(dstEncryptBA);
	trace("暗号化済み文字列。これをSWFに返す。" + encrypted_str);
	//↑ここまで、サーバ側でやる処理
	//------------------------
	//【3】復号
	var rsa_verify : RSAKey= RSAKey.parsePublicKey(public_modulus, public_exponent);
	var srcDecryptBA:ByteArray = Base64.decodeToByteArray(encrypted_str);
	//復号したデータを格納するためのByteArray
	var dstDecryptBA:ByteArray = new ByteArray();
	//復号実行
	try{
		rsa_verify.verify(srcDecryptBA, dstDecryptBA, srcDecryptBA.length);
		trace("復号した文字列:" + dstDecryptBA.toString());
		if(dstDecryptBA.toString()==original_str){
			trace("改竄なし");
		}else{
			trace("改竄あり");
		}
	}catch (e) {
		trace("verify失敗。不正なデータ。");
	}
	rsa_verify.dispose();
}

鍵の生成はAS3 Crypto Demo pageでやると簡単。

余談:「暗号化」の対義語は「復号化」ではなく「復号」なんだって。

追記:(09/06/15 02:50)
もっと厳しくやるには、タイムスタンプを含めておいてサーバ時間と比較しないといけないかも。
「アイテム1個持ってます」というサーバレスポンスを解析保存され
アイテム0個の時にそのデータを送り込まれる、ということを防ぐために。
素直にSSL使えば何も考えなくていいのかな。

アーカイブ