…and Ruby, and Python, and Java. Chris Allen dropped a comment in a previous entry and got me all giddy!
More info on Red5.
…and Ruby, and Python, and Java. Chris Allen dropped a comment in a previous entry and got me all giddy!
More info on Red5.
I really liked this entry Graeme Harker wrote about marketing Flex to developers. I feel like I wasted the past year (2005) evangelizing Flex 1.5. Flex 2 is really gaining momentum, but still, it’s not the explosion a lot of us were expecting. I think if Adobe takes heed of some of what Graeme says, that should help with some much deserved inroads. Adobe has worked hard on Flex 2 and I think if a lot of the demographics Graeme talks about see her in action, they’ll agree and dive in.
Developing Flex 2 applications that talk with Flash Media Server is really fun. I’m sure Adobe will tell you that Flex Data Services are the way to go, but that is overkill for a lot of the things I want to do. If you want to create a set of widgets that support multiuser activity such as chat and sharing of real-time data, you don’t need the Enterprise data services that are FDS. While Flash Media Server has real-time video and audio, I use it strictly for the real-time data via Remote SharedObjects , and the server-side scripting done in case-sensitive JavaScript. The persistent data of Remote SharedObjects with a simple, yet flexible coding model makes them a very powerful way to get multi-user Flex 2 applications. Combined with some simple server-side logic to maintain “data authority” as I like to call it, and you’ve got a really fun way to create real-time data, mult-user apps.
What are the alternatives?
XMLSocket ? Parsing XML is more programmer friendly now with E4X, ECMAScript for XML , in ActionScript 3. XMLSocket servers do not enforce XML, however… they are merely string servers that run on ports of 1024 or higher, thus making them flexible. The cons? They run on ports 1024 or higher… ONLY. Client got a firewall? Tough. There are not a lot of big name XMLSocket hosting services. Those that are out there are extremely expensive for what you get. While the hosting might not be expensive, the license fee’s are. A lot of the open source solutions aren’t finished. Those that are assume you know PHP, Ruby, Perl, or Python well enough to finish it. Assuming I could, where do I host these servers? My host isn’t going to dig me having a PHP script stuck in a while loop…
FDS? Sorry, can’t afford 60k for fun side projects that have the potential to go to extremely small commercial markets.
Some other TCP/IP binary server? I’d like to create things today instead of getting burnt out writing something from scratch in ActionScript 3’s binary Socket API.
It’d be nice if Adobe would release a Remote SharedObject server, complete with server-side JavaScript on a totally different pricing and license scheme than Flash Media Server or Flex Data Services are. There are a lot of use developing mulit-user Flash Player applications on FMS that have no need for real-time streaming video and audio. I think the same crackheads that ran pre-Flex 2’s business unit took over Flashcom’s.
At any rate, there is no denying FMS is elegant to code with. With a little work via the Observer pattern , you can implement FMS server-side components into Cairngorm 2 . For ever server-side component you create, you merely create a client side equivalent as an Observer. It listens for NetConnection or Remote SharedObject sync events and dispatches events. These events run Commands which in turn set data… just like normal. If your Views are bound to said data, boom, you’ve got a nice, clean separation.
One thing that sucks about the work flow, though, is:
That sucks. The better way is to use some ANT tasks I’ve created along with a special SWF. This SWF connects to the FMS Admin server, reloads the app, and then immediately quits. I go into more detail below, or you can just download the source. You could re-write the ActionScript 2 into ActionScript 3, but AS2 is just quicker than AS3 for this type of stuff.
Bottom line, you just double-click an ANT task in Eclipse, it does all that for you, and you don’t have to leave Eclipse. That’s so hot!
First, the ANT tasks.
<project name=”DnDTools” default=”Deploy FCS” basedir=”.”>
<description>
by Jesse R. Warden
jesterxl@jessewarden.com
https://www.jessewarden.com
jesse@universalmind.com
http://www.universalmind.com
A series of tasks to work with Flashcom.
************************************************
Start Flashcom will write out a bat file, and run it to start
Flashcom as well as the Flashcom Admin service.
Stop Flashcom stops both the Flashcom service and the
Flashcom Admin service.
Deploy FCS does a few things:
* copies your .asc files to the FCS applications directory
* writes a configuration XML file for the FCS App Reloader
* runs the FCS App Reloader to reload your application instance on the FCS server
</description>
<!– ************************************************ –>
<!– set global properties for this build –>
<property name=”bin” value=”bin” />
<property name=”localFCSDir” value=”../flashcom” />
<property name=”deployFCSDir” value=”C:\Program Files\Macromedia\Flash Communication Server MX\applications\fcsappdirectory” />
<property name=”FCSAppReloader” value=”FCS_App_Reloader.exe” />
<property name=”FCSAdminHost” value=”localhost” />
<property name=”FCSAdminPort” value=”1111″ />
<property name=”FCSAppName” value=”fcsappdirectory” />
<property name=”FCSAdminUser” value=”myUsername” />
<property name=”FCSAdminPassword” value=”myPassword” />
<property name=”appWaitTime” value=”3″ />
<property name=”bat” location=”bat” />
<!– Copy files into the FCS app directory, write a config xml, and reload an FCS app –>
<target name=”Deploy FCS”>
<!– ************************************************ –>
<!– Copy new FCS files to the applications directory –>
<echo message=”Copying Flashcom files to application directory…” level=”info” />
<copy file=”${localFCSDir}/Application.xml” todir=”${deployFCSDir}” />
<copy file=”${localFCSDir}/asc/main.asc” todir=”${deployFCSDir}” />
<copy file=”${localFCSDir}/asc/Chat.asc” todir=”${deployFCSDir}” />
<!– ************************************************ –>
<!– Write the config xml file for the FCS Reloader App –>
<echo message=”Writing FCSAppReloader configuration xml file…” level=”info” />
<!– Host –>
<echo message=”<config host=’${FCSAdminHost}’ “
file=”${localFCSDir}/fcs_app_reloader_config.xml” />
<!– Port –>
<echo message=”port=’${FCSAdminPort}’ “
append=”true”
file=”${localFCSDir}/fcs_app_reloader_config.xml” />
<!– App Name –>
<echo message=”appname=’${FCSAppName}’ “
append=”true”
file=”${localFCSDir}/fcs_app_reloader_config.xml” />
<!– Username –>
<echo message=”username=’${FCSAdminUser}’ “
append=”true”
file=”${localFCSDir}/fcs_app_reloader_config.xml” />
<!– Password –>
<echo message=”password=’${FCSAdminPassword}’ “
append=”true”
file=”${localFCSDir}/fcs_app_reloader_config.xml” />
<!– Hang Time –>
<echo message=”hangtime=’${appWaitTime}’ />”
append=”true”
file=”${localFCSDir}/fcs_app_reloader_config.xml” />
<!– ************************************************ –>
<!– Launch the FCS Reloader App –>
<echo message=”Re-loading the FCS application…” level=”info” />
<exec executable=”${FCSAppReloader}”
dir=”${localFCSDir}/”
spawn=”false”
resolveexecutable=”true” />
</target>
<target name=”Start Flashcom”>
<echo message=”Starting Flashcom…” level=”info” />
<mkdir dir=”${bat}” />
<echo file=”${bat}/start_fcs.bat”>%SYSTEMROOT%\system32\net.exe start "FlashCom"
</echo>
<exec executable=”start_fcs.bat”
dir=”${bat}”
spawn=”false”
resolveexecutable=”true” />
</target>
<target name=”Stop Flashcom”>
<echo message=”Stopping Flashcom…” level=”info” />
<mkdir dir=”${bat}” />
<echo file=”${bat}/stop_fcs.bat”>%SYSTEMROOT%\system32\net.exe stop "FlashCom"
</echo>
<echo message=”%SYSTEMROOT%\system32\net.exe stop "FlashComAdmin"”
append=”true”
file=”${bat}/stop_fcs.bat” />
<exec executable=”stop_fcs.bat”
dir=”${bat}”
spawn=”false”
resolveexecutable=”true” />
</target>
</project>
Now, the ActionScript 2 for the FCS Reloader App:
/*
FCS App Reloader
by Jesse R. Warden
jesterxl@jessewarden.com
http://www.jessewarden.com
jesse@universalmind.com
http://www.universalmind.com
This is release under a Creative Commons license.
More information can be found here:
http://creativecommons.org/licenses/by/2.5/
v 1.0.0
This app connects to your FCS server, and reloads
an application instance. The typical use case is to
launch this app from ANT in Eclipse.
The alternative is to:
- toggle to the Communication App Inspector
- click the app instance and click "reload"
- if it's not there, type in it's name, and then click "load"
- toggle back to Eclipse
Now, you can just click once on your ANT task, and you're done!
This API assumes Flash Communication Server 1.0 and Flash Player 6.
I tested this in Flash Player 8, and the API should work
in Flash Media Server 2 per the livedocs.
*/
import mx.utils.Delegate; function init() { Stage.align = "TL"; Stage.scaleMode = "noScale"; Stage.addListener(this); createTextField("debug_txt", 0, 0, 0, 100, 100); debug_txt.background = true; debug_txt.backgroundColor = 0xFFFFFF; debug_txt.border = true; debug_txt.borderColor = 0x000000; debug_txt.html = true; debug_txt.multiline = true; debug_txt.selectable = true; debug_txt.wordWrap = true;
var fmt:TextFormat = new TextFormat(); fmt.font = "Verdana"; fmt.size = 11; debug_txt.setTextFormat(fmt); debug_txt.setNewTextFormat(fmt); onResize(); debugHeader(); debug("Loading config xml..."); NETCONNECTION_CALL_SUCCESS = "NetConnection.Call.Success"; CONFIG_XML_FILE = "fcs_app_reloader_config.xml"; TIMEOUT_WIDTH = 100; TIMEOUT_HEIGHT = 12;
config_xml = new XML(); config_xml.ignoreWhite = true; config_xml.owner = this; config_xml.onLoad = Delegate.create(this, onConfigLoaded); config_xml.load(CONFIG_XML_FILE); }
function onConfigLoaded(success:Boolean):Void { debugHeader(); if(success == true) { debug("Config XML loaded, parsing..."); var at:Object = config_xml.firstChild.attributes theHost = (at.host == null) ? "localhost" : at.host; thePort = (at.port == null) ? "1111" : at.port; theAppName = at.appname; theUsername = at.username; thePassword = at.password; theHangTime = (at.hangtime == null) ? 3 : parseInt(at.hangtime); if(isNaN(theHangTime) || theHangTime < 0 || theHangTime == null) theHangTime = 3;
var tabs:String = "\t\t\t\t"; debug("host: " + theHost); debug("port: " + thePort); debug("app name: " + theAppName); debug("user: " + theUsername); debug("password: " + thePassword); debug("hang time: " + theHangTime); connect(); } else { debug("Failed to find " + CONFIG_XML_FILE + ", aborting."); lightFuse(); } }
function connect() { debugHeader(); debug("Connecting to FCS Admin..."); nc = new NetConnection(); nc.owner = this; nc.onStatus = function(info) { if(info.code == "NetConnection.Connect.Success") { debugHeader(); debug("Successfully connected!"); this.owner.onConnected(); } else { debugHeader(); debug("Problem connecting to FCS Admin, aborting."); this.owner.lightFuse(); } };
nc.connect("rtmp://" + theHost + ":" + thePort + "/" + theAppName, theUsername, thePassword); } function onConnected() { //getActiveInstances(); reloadApp("flex2test"); }
function reloadApp(str:String) { debugHeader(); debug("Reloading app..."); nc.call("reloadApp", new Responder(this, reloadApp_result, reloadApp_fault), str); }
function reloadApp_result(o) { debugHeader(); if(o.code == NETCONNECTION_CALL_SUCCESS) { debug("App successfully rebooted, exiting."); } else { debug("Failed to reboot the app!"); debugProps(o); debugProps(o.level); debugProps(o.error); debugProps(o.code); debug(o.description); debugProps(o.description); debug("Exiting."); } //debugProps(o); lightFuse(); }
function reloadApp_fault(o) { debugHeader(); debug("reloadApp_fault"); debugProps(o); lightFuse(); }
/*
function getActiveInstances()
{
debugHeader();
debug("getActiveInstances...");
nc.call("getActiveInstances",
new Responder(this,
getActiveInstances_result,
getActiveInstances_fault));
}
function getActiveInstances_result(o)
{
debugHeader();
debug("getActiveInstances_result");
if(o.code == NETCONNECTION_CALL_SUCCESS)
{
debug(o.data.length + " active instances.");
debugProps(o.data);
}
}
function getActiveInstances_fault(o)
{
debugHeader();
debug("getActiveInstances_fault");
debugProps(o);
}
*/
function lightFuse() { nc.onStatus = null; nc.close(); nc = null; createEmptyMovieClip("timeout_mc", 1); timeout_mc.st = getTimer(); timeout_mc.timeout = 3; // seconds timeout_mc.onEnterFrame = function() { var ct:Number = getTimer(); var et:Number = ct - this.st; if(et > (this.timeout * 1000)) { this._parent.boom(); } else { var p:Number = et / (this.timeout * 1000); var w:Number = TIMEOUT_WIDTH * p; }
timeout_mc.clear(); timeout_mc.lineStyle(0, 0x000000); timeout_mc.lineTo(TIMEOUT_WIDTH, 0); timeout_mc.lineTo(TIMEOUT_WIDTH, TIMEOUT_HEIGHT); timeout_mc.lineTo(0, TIMEOUT_HEIGHT); timeout_mc.lineTo(0, 0); timeout_mc.moveTo(1, 1); timeout_mc.beginFill(0x666666); timeout_mc.lineTo(w, 1); timeout_mc.lineTo(w, TIMEOUT_HEIGHT - 1); timeout_mc.lineTo(1, TIMEOUT_HEIGHT - 1); timeout_mc.lineTo(1, 1); timeout_mc.endFill(); }; timeout_mc.onEnterFrame(); // draw immediately onResize(); }
function boom() { delete timeout_mc.onEnterFrame; timeout_mc.removeMovieClip(); fscommand("quit"); } function onResize() { debug_txt._x = 0; debug_txt._y = 0; debug_txt._width = Math.max(12, Stage.width); debug_txt._height = Math.max(12, Stage.height); var sx:Number = Math.max(6, Stage.width); var sy:Number = 6; timeout_mc._x = sx - (timeout_mc._width) - 6; timeout_mc._y = sy; }
function debug(o) { trace(o); debug_txt.htmlText += o + ""; } function debugHeader() { //debug("---------------"); } function debugProps(o) { if(o instanceof Array) { var len:Number = o.length; for(var i:Number = 0; i<len; i++) { trace(o[i]); } } else { for(var p in o) { debug(p + ": " + o[p]); } } } init();
Source Files – ZIP
BEP Interactive is looking for a full time flash developer/animator. Give them a shout if you are interested.