Save Application state on Disk or some where so user access it later - actionscript-3

In flex builder 4.5 i'm working on a project like cacoo.
I want to save diagrams(display object,ui components,text) before close the application into somewhere than I would be able to access after the application open again.
more clear:-If user edit some uml diagram on this project and save it for edit later and close application.after some days he/she want to edit previously saved diagram.
now how i'm save this diagram for future edit.

If save/open dialog will work for you, you can yse FileReference API. Before doing this, you have to implement serialization/deserialization of your state into/from String/ByteArray/XML object.
private var fileReference:FileReference;
// due to security restrictions, this method must be called from an
// event handler that responds to a user event (mouse click or key
// press), otherwise it will fail.
private function saveState(serializedState:*, fileName:String):void {
fileReference = new FileReference();
fileReference.addEventListener(Event.COMPLETE, onSaved);
fileReference.addEventListener(IOErrorEvent.IO_ERROR, onSavingError);
try {
fileReference.save(serializedState, fileName); // will open save dialog
} catch (e:Error) {
trace("error saving data: " + e.toString());
freeListeners();
}
}
private function onSaved(e:Event):void {
trace("saved!");
freeListeners();
}
private function onSavingError(e:ErrorEvent):void {
trace("error saving data: " + e.toString());
freeListeners();
}
private function freeListeners():void {
fileReference.removeEventListener(Event.COMPLETE, onSaved);
fileReference.removeEventListener(IOErrorEvent.IO_ERROR, onSavingError);
}
Similarly with restoring the state (use FileReference.browse(), then FileReference.load()).
If you need to save/restore app state without any dialogs, then you should probably use AIR (or SharedObject, as Raja Jaganathan suggested). But it seems to be not the case, as you want the user to be able to re-open the diagram in another system. To achieve this, you should allow the user to save his work to the appropriate place, so later he can move it to another machine/system and re-open it with your application.
Another alternative is to store everything on the server and provide the user with a list of saved files (like Cacoo does). If you go this way, you'll have to implement the corresponding server-side API. It may be REST API or smth like RTMP server. In the case of REST API, use FileReference.upload() to upload the data to your server, and URLLoader.load() to obtain it back.

You can store your diagram state through SharedObject for better you create one class which hold all of your state of Diagram so that later you can use
SharedObject using http://livedocs.adobe.com/flex/3/html/help.html?content=lsos_5.html
you can use registerClassAlias for custom class stored in sharedobject.
myClassInstance = new MyClass();
myClassInstance.x = 100;
myClassInstance.y = 100;
myClassInstance.text = "diagrams";
registerClassAlias("com.path.to.MyClass", MyClass);
myStuff = SharedObject.getLocal("myAppStuff");
myStuff.data.whatINamedIt = myClassInstance;
myStuff.flush();
now when get it back out... you can say:
myStuff = SharedObject.getLocal("myAppStuff");
var mySavedClass:MyClass = myStuff.data.whatINamedIt as MyClass;
Read mySavedClass instance value then inject to your diagram model when open again.
To implement application close event
http://www.flexer.info/2007/10/25/fabridge-warn-on-flex-application-exit/

Sprite or MovieClip other DisplayObject objects can not be direct serialized. So you should stored objects information (origin x,y, width, height, color, child info...). using a ByteArray or Array or Dictionary ... and that save to ShareObjects. later roll back from ShareObject and re-create Original Object. MovieClip or Sprite appropriate purpose is container.
Here is my test code.
1. create a Movieclip. purpose is container.
2. draw a rectangle using a graphics. And set the coordinates.
var drawWidth:Number = 500;
var drawHeight:Number = 300;
var rect:MovieClip = new MyRect();
rect.graphics.beginFill(0xffffff*Math.random(),1);
rect.graphics.drawRect(0,0,drawWidth,drawHeight);
rect.graphics.endFill();
rect.x= 300;
rect.y= 100;
3. Stores the information in the array.
var myRectInformation:Array = new Array();
myRectInformation.push(rect.x);
myRectInformation.push(rect.y);
myRectInformation.push(drawWidth);
myRectInformation.push(drawHeight);
var bmd:BitmapData = new BitmapData(rect.width, rect.height,true,0);
bmd.draw(rect);
//is byteArray.
myRectInformation.push(bmd.getPixels(new Rectangle(0,0,bmd.width,bmd.height)));
4. save to SharedObjects, array.
var mySaveData:SharedObject = SharedObject.getLocal("myStorage")
mySaveData.data.myRectInformation = myRectInformation;
mySaveData.flush();
5. this is load from SharedObject data stored. and recreate Objects.
var rect:MovieClip = new MyRect();
var loadBmd:BitmapData = new BitmapData(mySaveData.data.myRectInformation[2], mySaveData.data.myRectInformation[3], true, 1);
loadBmd.setPixels(new Rectangle(0,0,loadBmd.width,loadBmd.height), mySaveData.data.myRectInformation[4]);
var bmp:Bitmap = new Bitmap(loadBmd);
rect.addChild(bmp);
rect.x = mySaveData.data.myRectInformation[0];
rect.y = mySaveData.data.myRectInformation[1];
addChild(rect);

