From 9b266891dd8c6fab33f6ab5c21ea863b4007430b Mon Sep 17 00:00:00 2001 From: Joshua Granick Date: Fri, 4 Sep 2015 09:35:00 -0700 Subject: [PATCH] Improve Assets, use Futures and Events --- lime/Assets.hx | 364 +++++++++----------------- lime/app/Future.hx | 2 +- templates/haxe/DefaultAssetLibrary.hx | 193 +++++++++----- 3 files changed, 253 insertions(+), 306 deletions(-) diff --git a/lime/Assets.hx b/lime/Assets.hx index a98d9f463..47b45029c 100644 --- a/lime/Assets.hx +++ b/lime/Assets.hx @@ -1,4 +1,7 @@ package lime; +import lime.app.Event; +import lime.app.Promise; +import lime.app.Future; #if !macro @@ -33,6 +36,7 @@ class Assets { public static var cache = new AssetCache (); public static var libraries (default, null) = new Map (); + public static var onChange = new EventVoid> (); 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 * @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 { initialize (); + var promise = new Promise (); + #if (tools && !display) if (useCache && cache.enabled && cache.audio.exists (id)) { @@ -629,8 +566,8 @@ class Assets { if (isValidAudio (audio)) { - handler (audio); - return; + promise.complete (audio); + return promise.future; } @@ -644,46 +581,41 @@ class Assets { if (library.exists (symbolName, cast AssetType.SOUND)) { + var future = library.loadAudioBuffer (symbolName); + if (useCache && cache.enabled) { - library.loadAudioBuffer (symbolName, function (audio:Dynamic):Void { - - cache.audio.set (id, audio); - handler (audio); - - }); - - } else { - - library.loadAudioBuffer (symbolName, handler); + future.onComplete (function (audio) cache.audio.set (id, audio)); } - return; + promise.completeWith (future); } 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 { - trace ("[Assets] There is no asset library named \"" + libraryName + "\""); + promise.error ("[Assets] There is no asset library named \"" + libraryName + "\""); } #end - handler (null); + return promise.future; } - public static function loadBytes (id:String, handler:ByteArray -> Void):Void { + public static function loadBytes (id:String):Future { initialize (); + var promise = new Promise (); + #if (tools && !display) var libraryName = id.substring (0, id.indexOf (":")); @@ -694,32 +626,70 @@ class Assets { if (library.exists (symbolName, cast AssetType.BINARY)) { - library.loadBytes (symbolName, handler); - return; + promise.completeWith (library.loadBytes (symbolName)); } 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 { - trace ("[Assets] There is no asset library named \"" + libraryName + "\""); + promise.error ("[Assets] There is no asset library named \"" + libraryName + "\""); } #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 { initialize (); + var promise = new Promise (); + + #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 { + + initialize (); + + var promise = new Promise (); + #if (tools && !display) if (useCache && cache.enabled && cache.image.exists (id)) { @@ -728,8 +698,8 @@ class Assets { if (isValidImage (image)) { - handler (image); - return; + promise.complete (image); + return promise.future; } @@ -743,45 +713,40 @@ class Assets { if (library.exists (symbolName, cast AssetType.IMAGE)) { + var future = library.loadImage (symbolName); + if (useCache && cache.enabled) { - library.loadImage (symbolName, function (image:Image):Void { - - cache.image.set (id, image); - handler (image); - - }); - - } else { - - library.loadImage (symbolName, handler); + future.onComplete (function (image) cache.image.set (id, image)); } - return; + promise.completeWith (future); } 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 { - trace ("[Assets] There is no asset library named \"" + libraryName + "\""); + promise.error ("[Assets] There is no asset library named \"" + libraryName + "\""); } #end - handler (null); + return promise.future; } - public static function loadLibrary (name:String, handler:AssetLibrary -> Void):Void { + public static function loadLibrary (name:String):Future { - initialize(); + initialize (); + + var promise = new Promise (); #if (tools && !display) @@ -792,89 +757,27 @@ class Assets { var info = Json.parse (data); var library = Type.createInstance (Type.resolveClass (info.type), info.args); libraries.set (name, library); - library.eventCallback = library_onEvent; - library.load (handler); - return; + library.onChange.add (onChange.dispatch); + promise.completeWith (library.load ()); } else { - trace ("[Assets] There is no asset library named \"" + name + "\""); + promise.error ("[Assets] There is no asset library named \"" + name + "\""); } #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 { initialize (); - #if (tools && !display) - - 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 (); + var promise = new Promise (); #if (tools && !display) @@ -886,24 +789,23 @@ class Assets { if (library.exists (symbolName, cast AssetType.TEXT)) { - library.loadText (symbolName, handler); - return; + promise.completeWith (library.loadText (symbolName)); } 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 { - trace ("[Assets] There is no asset library named \"" + libraryName + "\""); + promise.error ("[Assets] There is no asset library named \"" + libraryName + "\""); } #end - handler (null); + return promise.future; } @@ -912,13 +814,21 @@ class Assets { if (libraries.exists (name)) { - unloadLibrary (name); + if (libraries.get (name) == library) { + + return; + + } else { + + unloadLibrary (name); + + } } if (library != null) { - library.eventCallback = library_onEvent; + library.onChange.add (library_onChange); } @@ -938,8 +848,8 @@ class Assets { if (library != null) { cache.clear (name + ":"); + library.onChange.remove (library_onChange); 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 (); - //dispatchEvent (new Event (Event.CHANGE)); - - } + cache.clear (); + onChange.dispatch (); } @@ -975,7 +881,7 @@ class Assets { class AssetLibrary { - public var eventCallback:Dynamic; + public var onChange = new EventVoid> (); 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 { return null; @@ -1073,73 +972,60 @@ class AssetLibrary { } - private function load (handler:AssetLibrary -> Void):Void { + private function load ():Future { - handler (this); + return new Future (function () return this); } - public function loadAudioBuffer (id:String, handler:AudioBuffer -> Void):Void { + public function loadAudioBuffer (id:String):Future { - handler (getAudioBuffer (id)); + return new Future (function () return getAudioBuffer (id)); } - public function loadBytes (id:String, handler:ByteArray -> Void):Void { + public function loadBytes (id:String):Future { - handler (getBytes (id)); + return new Future (function () return getBytes (id)); } - public function loadFont (id:String, handler:Font -> Void):Void { + public function loadFont (id:String):Future { - handler (getFont (id)); + return new Future (function () return getFont (id)); } - public function loadImage (id:String, handler:Image -> Void):Void { + public function loadImage (id:String):Future { - handler (getImage (id)); + return new Future (function () return getImage (id)); } - //public function loadMusic (id:String, handler:Dynamic /*Sound*/ -> Void):Void { + public function loadText (id:String):Future { - // handler (getMusic (id)); - - //} - - - public function loadText (id:String, handler:String -> Void):Void { - - #if (tools && !display) - - var callback = function (bytes:ByteArray):Void { + return loadBytes (id).then (function (bytes) { - if (bytes == null) { + return new Future (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 + }); } diff --git a/lime/app/Future.hx b/lime/app/Future.hx index 911e01ea6..ed6199b7a 100644 --- a/lime/app/Future.hx +++ b/lime/app/Future.hx @@ -8,7 +8,7 @@ class Future { public var isCompleted (get, null):Bool; - public var value:T; + public var value (default, null):T; private var __completed:Bool; private var __completeListeners:ArrayVoid>; diff --git a/templates/haxe/DefaultAssetLibrary.hx b/templates/haxe/DefaultAssetLibrary.hx index c418e1b85..83532f275 100644 --- a/templates/haxe/DefaultAssetLibrary.hx +++ b/templates/haxe/DefaultAssetLibrary.hx @@ -3,7 +3,9 @@ package; import haxe.Timer; import haxe.Unserializer; +import lime.app.Future; import lime.app.Preloader; +import lime.app.Promise; import lime.audio.AudioSource; import lime.audio.openal.AL; import lime.audio.AudioBuffer; @@ -26,6 +28,8 @@ import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Loader; import flash.events.Event; +import flash.events.IOErrorEvent; +import flash.events.ProgressEvent; import flash.media.Sound; import flash.net.URLLoader; import flash.net.URLRequest; @@ -112,11 +116,7 @@ class DefaultAssetLibrary extends AssetLibrary { lastModified = modified; loadManifest (); - if (eventCallback != null) { - - eventCallback (this, "change"); - - } + onChange.dispatch (); } @@ -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 { + + var promise = new Promise (); #if (flash) @@ -503,28 +505,45 @@ class DefaultAssetLibrary extends AssetLibrary { var audioBuffer:AudioBuffer = new AudioBuffer(); 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))); } else { - handler (getAudioBuffer (id)); + promise.complete (getAudioBuffer (id)); } #else - handler (getAudioBuffer (id)); + promise.complete (getAudioBuffer (id)); #end + return promise.future; + } - public override function loadBytes (id:String, handler:ByteArray -> Void):Void { + public override function loadBytes (id:String):Future { + + var promise = new Promise (); #if flash @@ -537,14 +556,28 @@ class DefaultAssetLibrary extends AssetLibrary { bytes.writeUTFBytes (event.currentTarget.data); 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))); } else { - handler (getBytes (id)); + promise.complete (getBytes (id)); } @@ -556,15 +589,32 @@ class DefaultAssetLibrary extends AssetLibrary { loader.dataFormat = BINARY; 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))); } 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 + return promise.future; + } - public override function loadImage (id:String, handler:Image -> Void):Void { + public override function loadImage (id:String):Future { + + var promise = new Promise (); #if flash @@ -593,14 +647,28 @@ class DefaultAssetLibrary extends AssetLibrary { loader.contentLoaderInfo.addEventListener (Event.COMPLETE, function (event:Event) { 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))); } else { - handler (getImage (id)); + promise.complete (getImage (id)); } @@ -611,14 +679,15 @@ class DefaultAssetLibrary extends AssetLibrary { var image = new js.html.Image (); image.onload = function (_):Void { - handler (Image.fromImageElement (image)); + promise.complete (Image.fromImageElement (image)); } + image.onerror = promise.error; image.src = path.get (id); } 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 + return promise.future; + } @@ -703,36 +774,9 @@ class DefaultAssetLibrary extends AssetLibrary { #end - /*public override function loadMusic (id:String, handler:Dynamic -> Void):Void { + public override function loadText (id:String):Future { - #if (flash || html5) - - //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 { + var promise = new Promise (); #if html5 @@ -741,38 +785,55 @@ class DefaultAssetLibrary extends AssetLibrary { var loader = new URLLoader (); 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))); } else { - handler (getText (id)); + promise.complete (getText (id)); } #else - var callback = function (bytes:ByteArray):Void { + promise.completeWith (loadBytes (id).then (function (bytes) { - if (bytes == null) { + return new Future (function () { - handler (null); + if (bytes == null) { + + return null; + + } else { + + return bytes.readUTFBytes (bytes.length); + + } - } else { - - handler (bytes.readUTFBytes (bytes.length)); - - } + }); - } - - loadBytes (id, callback); + })); #end + return promise.future; + }