save load error 1010 array objects with sharedObjects as3 - actionscript-3

i cant figure out this one so maby you guys can help me out.
i store some data in the form of a array filled with Objects in this example cards.
in my main class i have the following code:
deckSprite.savedData = SharedObject.getLocal("cardsdata");
deckSprite.savedData.data.savedArray = deckSprite.deckArr;
deckSprite.savedData.flush();
trace(deckSprite.savedData.data.savedArray);
the trace will output something like [object card1, object card2, object card3]
now in a static class called "deckSprite" i have this:
savedData = sharedObject.getLocal("cardsdata");
if (savedData.data.savedArray == undefined)
{
trace("no save yet");
}
{
else
{
trace("save loaded");
deckArr = savedData.data.savedArray;
trace(savedData.data.savedArray);
now my trace data turns out only ", ," (somehow the cards are gone).
now after i got saved data i restart my application and whenever he tryes to acces the deckArr it crashes giving me the error "A term is undefined and has no properties".
how is it possible that when i save the array it saves all the cards inside the array and when i restart the application its suddenly only ",,"but the cards are gone?

When serializing objects in AS3, you have to register their class using registerClassAlias() from package flash.net. So you have to call something like
registerClassAlias('com.example.deck', Deck)
in the program before any saving or loading happens.
See full reference at AS3 API Reference
NOTE: As pointed out by #BadFeelingAboutThis in comments, you have to register all referenced class in your Deck, i.e. if your deck looks like this:
class Deck {
var firstCard:Card;
var type:DeckType;
}
to be able to save Deck to SharedObject you have to call
registerClassAlias('com.example.deck', Deck);
registerClassAlias('com.example.card', Card);
registerClassAlias('com.example.decktype', DeckType);
before any saving/loading is done.
EDIT
Everything depends on content of your array
If I assume, your deckSprite is declared like this:
var deck1:Deck = new Deck();
var deck2:Deck = new Deck();
var deckArr:Array = new Array(deck1, deck2);
var deckSprite:DeckSprite = new DeckSprite()
deckSprite.setDeckArr(deckArr);
then before adding deckArray to SharedObject, you have to call registerClassAlias(). Sou your save code will look like this:
registerClassAlias('com.example.deck', Deck);
deckSprite.savedData = SharedObject.getLocal("cardsdata");
deckSprite.savedData.data.savedArray = deckSprite.deckArr;
deckSprite.savedData.flush();
trace(deckSprite.savedData.data.savedArray);
(replace the Deck with actual class you use for representing your decks)
Similarly the first line has to be called before you do any loading.
Of course it is best not to repeat yourself, so you should call registerClassAlias('com.example.deck', Deck); only once in your program, so for example in some init() method in your main class, if you have something like that.

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.

Modifying an external XML from within AS3

Can anybody show me some easy to understand guides on how to modify and update an external XML file from within ActionScript 3. I've been looking into it for a long time now, but haven't found a tutorial that I was able to understand. My goal is to create a really basic database.. Let's say, a Database of Contacts.. I want to load those contacts through an xml file. but I also want to be able to add new contacts and modify the existing ones. How do I do that?
I want to that in an Desktop AIR Application.
An easy task:
First, load the external XML. For the sake of an example let's imagine the XML to be of this structure:
<contactData>
<contact firstName="John" lastName="Smith" phone="285-493-5421-793" email="example#mail.com"/>
<contact firstName="Jane" lastName="Roberts" phone="285-493-5421-214" email="example#gmail.com"/>
</contactData>
Second, parse that XML. For that create a value object type of a class, let's call it ContactData. It might look something like this:
package
{
public class ContactData
{
public var firstName:String;
public var lastName:String;
public var email:String;
public var phone:String;
public var id:int; // always nice to store an ID
}
}
Loop through your XML - for every contact node create a ContactData class object and fill it the data from the XML.
Store an array with your ContactData objects somewhere, you'll need them later.
Third, edit the ContactData object, or even remove it from an array if you will. Adding is not a problem too.
Fourth and last, create a new xml with AS3 and loop through the ContactData object array to add contact nodes, then save the XML. Use File and FileStream classes to save the file on the hard drive or URLLoader to pass it to the server.
This is how a primitive XML creation code could look like:
var xml:XML = <contactData></contactData>;
for (var i:int = 0; i < contactDataArray.length; i++)
{
var cd:ContactData = contactDataArray[i];
xml.appendChild(<contact></contact>);
xml.contact[i].#firstName = cd.firstName;
xml.contact[i].#lastName = cd.lastName;
xml.contact[i].#phone = cd.phone;
xml.contact[i].#email = cd.email;
}
I hope it's helpful and easy to understand. Good luck!

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");
}