Related

Actionscript SharedObject data go randomly missing

I'm designing a game with save and load functions. Using the SharedObject technique I'm successfully saving most of the variables. However, there is this one Dictionary that sometimes ends up as "undefined". This happends perhaps 1/3 of the times i load the game.
This is my save function. It runs approximately every 2. second. I've removed a lot of code lines since they aren't relevant (saving the other variables). The trace line is for debugging purposes. Every time, also when the above mentioned error accurs, this line works. Consequently the dictionary isn't "undefined" at this moment.
private function saveGame():void
{
so = SharedObject.getLocal("progress", "/");
so.data.saved = true;
so.data.airportDict = airportDict;
trace(dayCount, so.data.airportDict["Australia"]);
so.flush();
}
The following lines run every time you open the program. The purpose is to decide whether there is a saved file to load or not.
so = SharedObject.getLocal("progress", "/");
if (so.data.saved == true)
{
loadProgress();
}
else
{
airportDict = new Dictionary();
resetAirportDict(); // This function just add lots of data to the dictionary.
}
And finally, the loading function:
private function loadProgress():void
{
so = SharedObject.getLocal("progress", "/");
airportDict = so.data.airportDict;
trace("Successfull start? " + airportDict);
}
As already mentioned, the airport dictionary's value is "undefined" maybe 1/3 of the time I run the program. For no appearant reason. This is a mystery.
SharedObject can store primitive types + Array and Object types. For other complex types you have to register the classes you wish to store using registerClassAlias(). In your cases Dictionary is a complex type that is not handled by default by SharedObject + the complex objects you might use as keys will have to be registered as well. So any complex class you use has to be register or else SharedObject will fail.
registerClassAlias("YourClassName", YourClassName);
This piece of code produce error because SharedObject doesn't understand complex objects:
var shareddata:SharedObject = SharedObject.getLocal("mydata")
var instance:MyClass;
if(shareddata.data["instance"] == undefined)
{
instance = new MyClass();
shareddata.data["instance"] = instance;
}
else
{
instance = shareddata.data["instance"];
}
But you can easily fix it by registering your class:
import flash.net.registerClassAlias;
registerClassAlias("MyClass", MyClass);
var shareddata:SharedObject = SharedObject.getLocal("mydata")
var instance:MyClass;
if(shareddata.data["instance"] == undefined)
{
instance = new MyClass();
shareddata.data["instance"] = instance;
}
else
{
instance = shareddata.data["instance"];
}
//no error and you truly get back your custom class instance the second time around.

Is it possible not to flush sharedobject on close?

