Apr 25 2009

The Trick to Using SharedObject

sshot-2013-10-21-[2]

Having a SAVE feature in a project/game is one of the common things that could lead users to be more attracted and increase replay value for potentially recovering what was done in a previous visit. Using the SharedObject class in actionscript project/games is sometimes a requirement or at least a need, may it be AS1 (FP6), 2 or 3. It can be used to store valuable data like progress, settings, statistics, achievements and a lot more without dedicating storage on the server. Basically, SO or shared objects are like the browser cookies for flash, it is used to store some amount of data on the user machine, therefore it also has some limitations on its use (for security purposes really).

By reading the documentations or the livedocs, either for AS2 or AS3 (it’s really the same), you might have a gut start of what key points to remember.

  • created shared objects can only be stored/retrieved on the same domain
  • shared objects cannot be shared across domains, meaning a shared objects from “examplesite.com” cannot read shared objects created by another domain “mywebsite.com”
  • users can disallow local storage per domain or opt-out globaly, then the object would not be saved locally
  • data size to be stored is limited by which the user sets it (none, 10kb, 100kb, 1mb, 10mb, unlimited), although a dialog box can request an increase in storage if what is set is exceeded
  • if the user sets a limit smaller than the shared object, the shared object would be deleted
  • shared objects are stored on the user’s local machine not on the server, something shared over another machine cannot be accessed by another machine

The most essential part when using SharedObject is the SharedObject.getLocal() method, which is what we will be discussing more through out. Basically, AS1/2 and AS3 syntax haven’t really changed (although a few more rules were imposed in as3).

getLocal(name:String, localPath:String = null, secure:Boolean = false):SharedObject
[static] Returns a reference to a locally persistent shared object that is only available to the current client.

Avoiding Known Issues

It might seem that using shared objects could be the simplest when you are only storing a couple of variables and objects like statistics or even could be the more complex when saving a game progress/state together with the players preferences. The bigger the system, more likely, the more complex your variables/objects are (same for games), and this is where possible issues during saving/loading/handling the shared objects.

Some common things to remember when using shared objects with your games:

  1. Don’t use direct reference to objects; only save the game’s data, not the actual game objects.
  2. Use a localPath param on the getLocal() method.
  3. Taking advantage of unique names for your shared object name.
  4. Prompt and request for shared object size limit increase when your game needs it.

While I only have these that I can explain, you are free to share more if you have one or more. We can discuss and make the lives of other actionscript writers a little better.

 


Don’t use direct reference to objects; only save the game’s data, not the actual game objects.

Why do I say this? Basically it is fine to store various data types such as objects and arrays on SO (shared objects), same goes for strings, numbers (int/uint), booleans and bytearray (as3). If you store references to local game objects on your shared objects, one way or another, your in for a later problem.

When you do something like the above, instead of storing the needed values only, you are referencing the local game object (w/c also contains references to other local game objects) in which Flash defines this as a copy of the local object but is really just a reference. Any change through out the game values/data would update the local object (which the shared object references) and Flash Player would write that into the shared object file when flush() is called or as soon as the shared object session ends – that is then swf file is closed.

The worst case here is that when you clear, reset or even delete your local game objects, the shared object which references to it will be affected and updated (as session has ended).

Also, even if you don’t experience the immediate issues when you do the above there, a problem might still arise when you load the shared object in a later time. This case might not be so common but is possible to experience if loading the shared object is sloppily made. You can have inconsistent object references where the object contained within loaded shared object is conflicting, a duplicate, or not referencing the correct object.

A better and safest way of using shared objects (something like):

Now you see that we store each valuable data only. We now transfer only the value of the variable not it’s reference. So while it’s fine to use objects and arrays on your shared objects, keeping be wary of how you do it, be careful.

You probably wouldn’t want to directly store instances of display objects, bitmapdata, functions, etc on share objects.


Use a localPath param on the getLocal() method

While passing the localPath parameter upon calling getLocal() is optional, although I would have to recommend using it by this time. In the case where you are building an independent system, prototypes, does not consider expansion/addons and will never be a part of a larger system, you can probably opt-out the localPath and never mind it.

