Wednesday, September 20, 2017

Beware of THIS in Javascript

Coming to Javascript from other object oriented languages can be frustrating due to its alien syntax structure.  I was told and have read that a common hang up is with the this construct behavior in Javascript.  So, naturally, I fell for it right off the bat.

I was staring at the following code and everything looked fine:


    update () {
        
        this._renderer.render(this._world);

        requestAnimationFrame(this.update);
    }

But alas when run I was encountering the dreaded "Uncaught TypeError: Cannot read property '_renderer' of undefined." This is because this in Javascript does not retain its original context when called from a callback.

The most concise description of the problem I could find came from TypeScript's github here: https://github.com/Microsoft/TypeScript/wiki/'this'-in-TypeScript

This is where some of the new ES6 syntax comes in handy. This still feels alien and unnatural to me coming from AS3/C# as it should "just work right?"

So I chose to use the fat arrow approach to the problem so that update could be called as a callback and landed on this as a solution:



    public update = () => {
        
        this._renderer.render(this._world);

        requestAnimationFrame(this.update);
    }

Flash Refugee finds love with... JavaScript?

I was late to the Flash/Actionscript party when I first got involved with it for a Facebook game-startup back in 2010.  By this time AS2 had fallen out of favor and AS3 was becoming the standard.  Code snippets directly on the Flash file timeline (a horror of potential pit falls) fell to an object oriented language that gave "system level" control to the programmer (as far as byte code can be called "system level").

I was aware of Javascript at this time but avoided it feverishly due to its reputation as a "script kiddie" adventure in terrible coding standards.  Code snippets I read violated most of the coding standards I had developed throughout my career (in-line functions, weakly typed, dependent on 3rd party libraries like jQuery to do data queries etc) so I could not consider it a viable option for any kind of client development.

Actionscript 3, as it turns out, was a fork of ECMAScript 4  and was being championed as a standard by Adobe and ex-Microsoft employees at BEA Systems.  This, of course, was doomed to failure as AS3 could not converge to the current state of Javascript.  In April of 2010, the very year I was discovering and falling in love with AS3/Flash, Steve Jobs published his "Thoughts on Flash" which would be the beginning of the end for Flash. I, however, was blissfully unaware of these goings on and happily delved into the Facebook Gaming startup I was with to develop "Cruise Time" for Facebook with Flash/AS3.

When the startup folded, my love for AS3 could not be diminished and I pursued it faithfully at Williams Interactive (developing Flash-based "Goldfish Casino") and then subsequently in mobile by leveraging Starling and Adobe AIR at Jam City (developing Juice Jam, then leading teams for the subsequent Family Guy: Another Freaking Mobile Game titles).  All of this, I knew, was on borrowed time because of the steadily diminishing Flash community.

Unity was the only viable option to Flash during this period, but I did not favor it for similar reasons I didn't care for AS2.  Unity inverted the game development paradigm by basically providing an editor very much like Flash, where code could be attached to game objects.  This was too similar to "coding on the timeline" in the AS2 days for me to adopt Unity as the clear alternative to pure-actionscript AS3/Flash development.

Scroll forward to 2017 and my time at Jam City came to an end and I was left with a feeling of "what could be next?"  Previously I had dismissed HTML5 as premature as it could not yet do all of the things Flash/AS3 could do without a myriad of third party libraries.  But I was impelled to look again when I found that even the old Flash Guru Rex van der Spuy had converted to HTML5 and had endorsed PixiJS as the way forward.

Seeing that PixiJS had adopted the container concepts of AS3 made it seem like a very easy choice to convert from AS3 to Javascript.  The only thing wrong with it was, well, Javascript.  It was still this weakly typed script-style language that I could not embrace for large gaming projects.  But PixiJS was so intriguing  I could not give up on it.

Alas the answer was in TypeScript. TypeScript brings to Javascript what it had always lacked, strong typings, object oriented programming capabilities with class and modular programming concepts. Timed perfectly, Visual Studio Code was release in 2016 to make this transition easier and I could finally see the way forward from Flash/AS3 development.

As a predominantly 2D gaming enthusiast finding this combination of technologies in the sea of JavaScript frameworks gave me a renewed vigor for game development.  With technologies like Ludei/Cocoon and Cordova to bring HTML5 games down to mobile as hybrid apps further confirmed that HTML5 had finally arrived and was the way forward for cross platform game development.