I'm having a problem with the sharedObject flush method. Is it possible to not flush the new data when the swf is closed? My savegame function is the only function that calls the flush method and it also determines which array goes where in the sharedObject data.
parentMC.sharedObject.data.moveSpdUpgrade = parentMC.upgrades.tempMoveSpdUpgrade;
parentMC.sharedObject.flush();
However, when I modify the tempMoveSpdUpgrade array, it also saves the new data in the sharedObject even if the flush hasn't been called yet.
tempMoveSpdUpgrade[0][2] = 1;
trace(parentMC.sharedObject.data.moveSpdUpgrade);
This trace shows that the data has changed but I don't understand since the flush hasn't been called and the swf hasn't been closed. I'm wondering why the modifications made in the array automatically changes the sharedObject data.
Thank you for the help.
Edit:
public function saveGame(){
parentMC.sharedObject.data.money = parentMC.money;
parentMC.sharedObject.data.moveSpdUpgrade = parentMC.upgrades.tempMoveSpdUpgrade;
parentMC.sharedObject.flush();
}
Like I stated in the comments with hackattack, the money is the correct data when I don't save but the moveSpdUpgrade array is modified either way.
I think you are running into a bit confusing thing about arrays.
What is happening never saves any data by another function or has something to do with the rest of your code.
Simple the method how you try to copy the array of that Sharedobject doesnt create two seperate Obejcts. Instead you are always working live on the sharedObject-array.
package
{
import flash.display.Sprite;
public class arraytester extends Sprite
{
private var a:Array = new Array(1,2,3,4,5);
private var b:Array = new Array("a","b","c","d","e","f");
public function arraytester()
{
// Both arrays are different Objects
trace(a); // 1,2,3,4,5
trace (b); //a,b,c,d,e,f
// Both arrays are pointing to the same Object
a= b;
trace(a); //a,b,c,d,e,f
trace (b); //a,b,c,d,e,f
b[1] = 2;
trace(a); //a,2,c,d,e,f
trace (b); //a,2,c,d,e,f
// a is a copy of b while becoming a seperate Object
a = b.concat();
b[0]= 1;
trace(a); //a,2,c,d,e,f
trace (b); //1,2,c,d,e,f
}
}
}
What is in the sharedObject that is loaded into memory and what is saved to disc are separate things. You are tracing the sharedObject in memory. Until you flush this sharedObject it probably wont save its image to disc. Try creating a new SharedObject and tracing that, it should be different.
Edit:
I see an error in what i told you to do before (below in the comments). I told you to make a new instance of moveSpdUpgrade to place in your shared object with the .concat method. The reason this doesnt work is because moveSpdUpgrade is a 2d array, i missed this detail. So calling .concat creates a new instance, but the new instance is still filled with references to arrays and it those arrays that you are editing. So to solve this we have to do a deep copy of moveSpdUpgrade. Here is a function that will accomplish that
function array2DConcat(target : Array) : Array {
var buff : Array = new Array(target.length);
for(var i : int = 0;i < target.length;i++) }
buff[i] = target[i].concat();
}
return buff;
}
and then if you change your method to
public function saveGame(){
parentMC.sharedObject.data.money = parentMC.money;
parentMC.sharedObject.data.moveSpdUpgrade = array2DConcat(parentMC.upgrades.tempMoveSpdUpgrade);
parentMC.sharedObject.flush();
}
hopefully that will work.

ActionScript: How to Import XML data as a variable (not as an Event)

I am attempting to import some simple XML data into Flash ActionScript 3.0. I can easily do this as an import that is posted to the stage, but I want to save it as a global variable instead. Here is the XML file that I am pulling from:
<utilitySavings>
<nameof file="academicWaterSavings">
<waterValue>100</waterValue>
<elecValue>200</elecValue>
</nameof>
<nameof file="dormWaterSavings">
<waterValue>300</waterValue>
<elecValue>400</elecValue>
</nameof>
<nameof file="greekWaterSavings">
<waterValue>500</waterValue>
<elecValue>600</elecValue>
</nameof>
<nameof file="totalWaterSavings">
<waterValue>1500</waterValue>
<elecValue>1600</elecValue>
</nameof>
...and here is the actionscript:
var req:URLRequest = new URLRequest("data.xml");
var loader:URLLoader = new URLLoader();
var utilitySavings:XML;
function xmlLoaded(event:Event):void
{
utilitySavings = new XML(loader.data);
academicWater.text = utilitySavings.nameof[0].waterValue;
academicElec.text = utilitySavings.nameof[0].elecValue;
var dormWater:String = utilitySavings.nameof[1].waterValue;
trace (dormWater);
}
loader.addEventListener(Event.COMPLETE, xmlLoaded);
loader.load(req);
trace(academicWater.text);
Notice the 'trace (dormWater)' I want to trace this outside of the function so it is accessible in later in my script. I can trace within the function, but this does me no good. I also am able to get the dynamic text to show up on the stage but, likewise, this does me little good.
I appreciate any help or insights.
I can see a couple of ways of achieving this , if you want to create a globally accessible Object, create a Singleton( not recommended ) , load your XML data into it then every object in your app will be able to access the loaded XML data.
http://www.gskinner.com/blog/archives/2006/07/as3_singletons.html
The resulting code would give you something like this:
//Available throughout your app after the XML has been loaded & parsed
var dormWater:String = Singleton.dormWater;
Although you state you don't want Events, I think that using Signals could be a better approach. Load your XML and dispatch a Signal containing the relevant String to the object that needs it, when receiving the Signal , assign the String to a variable.
http://www.peterelst.com/blog/2010/01/22/as3-signals-the-best-thing-since-sliced-bread/
//In a specific class
private var _dormWater:String;
private function signalListener(value:Object ):void
{
_dormWater = value.dormWater;
}

