AS3SX入門3。
やること
ServerAS3SX.eventでEventManagerオブジェクトを取得できる。
Eventとつくのだが、特にEventクラスとそのサブクラスとは関係がない。
EventDispatcherとも関係がない。
何をするクラスなのかというと、公式のSimple Sample Appでの使われ方を見ると、どうやら多数に送信する命令のようだ。
EventManager
addUserEventHandlerをリクエストハンドラ内で実行すると、現在のセッションがselectorに関連付けられる。これをしておくと、dispatchServerEventで第二引数にそのselectorを指定したときに第一引数のレスポンスオブジェクトが送られる。
例えばクライアント1のリクエストハンドラで"group1"に、
クライアント2のリクエストハンドラで"group1"に、
クライアント3のリクエストハンドラで"group2"に、
クライアント4のリクエストハンドラで"group2"に、
それぞれaddUserEventHandlerしておくと、
クライアント1のリクエストハンドラでdispatchServerEvent("hello","group1")とすることでクライアント1とクライアント2に"hello"がレスポンスとして送られる。クライアント3と4には送信されない。
クライアント1のリクエストハンドラでdispatchServerEvent("hello","group2")とすると、今度はクライアント3と4に"hello"が送られるが、クライアント2にも、リクエストを送ったクライアント1にも送信されない。
このことから、selectorはグループ名のようなもので、グループ名を選択してdispatchServerEventすることでそのグループにあらかじめ登録してあるセッションにオブジェクトが一斉に送信されるようだ。
内部ではおそらくループで全員に送っているだけだろうが、送るグループを簡単に管理することができる。
(*注)と思っているのだが、このremoveUserEventHandlerも働いていない気がする。
現在のセッションを一度登録したグループから抜けださせるメソッドだと思うのだが、試してみると期待の動きをしない(登録が解除されず、一度登録してあったselectorにdispatchServerEventすると相変わらず送られる)ようだ。
使い方の解釈が間違ってるかもしれないので、とりあえずこれも保留。
なぜEventという名前がついているのかもちょっと謎なので他の使い方があるのかもしれない。リファレンス待ちだ。
EventManagerを利用してこんな簡易チャットを作ってみる。
名前を入力して部屋を選び、ボタンを押すとその部屋に入室する。そこで発言すると、同じ部屋に入った全員にメッセージが伝わるものだ。
友達のいない人はウィンドウを2つ開いて一人チャットをやってみよう。
また、もう1つ別のウィンドウで別の部屋にも入ってみると、他の部屋のメッセージは受信しないし、この部屋の発言も他の部屋には伝わらない。
部屋は4つあるが、起動しているサーバーサイドswfは1つだけだ。
本当は部屋を変えられるようにしたかったが、removeUserEventHandlerが働かないため、この方法では無理だった。
省略。
があるだけ。最初の接続時に自分の名前と入った部屋をサーバーに教えておくためのもの。
実は今回、部屋も名前もクライアントサイドに情報は残さず、サーバーサイドのSessionに保存させてみた。これでいちいち部屋も名前もメッセージに付加して送らずに済む。
今回もカスタムクラスを送信するが、実はこのソケット通信ではデータをAMFにしてやりとりしている。
カスタムクラスをAMF化するときは復元する時のためにregisterClassAliasでクラスを登録しなくてはならないが、AS3SXでは送信するカスタムクラスは自分で登録しなくても内部でregisterClassAliasしてくれているので何も考えずに使える。
しかし、カスタムクラスにさらにカスタムクラスを持たせる時は、そこまではやってくれないので自分でサーバーサイドとクライアントサイド両方でそのカスタムカラスをregisterClassAliasしなくてはならない。
package { import com.as3sx.connection.SocketConnection; import com.bit101.components.ComboBox; import com.bit101.components.InputText; import com.bit101.components.Label; import com.bit101.components.Panel; import com.bit101.components.PushButton; import com.bit101.components.Style; import com.bit101.components.TextArea; import flash.display.Sprite; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.ui.Keyboard; /** * ... * @author */ public class ClientMain extends Sprite { /// change endpoint private static const END_POINT:String = "http://as3sx.com/Apps/ふんぐるいむぐるうなふ/"; // private var enterPanel:Panel; private var nameInput:InputText; private var roomChoose:ComboBox; private var enterButoon:PushButton; // private var roomLabel:Label; private var log:TextArea; private var messageInput:InputText; private var sendButton:PushButton; public function ClientMain():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point ClientAS3SX.setEndPoint(END_POINT); addEventListener(Event.ENTER_FRAME, connectionCheck); } private function connectionCheck(e:Event):void { if (SocketConnection.instance.connected){ removeEventListener(Event.ENTER_FRAME, connectionCheck); // connected initUI(); ClientAS3SX.setResponseHandler(String, onString); } } private function initUI():void { Style.embedFonts = false; Style.fontName = "PF Ronda Seven"; Style.fontSize = 12; // log = new TextArea(this, 100, 100); log.setSize(600, 400); log.html = true; log.editable = false; messageInput = new InputText(this, 100, 500, ""); messageInput.setSize(600, 20); messageInput.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown); messageInput.enabled = false; sendButton = new PushButton(this, 600, 520, "SEND", onPush); sendButton.enabled = false; roomLabel = new Label(this, 100, 40); // enterPanel = new Panel(this, 250, 250); enterPanel.setSize(300, 100); roomChoose = new ComboBox(enterPanel.content, 40, 60, "", ["room0", "room1", "room2", "room3"]); roomChoose.selectedIndex = 0; roomChoose.numVisibleItems = 4; nameInput = new InputText(enterPanel.content, 40, 20, ""); nameInput.setSize(220, 20); enterButoon = new PushButton(enterPanel.content, 160, 60, "Enter", onEnter); } private function onEnter(e:MouseEvent):void { var name:String = nameInput.text; if (name != ""){ var entry:Entry = new Entry(); entry.room = roomChoose.selectedIndex; entry.name = name; ClientAS3SX.sendRequest(entry); // roomLabel.text = "room" + entry.room; enterButoon.removeEventListener(MouseEvent.CLICK, onEnter); removeChild(enterPanel); messageInput.enabled = true; sendButton.enabled = true; } } private function onPush(e:MouseEvent):void { sendMessage(); } private function onKeyDown(e:KeyboardEvent):void { if (e.keyCode == Keyboard.ENTER){ sendMessage(); } } private function sendMessage():void { var message:String = messageInput.text; if (message != ""){ ClientAS3SX.sendRequest(message); messageInput.text = ""; } } private function onString(str:String):void { log.text = str + "\n" + log.text; } } }
ちょっとUI部分大目。
受けるレスポンスはStringにしよう。StringでsetResponseHandlerする。ハンドラメソッドではStringが来たらテキストの上に送られてきたStringを追加して表示する。
名前と部屋を選び、Enterボタンを押したらEntryオブジェクトを作ってnameとroomをそれぞれのコンポーネントから取得し、sendRequestする。
あとはmessageInputに文字列を入力してSENDボタンを押すかキーボードのEnterを押すとmessageInputのテキスト文字列をsendRequestしている。
package { import com.as3sx.server.EventManager; import com.as3sx.server.SessionManager; import com.as3sx.session.Session; import flash.display.Sprite; /** * ... * @author */ public class ServerMain extends Sprite { private var eventManager:EventManager; private var sessionManager:SessionManager; public function ServerMain():void { // entry point eventManager = ServerAS3SX.event; sessionManager = ServerAS3SX.session; ServerAS3SX.setRequestHandler(Entry, onEntry); ServerAS3SX.setRequestHandler(String, onString); } private function onEntry(entry:Entry):void { var room:uint = entry.room; var name:String = entry.name; var session:Session = sessionManager.getSession(); session.setValue("name", name); session.setValue("room", room); eventManager.addUserEventHandler("room" + room); eventManager.dispatchServerEvent("<b>System : " + name + " さんが入室しました</b>", "room" + room); } private function onString(str:String):void { var session:Session = sessionManager.getSession(); eventManager.dispatchServerEvent(session.getValue("name") + " : " + str, "room" + session.getValue("room")); } } }
まずsetRequestHandlerでEntryオブジェクトとStringオブジェクトを受ける準備をしよう。
Entryオブジェクトが送られてきたときは引数から名前を部屋番号を取り出す。
前回も使ったSessionManagerで現在のセッションを取得し、"name"と"room"をキーにしてそれぞれ保存する。
そしてaddUserEventHandlerでselctorを"room"+部屋番号にして現在のセッションを登録する。
その後dispatchServerEventで、そのselectorに登録された全員にシステムメッセージを送る。
Stringオブジェクトが送られてきたときは名前を追記して、セッションが入っている部屋全員にレスポンスを送りたい。
現在のセッションを取得し、それぞれのキーで値を取り出して使おう。
今回のプロジェクトのソースファイルはこちら。setEndPointの引数は変えてあるので注意。
一斉送信でした。実際には一斉じゃないだろうけど。好きにグループ分け出来るのがいいね。欲を言えば現在のセッションしか登録できないので、GUIDを指定することで他のセッションをイベント登録できるといいなあ。
removeUserEventHandlerが使えるともうちょっと幅が広がると思う。まあ自分で管理すればいいだけの話だけど、せっかく提供してくれてる機能は使いたいよね。addSessionDisposeHandlerと併せてなんとかしてね。
次回はたぶんデータベースのお話。
Author:9ballsyn
ActionScriptについて
最近はMolehill