So, here on this blog, I will attempt to catalog my progress as a Flash/AS3 refugee converting to Javascipt.  Javascript is the "assembly language of the web" and the filter through which you view it is the best way forward.  For me that filter is TypeScript.


Thursday, October 6, 2011

Bye Bye Heroku, PHPfog Phail, Hello my old Friend Amazon Cloud Formation

So my investigation of the Heroku platform failed at the database access point.  Though Heroku supports PHP it does not support the mysqli class and therefore cannot be used for my PHP/MySQL project :(  Unless I switch to Ruby!  I'm far too long in the tooth to switch development languages so the decision to ditch Heroku was made!

Before giving up completly on the distributed application model I investigated PHPfog to see if it could help where Heroku failed.  The description of their services sounds wonderful and exactly what I would need, basically an abstracted PHP environment distributed ontop of an Amazon Cloud EC2 backbone.  However, when I created a test application in their "shared cloud" environment it failed to ever complete its initial configuration. So, yeah, its broken! lol

All of this effort and time investigating distributed applications was really just my typical "path of least resistance" mentality and my general fear of maintaining Linux servers.  However, it simply cannot be avoided.  The level of control and removal of the "middle man" makes the best financial sense and leaves me with the most control over my application.

So back to the Amazon AWS services I went to investigate topologies for my application.  In my search I found this very useful link describing the implementation of a Facebook stack right in the AWS Dashboard: http://aws.amazon.com/articles/1044

This page shows the ideal topology for a Facebook app:



Looking at this picture made me realize a fundamental flaw in how I delivered Cruise Time.  Cruise Time operated on a single EC2 instance which had the PHP application and executable .SWF file on it.  As the game grew we only increased the size of the ECU to scale to demand.  Since the game peaked at only 125,000 users it never stressed out the single EC instance. 

To distribute this across many multiples of EC2 instances in a high traffic situation would require the entire EC2 image to be saved as AMI and allowed to scale within the Auto Scaling Group in the picture above.

So the new approach would have to divorce as much as possible from the scalable EC2 instances so they can be scaled on demand and put the deliverables off in their own space.  That's S3 in Amazon parlance, their file storage cloud.  Putting music files, the game executable, and other data files on S3 would allow the PHP application to swing freely with scalable demand while reading the executable from S3 at a known location.

What this does is allows me to push new code changes in the EXE/SWF straight to S3 without having to go and modify the AMI for the EC2 instances or some other crazy live-update scheme on running EC2 instances of the application.

The down side is that this is not true for the PHP application.  Any changes to the PHP application WOULD necessitate an update to the AMI.  So now the problem becomes: how do I make the PHP application dynamic enough that changing it wont cause a lot of AMI updates or downtime on the app?

Ah the joys of software design :)

Monday, October 3, 2011

redirect_uri gotcha!

I couldn't leave it alone! So I added the authorization code to the index.php file from my previous post.  Here is the latest code:

$config = array();
$config['appId'] = $app_id;
$config['secret'] = $app_secret;
$config['fileUpload'] = false; // optional

$facebook = new Facebook($config); // constructor will start the PHP session
$user = $facebook->getUser();
if ($user) {
   try {
       $user_profile = $facebook->api('/me');
   } catch (FacebookApiException $e) {
       error_log($e);
       $user = null;
   }
} else {
   //
   // no user! attempt to authorize using new OAuth2.0 garbage
   //
   $params = array(
       'scope' => 'publish_actions, email',
       'redirect_uri' => 'http://apps.facebook.com/piratestradewinds/',
       'canvas' => 1
   );

   $loginUrl = $facebook->getLoginUrl($params);
   // redirect to login
   echo("<script> top.location.href='" . $loginUrl . "'</script>");

}

The problem encountered when adding the code for when $user == null was in the redirect_uri parameter for the call to getLoginUrl().  The desired behavior is after the authorization, the user is redirected to your app within facebook.  DO NOT use the Canvas URL but instead use the "Canvas Page" from your app's basic settings page. 

AND! Don't forget the trailing slash as well else youll get the infamous "URL does not belong to your application" error from Facebook.


Now this knowledge will live forever, here, on the internet... Until Facebook changes everything AGAIN, in 2 days.  Maybe they're changing it right now! /paranoia  /sigh

Heroku Facebook PHP Canvas App!

