loading an RSL without using flex? - apache-flex

If I have rolled my own RSL, and I want to use it in my pure as3 apps, is there documentation or an example of how to do this?
Or do I need to traverse the flex source code to figure out what adobe's engineers have done?

This is a very tricky one, with lots to go into I'm afraid. Some pointers:
To get a class from an externally loaded SWF use the getDefinition method on an application domain e.g.
public function loadHandler(evt:Event):void
{
var loaderInfo:LoaderInfo = evt.target as LoaderInfo;
var clazz:Class = loaderInfo.applicationDomain.getDefinition("your.external.class");
}
This will give you the class definition if you know the name of the class you want.
To 'join' class domains into each other (so applications can compile against a swc, but not include the classes and load them externally) you need to specify a loaderContext of the same security domain.
var loader:Loader = new Loader();
var context:LoaderContext = new LoaderContext();
context.applicationDomain = ApplicationDomain.currentDomain;
loader.load(new URLRequest("library.swf"), context);
The third pointer I can give you is the compiler option "-external-library-path", use this to specify a list of swc's to compile time check against, but not include (resulting in a lower filesize).
mxmlc -source-path="dir/src" -external-library-path="dir/lib/framework.swc" --main.swf
Sorry I couldn't elaborate more, it's a very expansive topic, hope this gets you started....

Related

Meteor helper methods