So whats the issue of using and not using the localPath? By default, if you leave localPath by it’s default (null), it would use the the full path of the swf file (w/c includes the swf file itself) to store your shared objects.

#SharedObjects/<random code>/<domain>/<path>/<swf filename>.swf/<object name>.sol

  • Windows XP and Vista:
    • For Web sites: %APPDATA%\Macromedia\Flash Player\#SharedObjects\<random code>\<domain>\<path>\<object name>.sol
    • For AIR Applications, %APPDATA%\<AIR Application Reverse Domain Name>\Local Store\#SharedObjects\<flash filename>.swf\<object name>.sol
  • Mac OS X:
    • For Web sites, ~/Library/Preferences/Macromedia/Flash Player/#SharedObjects/<random code>/<domain>/<path from webserver>/<object name>.sol
    • For AIR Applications, ~/Library/Preferences/<AIR Application Name>/Local Store/#SharedObjects/<flash filename>.swf/<object name>.sol
  • Linux/Unix:
    • ~/.macromedia/Flash_Player/#SharedObjects/<random id>/<domain>/<path>/<flash filename>.swf/<object name>.sol

-taken from wiki

For example, I have a swf  at http://example.com/games/uploads/supergame.swf, and when it creates a shared object (SharedObject.getLocal("supergameData");), the SO can be found at #SharedObjects/<random code>/example.com/games/uploads/supergame.swf/supergameData.sol.

While it works well and fine without the localPath parameter, there are certain sites/APIs/services that change the game’s file name by using their automated uploads, feeds, etc as the file name is used in the path. When the developer tries to upload a bug fixed build (v1.0.1), the swf might have another file name hence a problem appears where the previous build’s (v1.0.0) shared object won’t be accessible from the new build due to security reasons.

Although your shared object would be secured against any another swf so it can’t read it, by setting a localPath parameter like “/games” or simply “/”, the shared object can now be read by another swf file within the same domain. Using “/games” would make the SO accessible for any swf within the game directory, while having “/” would make it accessible from any swf within the domain.

Again from the same example but using SharedObject.getLocal("supergameData","/");, makes the SO at #SharedObjects/<random code>/example.com/supergameData.sol. This would make it not dependant on the swf’s file name.

Note: If you set a localPath in which the path doesn’t physically exists as a (sub) directory of the domain the shared object won’t be stored. Luckily with AS3 you’ll be prompted with an Error #2134 when the SharedObject can’t be created, but for AS2, you have to be well aware of this.

 


Taking advantage of unique names for your shared object name

This is one is something that I have just learned through experimentation and will soon incorporate on my next projects. While setting the localPath parameter to “/” makes the SO accessible to other swfs, there is also a risk that your SO name might be the same with another swf. We wouldn’t want another swf to mess up or overwrite our SO file just because it both uses general and/or generic SO names like “saveData” or “gameData“.

The ultimate tip: Since the SO names allow certain special characters like / (slash), we take advantage of that! Together with the localPath “/”, what I recommend is do a standard setup for your SO names something like:

<company/developer name>/<game name>/<data name>

…will store your SO at something like: #SharedObjects/<random code>/<domain>/#jaycsantos/neolithic/gameData.sol

Although I still have to confirm this with Mac OS and Linux machines. Drop me a line if you find it working with those.

Now ain’t that a little more organized than having all SO on the “/” of the domain!


Prompt and request for shared object size limit increase when your game needs it

localstorageThere are cases when your shared object has a lot of data in it and it could exceed the default 100kb limit of a user. If this happens, it may cause your shared objects to be written but not read. I have read quite a few times where issues of reading/writing of share objects were resolved by prompting a request to increase local storage limit. This is where the flush() method takes it stand.

Immediately writes a locally persistent shared object to a local file.
AS3
flush
(minDiskSpace:int = 0):String

minDiskSpace:int (default = 0) — The minimum disk space, in bytes, that must be allotted for this object.

AS2
flush([minDiskSpace:Number]):Object

minDiskSpace:Number [optional] – An integer specifying the number of bytes that must be allotted for this object. The default value is 0.

With AS3, the flush() method basically does the same thing as with AS2 including their parameters, the only difference made was their return values. AS3 returns a string constant but AS2 returns an object; still their purpose is the same, only the data types have changed.

