PatrickBay.ca

This time, it's personal.

MovieClips have been the stalwart workhorse of Flash since the very earliest days of ActionScript, so I’ll skip the details and gushing adoration and instead move on to some of the shortcomings:

  • MovieClips only play forward from the current frame. When building out animations without extra MovieClip functionality, playing animations backwards — a character in a game reversing, for example –requires a whole new timeline with the animation reversed. This produces a little extra memory overhead, even if symbols are re-used, but more importantly makes cross-linking the “forward” and “backward” clips challenging. For example, if a character is in a specific position on frame 5 of a “forward” animation, that frame might appear in frame 45 of the “backward” animation.
  • MovieClips only play at the global frame rate. Effectively, your game character can only run at one speed unless you include other MovieClips and then match them up, as best as possible, to the target speed.
  • MovieClips play holistically. When you play them, you must monitor their current frame if you want to stop at a specific point otherwise they’ll play all the way through. If you want to do this backwards, that’s even more work. If you want to play at a specific rate, ditto.
  • MovieClips have no triggers. If you want to know if frame 5 (or some label) has been reached, for example, you once again need to monitor the clip. Setting up multiple playback monitors is a bit of a pain.

So you can now probably guess what SwagMovieClip does.

As with most of the SwAG classes, SwagMovieClip doesn’t make assumptions about how it’s going to be used. You can either extend it:

