/**
* Copyright kske ( http://wonderfl.net/user/kske )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/buue
*/
package {
import flash.display.DisplayObjectContainer;
import flash.events.EventDispatcher;
import flash.display.Sprite;
public class RtmfpTest extends Sprite {
public function RtmfpTest() {
// write as3 code here..
}
}
}
import flash.net.NetConnection;
import flash.net.NetGroup;
import flash.net.GroupSpecifier;
import flash.text.TextField;
import flash.events.EventDispatcher;
import flash.events.Event;
import flash.events.NetStatusEvent;
import flash.display.DisplayObjectContainer;
import flash.events.IEventDispatcher;
class UIController implements IEventDispatcher{
private var user:TextField;
private var message:TextField;
private var log:TextField;
private var parent:DisplayObjectContainer;
private var dispatcher:EventDispatcher;
public function UIController(stage:DisplayObjectContainer){
if(!stage){
throw(new Error("Parent display-object-container is not valid."));
}
parent = stage;
if(!createViews()){
throw(new Error("can't create UI"));
}
if(!putViews()){
throw(new Error("sonnna bakana"));
}
dispatcher = new EventDispatcher(this);
}
private function createViews():Boolean{
user = new TextField();
message = new TextField();
log = new TextField();
log.multiline = true;
return true;
}
private function putViews():Boolean{
parent.addChild(user);
parent.addChild(message);
parent.addChild(log);
return true;
}
public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void{
dispatcher.removeEventListener(type, listener, useCapture);
}
public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void{
dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
}
public function hasEventListener(type:String):Boolean{
return dispatcher.hasEventListener(type);
}
public function willTrigger(type:String):Boolean{
return dispatcher.willTrigger(type);
}
public function dispatchEvent(e:Event):Boolean{
return dispatcher.dispatchEvent(e);
}
}
class RtmfpController implements IEventDispatcher{
private var dispatcher:EventDispatcher;
private const URI:String = "rtmfp:";
private var conn:NetConnection; // Connection / handler
private var grp:NetGroup; // なんかまとめてくれるよ
private var isConnected:Boolean;
private var model:ChatModel; // model? への参照
public function get IsConnected():Boolean{
return isConnected;
}
public function RtmfpController(_model:ChatModel){
dispatcher = new EventDispatcher(this);
if(!model) throw new ArgumentError("Null Po(*‘ω‘ *)");
model = _model;
}
public function connect():Boolean{
conn = new NetConnection();
conn.addEventListener(NetStatusEvent.NET_STATUS, updateStatus);
try{
conn.connect(URI);
}catch(e:Error){
dispatchEvent(new Event("CONNECTION ERROR"));
model.addLog("can't connect to " + URI);
conn.removeEventListener(NetStatusEvent.NET_STATUS, updateStatus);
conn = null;
return false;
}
return true;
}
private function initGroup():void{
var gs:GroupSpecifier = new GroupSpecifier("kske.info/test1"); // nameはアプリケーションで統一されてたら任意でいいのかな?
gs.ipMulticastMemberUpdatesEnabled = true;
//グループメンバーシップに関する情報を IP マルチキャストソケットで交換できるかどうかを指定します。
//IP マルチキャストサーバーでグループメンバーシップの更新を送信して、P2P メッシュまたは修復パーティションをブートストラップすることができます。
//*日本語解読*
//たぶんブートストラップは自動実行的な意味で使われてる。メンバー情報を交換してネットワークを最適化してくれるオプション、かな?
gs.multicastEnabled = true;
//multicast(特定の複数対象への送信)ではなくて、
//NetGroup でストリーミングを有効にするかどうかを指定します、だそうだ。Stream使わないならいらないの?
gs.addIPMulticastAddress("224.0.0.123:141412");
//指定のIP multicast groupに参加して指定されたudp portを(listenして?)待機。
//送信先だけじゃなくて、受信のために待機するのもぽいんと。
//224.0.0.0/24はTTL 1でローカル専用。ルーティングされない
gs.serverChannelEnabled = true;
//NetGroup のメンバーがサーバーへのチャンネルを開くことができるかどうかを指定します。デフォルトでは、このプロパティは FALSE です。
//サーバーがグループメンバーにサポート機能を提供するには、サーバーへのチャンネルを開く必要があります。サーバーの設定に応じて、サポート機能がこのチャンネル経由で提供されたり、提供されなかったりします。
//よくわかんない(´・ω・`)サーバーからグループメンバーにサポート機能を提供したいときにtrueにすればいいのかな。Local P2Pならfalseでよい気も
//Question: サーバーのサポート機能ってなに?
// Cirrus 使わない時は serverChannelEnabled = true いらない #RTMFP_LAB from @superbacker 氏 らしい!
gs.postingEnabled = true;
// NetGroup でアップロードを有効にするかどうかを指定します。 関連 > post()メソッド
// post()は同grp内のメンバにメッセージを送信する。ただしメッセージは同一の物は送信されないことがあるのでsequenceなんかをつけて回避
// 順序・確実な送信は保証されない
gs.routingEnabled = true;
// NetGroup で転送メソッドを有効にするかどうかを指定。関連 >sendToNearest()メソッド
// sendToNearest()は指定されたグループアドレスに最も近いネイバー(ローカルノード)にメッセージを送信。
// リング全体のネイバーを考慮してメッセージが宛先に正しく送信された場合、NetGroupSendResult.SENT を返すので成否が分かる。
// 注意:呼び出す前に、NetGroup.Neighbor.Connect イベントをテスト
model.addLog("Group Spec:" + gs.groupspecWithAuthorizations());
// よくわかんない。Withoutもあるようだ
// NetStream および NetGroup コンストラクターに渡すことができる権限を含めた(Withoutの方は当然含めない)不透明な groupspec ストリングを返します。
// コンストラクタに必須の定義文字列とかそんな感じ?
grp = new NetGroup(conn, gs.groupspecWithAuthorizations());
// NetGroupのインスタンス生成。
// 通信を相互に行う集合の定義的な何かだと思う。
// 使うConnectionとGroupSpecifierのなんちゃら文字列をぶっこむ。
grp.addEventListener(NetStatusEvent.NET_STATUS, updateGroupStatus);
// NetStatusEvent.NET_STATUSのリスナー
// NetGroupの場合、NetStatusEvent.info.codeが"NetGroup."で始まると思う。
// なんでオブジェクト分けてないかは知らない。メソッドをまとめることも出来ると思うけど、どっちがいいだろう。
}
// GroupのStatus Handler
public function updateGroupStatus(e:NetStatusEvent):void{
}
// NetConnectionのステータスハンドラ
public function updateStatus(e:NetStatusEvent):void{
model.addLog(e.info.code);
switch(e.info.code){
case "NetConnection.Connect.Success":
initGroup();
//コネクションが作れたらより詳細なNetGroupを作成するよ
break;
case "NetConnection.Connect.Failed":
model.addLog("Connection Failed!");
}
}
public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void{
dispatcher.removeEventListener(type, listener, useCapture);
}
public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void{
dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
}
public function hasEventListener(type:String):Boolean{
return dispatcher.hasEventListener(type);
}
public function willTrigger(type:String):Boolean{
return dispatcher.willTrigger(type);
}
public function dispatchEvent(e:Event):Boolean{
return dispatcher.dispatchEvent(e);
}
}
class ChatModel implements IEventDispatcher{
private var dispatcher:EventDispatcher;
private var log:String;
private var user:String;
private var message:String;
public function get Log():String{
return log;
}
public function addLog(s:String):void{
log += s;
if(!s.match("\n")){
log += "\n"
}
this.dispatchEvent(new LogEvent("_LOG_CHANGED", log));
}
public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void{
dispatcher.removeEventListener(type, listener, useCapture);
}
public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void{
dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
}
public function hasEventListener(type:String):Boolean{
return dispatcher.hasEventListener(type);
}
public function willTrigger(type:String):Boolean{
return dispatcher.willTrigger(type);
}
public function dispatchEvent(e:Event):Boolean{
return dispatcher.dispatchEvent(e);
}
}
class LogEvent extends Event{
private var log:String;
public function LogEvent(type:String, newLog:String, bubbles:Boolean = true, cancelable:Boolean = false){
super(type, bubbles, cancelable);
log = newLog;
}
public function get Log():String{
return log;
}
public override function clone():Event{
return new LogEvent(type, log, bubbles, cancelable);
}
public override function toString():String{
return formatToString("LogEvent", "Log", "type", "bubbles", "cancelable", "eventPhase")
}
}