Improve Assets, use Futures and Events

This commit is contained in:
Joshua Granick
2015-09-04 09:35:00 -07:00
parent 0e7b5fa056
commit 9b266891dd
3 changed files with 253 additions and 306 deletions

View File

@@ -1,4 +1,7 @@
package lime; package lime;
import lime.app.Event;
import lime.app.Promise;
import lime.app.Future;
#if !macro #if !macro
@@ -33,6 +36,7 @@ class Assets {
public static var cache = new AssetCache (); public static var cache = new AssetCache ();
public static var libraries (default, null) = new Map <String, AssetLibrary> (); public static var libraries (default, null) = new Map <String, AssetLibrary> ();
public static var onChange = new Event<Void->Void> ();
private static var initialized = false; private static var initialized = false;
@@ -330,75 +334,6 @@ class Assets {
} }
/**
* Gets an instance of an embedded streaming sound
* @usage var sound = Assets.getMusic("sound.ogg");
* @param id The ID or asset path for the music track
* @return A new Sound object
*/
/*public static function getMusic (id:String, useCache:Bool = true):Dynamic {
initialize ();
#if (tools && !display)
if (useCache && cache.enabled && cache.sound.exists (id)) {
var sound = cache.sound.get (id);
if (isValidSound (sound)) {
return sound;
}
}
var libraryName = id.substring (0, id.indexOf (":"));
var symbolName = id.substr (id.indexOf (":") + 1);
var library = getLibrary (libraryName);
if (library != null) {
if (library.exists (symbolName, cast AssetType.MUSIC)) {
if (library.isLocal (symbolName, cast AssetType.MUSIC)) {
var sound = library.getMusic (symbolName);
if (useCache && cache.enabled) {
cache.sound.set (id, sound);
}
return sound;
} else {
trace ("[Assets] Sound asset \"" + id + "\" exists, but only asynchronously");
}
} else {
trace ("[Assets] There is no Sound asset with an ID of \"" + id + "\"");
}
} else {
trace ("[Assets] There is no asset library named \"" + libraryName + "\"");
}
#end
return null;
}*/
/** /**
* Gets the file path (if available) for an asset * Gets the file path (if available) for an asset
* @usage var path = Assets.getPath("image.jpg"); * @usage var path = Assets.getPath("image.jpg");
@@ -617,10 +552,12 @@ class Assets {
} }
public static function loadAudioBuffer (id:String, handler:AudioBuffer -> Void, useCache:Bool = true):Void { public static function loadAudioBuffer (id:String, useCache:Bool = true):Future<AudioBuffer> {
initialize (); initialize ();
var promise = new Promise<AudioBuffer> ();
#if (tools && !display) #if (tools && !display)
if (useCache && cache.enabled && cache.audio.exists (id)) { if (useCache && cache.enabled && cache.audio.exists (id)) {
@@ -629,8 +566,8 @@ class Assets {
if (isValidAudio (audio)) { if (isValidAudio (audio)) {
handler (audio); promise.complete (audio);
return; return promise.future;
} }
@@ -644,46 +581,41 @@ class Assets {
if (library.exists (symbolName, cast AssetType.SOUND)) { if (library.exists (symbolName, cast AssetType.SOUND)) {
var future = library.loadAudioBuffer (symbolName);
if (useCache && cache.enabled) { if (useCache && cache.enabled) {
library.loadAudioBuffer (symbolName, function (audio:Dynamic):Void { future.onComplete (function (audio) cache.audio.set (id, audio));
cache.audio.set (id, audio);
handler (audio);
});
} else {
library.loadAudioBuffer (symbolName, handler);
} }
return; promise.completeWith (future);
} else { } else {
trace ("[Assets] There is no audio asset with an ID of \"" + id + "\""); promise.error ("[Assets] There is no audio asset with an ID of \"" + id + "\"");
} }
} else { } else {
trace ("[Assets] There is no asset library named \"" + libraryName + "\""); promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
} }
#end #end
handler (null); return promise.future;
} }
public static function loadBytes (id:String, handler:ByteArray -> Void):Void { public static function loadBytes (id:String):Future<ByteArray> {
initialize (); initialize ();
var promise = new Promise<ByteArray> ();
#if (tools && !display) #if (tools && !display)
var libraryName = id.substring (0, id.indexOf (":")); var libraryName = id.substring (0, id.indexOf (":"));
@@ -694,32 +626,70 @@ class Assets {
if (library.exists (symbolName, cast AssetType.BINARY)) { if (library.exists (symbolName, cast AssetType.BINARY)) {
library.loadBytes (symbolName, handler); promise.completeWith (library.loadBytes (symbolName));
return;
} else { } else {
trace ("[Assets] There is no String or ByteArray asset with an ID of \"" + id + "\""); promise.error ("[Assets] There is no String or ByteArray asset with an ID of \"" + id + "\"");
} }
} else { } else {
trace ("[Assets] There is no asset library named \"" + libraryName + "\""); promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
} }
#end #end
handler (null); return promise.future;
} }
public static function loadImage (id:String, handler:Image -> Void, useCache:Bool = true):Void { public static function loadFont (id:String):Future<Font> {
initialize (); initialize ();
var promise = new Promise<Font> ();
#if (tools && !display)
var libraryName = id.substring (0, id.indexOf (":"));
var symbolName = id.substr (id.indexOf (":") + 1);
var library = getLibrary (libraryName);
if (library != null) {
if (library.exists (symbolName, cast AssetType.FONT)) {
promise.completeWith (library.loadFont (symbolName));
} else {
promise.error ("[Assets] There is no Font asset with an ID of \"" + id + "\"");
}
} else {
promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
}
#end
return promise.future;
}
public static function loadImage (id:String, useCache:Bool = true):Future<Image> {
initialize ();
var promise = new Promise<Image> ();
#if (tools && !display) #if (tools && !display)
if (useCache && cache.enabled && cache.image.exists (id)) { if (useCache && cache.enabled && cache.image.exists (id)) {
@@ -728,8 +698,8 @@ class Assets {
if (isValidImage (image)) { if (isValidImage (image)) {
handler (image); promise.complete (image);
return; return promise.future;
} }
@@ -743,45 +713,40 @@ class Assets {
if (library.exists (symbolName, cast AssetType.IMAGE)) { if (library.exists (symbolName, cast AssetType.IMAGE)) {
var future = library.loadImage (symbolName);
if (useCache && cache.enabled) { if (useCache && cache.enabled) {
library.loadImage (symbolName, function (image:Image):Void { future.onComplete (function (image) cache.image.set (id, image));
cache.image.set (id, image);
handler (image);
});
} else {
library.loadImage (symbolName, handler);
} }
return; promise.completeWith (future);
} else { } else {
trace ("[Assets] There is no Image asset with an ID of \"" + id + "\""); promise.error ("[Assets] There is no Image asset with an ID of \"" + id + "\"");
} }
} else { } else {
trace ("[Assets] There is no asset library named \"" + libraryName + "\""); promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
} }
#end #end
handler (null); return promise.future;
} }
public static function loadLibrary (name:String, handler:AssetLibrary -> Void):Void { public static function loadLibrary (name:String):Future<AssetLibrary> {
initialize(); initialize ();
var promise = new Promise<AssetLibrary> ();
#if (tools && !display) #if (tools && !display)
@@ -792,89 +757,27 @@ class Assets {
var info = Json.parse (data); var info = Json.parse (data);
var library = Type.createInstance (Type.resolveClass (info.type), info.args); var library = Type.createInstance (Type.resolveClass (info.type), info.args);
libraries.set (name, library); libraries.set (name, library);
library.eventCallback = library_onEvent; library.onChange.add (onChange.dispatch);
library.load (handler); promise.completeWith (library.load ());
return;
} else { } else {
trace ("[Assets] There is no asset library named \"" + name + "\""); promise.error ("[Assets] There is no asset library named \"" + name + "\"");
} }
#end #end
handler (null); return promise.future;
} }
/*public static function loadMusic (id:String, handler:Dynamic -> Void, useCache:Bool = true):Void { public static function loadText (id:String):Future<String> {
initialize (); initialize ();
#if (tools && !display) var promise = new Promise<String> ();
if (useCache && cache.enabled && cache.sound.exists (id)) {
var sound = cache.sound.get (id);
if (isValidSound (sound)) {
handler (sound);
return;
}
}
var libraryName = id.substring (0, id.indexOf (":"));
var symbolName = id.substr (id.indexOf (":") + 1);
var library = getLibrary (libraryName);
if (library != null) {
if (library.exists (symbolName, cast AssetType.MUSIC)) {
if (useCache && cache.enabled) {
library.loadMusic (symbolName, function (sound:Dynamic):Void {
cache.sound.set (id, sound);
handler (sound);
});
} else {
library.loadMusic (symbolName, handler);
}
return;
} else {
trace ("[Assets] There is no Sound asset with an ID of \"" + id + "\"");
}
} else {
trace ("[Assets] There is no asset library named \"" + libraryName + "\"");
}
#end
handler (null);
}*/
public static function loadText (id:String, handler:String -> Void):Void {
initialize ();
#if (tools && !display) #if (tools && !display)
@@ -886,24 +789,23 @@ class Assets {
if (library.exists (symbolName, cast AssetType.TEXT)) { if (library.exists (symbolName, cast AssetType.TEXT)) {
library.loadText (symbolName, handler); promise.completeWith (library.loadText (symbolName));
return;
} else { } else {
trace ("[Assets] There is no String asset with an ID of \"" + id + "\""); promise.error ("[Assets] There is no String asset with an ID of \"" + id + "\"");
} }
} else { } else {
trace ("[Assets] There is no asset library named \"" + libraryName + "\""); promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
} }
#end #end
handler (null); return promise.future;
} }
@@ -912,13 +814,21 @@ class Assets {
if (libraries.exists (name)) { if (libraries.exists (name)) {
unloadLibrary (name); if (libraries.get (name) == library) {
return;
} else {
unloadLibrary (name);
}
} }
if (library != null) { if (library != null) {
library.eventCallback = library_onEvent; library.onChange.add (library_onChange);
} }
@@ -938,8 +848,8 @@ class Assets {
if (library != null) { if (library != null) {
cache.clear (name + ":"); cache.clear (name + ":");
library.onChange.remove (library_onChange);
library.unload (); library.unload ();
library.eventCallback = null;
} }
@@ -957,14 +867,10 @@ class Assets {
private static function library_onEvent (library:AssetLibrary, type:String):Void { private static function library_onChange ():Void {
if (type == "change") { cache.clear ();
onChange.dispatch ();
cache.clear ();
//dispatchEvent (new Event (Event.CHANGE));
}
} }
@@ -975,7 +881,7 @@ class Assets {
class AssetLibrary { class AssetLibrary {
public var eventCallback:Dynamic; public var onChange = new Event<Void->Void> ();
public function new () { public function new () {
@@ -1020,13 +926,6 @@ class AssetLibrary {
} }
//public function getMusic (id:String):Dynamic /*Sound*/ {
// return getSound (id);
//}
public function getPath (id:String):String { public function getPath (id:String):String {
return null; return null;
@@ -1073,73 +972,60 @@ class AssetLibrary {
} }
private function load (handler:AssetLibrary -> Void):Void { private function load ():Future<AssetLibrary> {
handler (this); return new Future<AssetLibrary> (function () return this);
} }
public function loadAudioBuffer (id:String, handler:AudioBuffer -> Void):Void { public function loadAudioBuffer (id:String):Future<AudioBuffer> {
handler (getAudioBuffer (id)); return new Future<AudioBuffer> (function () return getAudioBuffer (id));
} }
public function loadBytes (id:String, handler:ByteArray -> Void):Void { public function loadBytes (id:String):Future<ByteArray> {
handler (getBytes (id)); return new Future<ByteArray> (function () return getBytes (id));
} }
public function loadFont (id:String, handler:Font -> Void):Void { public function loadFont (id:String):Future<Font> {
handler (getFont (id)); return new Future<Font> (function () return getFont (id));
} }
public function loadImage (id:String, handler:Image -> Void):Void { public function loadImage (id:String):Future<Image> {
handler (getImage (id)); return new Future<Image> (function () return getImage (id));
} }
//public function loadMusic (id:String, handler:Dynamic /*Sound*/ -> Void):Void { public function loadText (id:String):Future<String> {
// handler (getMusic (id)); return loadBytes (id).then (function (bytes) {
//}
public function loadText (id:String, handler:String -> Void):Void {
#if (tools && !display)
var callback = function (bytes:ByteArray):Void {
if (bytes == null) { return new Future<String> (function () {
handler (null); if (bytes == null) {
return null;
} else {
return bytes.readUTFBytes (bytes.length);
}
} else { });
handler (bytes.readUTFBytes (bytes.length));
}
} });
loadBytes (id, callback);
#else
handler (null);
#end
} }

View File

@@ -8,7 +8,7 @@ class Future<T> {
public var isCompleted (get, null):Bool; public var isCompleted (get, null):Bool;
public var value:T; public var value (default, null):T;
private var __completed:Bool; private var __completed:Bool;
private var __completeListeners:Array<T->Void>; private var __completeListeners:Array<T->Void>;

View File

@@ -3,7 +3,9 @@ package;
import haxe.Timer; import haxe.Timer;
import haxe.Unserializer; import haxe.Unserializer;
import lime.app.Future;
import lime.app.Preloader; import lime.app.Preloader;
import lime.app.Promise;
import lime.audio.AudioSource; import lime.audio.AudioSource;
import lime.audio.openal.AL; import lime.audio.openal.AL;
import lime.audio.AudioBuffer; import lime.audio.AudioBuffer;
@@ -26,6 +28,8 @@ import flash.display.Bitmap;
import flash.display.BitmapData; import flash.display.BitmapData;
import flash.display.Loader; import flash.display.Loader;
import flash.events.Event; import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.media.Sound; import flash.media.Sound;
import flash.net.URLLoader; import flash.net.URLLoader;
import flash.net.URLRequest; import flash.net.URLRequest;
@@ -112,11 +116,7 @@ class DefaultAssetLibrary extends AssetLibrary {
lastModified = modified; lastModified = modified;
loadManifest (); loadManifest ();
if (eventCallback != null) { onChange.dispatch ();
eventCallback (this, "change");
}
} }
@@ -492,7 +492,9 @@ class DefaultAssetLibrary extends AssetLibrary {
} }
public override function loadAudioBuffer (id:String, handler:AudioBuffer -> Void):Void { public override function loadAudioBuffer (id:String):Future<AudioBuffer> {
var promise = new Promise<AudioBuffer> ();
#if (flash) #if (flash)
@@ -503,28 +505,45 @@ class DefaultAssetLibrary extends AssetLibrary {
var audioBuffer:AudioBuffer = new AudioBuffer(); var audioBuffer:AudioBuffer = new AudioBuffer();
audioBuffer.src = event.currentTarget; audioBuffer.src = event.currentTarget;
handler (audioBuffer); promise.complete (audioBuffer);
}); });
soundLoader.addEventListener (ProgressEvent.PROGRESS, function (event) {
if (event.bytesTotal == 0) {
promise.progress (0);
} else {
promise.progress (event.bytesLoaded / event.bytesTotal);
}
});
soundLoader.addEventListener (IOErrorEvent.IO_ERROR, promise.error);
soundLoader.load (new URLRequest (path.get (id))); soundLoader.load (new URLRequest (path.get (id)));
} else { } else {
handler (getAudioBuffer (id)); promise.complete (getAudioBuffer (id));
} }
#else #else
handler (getAudioBuffer (id)); promise.complete (getAudioBuffer (id));
#end #end
return promise.future;
} }
public override function loadBytes (id:String, handler:ByteArray -> Void):Void { public override function loadBytes (id:String):Future<ByteArray> {
var promise = new Promise<ByteArray> ();
#if flash #if flash
@@ -537,14 +556,28 @@ class DefaultAssetLibrary extends AssetLibrary {
bytes.writeUTFBytes (event.currentTarget.data); bytes.writeUTFBytes (event.currentTarget.data);
bytes.position = 0; bytes.position = 0;
handler (bytes); promise.complete (bytes);
}); });
loader.addEventListener (ProgressEvent.PROGRESS, function (event) {
if (event.bytesTotal == 0) {
promise.progress (0);
} else {
promise.progress (event.bytesLoaded / event.bytesTotal);
}
});
loader.addEventListener (IOErrorEvent.IO_ERROR, promise.error);
loader.load (new URLRequest (path.get (id))); loader.load (new URLRequest (path.get (id)));
} else { } else {
handler (getBytes (id)); promise.complete (getBytes (id));
} }
@@ -556,15 +589,32 @@ class DefaultAssetLibrary extends AssetLibrary {
loader.dataFormat = BINARY; loader.dataFormat = BINARY;
loader.onComplete.add (function (_):Void { loader.onComplete.add (function (_):Void {
handler (loader.data); promise.complete (loader.data);
});
loader.onProgress.add (function (_, loaded, total) {
if (total == 0) {
promise.progress (0);
} else {
promise.progress (loaded / total);
}
});
loader.onIOError.add (function (_, e) {
promise.error (e);
}); });
loader.load (new URLRequest (path.get (id))); loader.load (new URLRequest (path.get (id)));
} else { } else {
handler (getBytes (id)); promise.complete (getBytes (id));
} }
@@ -576,14 +626,18 @@ class DefaultAssetLibrary extends AssetLibrary {
} }
threadPool.queue (id, { handler: handler, getMethod: getBytes }); threadPool.queue (id, { promise: promise, getMethod: getBytes });
#end #end
return promise.future;
} }
public override function loadImage (id:String, handler:Image -> Void):Void { public override function loadImage (id:String):Future<Image> {
var promise = new Promise<Image> ();
#if flash #if flash
@@ -593,14 +647,28 @@ class DefaultAssetLibrary extends AssetLibrary {
loader.contentLoaderInfo.addEventListener (Event.COMPLETE, function (event:Event) { loader.contentLoaderInfo.addEventListener (Event.COMPLETE, function (event:Event) {
var bitmapData = cast (event.currentTarget.content, Bitmap).bitmapData; var bitmapData = cast (event.currentTarget.content, Bitmap).bitmapData;
handler (Image.fromBitmapData (bitmapData)); promise.complete (Image.fromBitmapData (bitmapData));
}); });
loader.contentLoaderInfo.addEventListener (ProgressEvent.PROGRESS, function (event) {
if (event.bytesTotal == 0) {
promise.progress (0);
} else {
promise.progress (event.bytesLoaded / event.bytesTotal);
}
});
loader.contentLoaderInfo.addEventListener (IOErrorEvent.IO_ERROR, promise.error);
loader.load (new URLRequest (path.get (id))); loader.load (new URLRequest (path.get (id)));
} else { } else {
handler (getImage (id)); promise.complete (getImage (id));
} }
@@ -611,14 +679,15 @@ class DefaultAssetLibrary extends AssetLibrary {
var image = new js.html.Image (); var image = new js.html.Image ();
image.onload = function (_):Void { image.onload = function (_):Void {
handler (Image.fromImageElement (image)); promise.complete (Image.fromImageElement (image));
} }
image.onerror = promise.error;
image.src = path.get (id); image.src = path.get (id);
} else { } else {
handler (getImage (id)); promise.complete (getImage (id));
} }
@@ -630,10 +699,12 @@ class DefaultAssetLibrary extends AssetLibrary {
} }
threadPool.queue (id, { handler: handler, getMethod: getImage }); threadPool.queue (id, { promise: promise, getMethod: getImage });
#end #end
return promise.future;
} }
@@ -703,36 +774,9 @@ class DefaultAssetLibrary extends AssetLibrary {
#end #end
/*public override function loadMusic (id:String, handler:Dynamic -> Void):Void { public override function loadText (id:String):Future<String> {
#if (flash || html5) var promise = new Promise<String> ();
//if (path.exists (id)) {
// var loader = new Loader ();
// loader.contentLoaderInfo.addEventListener (Event.COMPLETE, function (event) {
// handler (cast (event.currentTarget.content, Bitmap).bitmapData);
// });
// loader.load (new URLRequest (path.get (id)));
//} else {
handler (getMusic (id));
//}
#else
handler (getMusic (id));
#end
}*/
public override function loadText (id:String, handler:String -> Void):Void {
#if html5 #if html5
@@ -741,38 +785,55 @@ class DefaultAssetLibrary extends AssetLibrary {
var loader = new URLLoader (); var loader = new URLLoader ();
loader.onComplete.add (function (_):Void { loader.onComplete.add (function (_):Void {
handler (loader.data); promise.complete (loader.data);
}); });
loader.onProgress.add (function (_, loaded, total) {
if (total == 0) {
promise.progress (0);
} else {
promise.progress (loaded / total);
}
});
loader.onIOError.add (function (_, msg) promise.error (msg));
loader.load (new URLRequest (path.get (id))); loader.load (new URLRequest (path.get (id)));
} else { } else {
handler (getText (id)); promise.complete (getText (id));
} }
#else #else
var callback = function (bytes:ByteArray):Void { promise.completeWith (loadBytes (id).then (function (bytes) {
if (bytes == null) { return new Future<String> (function () {
handler (null); if (bytes == null) {
return null;
} else {
return bytes.readUTFBytes (bytes.length);
}
} else { });
handler (bytes.readUTFBytes (bytes.length));
}
} }));
loadBytes (id, callback);
#end #end
return promise.future;
} }