Page MenuHomePhabricator

D9327.id22219.diff
No OneTemporary

D9327.id22219.diff

diff --git a/support/aphlict/client/build_aphlict_client.sh b/support/aphlict/client/build_aphlict_client.sh
--- a/support/aphlict/client/build_aphlict_client.sh
+++ b/support/aphlict/client/build_aphlict_client.sh
@@ -9,20 +9,13 @@
fi;
set -e
-set -x
-# cp -R $ROOT/externals/vegas/src $BASEDIR/src/vegas
-
-(cd $BASEDIR && $MXMLC \
- -output aphlict.swf \
+$MXMLC \
+ -output=$ROOT/webroot/rsrc/swf/aphlict.swf \
-default-background-color=0x444444 \
-default-size=500,500 \
-warnings=true \
-debug=true \
-source-path=$ROOT/externals/vegas/src \
-static-link-runtime-shared-libraries=true \
- src/Aphlict.as)
-
-mv $BASEDIR/aphlict.swf $ROOT/webroot/rsrc/swf/aphlict.swf
-
-# -target-player=10.2.0 \
+ $BASEDIR/src/AphlictClient.as
diff --git a/support/aphlict/client/src/Aphlict.as b/support/aphlict/client/src/Aphlict.as
--- a/support/aphlict/client/src/Aphlict.as
+++ b/support/aphlict/client/src/Aphlict.as
@@ -1,117 +1,38 @@
package {
- import flash.net.*;
- import flash.utils.*;
- import flash.media.*;
- import flash.display.*;
- import flash.events.*;
+ import flash.display.Sprite;
import flash.external.ExternalInterface;
+ import flash.net.LocalConnection;
- import vegas.strings.JSON;
public class Aphlict extends Sprite {
- private var client:String;
+ /**
+ * A transport channel used to receive data.
+ */
+ protected var recv:LocalConnection;
- private var socket:Socket;
- private var readBuffer:ByteArray;
+ /**
+ * A transport channel used to send data.
+ */
+ protected var send:LocalConnection;
- private var remoteServer:String;
- private var remotePort:Number;
public function Aphlict() {
super();
- ExternalInterface.addCallback('connect', this.externalConnect);
- ExternalInterface.call(
- 'JX.Stratcom.invoke',
- 'aphlict-component-ready',
- null,
- {});
- }
-
- public function externalConnect(server:String, port:Number):void {
- this.externalInvoke('connect');
-
- this.remoteServer = server;
- this.remotePort = port;
-
- this.connectToServer();
- }
-
-
- public function connectToServer():void {
- var socket:Socket = new Socket();
-
- socket.addEventListener(Event.CONNECT, didConnectSocket);
- socket.addEventListener(Event.CLOSE, didCloseSocket);
- socket.addEventListener(ProgressEvent.SOCKET_DATA, didReceiveSocket);
-
- socket.addEventListener(IOErrorEvent.IO_ERROR, didIOErrorSocket);
- socket.addEventListener(
- SecurityErrorEvent.SECURITY_ERROR,
- didSecurityErrorSocket);
-
- socket.connect(this.remoteServer, this.remotePort);
-
- this.readBuffer = new ByteArray();
- this.socket = socket;
- }
-
- private function didConnectSocket(event:Event):void {
- this.externalInvoke('connected');
- }
-
- private function didCloseSocket(event:Event):void {
- this.externalInvoke('close');
- }
-
- private function didIOErrorSocket(event:IOErrorEvent):void {
- this.externalInvoke('error', event.text);
- }
-
- private function didSecurityErrorSocket(event:SecurityErrorEvent):void {
- this.externalInvoke('error', event.text);
- }
-
- private function didReceiveSocket(event:Event):void {
- var b:ByteArray = this.readBuffer;
- this.socket.readBytes(b, b.length);
-
- do {
- b = this.readBuffer;
- b.position = 0;
-
- if (b.length <= 8) {
- break;
- }
-
- var msg_len:Number = parseInt(b.readUTFBytes(8), 10);
- if (b.length >= msg_len + 8) {
- var bytes:String = b.readUTFBytes(msg_len);
- var data:Object = vegas.strings.JSON.deserialize(bytes);
- var t:ByteArray = new ByteArray();
- t.writeBytes(b, msg_len + 8);
- this.readBuffer = t;
-
- this.receiveMessage(data);
- } else {
- break;
- }
- } while (true);
-
- }
+ this.recv = new LocalConnection();
+ this.recv.client = this;
- public function receiveMessage(msg:Object):void {
- this.externalInvoke('receive', msg);
+ this.send = new LocalConnection();
}
- public function externalInvoke(type:String, object:Object = null):void {
+ protected function externalInvoke(type:String, object:Object = null):void {
ExternalInterface.call('JX.Aphlict.didReceiveEvent', type, object);
}
- public function log(message:String):void {
- ExternalInterface.call('console.log', message);
+ protected function log(message:String):void {
+ this.externalInvoke('log', message);
}
}
diff --git a/support/aphlict/client/src/AphlictClient.as b/support/aphlict/client/src/AphlictClient.as
new file mode 100644
--- /dev/null
+++ b/support/aphlict/client/src/AphlictClient.as
@@ -0,0 +1,129 @@
+package {
+
+ import flash.events.TimerEvent;
+ import flash.external.ExternalInterface;
+ import flash.utils.Timer;
+
+
+ public class AphlictClient extends Aphlict {
+
+ /**
+ * The connection name for this client. This will be used for the
+ * @{class:LocalConnection} object.
+ */
+ private var client:String;
+
+ /**
+ * The expiry timestamp for the @{class:AphlictMaster}. If this time is
+ * elapsed then the master will be assumed to be dead and another
+ * @{class:AphlictClient} will create a master.
+ */
+ private var expiry:Number = 0;
+
+ /**
+ * The interval at which to ping the @{class:AphlictMaster}.
+ */
+ public static const INTERVAL:Number = 3000;
+
+ private var master:AphlictMaster;
+ private var timer:Timer;
+
+ private var remoteServer:String;
+ private var remotePort:Number;
+
+
+ public function AphlictClient() {
+ super();
+
+ ExternalInterface.addCallback('connect', this.externalConnect);
+ ExternalInterface.call(
+ 'JX.Stratcom.invoke',
+ 'aphlict-component-ready',
+ null,
+ {});
+ }
+
+ public function externalConnect(server:String, port:Number):void {
+ this.externalInvoke('connect');
+
+ this.remoteServer = server;
+ this.remotePort = port;
+
+ this.client = AphlictClient.generateClientId();
+ this.recv.connect(this.client);
+
+ this.timer = new Timer(AphlictClient.INTERVAL);
+ this.timer.addEventListener(TimerEvent.TIMER, this.keepalive);
+
+ this.connectToMaster();
+ }
+
+ /**
+ * Generate a unique identifier that will be used to communicate with the
+ * @{class:AphlictMaster}.
+ */
+ private static function generateClientId():String {
+ return 'aphlict_client_' + Math.round(Math.random() * 100000);
+ }
+
+ /**
+ * Create a new connection to the @{class:AphlictMaster}.
+ *
+ * If there is no current @{class:AphlictMaster} instance, then a new master
+ * will be created.
+ */
+ private function connectToMaster():void {
+ this.timer.stop();
+
+ // Try to become the master.
+ try {
+ this.log('Attempting to become the master...');
+ this.master = new AphlictMaster(this.remoteServer, this.remotePort);
+ this.log('I am the master.');
+ } catch (x:Error) {
+ // Couldn't become the master
+ this.log('Cannot become the master... probably one already exists');
+ }
+
+ this.send.send('aphlict_master', 'register', this.client);
+ this.expiry = new Date().getTime() + (5 * AphlictClient.INTERVAL);
+ this.log('Registered client ' + this.client);
+
+ this.timer.start();
+ }
+
+ /**
+ * Send a keepalive signal to the @{class:AphlictMaster}.
+ *
+ * If the connection to the master has expired (because the master has not
+ * sent a heartbeat signal), then a new connection to master will be
+ * created.
+ */
+ private function keepalive(event:TimerEvent):void {
+ if (new Date().getTime() > this.expiry) {
+ this.connectToMaster();
+ }
+
+ this.send.send('aphlict_master', 'ping', this.client);
+ }
+
+ /**
+ * This function is used to receive the heartbeat signal from the
+ * @{class:AphlictMaster}.
+ */
+ public function pong():void {
+ this.expiry = new Date().getTime() + (2 * AphlictClient.INTERVAL);
+ }
+
+ /**
+ * Receive a message from the Aphlict Server, via the
+ * @{class:AphlictMaster}.
+ */
+ public function receiveMessage(msg:Object):void {
+ this.log('Received message.');
+ this.externalInvoke('receive', msg);
+ }
+
+ }
+
+}
diff --git a/support/aphlict/client/src/AphlictMaster.as b/support/aphlict/client/src/AphlictMaster.as
new file mode 100644
--- /dev/null
+++ b/support/aphlict/client/src/AphlictMaster.as
@@ -0,0 +1,166 @@
+package {
+
+ import flash.events.Event;
+ import flash.events.IOErrorEvent;
+ import flash.events.ProgressEvent;
+ import flash.events.SecurityErrorEvent;
+ import flash.events.TimerEvent;
+ import flash.net.Socket;
+ import flash.utils.ByteArray;
+ import flash.utils.Dictionary;
+ import flash.utils.Timer;
+ import vegas.strings.JSON;
+
+
+ public class AphlictMaster extends Aphlict {
+
+ /**
+ * The pool of connected clients.
+ */
+ private var clients:Dictionary;
+
+ /**
+ * A timer used to trigger periodic events.
+ */
+ private var timer:Timer;
+
+ /**
+ * The interval after which clients will be considered dead and removed
+ * from the pool.
+ */
+ public static const PURGE_INTERVAL:Number = 3 * AphlictClient.INTERVAL;
+
+ /**
+ * The hostname for the Aphlict Server.
+ */
+ private var remoteServer:String;
+
+ /**
+ * The port number for the Aphlict Server.
+ */
+ private var remotePort:Number;
+
+ private var socket:Socket;
+ private var readBuffer:ByteArray;
+
+
+ public function AphlictMaster(server:String, port:Number) {
+ super();
+
+ this.remoteServer = server;
+ this.remotePort = port;
+
+ // Connect to the Aphlict Server.
+ this.recv.connect('aphlict_master');
+ this.connectToServer();
+
+ this.clients = new Dictionary();
+
+ // Start a timer and regularly purge dead clients.
+ this.timer = new Timer(AphlictMaster.PURGE_INTERVAL);
+ this.timer.addEventListener(TimerEvent.TIMER, this.purgeClients);
+ this.timer.start();
+ }
+
+ /**
+ * Register a @{class:AphlictClient}.
+ */
+ public function register(client:String):void {
+ if (!this.clients[client]) {
+ this.log('Registering client: ' + client);
+ this.clients[client] = new Date().getTime();
+ }
+ }
+
+ /**
+ * Purge stale client connections from the client pool.
+ */
+ private function purgeClients(event:TimerEvent):void {
+ for (var client:String in this.clients) {
+ var checkin:Number = this.clients[client];
+
+ if (new Date().getTime() - checkin > AphlictMaster.PURGE_INTERVAL) {
+ this.log('Purging client: ' + client);
+ delete this.clients[client];
+ }
+ }
+ }
+
+ /**
+ * Clients will regularly "ping" the master to let us know that they are
+ * still alive. We will "pong" them back to let the client know that the
+ * master is still alive.
+ */
+ public function ping(client:String):void {
+ this.clients[client] = new Date().getTime();
+ this.send.send(client, 'pong');
+ }
+
+ private function connectToServer():void {
+ var socket:Socket = new Socket();
+
+ socket.addEventListener(Event.CONNECT, didConnectSocket);
+ socket.addEventListener(Event.CLOSE, didCloseSocket);
+ socket.addEventListener(ProgressEvent.SOCKET_DATA, didReceiveSocket);
+
+ socket.addEventListener(IOErrorEvent.IO_ERROR, didIOErrorSocket);
+ socket.addEventListener(
+ SecurityErrorEvent.SECURITY_ERROR,
+ didSecurityErrorSocket);
+
+ socket.connect(this.remoteServer, this.remotePort);
+
+ this.readBuffer = new ByteArray();
+ this.socket = socket;
+ }
+
+ private function didConnectSocket(event:Event):void {
+ this.externalInvoke('connected');
+ }
+
+ private function didCloseSocket(event:Event):void {
+ this.externalInvoke('close');
+ }
+
+ private function didIOErrorSocket(event:IOErrorEvent):void {
+ this.externalInvoke('error', event.text);
+ }
+
+ private function didSecurityErrorSocket(event:SecurityErrorEvent):void {
+ this.externalInvoke('error', event.text);
+ }
+
+ private function didReceiveSocket(event:Event):void {
+ var b:ByteArray = this.readBuffer;
+ this.socket.readBytes(b, b.length);
+
+ do {
+ b = this.readBuffer;
+ b.position = 0;
+
+ if (b.length <= 8) {
+ break;
+ }
+
+ var msg_len:Number = parseInt(b.readUTFBytes(8), 10);
+ if (b.length >= msg_len + 8) {
+ var bytes:String = b.readUTFBytes(msg_len);
+ var data:Object = vegas.strings.JSON.deserialize(bytes);
+ var t:ByteArray = new ByteArray();
+ t.writeBytes(b, msg_len + 8);
+ this.readBuffer = t;
+
+ // Send the message to all clients.
+ for (var client:String in this.clients) {
+ this.log('Sending message to client: ' + client);
+ this.send.send(client, 'receiveMessage', data);
+ }
+ } else {
+ break;
+ }
+ } while (true);
+ }
+
+ }
+
+}
diff --git a/webroot/rsrc/swf/aphlict.swf b/webroot/rsrc/swf/aphlict.swf
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 0
Hc$@<O00001

File Metadata

Mime Type
text/plain
Expires
Wed, May 15, 7:51 AM (2 w, 3 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6288240
Default Alt Text
D9327.id22219.diff (13 KB)

Event Timeline