import core.instances.SwagMovieClip;
public class myMovieClip extends SwagMovieClip {
...

…or you can instantiate it (just make sure to pass a reference to the MovieClip instance you want to control to the constructor):

import flash.display.MovieClip;
import core.instances.SwagMovieClip;
public class myMovieClip extends MovieClip {
   var swgClip:SwagMovieClip;
   public function myMovieClip() {
      this.swgClip=new SwagMovieClip(this);
...

If you’re extending SwagMovieClip, all of the class’ methods can be invoked using simply “this”. For example, to play the clip 3 times at double speed from the “startAnimation” frame to the “endAnimation” frame would look like:

this.playRange("startAnimation", "endAnimation", false, true, 3, 2);

Using the instantiation method, this would be:

swgClip.playRange("startAnimation", "endAnimation", false, true, 3, 2);

As you can probably guess, the playRange method plays from a starting frame (label or index), to an ending frame (label or index), optionally resetting to the start of the animation when done, optionally looping (including a specified number of loops), and at an optional playback speed (2 for double speed, 0.5 for half speed, etc.) This works just as well if the starting frame comes after the ending frame — the clip will play backwards with the specified parameters. Essentially, this one function covers the first three shortcomings I mentioned above.

A related method is playToNextLabel which, as the name suggests, will play the clip from its current position with the supplied parameters until the next label is detected. As with the playRange method, the label might precede the current (starting) frame. Here’s the method signature from the SwagMovieClip class showing all the parameters:

playToNextLabel(startFrame:*, includeLabelFrame:Boolean=false, resetOnEnd:Boolean=false, loop:Boolean=false, repeatLoops:uint=0, playSpeed:Number=1)

The addition of a pause method might seem a little redundant but it comes in handy when you need to determine if an active animation is currently in progress (and paused), or simply stopped. One great reason for wanting this information is to know whether to play out the remaining animation loop such as when a game character is coming to a rest after moving. In fact, games are primarily why I built SwagMovieClip (I used similar functionality to control the animations in the Sidekicks vs. Villains game).

Along these lines I also added a frame trigger mechanism, a method call that’s made whenever a target frame is reached during an animation. Here’s an example (minus the “this” or “swgClip” part):

public function myFrameTrigger():void {
 trace ("Frame 9 was just reached in the animation");
 }
 ...
 addFrameTrigger(9, myFrameTrigger);

Obviously I skipped a detail; the addFrameTrigger call belongs in a function somewhere, but otherwise this is pretty much all you’d need. In this case, the myFrameTrigger method will be triggered any time frame 9 of the animation is reached. Since the other methods (playRange, etc.) are responsible for actually starting the animation, all of their features can be used in combination with addFrameTrigger. Here’s a slightly extended example from the one above:

public function myFrameTrigger():void {
 trace ("Frame 9 was just reached in the animation");
 }
 ...
 addFrameTrigger(9, myFrameTrigger);
 playRange (10, 8);

Here we’re playing the clip backwards from frame 10 to frame 8 (all other parameters are default — no looping or custom speed), and the myFrameTrigger method should be invoked on frame 9 of the animation. To loop the animation 7 times (stopping on the last frame), at half the current frame rate, the same code would be updated to:

public function myFrameTrigger():void {
 trace ("Frame 9 was just reached in the animation");
 }
 ...
 addFrameTrigger(9, myFrameTrigger);
 playRange (10, 8, false, true, 7, 0.5);

Every time frame 9 is encountered here during playback our trigger will be invoked.

Triggers are useful for figuring out how a game character is supposed to be interacting with an environment, for example. If your game character is picking something up from a table, the target object should be “attached” to the character’s hand only when the hand animation is at a certain position. This isn’t hard to achieve by hardcoding frame values into your code but becomes cumbersome if you need to change the animation length, and even more difficult when you need to start playing the animation in various non-standard ways.

Most of SwagMovieClip‘s other functionality comes straight from MovieClip along with expected behaviours. As with any other SwAG class, I won’t recommend using SwagMovieClip unless you actually have a need for such functionality (why bloat your code?) but if you find you’re struggling with customizing your MovieClip animations then this might be worth a gander.

P.S. Thanks for another mention, Adobe!

September 19th, 2014

Posted In: Development / Coding, Internet / Web

Tags: , , , , , , , ,

Leave a Comment

SocialCastr is, in a nutshell, a censorship-resistant, peer to peer, personal broadcasting system.

You can grab yourself a compiled copy here: http://www.socialcastr.com/
And you can download the source code here: https://github.com/Patrick-Bay/SocialCastr (you’ll need Adobe Flash to compile it at the moment)

SocialCastr works with Adobe’s RTMFP, a UDP-based protocol for low-latency peer to peer data delivery. The latency is low enough that you can comfortably transmit audio/video streams from a home computer (with a typical residential network connection), among other neat things — I covered a few in a previous post.

What makes RTMFP especially interesting is how it’s able to propagate things like audio/video streams to other peers. When doing so, you only need to send data to two or three peers and they in turn re-distribute that data to a few others peers, effectively amplifying your bandwidth. This means that you can send data (like an audio/video stream), using a standard computer with a basic internet connection to a potential audience of millions. At least in theory (I was never able to get an audience of millions to test with).

Like most peer to peer protocols, RTMFP depends on a centralized host to introduce peers to each other — the Rendezvous Server. I went over a few options for this in that previous post I’d mentioned above. For the SocialCastr project I decided to test with Cumulus since it’s open source and can eventually be bundled into the application package. Plus, Cumulus is programmable via LUA so it’s a really nice option. You’ll find Cumulus (including the C++ source code), in the GitHub repository for SocialCastr. You can also go with Cirrus or Adobe Media Server, and ArcusNode for Node.js seems like another good alternative (though I haven’t tested it).

SocialCastr uses a “Live Timeline” system — synchronized events like live audio and video effects. The audio and video stream continue to run as-is “beneath” the Live Timeline with the effects applied in realtime on the peer’s computer/device. Although I’ve only tested a couple of effects like video fades and blurs, it can support much more: subtitles, picture in picture, starting or cueing pre-recorded video/audio,  Pixel Bender filters, graphical overlays, audio effects (echo, pitch shift), and so on. Creating a LiveTimeline effect requires only that your class extends the socialcastr.core.timeline.BaseTimelineEffect class and that it then be triggered somehow during a broadcast (I used keystrokes for this). You can see this in action in the socialcastr.core.timeline.SimpleVideoEffect class.

On top of this, I built SocialCastr to be very customizable. The web-based SocialCastr player (Receivr), for example, can be entirely stripped of any identifying UI elements. You don’t like the logo I designed? No problem, just tell the player to strip it out. Or use your own. Up to you.

With SocialCastr I had to deal with the problem of connecting people to each other. Basically, once connected to the Rendezvous Server (to which everyone is connected), how do you establish private or semi-private groups without first agreeing to specific group names or specs?

I did this in one-and-a-half ways:

  1. The AnnounceChannel (socialcastr.core.AnnounceChannel), which all peers join in order to get a list of broadcasters. It’s essentially a public directory of everyone who’s currently connected and broadcasting.
  2. The SocialCastr ID system (socialcastr.core.SCID), which is used to connect a peer directly to an existing stream identified by the SCID (read more about it here). The SCID system runs on top of the AnnounceChannel (hence the “half”), but bypasses the AnnounceChannel’s default “list all broadcasters” call.

This is something that sometimes trips people up when they first start working with RTMFP (or any p2p technology, actually). I don’t know if the AnnounceChannel is the most elegant solution, but it certainly works so I figure it might be a good launchpad for someone trying to do the same thing.

So as with all the other projects I’ve posted about, even if SocialCastr isn’t the type of software you might end up using, it might at least help you to work around some of the hurdles and headaches of getting a robust peer to peer network up and running with RTMFP. If you’ve read about how peer to peer networking works in SwAG, you’re already halfway there.

P.S. Thanks for the mention, Adobe!

September 4th, 2014

Posted In: Internet / Web, Privacy / Surveillance

Tags: , , , , , , , ,

One Comment

connected

Node-RED in a Nutshell

Roughly a year ago the folks at IBM Emerging Technology started work on Node-RED, a solution to the problem of a lack of “tools that make it easier for developers at all levels to bring together the different streams of events, both physical and digital, that make up the Internet of Things.”

This was a bit too heady for me so I decided instead to look at some of the “flows”, the projects that Node-RED uses, as a sampling of what the software can do. Here are a few:

  • Weather reports to SMS and MQTT topic: “…a couple of flows to read weather forecast info from the BBC weather site using RSS. The feeds are specific to my location so should be changed for your own. The output is directed to 2 nodes, one for Twilio to send a SMS, the other to publish to a MQTT topic that in my case has a printer listening to it.”
  • Twitter Sentiment Analysis: “This flow listens for tweets mentioning a specific term, passes it through sentiment analysis and then routes the messages to one of three outputs depending if the sentiment is positive, neutral or negative.”
  • Turn off HDMI connected TV when leaving WIFI + presence dashboard: “If the user leaves home with the phone and gets disconnected from the WIFI the attached TV gets turned off over HDMI CEC. All tested with a Nexus5, Raspberry Pi and a old Philips LCD TV.”

Clearly Node-RED has a wide range of applications, but what makes it truly unique is that it simultaneously eschews traditional code while favouring visual design. Some JavaScript is still in there, but it’s minimized.

Furthermore, once a flow has been created and tested, it can be packaged into independent npm modules (for Node.js), to distribute and use outside of Node-RED. I think that’s pretty neat.

code-red_ui

The one downside was that it’s a bit tricky to get running if you’re not up to snuff with Node.js, so I whittled it down a bit (results below).

It made me wonder if standalone flows couldn’t be distributed with Node.js in the same way; I think this makes for some neat projects and interesting opportunities.

Installation

Step 1: Download: Node-RED_0.8.1ws (The “ws” stands for Windows Standalone — it should theoretically run from a thumb drive).

Step 2: Unzip anywhere you like and run the included “Launch.cmd” file.

Launching_Node-RED

Step 3: Use it.

The launcher will open Node-RED in a new process window…

Node-RED_running

…and then launch your default browser to connect to it. If your browser opens too fast you might get a “connection rejected” error; just wait a few moments for the Node-RED server to start and reload the page.

To disconnect and shut down Node-RED, simply close the open process window.

After you’ve tried this out you might want to swing on by the “Creating your first flow” page to get better acquainted.

N.B. I’m not affiliated with either Node.js or the Node-RED project so everything here is 100% unofficial.

Update, October 23: I’ve re-packed the standalone with the newest version of Node-Red (0.9.0): Node-RED 0.9.0ws

August 27th, 2014

Posted In: Development / Coding, Internet / Web

Tags: , , ,

Leave a Comment

Problem

When the ActionScript VM gets it wrong, sometimes it gets it really wrong. Makes you wanna drink.

Take the following runtime error:

TypeError: Error #1034: Type Coercion failed: cannot convert ca.patrickbay.events::CustomEvent@9262359 to ca.patrickbay.MyClass.
 at flash.events::EventDispatcher/dispatchEventFunction()
 at flash.events::EventDispatcher/dispatchEvent()
 at ca.patrickbay::MyClass/onTimerEnd()[C:\Users\patrick\Desktop\project\ca\patrickbay\MyClass.as:375]
 at flash.events::EventDispatcher/dispatchEventFunction()
 at flash.events::EventDispatcher/dispatchEvent()
 at flash.utils::Timer/tick()

Okay, obviously MyClass has a problem on line 375. Seems it should be using a CustomEvent type but it’s using a MyClass type instead. Clearly this is an easy fix. Here’s around line 375 of MyClass:

 var event:CustomEvent = new CustomEvent(CustomEvent.NOTIFY); 
 this.dispatchEvent(event);

Hmm, looks okay. I check both this class and CustomEvent to make sure they’re both actually the same thing … only one file of each, and renaming causes the compiler to complain, so they’re okay. Custom event does have a NOTIFY property. All good there.

The class paths are all okay and there are definitely no other conflicts. MyClass is extending EventDispatcher and being properly instantiated, so no issues there.

And here’s the weird part — I use this dispatch call liberally throughout MyClass, often verbatim, and it appears to work everywhere else except in this one enclosing function:

public function onTimerEnd(eventObj:TimerEvent):void {
   trace ("This appears in the trace log. eventObj is "+eventObj);
   var event:CustomEvent = new CustomEvent(CustomEvent.NOTIFY);
   this.dispatchEvent(event);
   trace ("This doesn't appear!");
}

The output log shows that, indeed, this function is invoked and that eventObj is an actual TimerEvent. Except that once I try to dispatch this one event, from this one function, the event dispatcher throws a runtime error. And did I mention that it’s only this one function?

Okay, so at least I can catch that sucker:

public function onTimerEnd(eventObj:TimerEvent):void {
   trace ("This appears in the trace log, part 2. eventObj is "+eventObj);
   try {
      var event:CustomEvent = new CustomEvent(CustomEvent.NOTIFY);
      this.dispatchEvent(event);
   } catch (err:*) {
      trace ("Here's what I caught: "+err);
   }
   trace ("This doesn't appear!");
}

I clear all the compiler caches, wipe the SWFs, make sure everything is fresh. I even update the trace log, as you may note.

And the error isn’t caught.

The “This appears…” line shows up but no “Here’s what I caught…”. Instead I get the same runtime error.

So it seems that I’ve encountered a non-catchable, fatal application error being thrown by the event dispatcher. I’ve gone over both classes – MyClass and the document class listening to it – about 15 times now, even going so far as to just accept any type of anything on the listener:

public function onCustomEvent(... args):void {
   trace ("Why won't I get called?!");
}

Again, wipey wipey. Re-compile and … the same runtime error.

The Flash VM is telling me that on line 375 of MyClass, I am providing a CustomEvent, which I head-shakingly re-verify, and that that event can’t be converted into a MyClass type.

But why … why is the event dispatcher trying to do this?

My listener’s wide open, it’ll take ANYTHING, and the event dispatcher is still trying to do something weird. Seriously … what does it have against me?

So I comment out that event dispatch, in that one function, and sure enough the application works again, albeit without this very necessary event dispatch. So that’s not a solution.

Solution

Ahem.

Well, I felt kinda dumb after figuring out this one. But, in my defense, the runtime error message was not at all clear.

In fact, the error originated in an entirely different class that I had imported at some point and that also happened to be listening to that CustomEvent from MyClass.

In this entirely different class, the listener was declared like so:

private function onTimerEventNotification(eventObj:MyClass):void {
   trace ("Timer has completed");
}

Late night, drink, code … I tell ya.

Anyway, as soon this was corrected, everything ran as expected.

The takeaway here is that if you’re seeing an error like this from EventDispatcher, it’s probably not giving you the whole story. A project-wide search may save you from the torment I’ve described.

Remember my anguish, drink to my memory.

August 19th, 2014

Posted In: Development / Coding

Tags: , , , ,

One Comment

Capitalizing on the fear of near-daily revelations of data breaches and widespread government surveillance, companies like Google, Microsoft, and Yahoo! are implementing so-called “end-to-end” encryption to much applause. Unfortunately, this provides a false sense of security.

That’s not to say that encrypting data for transit is a bad thing — not at all. That’s also not to say that, despite problems, I know of any gaping, insurmountable flaws in TLS.

However, consider how a typically HTTPS handshake is carried out: the browser negotiates the initial symmetric key exchange using a locally generated asymmetric key. Both the fast symmetric key and the asymmetric key are at some point stored, either in memory, or in a security container on disk. In order to be able to successfully continue to communicate with the remote server, the browser must then have access to the symmetric key at all times.

While there are chances that the keys or pre-encrypted communications may be intercepted by malware running on the host computer, little consideration is given to the browser, operating system, or hardware. We may have some protection against nefarious outside forces, but what about the fact that Google or Microsoft have potentially full access to all of our communications, encrypted or not?

This is a perfectly reasonable point to raise; Microsoft regularly co-operates with the NSA, as does Google, and with Yahoo! not far behind.

So if Google’s Chrome browser, for example, encrypts your communication, Google has full access to not only the unencrypted communication but also any and all keys used during the crypto operation(s). Microsoft can do one better since they run the underlying operating system. That means that they can capture key strokes, web requests, key storage, and pretty much anything else they like at their whim — in any software. They can also hide network transactions from software like Fiddler or Wireshark; Microsoft provides the network stack, after all.

Naturally, the hardware running the OS and browser, or really any network hardware, are equally as capable of spying on you without your knowledge. In reality we have a three-deep layer of “just trust us” security over which is slapped HTTPS. The HTTPS part is great, but what about the other pieces? As the Wikipedia entry on “end-to-end encryption” points out, “[this] paradigm does not directly address risks at the communications endpoints themselves…”

So HTTPS really only protects you from outside, third-party interlopers, not in any meaningful way from government or corporate surveillance. Heck, they come right out and tell us that this is so — all for our benefit, of course.

Unfortunately, discussion on this topic is often lacking from online security and privacy forums yet this is perhaps a more vital topic than protecting your data in transit. While it’s great that we have improved security to deal with Man In The Middle techniques, privacy and anonymity in corporate and government spheres are essentially non-existent.

This is daunting problem recognized by professional cryptographers and security experts but there doesn’t seem to be much in the way of solutions. We can use something like Tails or Subgraph OS to secure the operating system level but we’re still faced with the hardware both at the host and at the networking level (the router, for example).

Does this mean that there’s practically nothing that can be done? I don’t believe this and there are some proposals that I think are worth discussing, if for no other reason than to inspire imaginations. While this is a somewhat steep uphill climb, it’s also an opportunity for the creation of a whole new class of security-minded software, hardware, and services. If the corporate-government surveillance news did anything, at the very least it made more people aware of just how precious and fragile their own privacy and anonymity are.

In the meantime, by all means keep insisting on HTTPS, just don’t be lulled into a false sense of security.

August 16th, 2014

Posted In: Government, Internet / Web, Privacy / Surveillance

Tags: , , , , , , , , ,

Leave a Comment

I admit to having more than a soft spot for Adobe’s technology, but that’s not why I was so peeved when Steve Jobs made his much ballyhooed announcement that Flash on mobile was just the worst thing since Hitler.

For starters, much of Jobs’ characterization was just plain wrong. His problems with Flash not being “open” were gloriously misinformed as Adobe’s open source efforts, which included Flash and Flex, plus many of the video, audio, and networking technologies they contain, were already well established as open source technologies at that point.

If the argument is that the Flash virtual machine isn’t open source, one can easily turn that around and correctly state that Safari, the virtual machine for HTML5 / JavaScript, is also not open source. To say that Apple has been a champion of giving people choice is not exactly true, so claiming that they’re championing HTML5 “for the people” is equally as dubious. In fact, HTML5 itself is not as open as some may claim.

Jobs’ insistence that HTML5 is a “standard” was equally incorrect, as the myriad of sites, services, and projects that have sprung up trying to either work around or at least document the many incompatibilities, quirks, and just old plain lack of support in the HTML5 “standard” attest to. This lack of standardization is so pronounced that it alone can be used to successfully identify individual browsers and de-anonymize users (perhaps even much worse).

Jobs didn’t think Flash was needed to access video since HTML5 formats like H.264 (MP4) were so ubiquitous. On this point I’d have to agree, but that was never the sole reason that Flash became synonymous with online video. The reason Adobe’s player won out was because of a robust server architecture that, coupled with Adobe’s edge Content Delivery Networks, makes the technology a viable solution. Delivering MP4 streams over HTTP to browsers from a server farm is a lot more prohibitive, with cost being just one factor.

In his Flash critique, Jobs made the unsubstantiated claim that Flash was the number one reason why Macs crashed, a statement he never bothered to back up and which, as far as I know, was purely fictional or at best incredibly scape-goaty.

While Flash wasn’t a great mobile experience, mostly because it wasn’t set up to both deal with mobile gestures and co-exist on a web page, the same could be said of unresponsive or badly designed HTML5 sites. And whatever shortcomings the Flash player had, I’m sure they could have been addressed.

I’m not the only person to think that Flash was done away with unjustly. Amazon is trying to revive it for their devices, BlackBerry never lost it, and it Android can’t quite seem to shake itself of it. Judging by the popularity of the “How do I manually install Flash on my mobile device?” topic over at Adobe — 24.7 million views as of today– Flash is far from fading away.

The biggest injustice in this debacle, however, is the one I see being imposed on web developers.

As someone who has worked in ActionScript 3 for years, going to HTML5 / CSS3 / JavaScript was like taking a step ten years into the past. The same issues we were dealing with in Flash back then — performance, load times, code organization — are all back with a vengeance.

Even something as basic as frame-based animation is only partially supported in most modern HTML5-capable browsers with most requiring reams of code and workarounds for “full compatibility”.

Rudimentary sound support, along with the same issues we were grappling with in Flash many years ago (synchronization, maximum mixing channels, etc.), has made it’s way into HTML5. Instantly, developers learned that the web “standard” wasn’t quite up living up to its name.

Companies too have learned that they prematurely jumped on the HTML5 bandwagon and have had to re-think their approaches.

But it’s not that HTML5 is terrible.

In fact, as devices get faster and capable of handling more complex code, the mobile browser will slowly evolve to become it’s own robust virtual machine, much as Java and the Flash player are now. Eventually, much of what is today erroneously called a “standard” will actually become one, mostly, give or take, across most browsers. But we’re not even close to getting there yet.

Still, now that I’ve had time to truly immerse myself in HTML5 and experience how it works across browsers and devices, I see the potential for a much improved web experience.

As I mentioned, HTML5 to me seems like Flash circa 2003, around version 7 , with the additional complexity of browser fragmentation. Under the hood, there isn’t much that close-to-metal developers have at their disposal. Execution speed, JavaScript functionality, even things like error handling, are up to the whims of the browser manufacturers. jQuery definitely helps a lot, but cross-browser compatibility requires that you don’t push things to the bleeding edge.

Up front, the only real improvements are in the tools available for expression — better ways to animate, addition of sound and audio, improved font handling, dynamic drawing APIs, and so on. This can all be put together to produce nice, expressive web sites and games, examples of which are more quite numerous these days. I’ve even done a couple myself. And it’s because of this exposure that I can’t recommend HTML5 for anything other than nice, expressive sites and somewhat basic interactive games. The so-called “standards” required for more demanding code are simply not up to snuff and are more often a hindrance than a help.

The fact that HTML, regardless of version, is still the de facto markup language, and that JavaScript is still the de facto functional language, means that they simply aren’t likely to go away any time soon. That’s not necessarily a bad thing, but we really should be checking our (often deluded) exuberance about the browser technologies at the door.

August 14th, 2014

Posted In: Development / Coding, Internet / Web

Tags: , , , , , , ,

Leave a Comment

I want to jump right into this topic because it’s neat and interesting and fun, but there are a couple of things we need to set up first.

Sorry.

But I promise it’s not going to kill you.

Step 1 of 3 — get yourself a RTMFP rendezvous server. Absolute must unless you’re only communicating over a LAN or Wireless LAN (then it just works by magic).

Some options:

  1. If you prefer free and in control, grab the open source Windows command-line version of OpenRTMFP/Cumulus http://jazzmatazz.free.fr/Cumulus/CumulusServer.zip
  2. If you’re feeling adventurous, you can also compile the C++ source code for your own setup:  https://github.com/OpenRTMFP/Cumulus
  3. If you’re not willing to muck around with a command prompt or a compiler, sign up for a free Cirrus / rtmfp.net account at:https://www.adobe.com/cfusion/entitlement/index.cfm?e=cirrus
  4. If you’re able and willing to plunk down a cool wad, go with Adobe’s Media thingie: http://www.adobe.com/ca/products/adobe-media-server-family.html

Step 2 of 3 — grab a copy of SwAG and have a quick look at how SwAG events work; the P2P stuff uses these kinds of events to communicate, so it’ll come in handy.

Step 3 of 3 – now the good stuff — write your application!

SwAG’s most important class is swag.network.SwagCloud. I chose the name because it sounds nice and fluffy and amorphous. A SwagCloud is an enhanced, self-managing NetGroup, if you want to get technical (but you don’t need to).

Creating a SwagCloud is as simple as creating an instance, adding a listener to it, and then launching the new connection on its way.

Here’s a shortened version of how that might look in real life…first we declare some variables (or you can always hard-code these if you like):

private var _RTMFPServerAddress:String="rtmfp://p2p.rtmfp.net/"; //Or rtmfp://127.0.0.1/ if running Cumulus locally
private var _developerKey:String="SOMEVALUE"; //Get this via the Cirrus sign-up process; ignore (or set to empty) if using Cumulus
private var _group:SwagCloud=null;
private var _groupName:String="MyTotallyUniqueGroupName123";
private var _groupPassword:String="Members0nly!";
private var _groupHash:String="E><tr4$3curity";

…then we set up a few useful listeners and start up our group:

SwagDispatcher.addEventListener(SwagCloudEvent.GROUPCONNECT, this.onConnect, this, this._group);
SwagDispatcher.addEventListener(SwagCloudEvent.BROADCAST, this.onBroadcast, this, this._group);
SwagDispatcher.addEventListener(SwagCloudEvent.DIRECT, this.onDirectMessage, this, this._group);
this._group=new SwagCloud(this._RTMFPServerAddress, this._developerKey);
this._group.connectGroup(this._groupName, true, this._groupPassword, this._groupHash, false);

…and then we define the listeners to do some stuff we want:

public function onConnect(eventObj:SwagCloudEvent):void {
 trace ("We're connected to the group.");
 ...
public function onBroadcast(eventObj:SwagCloudEvent):void {
 trace (eventObj.remotePeerId+" broadcast this to the group: "+eventObj.data);
 ...
public function onDirectMessage(eventObj:SwagCloudEvent):void {
 trace (eventObj.remotePeerId+" sent this directly to me: "+eventObj.data);
 ...

…and that’s it.

As I mentioned earlier, if you wanted to hard-code variables right into the functions, you could shorten this example by a third. And if you only wanted to create a “beacon” type of app that didn’t need to listen to broadcasts or direct messages, you could further cut the example by another two-thirds.

If you store peer IDs when they connect to the group (another event you can listen to), sending an encrypted message directly to a peer is as easy as:

var someData:Object=new Object();
someData.message="Hello there!";
this._group.send(someData, peerID);

…and broadcasting to all peers even easier:

var someData:Object=new Object();
someData.message="Hello there!";
this._group.broadcast(someData);

You don’t need to create an object for the data, I just do that for extensibility. The following is also perfectly valid:

var someData:String="Hello there!";
this._group.broadcast(someData);

…or even:

this._group.broadcast("Hello there!");

“But hang on, Patrick,” you say astutely,” isn’t this just an overlay on top of the built-in RTMFP networking?”

Yup, that’s pretty much exactly what it is. It doesn’t do anything that RTMFP don’t does, it just simplifies things a lot.

Just have a look at the guts of SwagCloud to see what I mean. A from-scratch NetGroup connection, for example, is a two-step process; this is handled in SwAG and simplified to one step. Even something as seemingly trivial as streaming peer-to-peer audio via a NetStream.publish call is likely to encounter a microphone security dialog — handled by SwAG. Plus, SwagCloud takes care of converting native data types for transmission (I’ve even successfully transferred prototype MovieClip instances extracted from running code, believe it or not), so you may get some useful info out of it even if you’re already RTMFP-experienced.

In other words, if you haven’t worked much with RTMFP and/or want to skip the hassle of getting it up and running (i.e. you’d rather focus on doing something with it), I’d recommend having a look at SwAG’s SwagCloud class as well as its supporting classes like SwagCloudData, SwagCloudShare, and SwagCloudEvent.

So what exactly can you do with RTMFP and, by extension, with SwAG?

  • Any type of low-latency, peer-to-peer chat or gaming application one could care to imagine.
  • A one-to-many video or audio (or both) broadcasting system. The RTMFP server only takes care of connecting peers to each other, the streams are then distributed and multiplied peer to peer. If you host the RTMFP server on the same computer or device, you can stream to many thousands directly — no third-party needed. Read more about how this works in SocialCastr.
  • A distributed file sharing service. Despite the contrary claims that Adobe makes on the subject, SwAG has an entire class that proves that this is very possible. Adobe doesn’t make it super easy to use though, hence SwAG.
  • A distributed computing platform. Distributing a module designed to run concurrent calculations (Bitcoin mining, for example), is simply a matter of sharing the “workhorse” SWF with your peer group (SwagCloud instance), and having them run it with some custom startup parameters. Or that module could be part of a more static, custom-built application; that’s a perfectly valid approach too, albeit not as flexible in the long run.
  • An overlay network. If enough peers participate in it, many of the above features could be combined into a very robust overlay network that could have many of the features of something like Tor. Or BitTorrent. Or Hulu. Or maybe with the assistance of dedicated peers, a new type of content delivery network.

The (personally tested) possibilities of RTMFP are tough to shrug off, but at the same time they seem somewhat secondary to Adobe who seem content to leave some of the detailed implementation questions to developers. For example, object replication (peer-to-peer data sharing) is still somewhat of a black art, and even something as basic as a NetGroup connection event is not well-advertised as actually coming from the NetConnection object. So as I mentioned earlier, even if you don’t end up using SwagCloud, hopefully you’ll be able to use what I learned in what was often good old-fashioned trial-and-error to build your exciting new p2p products.

August 13th, 2014

Posted In: Development / Coding

Tags: , , , , , ,

Leave a Comment

First of all, SwAG isn’t for everything.

There are many things that Adobe and other excellent developers have done that I don’t think needs any improving.

Standard events, for example, are pretty rock-solid:

this.addEventListener (Event.ENTER_FRAME,  this.frameLoop);

And event listeners are easy to set up:

private function frameLoop (eventObj:Event):void {
   trace ("Another frame has just elapsed...");
}

It works great … why mess with it?

But there are some obvious limitations.

First of all, you need to attach, or couple, your event to something. In the above example, it’s “this”, which would be the equivalent to:

addEventListener (Event.ENTER_FRAME,  frameLoop);

The default is “this” unless otherwise specified, so a source is always specified.

In SwAG, the source is optional. Listeners are created through the swag.core.SwagDispatcher class:

SwagDispatcher.addEventListener(SwagEvent.SOME_EVENT, this.eventResponder, this);

There’s an extra parameter at the end where you specify the “this” object in which the event listener will run (that’s not what you’re listening to!)

I usually add it, and 99% of the time it’s “this”, but I think it’s optional anyway.

SwAG listeners look pretty much the same:

public function eventResponder(eventObj:SwagEvent):void {
   trace ("I got that event!");
}

STOP! READ! NOW! IMPORTANT!

SwAG event listeners MUST BE PUBLIC!

For example, the following function would never be called when the associated event fires:

private function eventResponder(eventObj:SwagEvent):void {
   trace ("I will never get that event!");
}

Nothing will crash terribly if you do this, I should mention, it just won’t work.

Dispatching an event can be done from anywhere with relative ease:

var newEvent:SwagEvent=new SwagEvent(SwagEvent.SOME_EVENT);
SwagDispatcher.dispatchEvent(newEvent, this);

One upshot with SwAG here is the fact that you don’t need to sub-class anything (like the EventDispatcher class, for example).

If you want to make things more interesting, there’s nothing holding you back from extending EventDispatcher either.

And that’s what decoupled, or source-optional means. The listeners are not attached to, tied to, associated with, coupled to, or otherwise source-specific.

But they can be, with an extra optional parameter:

var sourceObject:YourOwnClass=new YourOwnClass();
SwagDispatcher.addEventListener(SwagEvent.SOME_EVENT, this.eventResponder, this, sourceObject);

Your own class can dispatch events into the æther and any part of the same application context can listen for them.

In practical terms that means one event listener for any number of event broadcasters, unlike the standard ActionScript one-to-one ratio. Here’s what I mean…standard ActionScript 3:

public function eventListener(eventObj:Event):void {
}
for (var count:uint=0; count<1000; count++) {
   var newObj:YourOwnClass=new YourOwnClass();
   newObj.addEventListener(Event.ENTER_FRAME, this.eventListener);
}

…and the same in SwAG:

public function eventListener(eventObj:SwagEvent):void {
}
SwagDispatcher.addEventListener(SwagEvent.SOME_EVENT, this.eventListener, this);
for (var count:uint=0; count<1000; count++) {
   var newObj:YourOwnClass=new YourOwnClass();
}

Subtle, but I find it useful.

And you can create listeners for events for objects that don’t exist yet. I also find that useful.

Other subtleties can be found in the SwagDispatcher class and the few helper classes that accompany it. Other parts of SwAG make use of this event system, so it’s worth a look.

 

February 5th, 2014

Posted In: Development / Coding

Tags: , , , , ,

Leave a Comment

Yeah,  application frameworks are a dime a dozen these days. That’s why SwAG isn’t a framework but rather a toolkit — the Swiss Army Gear toolkit for ActionScript 3, to be exact.

As the name implies, this small-ish library contains useful code that can be employed wherever needed, as needed, without the full weight or presumptuousness of a full-on framework. In other words, it should be useful for you whether you have a project that’s well under way or are just starting out.

Because the library has lots of disparate features, I’ll be dedicating a number of future posts discussing them in detail and providing (hopefully) useful examples. Here is a brief preview:

  • Peer-to-peer networking, streaming, data sharing using RTMFP (via swag.network.SwagCloud and swag.network.SwagCloudShare)
  • Decoupled or source-optional event broadcasting (via swag.core.SwagDispatcher)
  • Extended MovieClip playback control (via swag.core.instances.SwagMovieClip)
  • SQLite database functionality for Adobe AIR (via swag.core.instances.SQLiteDatabase)
  • Extended date/time manipulation and comparison functionality (via swag.core.instances.SwagDate and swag.core.instances.SwagTime)
  • Extensive data analysis/conversion functionality (via swag.core.SwagDataTools)
  • ZIP data access and extraction (via swag.core.instances.SwagZip)
  • Extended runtime introspection (via swag.core.SwagSystem)
  • Extended HTTP/HTTPS data loading functionality (via swag.core.instances.SwagLoader)

Some of these are used extensively in SocialCastr and Araknid and they definitely made my life easier. SwAG doesn’t presume to be the only code running so adding it or removing it, mid-project or otherwise, is easy and straightforward. I’ve chosen to not include it in pure AS3 projects like TorAS or WRASE because they’re much more useful as autonomous libraries. Applications, however, are meant to be used rather than incorporated into other projects, so in those cases I happily stick SwAG into every nook and cranny.

SwAG had additional functionality which has been yanked since these make way more sense as individual projects:

  • BMPImage, for loading and displaying Windows Bitmap (BMP) images
  • RSSChannel, for reading and manipulating RSS feed (XML) data
  • XLSXFile, for reading and manipulating a Microsoft Office Open XML Format Spreadsheet (Excel) file

I don’t know if / when I’ll get around to updating and publishing these but I’m not averse to sharing if you want to get in touch with me directly.

Otherwise, grab yourself a copy of the pretty-well-documented toolkit here: https://code.google.com/p/swag-as/source/browse/#svn%2Ftrunk
…and stay tuned for SwAG fun and frolic.

January 29th, 2014

Posted In: Development / Coding, Internet / Web

Tags: , , , , , , , ,

2 Comments

Next Page »