I want to write some helper functions that I can use in my other JavaScript files.
It says here:
Some JavaScript libraries only work when placed in the client/compatibility subdirectory. Files in this directory are executed without being wrapped in a new variable scope.
It seems a bit bizarre to me that I should have to throw all my libraries in a folder called compatibility. Generally "compatible" stuff is for legacy code that hasn't been upgraded to the new style. Is there no way to export modules so that I can access them in my other files?
Using this pattern now:
Util = (function(exports) {
exports.getFileExtension = function(filename) {
var i = filename.lastIndexOf('.');
return (i < 0) ? '' : filename.substr(i);
};
// more functions
return exports;
})(typeof Util !== 'undefined' ? Util : {});
Not sure if that's the best or not...but it appears to work.
It would be bizarre, you are right. Write your own code, just put it somewhere and it works. This refers to complicated frameworks that make a lot of functions all over the place, where no one has 'tamed' them to only expose a root object that all its powers spring from.
Please read "Namespacing and Modules" at
http://www.meteor.com/blog/2013/08/14/meteor-065-namespacing-modularity-new-build-system-source-maps
It's helping you with built in maintainability for avoiding collisions with other things you write, which is largely what namespaces is for.
A good practice is to have your own helper object, named helper or util, where you put grouped things:
utils = {
distance_between: function(lat1,lng1,lat2,lng2) {
var radiusEarth = 3963.1676; // miles radius earth
var dLat = deg2rad(lat2-lat1); // deg2rad below
...
displayHumanReadableTime: function(timestamp){
var a = new Date(timestamp);
If the intention is to write Utility method then it can be written using the ECMA6 Script standard.
Write your method by exporting once in method.js and use it by importing in the desired file(s)
Ex:
export const MyUtilityMethod = function (){...} in /method.js
import {MyUtilityMethod} from './method.js'
Hope this helps.

How do i read embedded bytarray file?

I created a tile map editor for my game and it will generate a file when the user is done with the design. The file will store the assets used and other information.
this is the code on how i generate the file
var ba:ByteArray = new ByteArray();
var masterData:Object = { map:Data.instance.mapLayerArr,
asset:assetCollection,
gridrow:Data.instance.gridRow,
gridColumn: Data.instance.gridColumn,
cellWidth: Data.instance.cellWidth,
cellHeight: Data.instance.cellHeight,
assetCount: Data.instance.assetCount,
layerCount: Data.instance.layerCount,
version: Data.instance.version};
ba.writeObject(masterData);
ba.compress();
file = new FileReference();
file.save(ba, Data.instance.fileName);
problem starts when i want to embed the generated file inside my game.
this is the code in my program.
[Embed(source='../../../../res/tilemapdata/File Name', mimeType='application/octet-stream')]
public static const TileMapFile:Class;
public function TileMapLoader()
{
var byteArray:ByteArray;
byteArray = new TileMapFile();
byteArray.uncompress();
var obj:Object;
obj = byteArray.readObject();
trace(fileReference);
}
whenever i run it ends in "obj = byteArray.readObject();" and will display this error.
[Fault] exception, information=ArgumentError: Error #2173: Unable to read object in stream. The class flex.messaging.io.ArrayCollection does not implement flash.utils.IExternalizable but is aliased to an externalizable class.
You are using a strange class flex.messaging.io.ArrayCollection - try replacing all such imports with mx.collections.ArrayCollection.
Also make sure that all classes that are stored in file has [RemoteClass] metatag or they would be restored as Object instances.
A good read about the situation (adobe's official documentation): (Explicitly mapping ActionScript and Java objects)
I have experianced the same problem. General rules which help me to solve:
better to have explicitly declared metatag [RemoteClass] on your client-side actionscript classes.
collections of server-side classes (lists,arrays,etc.) easier to handle when represented client-side flash by mx.collections.ArrayCollection.
at some point you may need to explicitly declare client-side Flash class with the server-side class relationship by coding before any deserialization occures flash.net.registerClassAlias("net.acme.serverside.Foo", clientside.Foo ); otherwise your objects goes untyped as generic flash.Object after deserialization.
Check, that your ArrayCollection alias registered by this way:
import mx.collections.ArrayCollection;
...
registerClassAlias("flex.messaging.io.ArrayCollection", ArrayCollection);
instead of:
import mx.collections.ArrayCollection;
...
registerClassAlias("mx.collections.ArrayCollection", ArrayCollection);
There explanation:
http://livedocs.adobe.com/blazeds/1/javadoc/flex/messaging/io/ArrayCollection.html

Using asMock, how can I satisfy a concrete and interface requirement in SetupResult.forCall

The ValidationManager has a public Dictionary for storing UI components that implement the IValidatable interface.
I am testing a command class that needs an instance of ValidationManager and I want it to fail the validations. So I override the ValidationManager's "validateItem()" method like so:
var validationManagerRepos:ValidationManager = ValidationManager(mockRepository.createStub(ValidationManager));
var validationItem:IValidatable = IValidatable(mockRepository.createStub(IValidatable));
var validatableItems:Dictionary = new Dictionary();
validatableItems[validationItem] = false;
SetupResult.forCall(validationManagerRepos.validateItem(validationItem)).returnValue(false);
My problem is in the execute method of the command. It checks to see if the validationItem is both a DisplayObject (isVisble) and IValidatable. Any slick way to stub a typed object AND an interface? Or do I just need to create an instance of some existing object that already satisfies both?
for (var iVal:Object in validationManager.validatableItems)
{
if (isVisible(DisplayObject(iVal)))
{
passed = validationManager.validateItem(IValidatable(iVal));
eventDispatcher.dispatchEvent(new ValidationEvent(ValidationEvent.VALIDATE_COMPLETED, IValidatable(iVal), passed));
if (!passed)
{
allPassed = false;
}
}
}
I'm fairly sure you can't do both within asMock. It's a limitation of the Flash Player because of lack of polymorphism.
I believe what you'll have to do is create a testing object that does both (extend DisplayObject and implement IValidatable) and create a mock object of that.
The concept of a "multimock" is certainly possible, but floxy (the framework that asmock uses to generate dynamic proxies) doesn't support it. I previously considered adding support for it, but it would be difficult to expose via the various Mock metadata and there's be other issues to worry about (like method name clashes).
I agree with J_A_X's recommendation of creating a custom class and then mocking that.

How do you access the public properties and methods of a loaded SWF

How would you go about calling the public properties and methods of a SWF you load in actionscript?
I have been using stackoverflow for years to answer my programming questions so I wanted to give back by writing a guide to an issue I had a lot of trouble figuring out. This is my first user guide so tell me if there is anything I can do to improve it.
One of the more powerful features of the flash engine is the ability to load flash programs within flash programs, through the use of the Loader class. Unfortunately communication with the loaded program is limited. While you can establish a LocalConnection object there is a limit to the traffic it can safely support.
A simple solution is to load the SWF file within your main program’s security domain. This has the benefit of exposing the public methods and properties of the loaded SWF to the loader and vice versa.
First extend the Loader class this class will be used to interact with the loaded file.
public class ParentChildLoader extends Loader
Next we must store the SWF file as a ByteArray. The variable path is a file path on the system. You could use a URLStream object instead of a FileStream for a http url.
var swfBytes:ByteArray = new ByteArray();
var file:File = new File(path);
if (file.exists)
{
var loadStream:FileStream = new FileStream();
loadStream.open(file, FileMode.READ);
loadStream.readBytes(swfBytes);
loadStream.close();
}
Now that we have the SWF stored as a ByteArray, we load the SWF into our security domain and listen for the complete event.
var context:LoaderContext = new LoaderContext();
context.allowLoadBytesCodeExecution = true;
addEventListener(Event.COMPLETE, onSwfLoad);
loadBytes(swfBytes, context);
If you want to access the loaded SWF’s properties from the loader use the content property.
Object(this.content).foo(bar);
Object(this.content).a = b;
If you want to access the loader’s public properties from the SWF use the parent property.
Object(this.parent).foo(bar);
Object(this.parent).a = b;
This has many practical applications from allowing re-usability of common functions to taking some of the programming load off your creative team. A note of caution; the loaded SWF exists within your main program’s security domain so it is key that you only load files which you trust with this method.
A cleaner way would be to use interfaces. Here's an example. Consists of two projects. Project 1 (PluginLoader) is your base app. Part of this project includes an AS3 Interface:
/* IPlugin.as */
package
{
public interface IPlugin
{
function getID():String;
function doStuff():void;
}
}
Then, your loaded swf in a second project (TestPlugin) implements the interface:
package
{
import flash.display.Sprite;
public class TestPlugin extends Sprite implements IPlugin
{
private const _ID:String = "TestPlugin";
public function getID():String {
return _ID;
}
public function doStuff():void {
trace('Test plugin: Doing Stuff');
}
}
}
The interface would be included in that project via source files or swc.
Then to load it (back in PluginLoader project):
public class PluginLoader extends Sprite
{
public function PluginLoader()
{
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
loader.load(new URLRequest('plugins/TestPlugin.swf'));
}
private function completeHandler(e:Event):void {
var plugin:IPlugin = e.target.content as IPlugin;
trace(plugin.getID());
plugin.doStuff();
}
}
}
I would have to disagree with you on your approach. It is 'hackish' in my opinion. There is a reason why you have limited communication with loaded swfs, and that is because you don't know what they do and how they're suppose to be managed.
If you want to talk architecturally, this is a very bad way of trying to access information. The main application should not have to access properties within the loaded swf. If you're building a large scale application and the loaded swf (a module maybe?) needs information that the main app has, you should look into an application framework like Parsley which does dependency injection to give the data your loaded swf needs. It is a much cleaner approach and is architecturally sound.

Can't inherit from classes defined in an RSL?

Note: This is an Actionscript project, not a Flex project.
I have class A defined in an RSL (technically, it's an art asset that I made in the Flash IDE and exported for actionscript. The entire .FLA was then exported as a SWC/SWF).
I my main project I have class B, which inherits from class A. The project compiles fine, with no errors.
However, when when the project runs and I try to create an instance of Class B, I get a verify error. Creating an instance of Class A works just fine, however:
import com.foo.graphics.A; // defined in art.swf / art.swc
import com.foo.graphics.B; // defined locally, inherits from A
...
<load art.SWF at runtime>
...
var foo:A = new A(); // works fine
var bar:B = new B(); // ERROR!
// VerifyError: Error #1014: Class com.foo.graphics::A could not be found.
For reference, here is how I'm loading the RSL:
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onArtLoaded);
var request:URLRequest = new URLRequest("art.swf");
var context:LoaderContext = new LoaderContext();
context.applicationDomain = ApplicationDomain.currentDomain;
loader.load(request, context);
Class B is defined as follows:
import com.foo.graphics.A;
class B extends A {}
I don't think this is a bug. It's more a linkage problem.
The verifier error doesn't happen when you try to create an instance of B. It happens as soon as your main swf is loaded and verified by the player. This is an important distinction. To see what I mean, change this code:
var bar:B = new B();
to
var bar:B;
You'll still get the error.
I don't know how you are builing the swf, but from the error it seems evident that the A class (B's parent) is being excluded from the swf. I can reproduce this using this mxmlc switch:
-compiler.external-library-path "lib.swc"
However, changing it to:
-compiler.library-path "lib.swc"
The problem goes. Obviously, this kind of defeats the purpose of loading the assets at runtime, since these assets are already compiled into your main.swf (in fact, it's worse, because by doing that you've just increased the global download size of your app).
So, if you set your art lib as external, the compiler can do type checking, you'll get auto-complete in your IDE, etc. Your B class still depends on A being defined, though. So, at runtime, A has to be defined whenever B is first referenced in your code. Otherwise, the verifier will find an inconsitency and blow up.
In the Flash IDE, when you link a symbol, there's a "export in first frame" option. This is how your code is exported by default, but it also means it's possible to defer when the definition of a class is first referenced by the player. Flex uses this for preloading. It only loads a tiny bit of the swf, enough to show a preloader animation while the rest of the code (which is not "exported in first frame") and assets are loaded. Doing this by hand seems a bit cumbersome, to say the least.
In theory, using RSL should help here if I recall correctly how RSL works (the idea being the a RSL should be loaded by the player transparently). In my experience, RSL is a royal pain and not worth the hassle (just to name a few annoying "features": you have to hard-code urls, it's rather hard to invalidate caches when necessary, etc. Maybe some of the RSL problems have gone and the thing works reasonably now, but I can tell you I've been working with Flash since Flash 6 and over the years, from time to time I'd entertain the idea of using RSL (because the idea itself makes a lot of sense, implementation aside), only to abandon it after finding one problem after the other.
An option to avoid this problem (without using RSL at all) could be having a shell.swf that loads the art.swf and once it's loaded, loads your current code. Since by the time your code.swf is loaded, art.swf has been already loaded, the verifier will find com.foo.graphics.A (in art.swf) when it checks com.foo.graphics.B (in code.swf).
public function Shell()
{
loadSwf("art.swf",onArtLoaded);
}
private function loadSwf(swf:String,handler:Function):void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, handler);
var request:URLRequest = new URLRequest(swf);
var context:LoaderContext = new LoaderContext();
context.applicationDomain = ApplicationDomain.currentDomain;
loader.load(request, context);
}
private function onArtLoaded(e:Event):void {
loadSwf("code.swf",onCodeLoaded);
}
private function onCodeLoaded(e:Event):void {
var li:LoaderInfo = e.target as LoaderInfo;
addChild(li.content);
}
In your current main class, add this code:
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
Move your constructor logic (if any) to the init method, and it should work fine.
But what I don't like about this approach is that you have to create another project for the shell.
What I do, generally, is have a class that proxies the graphic asset.
private var _symbol:MovieClip;
public function B() {
var symbolDef:Class = ApplicationDomain.currentDomain.getDefinition("com.foo.graphics.A") as Class;
_symbol= new symbolDef();
addChild(_symbol);
}
Since com.foo.graphics.A is just a graphical asset, you don't really need to proxy other stuff. What I mean is, if you want to change x, y, width, etc, etc, you can just change these values in the proxy and the result is in practice the same. If in some case that's not true, you can add a getter / setter that actually acts upon the proxied object (com.foo.graphics.A).
You could abstract this into a base class:
public class MovieClipProxy extends MovieClip {
private var _symbol:MovieClip;
public function MovieClipProxy(linkagetName:String) {
var symbolDef:Class = ApplicationDomain.currentDomain.getDefinition(linkagetName) as Class;
_symbol = new symbolDef();
addChild(_symbol);
}
// You don't actually need these two setters, but just to give you the idea...
public function set x(v:Number):void {
_symbol.x = v;
}
public function get x():Number {
return _symbol.x;
}
}
public class B extends MovieClipProxy {
public function B() {
super("com.foo.graphics.A");
}
}
Also, injecting the app domain as a dependency (and moving the instantiation mechanism to other utility class) could be useful for some projects, but the above code is fine in most situations.
Now, the only problem with this approach is that the linkage name in the constructor of B is not checked by the compiler, but since it's only in one place, I think it's manageable. And, of course, you should make sure your assets library is loaded before you try to instantiate a class that depends on it or it will trhow an expection. But other than that, this has worked fairly well for me.
PS
I've just realized that in your current scenario this could actually be a simpler solution:
public class B extends MovieClip {
private var _symbol:MovieClip;
public function B() {
_symbol = new A();
addChild(_symbol);
}
}
Or just:
public class B extends MovieClip {
public function B() {
addChild(new A());
}
}
The same proxy idea, but you don't need to worry about instantiating the object from a string using the application domain.

Resources