Well today I decided to try my hand at launching the framework for my newest Facebook app using Facebook's new cloud computing partner, Heroku.

My apps launched to date have all been PHP/Javascript on the server side and Flash Clients.  Right off the bat I realized Heroku is a Ruby on Rails shop and should have thought then "Oh I'm in for it."  Apparently they made an exception for Facebook and support PHP applications, but ONLY if they are Facebook apps generated from the Facebook Developer app.

So I get started by simply following the instructions at the Facebook blog page.

This all worked flawlessly of course, if I were a Web site and not an APP! /doh  So I need to modify the thing! Facebook tells me to go to Heroku's website and follow their instructions.

Following their instructions I break down at the "git clone" step of their command line interface, failing with the following:

Permission denied (publickey).
fatal: The remote end hung up unexpectedly

Wonderful!  Long story short, the public key files created by the initial log on step were saved off in Users/User/.ssh and not in the /Git subdirectories.  So I simply copied the private and public rsa files to Git/.ssh and it was finally able to do pushes to my new app on Heroku! Yay!

But now, on to the Facebook integration.  I haven't done any Facebook coding since I first integrated Cruise Time last year so it felt like a time warp back to the desperate "oh shit" moments when I had to launch Cruise Time in 2 months and had not the faintest idea about facebook haha.

I cut and pasted alot of different, and conflicting examples from many places in Facebook's developer documentation with little success.  I finally decided to stop beating around the bush and go right to the source, literally, and just headed to https://github.com/facebook/php-sdk to get the offical and latest Facebook PHP SDK.  From the examples there I was able to publish my shiny new bootstrapping index.php to Heroku as follows:

<?php
// Enforce https on production
//if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == "http" && $_SERVER['REMOTE_ADDR'] != '127.0.0.1') {
//  header("Location: https://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]);
//  exit();
//}

require_once("php-sdk/facebook.php");

$app_id = getenv('FACEBOOK_APP_ID');
$app_secret = getenv('FACEBOOK_SECRET');

$my_url = "http://pottw.herokuapp.com/index.php";

$config = array();
$config['appId'] = $app_id;
$config['secret'] = $app_secret;
$config['fileUpload'] = false; // optional

$facebook = new Facebook($config);
$user = $facebook->getUser();
if ($user) {
   try {
       $user_profile = $facebook->api('/me');
   } catch (FacebookApiException $e) {
       error_log($e);
       $user = null;
   }
} else {
   $loginUrl = $facebook->getLoginUrl();
   // redirect to login
}


?>
<html>
    <head>  
    </head>
    <body>
        <?php if ($user): ?>
        <h3>You</h3>
        <img src="
https://graph.facebook.com/<?php echo $user; ?>/picture">
        <h3>Your User Object (/me)</h3>
        <pre><?php print_r($user_profile); ?></pre>
        <?php else: ?>
        <strong><em>You are not Connected.</em></strong>
        <?php endif ?>
    </body>
</html>


But when I tried to run this, PHP threw the ol' "invalid file" error.  I had copied the Facebook sdk into a subdirectory of my app tree and it was not automatically included in the commits.  More web browsing and I found this gem of a resource: http://spheredev.org/wiki/Git_for_the_lazy .  There I learned the command-line options to add a directory to my git source control (I'm a windows guy for 20+ years, so getting me to do command line work is really something! haha). 

To my sincerest pleasure, Git was able to commit and push all of my new lovely source files, including the Facebook SDK to my shiny new Heroku app and on running it was rewarded with the output:

You


Your User Object (/me)