AS3:
Returns 

String — Either of the following values:
  • SharedObjectFlushStatus.PENDING: The user has permitted local information storage for objects from this domain, but the amount of space allotted is not sufficient to store the object. Flash Player prompts the user to allow more space. To allow space for the shared object to grow when it is saved, thus avoiding a SharedObjectFlushStatus.PENDING return value, pass a value for minDiskSpace.
  • SharedObjectFlushStatus.FLUSHED: The shared object has been successfully written to a file on the local disk.

AS2:
Returns

Object - A Boolean value: true or false; or a string value of “pending“, as described in the following list:
  • If the user has permitted local information storage for objects from this domain, and the amount of space allotted is sufficient to store the object, this method returns true. (If you have passed a value for minimumDiskSpace, the amount of space allotted must be at least equal to that value for true to be returned).
  • If the user has permitted local information storage for objects from this domain, but the amount of space allotted is not sufficient to store the object, this method returns “pending“.
  • If the user has permanently denied local information storage for objects from this domain, or if Flash cannot save the object for any reason, this method returns false.
- taken from livedocs (as2 & as3)

So saying simply, if you know your game’s shared object will be greater than a few kbs, you should consider using flush(minDiskSpace) with as your expect maximum size for your data. Why maximum? – think of it as your allowance. Now don’t even consider 2mb or more as an allowance if it’ll never even reach it, play your game at it’s fullest and see what is the size of the shared object using the getSize() method (as2) and the property size (as3) then add around 5-10% of it’s size for an allowance.

Conclusion

After all I have read about using SharedObject is as simple as it could be, in reality it can be a whole lot more than spinning circles for all of what you would need. Well yes, storing a string and recovering it is a piece of icing but storing a complete game state/progress is a new kind of cake to bake. Basically what I have written here are tips and suggestions that I have experienced and with what I have experimented. Either it be accurate or I know not, using SharedObjects has its own work around and is not as simple as most people have said like building a chair, while yes it is not the most complicated but it could be a big question mark on the head for someone new or not familiar to its purpose. I might even consider making a SaveManager or the like but that would be when I get to it. Well, that’s it, this one is quite long but I know it’ll help!

  • farhad

    write a simple path for local

  • Tareq

    so.clear();///—-Is the method but If you once do it you your all of
    your .sol will data will be deleted. If you want to view high score
    then, it shows error.

    So don’t clear date, change .sol value.

    ///—This is an example of saving high score on enter frame event listener using AS 3.0
    Suppose
    you have main game into second frame and display high score into first
    frame. In the second frame you’ve a variable called ‘var
    gameScore:Number = 0; When game starts, ‘gameScore’ increases. And
    there is also a variable in the second frame called ‘ var
    saveCookie:Boolean = false;’ and ‘ var gameOver:Boolean = false;’ When
    game is over, ‘ saveCookie = true ‘ NB: When the gameScore is grather
    than previous gameScore, it automatically save date.

    ///—-Below example should type into second frame
    var var gameScore:Number = 0;
    var saveCookie:Boolean = false;
    var gameOver:Boolean = false;

    addEventListener(Event.ENER_FRAME, enterFrameHandler)

    function enterFrameHandler(e:Event){

    gameScore = 50;///—-suppose game score is 50

    if(gameOver){

    saveCookie = true;///—-when game is over
    }
    }

    ///—-Below example should type in first frame

    var removeCookie:Boolean = false;
    var ziroValue:Number = 0;
    var saveScore:Number;

    var so:sharedObject = sharedObject.getLocal(“String”, “/”);

    btn.addEventListener(MouseEvent.CLICK, clearCookie);
    function clearCookie(e:MouseEvent):void{
    removeCookie = true;
    }

    addEventListener(Event.ENTER_FRAME, saveCookie);
    function saveCookie(e:Event):void{

    if(saveCookie){

    if(gameScore>saveScore){/////——–Here game score shows after game is over

    so.data.highScore = saveScore ;
    }
    }
    if(removeCookie){

    so.data.highScore = ziroValue;
    }
    }

    ////—–If you think it is helpful, please visit and have some comment

    https://www.facebook.com/flashbdtutorial?ref_type=bookmark