Showing posts with label localStorage. Show all posts
Showing posts with label localStorage. Show all posts

Wednesday, January 8, 2014

Asynchronous Storage (Game Programming)

Recently, I wrote this post (http://firefoxosgaming.blogspot.com/2014/01/local-storage-game-programming.html) on Local Storage, a simple way to store data on your phone between sessions. Simple, easy, works on all browsers. Or so I thought! But storage turns out to be more complicated.



I got some comments from Gabriele Svelto and Dietrich Ayala. They told me that Local Storage is not recommended for Firefox OS. Since they are both on the FxOS team, that's good enough for me. Turns out that for Firefox OS, Local Storage is a performance and I/O problem.

This isn't terribly surprising, giving how critical computing is on a phone with limited resources. One thing I've learned over the years is that synchronous programming can be a problem. What is synchronous programming? It's where you make a call and wait until the call is returned. That's an oversimplified answer, but when things are tight, you don't want to wait.

So if you use Local Storage, your call will take place and nothing else can happen while you are doing it. That can be fine if your calls are small and there's nothing else going on and you have plenty of resources. But you can't afford to have your phone freeze up.

The opposite of synchronous programming is ... wait for it, asynchronous programming. What a difference that first letter of the alphabet makes (if you're using the English alphabet - for the tinkopar, it would be t). Asynchronous programming says that you can make a call and then go do something else. You register a callback (a separate function) and when your call goes through, the function will respond and take action. How cool is that?

So Local Storage is out. Don't use it on Firefox OS. What's the alternative? If you are doing any kind of serious game, you want to let the player save their game for another day. And memorizing codes went out with old game consoles. Well, there's another way to save data, and its name is IndexedDB. This is a more recent way to save data, and is asynchronous. If you want to know more about it, see this page on MDN https://developer.mozilla.org/en-US/docs/IndexedDB. This has been around in Firefox for a long time, but other browsers were slow to catch up and Safari still isn't there at all and some of the other mobile browsers are AWOL as well. But it works in Firefox OS and works well.

However, the API for IndexedDB is a bit more complicated. It uses real database concepts and you need to understand how it works. It uses transactions and the DOM, but not SQL (I hate SQL, don't you?) Read more about it here on MDN: https://developer.mozilla.org/en-US/docs/IndexedDB/Basic_Concepts_Behind_IndexedDB

So you can't use Local Storage and you may not want to use IndexedDB if you're starting out. What is a game programmer to do?

Riding to the rescue of overwhelmed developers everywhere, Mozilla has made a compromise API that is almost as simple as Local Storage, but uses IndexedDB under the hood. This technology comes in the form of a small JavaScript library called async_storage.js. You can get it here: https://github.com/mozilla-b2g/gaia/blob/master/shared/js/async_storage.js. Copy it all and save it as a file named async_storage.js. But before you do, make sure you understand that this is open source under the Apache license and that you follow all the rules. If you don't know those rules, read them here: https://github.com/mozilla-b2g/gaia/blob/master/LICENSE.

Once you've got your async_storage.js file prepared, save it to your project. To use this cool piece of software, insert this line into your web page:

    <script src="async_storage.js"></script>

If the async_storage.js file isn't in your root, change the line above to tell the packager where it is.

What does this new asynchronous storage API do? Pretty much exactly what Local Storage did. It uses the same concepts and names, but with one small difference. Every method must include an extra function call as a callback. And there are no properties now, only methods.

Maybe an example would be nice. I'll use the same example I used in my Local Storage post: http://firefoxosgaming.blogspot.com/2014/01/local-storage-game-programming.html. Here's what the UI looks like.



Enter a key and value in the top two boxes and touch Save to ... save. Retrieve your data with a key you enter in the third box and touch Retrieve. Clear the storage with Clear and get the number of key/value pairs by touching the Number of Items button. Each action will produce a message at the top of the screen (where it says "Watch this space!"

And here's the new code, run and tested on my brand new shiny Geeksphone Peak.

<!DOCTYPE HTML>
<html>
 
  <head>
    <meta charset="UTF-8">
    <title>
      Async Storage
    </title>
   
    <script src="async_storage.js"></script>
 
    <script type="text/javascript">

        // Load event listener
      window.addEventListener("load", getLoaded, false);
       
      // Function called on page load.
      function getLoaded() { 
     
        // Save button listener
        myButtonSave.addEventListener(
          "mousedown", myClickSave,false);

        // Retrieve button listener
        myButtonLoad.addEventListener(
          "mousedown", myClickLoad,false);
         
        // Clear button listener
        myButtonClear.addEventListener(
          "mousedown", myStorageClear,false);
         
        // Length button listener
        myButtonLength.addEventListener(
          "mousedown", myStorageLength,false);       
         
        // Background color
        document.body.style.backgroundColor = "GreenYellow";

            // Output to console.
        console.log("The page is loaded.");
      }
     
      // Save button event handler
      function myClickSave() {
     
        // Save data with key.
        asyncStorage.setItem(myKey.value, myData.value,
          function() {
            console.log("New value stored.");
            info.innerHTML = "Input - Key: " + myKey.value +
              " Value: " + myData.value;      
        });     
      }
     
      // Retrieve button event handler
      function myClickLoad() {
     
        // Get data from key.
        asyncStorage.getItem(getKey.value,
          function(myValue) {
            console.log("The value is ", myValue);
            myValue = "Retrieve - Key: " + getKey.value +
              " Value: " + myValue;          
            info.innerHTML = myValue;
        });           
      }
     
      // Clear button event handler.
      function myStorageClear() {
     
        // Clear the storage.
        asyncStorage.clear(function() {
          console.log("Storage cleared!");
          info.innerHTML = "Storage cleared!";
        });
      }
 
      // Number of Items button event handler.
      function myStorageLength() {

        // Get the number of items in storage.
        asyncStorage.length(function(myLength) {
          console.log("The number of items is ",  length);
          info.innerHTML = "Items: " + myLength;        
        });      
      }
      
    </script>
  </head>
 
  <body>
 
    <p id="info">Watch this space!</p>
 
    <p> Enter key here</p>
    <input id="myKey" autofocus>
    <br>
   
    <p> Enter data here</p>
    <input id="myData">
    <br>
   
    <button id="myButtonSave">Save</button>
   
    <p> Retrieve data with this key</p>
    <input id="getKey"><br>
   
    <button id="myButtonLoad">Retrieve</button>
    <br><br>
   
    <button id="myButtonClear">Clear</button>
    <br><br>
   
    <button id="myButtonLength">Number of Items</button>

  </body>

</html>


Short and simple. Enter some key/value pairs, retrieve them, add more, count them, and clear. If you don't clear the database, and you shut down and come back tomorrow, the data will still be there!

The API details are actually in the JavaScript file, but here's a quick summary:

setItem - saves an item with a key/value pair.

getItem - gets an item value with a specific key.

removeItem - removes an item with a specific key.

clear - clears the database.

length - tells you how many key/value pairs are in the database.

key - gives you the value at a specific location in the database.

Note on the key API: the order stored isn't necessary guaranteed, so use this only to loop through all the items.

Sounds pretty much like the Local Storage API. But there are two differences:

1. You must use the asyncStorage object for all your calls and attach your methods to it.

2. Each method must have an additional parameter that specifies the function that will be called when the asynchronous call is done.

This last one is cool and not that hard. Here's an example:

Instead of typing:

        asyncStorage.clear();

You must type:

       asyncStorage.clear(myClearCallback);

Then create a function called myClearCallback() that will be called when the asynchronous call returns. You don't need to call it myClearCallback, any function name is fine as long as it is unique and has a function to go to.

If you don't want to create a separate function for your callback, you can use an anonymous function. Anonymous functions look like this and you use them inside the parameter. Here is an anonymous function:

       function() {
          // Stuff here you want to have happen
          // when the asynchronous call returns.
       }

It looks like this if you insert it into the asyncStorage.clear function:


        asyncStorage.clear(function() {
          console.log("Storage cleared!");
          info.innerHTML = "Storage cleared!";
        });
 

This displays a console.log message and a UI message when the asynchronous call returns.

The syntax is a bit overcrowded with punctuation, but you can always have a separate callback function if you want. By the way, callbacks are called by event listeners, I just haven't called them callbacks before.

This is a little different from programming you may have seen, in that you can no longer type something like:

       myValue = localStorage(key);

Values are not returned directly. That would be synchronous! So you must insert a callback function in your parameters. If the localStorage didn't have any parameters (like clear and length), you have to add one (and length is now a function, not a property). If localStorage had parameters, add one more!

I used anonymous functions for my four functions. Here is how each bit looks:

setItem

        // Save data with key.
        asyncStorage.setItem(myKey.value, myData.value,
          function() {
            console.log("New value stored.");
            info.innerHTML = "Input - Key: " + myKey.value +
              " Value: " + myData.value;      
        }); 
    

getItem

        // Get data from key.
        asyncStorage.getItem(getKey.value,
          function(myValue) {
            console.log("The value is ", myValue);
            myValue = "Retrieve - Key: " + getKey.value +
              " Value: " + myValue;          
            info.innerHTML = myValue;
        });       
    

clear

        // Clear the storage.
        asyncStorage.clear(function() {
          console.log("Storage cleared!");
          info.innerHTML = "Storage cleared!";
        });


length

        // Get the number of items in storage.
        asyncStorage.length(function(myLength) {
          console.log("The number of items is ",  length);
          info.innerHTML = "Items: " + myLength;        
        });  


myKey  and myData are defined by their input boxes.

So this isn't really that hard to use and lets you be asynchronous with your data. The folks at Mozilla were geniuses to think of a way to be asynchronous with an API that was synchronous. All you need to do is load in the tiny library and add a callback for each function you use.

NOTE: The callbacks are not required for setItem() and clear(). But I recommend using them so you can be sure your items are saved or cleared. Remember the poster on Mulder's wall: "Trust No One." You will need the callbacks for getItem() and length() because otherwise you won't get the data you requested.

NOTE: After more discussion, the callbacks don't tell you much more than something worked. If it didn't work, something very wrong happened. But usually they work. If you need more confidence about success and failure, check out my post on IndexedDB at http://firefoxosgaming.blogspot.com/2014/01/indexeddb-game-programming.html. async_storage.js uses IndexedDB under the hood so if you want more than simple, check out IndexedDB.



Wednesday, January 1, 2014

Local Storage (Game Programming)

One of the things you might want to do when you are programming games is to save the game for the player between sessions. After all, phone games are all about short bursts of play that may need to be interrupted. A player is standing in line and ... time to leave the game. But coming back tomorrow is definitely a good thing.



There are two ways to save information to memory for later. The easiest is called Local Storage and is supported and I will cover it here. There is another, more complicated way called IndexedDB that is also supported, and allows more complex database storage management, and that will be the subject of a later post. But for the simple games I'm exploring, Local Storage is easy to use and doesn't require understanding of database theory. Local Storage has cousins named global and session, and you can read about all the storage family at https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage.

Local storage is very simple. You have some text data that you want to store. You store it with a specific name and retrieve it later by using that same name (called a key). This isn't necessarily data you want to sort through like you would with a database (that's what IndexedDB is for). You want to save and load, and that's it. The saving and loading takes place in a manner called synchronous, which means everything waits while you save your data. But since the data isn't across a network or to hard disk, the saving is pretty fast. You can't store more than 5MB, so this API is strictly for small potatoes.

If you know what cookies are, this API does much the same thing, but cookies are limited to 2KB. So use this for saving the status of your game, not a collection of last year's Twitter posts.

So, here's the code for a simple example of Local Storage.

<!DOCTYPE HTML>
<html>
 
  <head>
    <meta charset="UTF-8">
    <title>
      Local Storage
    </title>
 
    <script type="text/javascript">

        // Load event listener
      window.addEventListener("load", getLoaded, false);
       
      // Function called on page load.
      function getLoaded() { 
     
        // Save button listener
        myButtonSave.addEventListener(
          "mousedown", myClickSave,false);

        // Retrieve button listener
        myButtonLoad.addEventListener(
          "mousedown", myClickLoad,false);
         
        // Clear button listener
        myButtonClear.addEventListener(
          "mousedown", myStorageClear,false);
         
        // Length button listener
        myButtonLength.addEventListener(
          "mousedown", myStorageLength,false);       
         
        // Background color
        document.body.style.backgroundColor = "GreenYellow";

            // Output to console.
        console.log("The page is loaded.");
      }
     
      // Save button event handler
      function myClickSave() {
     
        // Display save inputs.
        info.innerHTML = "Input - Key: " + myKey.value +
          " Value: " + myData.value;
         
        // Save to local storage.
        localStorage.setItem(myKey.value, myData.value);     
      }
     
      // Retrieve buton event handler
      function myClickLoad() {
     
        // Get data from key.
        myValue = localStorage.getItem(getKey.value);
       
        // Create retrieve text string.
        myValue = "Retrieve - Key: " + getKey.value +
          " Value: " + myValue;
       
        // Display retrieve text string.
        info.innerHTML = myValue;  
      }
     
      // CLear the storage.
      function myStorageClear() {
     
        // Clear the storage.
        localStorage.clear();

        // Display storage cleared message.
        info.innerHTML = "Storage cleared!";   
      }
 
      // Determine how many storage items.
      function myStorageLength() {
     
        // Get the number of items.
        myLength = localStorage.length;
       
        // Display the number of items.
        info.innerHTML = "Items: " + myLength;
      }
 
      
    </script>
  </head>
 
  <body>
 
    <p id="info">Watch this space!</p>
 
    <p> Enter key here</p>
    <input id="myKey" autofocus>
    <br>
   
    <p> Enter data here</p>
    <input id="myData">
    <br>
   
    <button id="myButtonSave">Save</button>
   
    <p> Retrieve data with this key</p>
    <input id="getKey"><br>
   
    <button id="myButtonLoad">Retrieve</button>
    <br><br>
   
    <button id="myButtonClear">Clear</button>
    <br><br>
   
    <button id="myButtonLength">Number of Items</button>

  </body>

</html>


When you load it into the Firefox OS Simulator or into your Firefox OS phone, it looks like this:



To get started, press on the box below "Enter key here" and enter a word that will be the key for your data. Next, go to the box below it and enter the data you want to save. Then press the button that says "Save". I picked "a" for the key and "b" for the data, but any text (numbers and letters will do). The results should look like this:


The values you entered should be at the top (where it used to say "Watch this space!"), You've now entered data with key. The data will stay on your phone until you clear it out. Kill the app and when you bring it back, the data will be there. (You kill an app by doing a long press on the Home button and clicking on the upper left of the app you want to kill.)

Next, enter another key-value pair. I picked "c" and "d" and pressed Save. My new results should look like this:


We now have two key-value pairs. If you'd like to verify this, press the "Number of Items" button and you should see this:


And now the exciting part. Press on the box just below "Retrieve data with this key" and type in a key that is associated with some data. I picked the key "a" and pressed the "Retrieve" button. Here is what I got:


The key "a" retrieved the value "b". Remember, you can use any text for the key and any text for the data you store. If you want to store something that is not text, you must convert it to a number; easy to do in JavaScript. In my example, if you type the number 1, it will be treated as text.

Next, when you're finished with your testing, clear out the storage by pressing on the "Clear" button.


You'll see the message at the top, "Storage cleared!" And finally, to see that the storage is gone, press on the "Number of items" and you'll get this:


The number of items is 0!

The code is actually pretty simple. It follows the pattern that I've been using in other examples. The buttons are created in the body of the page (and I just laid them out loosely with break tags). Each button has an event listener associated with it, and a function that will handle the event.

Local Storage API

The code for Local Storage is pretty simple. You use the localStorage object and its methods and properties. Here's a quick list of the methods and properties:

     setItem(key, data) - stores data by using a key

     getItem(key) - retrieves data by using a key.

     clear() - clears the storage. Note that this is a method and needs those parentheses.

     length - indicates the number of items. Note that this is a property and does not use parentheses.

The rest of the code uses the value of the input boxes (for example, the box with the id of "getKey" will be used in getItem as getKey.value) and display paragraphs (with the id of "info" will be used as info.innerHTML).

All in all, very simple. But crucial for making your player happy. They want to come back and play again, so make it easy for them! The Local Storage API is easy to use and works. So use it!

This post concludes my thoughts on what are the basics needed to create a game in HTML5 with Firefox OS. Moving, colliding, tilting, music, touching, etc. There's always more, but these are the simple tasks that most games will use. It's been fun for me to explore each one and I hope it's been interesting for you. Blogger tells me I've had a total of 12,000 page views since I started this blog in early October and I've written 60 posts. I don't know if that's a lot or not very much, but like I say, I'm having fun!

Next I'll start by making a complete (but small) game. I may even enter it in the One Game a Month contest http://www.onegameamonth.com/. I'd like to spend the next year exploring various game types (all simple and small) and I'm also thinking of exploring some of the game libraries such as Phaser (http://phaser.io/). After that, I'd like to talk about mechanics, a bit like Three Hundred Mechanics (http://www.squidi.net/three/) but at a lower level. And maybe put some games in the Marketplace. But right now I'm having fun just sorting out ideas and focusing on Firefox OS. I also might write a book. Well, that's a lot of stuff to do, but I am obviously interested in all this and having a lot of fun.

Stay tuned, but not iTuned!