Array
(
    [id] => 1021302301
    [name] => Dean Dino Gibson
    [first_name] => Dean
    [middle_name] => Dino
    [last_name] => Gibson
    [link] => http://www.facebook.com/DrumDinoX
    [username] => DrumDinoX
    [location] => Array
        (
            [id] => 
            [name] => 
        )

    [bio] => The Philanthropy of Metal!

Drummer/Producer for Age & Treachery by night...

On-line coder-extraordinaire by day!
    [work] => Array
        (
            [0] => Array
                (
                    [employer] => Array
                        (
                            [id] => 140261279380556
                            [name] => Dracos Software
                        )

                    [location] => Array
                        (
                            [id] => 107602962595896
                            [name] => Cypress, Texas
                        )

                    [position] => Array
                        (
                            [id] => 108323085888596
                            [name] => Principal Engineer
                        )

                    [description] => Independant Gaming Contractor focused on the Facebook space.
                    [start_date] => 2011-09
                    [projects] => Array
                        (
                            [0] => Array
                                (
                                    [id] => 250060951707316
                                    [name] => Code name "PvP"
                                )

                        )

                )

            [1] => Array
                (
                    [employer] => Array
                        (
                            [id] => 105336196171423
                            [name] => A Little Entertainment
                        )

                    [location] => Array
                        (
                            [id] => 106308306067511
                            [name] => The Woodlands, Texas
                        )

                    [position] => Array
                        (
                            [id] => 108323085888596
                            [name] => Principal Engineer
                        )

                    [with] => Array
                        (
                            [0] => Array
                                (
                                    [id] => 1597972162
                                    [name] => Van Collins
                                )

                        )

                    [description] => Facebook games!
                    [start_date] => 2010-01
                    [end_date] => 2011-08
                    [projects] => Array
                        (
                            [0] => Array
                                (
                                    [id] => 159463290798105
                                    [name] => Space Trader Joe
                                    [start_date] => 2011-03
                                    [end_date] => 2011-08
                                )

                            [1] => Array
                                (
                                    [id] => 105315876173199
                                    [name] => Cruise Time
                                    [start_date] => 2010-09
                                    [end_date] => 2011-03
                                )

                        )

                )

            [2] => Array
                (
                    [employer] => Array
                        (
                            [id] => 106032226094178
                            [name] => New World Computing
                        )

                    [start_date] => 1998-08
                    [end_date] => 2003-08
                )

        )

    [education] => Array
        (
            [0] => Array
                (
                    [school] => Array
                        (
                            [id] => 106362256066529
                            [name] => Point Loma Senior High
                        )

                    [type] => High School
                )

            [1] => Array
                (
                    [school] => Array
                        (
                            [id] => 108727889151725
                            [name] => George Washington University
                        )

                    [year] => Array
                        (
                            [id] => 142673469093081
                            [name] => 1993
                        )

                    [type] => College
                )

        )

    [gender] => male
    [timezone] => -5
    [locale] => en_US
    [verified] => 1
    [updated_time] => 2011-09-29T15:40:25+0000
)

So there you have it.  My hair-greying attempt at bootstrapping a new Facebook app.  The next step is to improve the redirect logic to handle the uninstalled user and automatically perform the OAuth2.0 authentication step and then I can move on and think about a better program flow for the PHP application for my new game code-named "PvP."

Tuesday, September 27, 2011

Loosly Coupled Server Query List Manager for AS3

Well, after much deliberation, the solution was to stick with an external list manager to control Server Queries.  Adding the list management to the ServerQuery class module proved to be more trouble than it was worth since each query would need to track its own status and timing.  This could be better served with an external management class.

All that was needed to support an external manager was a simple callback function to allow the manager to know when queries finished execution.

