Thursday, December 20, 2012

Flash AS3 Class overriding template and tutorial

The biggest problem with class overriding I have found so far is that large classes can take a lot of work to "fix" to work with class overriding. So, I have created a template for class overriding that can make this process a vast amount easier.
It certainly has it's limitations, but where it works, it works well.
To be able to use it properly, you need to have a pretty good understanding of how it works.
I'll start with a quick overview of how it works, don't worry too much if you don't understand it properly yet:

A custom loader dynamically generates a SWF containing a single "nullified" class that has no methods or properties and uses class overriding to nullify the document class in an instance of the game SWF (this instance is used solely for accessing the original, unmodified classes in the game).


Template classes use the proxy extends hack to pretend to "extend" the original class in the game. e.g. if you were overriding the class "Money", the template does this by using the proxy hack to pretend to extend the original "Money" class held in the nullified instance of the SWF loaded by the custom loader.

Classes are overridden by extending the BasicOverrider class that is included in the template. This class and the custom loader do all the hard work for you. Any public methods and variables can now be modified, without having to "fix" any code you're not working with.

Here's a nice little visual explanation for the loader:



With any luck, you now have a decent understanding of how the loader works.
so, onto how the template extender works:



If you need more information, you can take a peek at it's source.

Now, hopefully you understand it's limitations. It cannot access private/protected vars and requires a little work to fix type casting problems (Note: a lot of the time this isn't a problem) due to the nature of how it functions.

Now, here's the mini-tutorial and template download link:
You'll need this SWF and the OverriderLoader template for this tutorial.
I will assume you know everything covered in this tutorial.

Setting up the template is really easy.
The SWF name is "Unhackable.swf" and if you open it up with a decompiler, it'll tell you the document class  is "Unhackable".
open up the Overrider class and change:

 loader.load("SWF NAME HERE.swf","DocumentClassNameHere");  

to:

 loader.load("unhackable.swf","Unhackable");  

Next, we need to fix the stage access problem. Unfortunately, template overriding doesn't work on the document class (because it gets nullified so you can load the other classes without running code), so we'll need to use the old method. We'll use the same code we wrote up in the last tutorial. Chuck this code in a new class called "Unhackable":

 package   
 {  
      import flash.utils.*;  
      import flash.display.*;  
      import flash.events.*;  
      public class Unhackable extends MovieClip  
      {  
           private var Player:Class = getDefinitionByName("UnhackableEngine.Player") as Class;  
           private var Money:Class = getDefinitionByName("UnhackableEngine.Money") as Class;  
           private var ScoreKeeper:Class = getDefinitionByName("UnhackableEngine.ScoreKeeper") as Class;  
           private var player:Object;  
           private var money:Object;  
           private var score:Object;  
           public function Unhackable()  
           {  
                this.addEventListener(Event.ADDED_TO_STAGE,addToStage);  
                return;  
           }  
           private function addToStage(e:Event)  
           {  
                this.player = new Player(int(Math.random() * 300) + 50,int(Math.random() * 200) + 50);  
                this.money = new Money(int(Math.random() * 300) + 50,int(Math.random() * 200) + 50);  
                this.score = new ScoreKeeper();  
                addChild(this.score as DisplayObject);  
                addChild(this.money as DisplayObject);  
                addChild(this.player as DisplayObject);  
                stage.addEventListener(Event.ENTER_FRAME, this.gameLoop);  
           }// end function  
           private function gameLoop(event:Event)  
           {  
                this.money.dealWithPlayer(this.player.playerX, this.player.playerY);  
                this.player.doMove();  
                return;  
           }// end function  
      }  
 }  

Now that that problem's solved, we can get on to using the template for something useful.

We'll make each money pickup give you 100 score.
Now, if you remember, the actual score variable is private, which means that template overriding can't touch it. however, the static "addMoney" method is public and can thus be edited.

So, let's start with a basic overrider template class, then.
Create a new class in the package "UnhackableEngine" called "ScoreKeeper" and give it this code:

 package UnhackableEngine {  
      import OverriderLoaderUtils.BasicOverrider;  
      import flash.utils.*;  
      public class ScoreKeeper extends BasicOverrider {  
           public function ScoreKeeper() {  
                super(getQualifiedClassName(this));  
                origObject = new origClass();  
           }  
           static function addMoney()  
           {  
                origClass.addMoney();  
           }  
      }  
 }  

This is just a basic overrider template class with a wrapper function for the addMoney static method (as all static methods need wrapper functions when using the template). Our goal right now is to get it to compile with this.

So, lets add the line

 var scoreKeeper:ScoreKeeper;  

to our Overrider class to make it all compile together and see what errors we get.
You'll get an error telling you it doesn't want to be added as a child. We've been here before, haven't we? last tutorial?
Change:

 addChild(this.score as DisplayObject);  

to:

 addChild(this.score.origObject as DisplayObject);  

in your Overrider class to make it quit it's bitching. Compile, and it should be happy.

Now all we need to do to make money add 100 score is go to our ScoreKeeper class and change:

           static function addMoney()  
           {  
                origClass.addMoney();  
           }  

to:

           static function addMoney()  
           {  
                for(var i:int = 0;i<100;i++){  
                     origClass.addMoney();  
                }  
           }  

Compile, and you're done!

Here's the finished source code for the tutorial.


Oh, and if you're wondering how to fix static variables, you do something like this:

           static function get varName():Object  
           {  
                return origClass.varName();  
           }  
           static function set varName(value:Object):Void  
           {  
                origClass.varName = value;  
           }  

Where varName is the name of the variable.

The pros of using the template:
- no need to fix any non-static functions
- static functions only need to be redirected, so fixing is much simpler compared to the old method

The cons of using the template
- doesn't work on the document class
- doesn't work on private/protected vars/methods
- type casting is still a problem

No comments: