I'm creating an HTML5 2D game and I want to request each asset only once and then store them in the user's filesystem, I'm using localStorage for this task, however AFAIK it has a limit of 5mb per origin, (my whole game will have more than that), and I want to know how to store my game assets in the user's machine without that limitation, this is what I've done until now:
items.js:
/**
* Copyright 2014 - Edgar Alexander Franco.
*
* @author Edgar Alexander Franco
* @version 1.0.0
*/
var items = [
{
name : 'characters_scott',
url : './img/game/characters/scott',
type : 'png'
},
{
name : 'map_1',
url : './img/game/map/1',
type : 'jpg'
},
{
name : 'map_2',
url : './img/game/map/2',
type : 'jpg'
}
];
Resource.js
/**
* Copyright 2014 - Edgar Alexander Franco.
*
* @author Edgar Alexander Franco
* @version 1.0.0
*/
var Resource = (function () {
var self = {};
self.get = {};
self.load = function (items) {
var xhr = (typeof XMLHttpRequest != 'undefined') ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP') ;
var item, content, mime;
for (var i in items) {
item = items[ i ];
content = localStorage.getItem(item.url);
if (content == null) {
xhr.open('GET', item.url, false);
xhr.send();
content = xhr.responseText;
localStorage.setItem(item.url, content);
}
if (item.type != 'audio') {
mime = (item.type == 'jpg') ? 'image/jpeg' : 'image/png' ;
self.get[ item.name ] = new Image();
self.get[ item.name ].src = 'data:' + mime + ';base64,' + content;
} else {
// Not yet...
}
}
}
return self;
})();
The code from above works great, but doesn't cover my needs, as you can see I'm using localStore and it has it's limitations, I want to adapt the same code but for an unlimited storage, any ideas?
After a long research I concluded that IndexedDB is probably the most capable local storage we can find for this task, so, I re-designed my code and now this is the full class:
/**
* Copyright 2014 - Edgar Alexander Franco.
*
* @author Edgar Alexander Franco
* @version 1.0.0
*/
var Resources = (function () {
var self = {};
self.get = {};
self.DOWNLOADED = 1;
self.LOADED = 2;
var DB_NAME = 'evilition';
var TABLE_NAME = 'resources';
self.audioType = (document.createElement('audio').canPlayType('audio/mp3') == '') ? '.ogg' : '.mp3' ;
/**
* Load the assets from the server / filesystem depending if each is cached or not.
*
* @param {object} resources Resources of the game.
* @param {function} callback1 Function to be called on progress.
* @param {function} callback2 Function to be called on error.
* @param {function} callback3 Function to be called once the resources are loaded.
*/
self.load = function (resources, callback1, callback2, callback3) {
var request = indexedDB.open(DB_NAME, 3);
request.onerror = function (evt) {
callback2({});
}
request.onupgradeneeded = function (evt) {
var db = evt.target.result;
var table = db.createObjectStore(TABLE_NAME, {
keyPath : 'name'
});
table.createIndex('name', 'name', {
unique : true
});
}
request.onsuccess = function (evt) {
function loadResource () {
var resource = resources[ i ];
var request = objectStore.get(resource.name);
request.onerror = function (evt) {
callback2(resource);
}
request.onsuccess = function (evt) {
var progress = Math.round(((i + 1) * 100) / total);
var content;
if (typeof request.result == 'undefined') {
if (resource.type == 'audio') {
resource.path += self.audioType;
}
xhr.open('GET', resource.path + '.b64', false);
xhr.send();
if (xhr.readyState == 4 && xhr.status == 200) {
content = xhr.responseText;
objectStore.add({
name : resource.name,
content : content
});
callback1(resource, progress, self.DOWNLOADED);
} else {
callback2(resource);
return;
}
} else {
content = request.result.content;
callback1(resource, progress, self.LOADED);
}
var mime;
if (resource.type != 'audio') {
mime = (resource.type == 'jpg') ? 'image/jpeg' : 'image/png' ;
self.get[ resource.name ] = new Image;
} else {
mime = (self.audioType == '.mp3') ? 'audio/mp3' : 'audio/ogg' ;
self.get[ resource.name ] = new Audio;
}
self.get[ resource.name ].src = 'data:' + mime + ';base64,' + content;
i++;
if (i == total) {
db.close();
callback3();
} else {
loadResource();
}
}
}
var db = evt.target.result;
var objectStore = db.transaction(TABLE_NAME, 'readwrite').objectStore(TABLE_NAME);
var xhr = (typeof XMLHttpRequest != 'undefined') ? new XMLHttpRequest : new ActiveXObject('Microsoft.XMLHTTP') ;
var total = resources.length;
var i = 0;
loadResource();
}
}
/**
* Delete the resources database requiring the creation of a new one in the next load.
*/
self.clearLocalCache = function () {
indexedDB.deleteDatabase(DB_NAME);
}
return self;
})();
Thank you Gregor for you recommendation :)