Here is the pre-test code for the ServerQuery and ServerQueryList classes:

 import flash.events.*;
 import flash.net.*;

 //
 // a static helper class to queue queries to the server, all in one place... reports average server response time as a bonus!
 //
 public class ServerQueryList {
 
  static var serverQs : Array = new Array(); // array of ServerQuery objects
  static var _networkLatency : int = 0;  // the average response time from the server
 
  function ServerQueryList() {
 
  }
 
  // request data from the server... takes URLstring and callback function as paramaters
  static function QueryServer(req:String, callback:Function, bMeasureLatency:Boolean=false) {
   // create a new ServerQuery object
   var sq : ServerQuery = new ServerQuery(req, callback);
  
   // set the callback to our private function
   sq.setListCallback(sqCallback);
  
   // push it onto our list
   newindex = serverQs.push(sq);
  
   // pass along the index... push actually adds to end of array not to beginning ala a stack... confusing function name there
   sq.setIndex(newindex);
  
   sq.Load(); // do it!
  
  }
 
 
  private function sqCallBack(target:ServerQuery) {
   // our target has returned...
  
   // record the round trip time and then delete it from the array
  
   var sqIndex = target.getIndex();
  
   if (sqIndex != -1 && sqIndex < serverQs.length) { // validate index
    accumulateLatency(ServerQuery(serverQs[sqIndex]).getResponseTime());
   
    //do the cleanup!
    ServerQuery(serverQs[sqIndex]).kill();
   
    delete serverQs[sqIndex];
    serverQs[sqIndex] = null;
   
    serverQs.splice(sqIndex, 1);
   }
  
  
  }
 
  private function accumulateLatency(newResponseTime:int) {
   // add the response time to our aggregate response time
   if (_networkLatency != 0)
    _networkLatency = (_networkLatency + newResponseTime) / 2;
   else
    _networkLatency = newResponseTime;
  }
 
 
 } // end ServerQueryList


 import flash.events.*;
 import flash.net.*;


 //
 // actually gunna leave this one as a generic query class.. will make a queue class to manage these
 // a helper class to queue queries to the server, all in one place... reports average server response time as a bonus!
 //
 public class ServerQuery {
 
  private var _urlloader : URLLoader;
  private var _callback : Function; // supplied by caller
  private var _urlrequest : URLRequest;
 
  private var _listCallback : Function; // supplied by query list manager
 
  private var _responseTime : int;  // milliseconds for round trip
  private var _opened : Number;   // time when connection opened
  private var _closed : Number;   // time when response has arrived
 
  //TODO: add timer for timeout failure
 
  private var _debugStr : String;
 
  private var _index : int;     // the index of this query within the external list of queries
 
  public function ServerQuery(req:String, callback:Function) {
   _debugStr = "";
   _urlloader = new URLLoader();
   configureListeners(_urlloader);
   _urlrequest = new URLRequest(req);
   _callback = callback;
   _urlloader.addEventListener(Event.COMPLETE, CTQueryPreCallback);
   _responseTime = -1;
   _index = -1;
   _listCallback = null;
  }
 
  public function setListCallback(newFunc:Function) {
   _listCallback = newFunc;
  }
 
  public function setIndex(newIndex:int) {
   _index = newIndex;
  }
 
  public function getIndex() : int {
   return _index;
  }
 
 
  public function getResponseTime() : int {
   return _responseTime;
  }
 
  public function Load() {
   try {
    var date : Date = new Date();
    _opened = date.getTime();
    _urlloader.load(_urlrequest);
   } catch (error:Error) {
    _debugStr += "ServerQuery: Unable to load requested document";
   }  
  }
 
  public function getDebugReport() : String {
   return _debugStr;
  }
 
  public function kill() {
   // do some cleanup
   delete _urlloader;
   delete _urlrequest;
  }
 
  private function CTQueryPreCallback(e:Event) {
   deconfigureListeners(_urlloader);
  
   var date : Date = new Date();
   _closed = date.getTime();
  
   _responseTime = _closed - _opened;  
  
   // inform the list manager, if any, we have returned.. pass it our this pointer
   if (_listCallback != null)
    _listCallback(this);
   if (_callback != null)
    _callback(e); // call our stored callback
    

  }
 
     private function configureListeners(dispatcher:IEventDispatcher):void {
            //dispatcher.addEventListener(Event.COMPLETE, completeHandler);
            //dispatcher.addEventListener(Event.OPEN, openHandler);
            //dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler);
            dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
            dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
        }
 
  private function deconfigureListeners(dispatcher:IEventDispatcher) {
            dispatcher.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            dispatcher.removeEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
            dispatcher.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);  
  }
 
     private function securityErrorHandler(event:SecurityErrorEvent):void {
            trace("ServerQuery: securityErrorHandler: " + event);
   _debugStr += "securityErrorHandler: " + event + "\n";
        }

        private function httpStatusHandler(event:HTTPStatusEvent):void {
            trace("ServerQuery: httpStatusHandler: " + event);
   _debugStr += "httpStatusHandler: " + event + "\n";
        }

        private function ioErrorHandler(event:IOErrorEvent):void {
            trace("ServerQuery: ioErrorHandler: " + event);
   _debugStr += "ioErrorHandler: " + event + "\n";
  }
 
 } // end class

Cohesive vs Coupled Server Query List in AS3

Well an esteemed colleague of mine suggested I should make a "bad ass" blog about my trials and tribulations in software development. ;)

I am a sole proprietor in the games industry, off and on for 18+ years.  My latest passion is Flash and Actionscript programming.  In short, the Flash IDE and Actionscript largely "think like I do" where as previously in my career I have frequently struggled with the IDE and the "gotchas" I've associated with them.

Well without further adieu, today I'm developing an Actionscript class to handle server queries for my up and coming Facebook game codenamed "PvP".  During the development of Cruise Time I experimented with various ways of handling server queries as cohesively as possible but was frustrated by a lack of time to get it really "right."