Reading in a File (AS3) and repeatedly/dyamically accessing the data

This may be a tired old question, but I have yet to find a good answer. Say for instance you have a class that reads in an xml file to get information such as a grocery store items, prices, etc. This class also allows you to retrieve the information about a grocery store item with a get() function.
var grocery:GroceryStore = new GroceryStore(); //create a class that
//reads in xml about
//grocery items
grocery.get("lettuce"); //get some data
In this scenario, I am constantly running into issues because the get() function is being called before the event that loads in the xml file. It wouldn't make sense to place the get() in the onLoad event for the xml file because I want it to be re-usable and dynamic. Also, AS3 doesn't have a wait() function so I can't stall until the file is loaded? Does anyone have an idea on how to read in a file and then be able to safely access the data dynamically and repeatedly? Hopefully this example and my question is thorough enough, if not let me know.
Thanks
You can use events - listen for the complete event to be dispatched.
Add the following code to GroceryStore class
//constructor or a load method
var ldr:URLLoader = new URLLoader();
ldr.addEventListener(Event.COMPLETE, onLoad);
ldr.load(new URLRequest(xmlurl));
function onLoad(e:Event):void
{
//process xml here
dispatchEvent(e);
}
Now use it as:
var grocery:GroceryStore = new GroceryStore();
grocery.addEventListener(Event.COMPLETE, onGroceryLoad);
function onGroceryLoad(e:Event):void
{
grocery.get("lettuce");
}

Actionscript - Variable Assignment without reference?

Should be easy. I have an object. I want to modify it, but before i do I want to save a copy of it that I can go back to. I tried setting copy = original but when i modify the attributes of the original the copy also shows the changes. I am assuming this is because in actionscript any time you assign, it really just stores a reference to the original object. So whats the best way for me to store a copy of the original object for later use?
var newObj:Object = Object(ObjectUtil.copy(oldObj));
"Copies the specified Object and returns a reference to the copy. The copy is made using a native serialization technique. This means that custom serialization will be respected during the copy.
This method is designed for copying data objects, such as elements of a collection. It is not intended for copying a UIComponent object, such as a TextInput control. If you want to create copies of specific UIComponent objects, you can create a subclass of the component and implement a clone() method, or other method to perform the copy."
http://livedocs.adobe.com/flex/3/langref/mx/utils/ObjectUtil.html#copy()
What you are looking for is a deep copy of the object rather then passing by reference. I found the answer here which uses the new ByteArray class in AS3:
http://www.kirupa.com/forum/showthread.php?p=1897368
function clone(source:Object):* {
var copier:ByteArray = new ByteArray();
copier.writeObject(source);
copier.position = 0;
return(copier.readObject());
}
Which you then use like this:
newObjectCopy = clone(originalObject);
Cheers!
// duplicate any given Object (not MCs)
Object.prototype.copy = function()
{
ASSetPropFlags(Object.prototype,["copy"],1);
var _t = new this.__proto__.constructor(this) //
for(var i in this){
_t[i] = this[i].copy()
}
return _t
};
Usage
x = ["1","2","3",[4,5],[{a:1,b:2}]]
y = x.copy()
y[0] = 0
y[3][0]="d"
trace(x)
trace(y)