PvP will require some pseudo-real-time elements in that the player will need to complete movement and firing orders within a set time limit orchestrated by a "heartbeat" on the server.  Each "turn" on the server will have a precise time limit and therefore the user must be aware of this heartbeat and must thus strive to get as many orders in as possible before "time is up."

Therefore, when making server queries the inherent network latency and server response time must be measured to properly estimate when "time is up" for each user.  For example, with a turn-time of 30 seconds, a user with a 5 second latency will need to complete all of his orders in 25 seconds to account for the round trip time of communication between client and server in order to keep all players on the same turn at any given moment.

But on to the coding right?  Here is one of the attempts at a generic query class I made for Cruise Time:


package
{
    import flash.events.*;
    import flash.net.*;
  
    public class CTServerQuery {
        private var _urlloader : URLLoader;
        private var _callback : Function; // supplied by caller
        private var _urlrequest : URLRequest;
      
        private var _debugStr : String;
      
        public function CTServerQuery(req:String, callback:Function) {
            _debugStr = "";
            _urlloader = new URLLoader();
            configureListeners(_urlloader);
            _urlrequest = new URLRequest(req);
            _callback = callback;
            _urlloader.addEventListener(Event.COMPLETE, CTQueryPreCallback);
        }
      
        public function Load() {
            try {
                _urlloader.load(_urlrequest);
            } catch (error:Error) {
                _debugStr += "CTServerQuery: Unable to load requested document";
            }          
        }
      
        public function getDebugReport() : String {
            return _debugStr;
        }
      
        private function CTQueryPreCallback(e:Event) {
            deconfigureListeners(_urlloader);
            if (_callback != null)
                _callback(e); // call our stored callback
        }
      
        private function configureListeners(dispatcher:IEventDispatcher):void {
            //dispatcher.addEventListener(Event.COMPLETE, completeHandler);
            //dispatcher.addEventListener(Event.OPEN, openHandler);
            //dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler);
            dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
            dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
        }
      
        private function deconfigureListeners(dispatcher:IEventDispatcher) {
            dispatcher.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            dispatcher.removeEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
            dispatcher.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);          
        }
      
        private function securityErrorHandler(event:SecurityErrorEvent):void {
            trace("securityErrorHandler: " + event);
            _debugStr += "securityErrorHandler: " + event + "\n";
        }

        private function httpStatusHandler(event:HTTPStatusEvent):void {
            trace("httpStatusHandler: " + event);
            _debugStr += "httpStatusHandler: " + event + "\n";
        }

        private function ioErrorHandler(event:IOErrorEvent):void {
            trace("ioErrorHandler: " + event);
            _debugStr += "ioErrorHandler: " + event + "\n";
        }  
      
    } // end class
} // end package


Each CTServerQuery handles its own error checking and event cleanups.  But now I would like to add the ability to queue all queries in one place and use the class as a cenral clearing house for all communication to the server.  Rather than the user instancing and using individual CTServerQuery classes ad hoc, I'd like the user to simply call into a singleton and request data from the server and simply wait for the call back response with success or failure.

My first stab at this was to leave CTServerQuery alone and make a new class that would simply manage a dynamic list of CTServerQuery objects as requests came in and, as a side effect, measure the inherent network latency and server response time as well.

This came out as:


package
{
    import flash.events.*;
    import flash.net.*;
  
  
    //
    // a helper class to queue queries to the server, all in one place... reports average server response time as a bonus!
    //
    public class ServerQueryList {
      
        private var queries : Array; // array of ServerQuery objects
      
        public function ServerQueryList() {
            queries = new Array();
        }
      
        // request data from the server... takes URLstring and callback function as paramaters
        public function QueryServer(req:String, callback:Function) {
          
        }
      
      
    } // end ServerQueryQueue
  
} // end package

That's as far as I got before realizing that ServerQueryList would be highly coupled to CTServerQuery and would be rather removed from where the action is happening.  For instance, a new callback would have to be added to CTServerQuery to inform ServerQueryList of callback activities.

So, rather than making a new highly coupled controller class, I am now thinking the list management should be a function of the CTServerQuery module and it will , itself, be a list managing and  server querying module resulting in a more cohesive all-in-one approach.

Well, that's all for now.  Stay tuned here to see how CTServerQuery ends up! ;)

Dracos