phaser/src/loader/LoaderPlugin.js

1075 lines
36 KiB
JavaScript
Raw Normal View History

2018-02-12 16:01:20 +00:00
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var Class = require('../utils/Class');
var CONST = require('./const');
2017-10-04 22:48:16 +00:00
var CustomSet = require('../structs/Set');
var EventEmitter = require('eventemitter3');
2018-01-19 14:47:16 +00:00
var FileTypesManager = require('./FileTypesManager');
var GetFastValue = require('../utils/object/GetFastValue');
var PluginCache = require('../plugins/PluginCache');
var XHRSettings = require('./XHRSettings');
2018-02-09 17:56:43 +00:00
/**
* @classdesc
* The Loader handles loading all external content such as Images, Sounds, Texture Atlases and data files.
* You typically interact with it via `this.load` in your Scene. Scenes can have a `preload` method, which is always
* called before the Scenes `create` method, allowing you to preload assets that the Scene may need.
*
* If you call any `this.load` methods from outside of `Scene.preload` then you need to start the Loader going
* yourself by calling `Loader.start()`. It's only automatically started during the Scene preload.
*
* The Loader uses a combination of tag loading (eg. Audio elements) and XHR and provides progress and completion events.
* Files are loaded in parallel by default. The amount of concurrent connections can be controlled in your Game Configuration.
*
* Once the Loader has started loading you are still able to add files to it. These can be injected as a result of a loader
* event, the type of file being loaded (such as a pack file) or other external events. As long as the Loader hasn't finished
* simply adding a new file to it, while running, will ensure it's added into the current queue.
*
* Every Scene has its own instance of the Loader and they are bound to the Scene in which they are created. However,
* assets loaded by the Loader are placed into global game-level caches. For example, loading an XML file will place that
* file inside `Game.cache.xml`, which is accessible from every Scene in your game, no matter who was responsible
* for loading it. The same is true of Textures. A texture loaded in one Scene is instantly available to all other Scenes
* in your game.
*
* The Loader works by using custom File Types. These are stored in the FileTypesManager, which injects them into the Loader
* when it's instantiated. You can create your own custom file types by extending either the File or MultiFile classes.
* See those files for more details.
2018-02-09 17:56:43 +00:00
*
* @class LoaderPlugin
2018-03-28 14:04:09 +00:00
* @extends Phaser.Events.EventEmitter
2018-02-09 17:56:43 +00:00
* @memberOf Phaser.Loader
* @constructor
* @since 3.0.0
*
* @param {Phaser.Scene} scene - The Scene which owns this Loader instance.
2018-02-09 17:56:43 +00:00
*/
var LoaderPlugin = new Class({
Extends: EventEmitter,
initialize:
function LoaderPlugin (scene)
{
EventEmitter.call(this);
var gameConfig = scene.sys.game.config;
var sceneConfig = scene.sys.settings.loader;
2018-02-09 17:56:43 +00:00
/**
* The Scene which owns this Loader instance.
2018-02-09 17:56:43 +00:00
*
* @name Phaser.Loader.LoaderPlugin#scene
* @type {Phaser.Scene}
* @protected
2018-02-09 17:56:43 +00:00
* @since 3.0.0
*/
this.scene = scene;
2018-02-09 17:56:43 +00:00
/**
* A reference to the Scene Systems.
2018-02-09 17:56:43 +00:00
*
* @name Phaser.Loader.LoaderPlugin#systems
* @type {Phaser.Scenes.Systems}
* @protected
2018-02-09 17:56:43 +00:00
* @since 3.0.0
*/
2018-01-19 14:47:16 +00:00
this.systems = scene.sys;
/**
* A reference to the global Cache Manager.
*
* @name Phaser.Loader.LoaderPlugin#cacheManager
* @type {Phaser.Cache.CacheManager}
* @protected
* @since 3.7.0
*/
this.cacheManager = scene.sys.cache;
/**
* A reference to the global Texture Manager.
*
* @name Phaser.Loader.LoaderPlugin#textureManager
* @type {Phaser.Textures.TextureManager}
* @protected
* @since 3.7.0
*/
this.textureManager = scene.sys.textures;
2018-01-19 14:47:16 +00:00
// Inject the available filetypes into the Loader
FileTypesManager.install(this);
/**
* An optional prefix that is automatically prepended to the start of every file key.
* If prefix was `MENU.` and you load an image with the key 'Background' the resulting key would be `MENU.Background`.
* You can set this directly, or call `Loader.setPrefix()`. It will then affect every file added to the Loader
* from that point on. It does _not_ change any file already in the load queue.
*
* @name Phaser.Loader.LoaderPlugin#prefix
* @type {string}
* @default ''
* @since 3.7.0
*/
this.prefix = '';
2018-02-09 17:56:43 +00:00
/**
* The value of `path`, if set, is placed before any _relative_ file path given. For example:
*
* ```javascript
* this.load.path = "images/sprites/";
* this.load.image("ball", "ball.png");
* this.load.image("tree", "level1/oaktree.png");
* this.load.image("boom", "http://server.com/explode.png");
* ```
*
* Would load the `ball` file from `images/sprites/ball.png` and the tree from
* `images/sprites/level1/oaktree.png` but the file `boom` would load from the URL
* given as it's an absolute URL.
*
* Please note that the path is added before the filename but *after* the baseURL (if set.)
*
* If you set this property directly then it _must_ end with a "/". Alternatively, call `setPath()` and it'll do it for you.
2018-02-09 17:56:43 +00:00
*
* @name Phaser.Loader.LoaderPlugin#path
* @type {string}
* @default ''
* @since 3.0.0
*/
this.path = '';
2018-02-09 17:56:43 +00:00
/**
* If you want to append a URL before the path of any asset you can set this here.
*
* Useful if allowing the asset base url to be configured outside of the game code.
*
* If you set this property directly then it _must_ end with a "/". Alternatively, call `setBaseURL()` and it'll do it for you.
2018-02-09 17:56:43 +00:00
*
* @name Phaser.Loader.LoaderPlugin#baseURL
* @type {string}
* @default ''
* @since 3.0.0
*/
this.baseURL = '';
this.setBaseURL(GetFastValue(sceneConfig, 'baseURL', gameConfig.loaderBaseURL));
2018-02-09 17:56:43 +00:00
this.setPath(GetFastValue(sceneConfig, 'path', gameConfig.loaderPath));
this.setPrefix(GetFastValue(sceneConfig, 'prefix', gameConfig.loaderPrefix));
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* The number of concurrent / parallel resources to try and fetch at once.
*
* Old browsers limit 6 requests per domain; modern ones, especially those with HTTP/2 don't limit it at all.
*
* The default is 32 but you can change this in your Game Config, or by changing this property before the Loader starts.
2018-02-09 17:56:43 +00:00
*
* @name Phaser.Loader.LoaderPlugin#maxParallelDownloads
* @type {integer}
* @since 3.0.0
*/
this.maxParallelDownloads = GetFastValue(sceneConfig, 'maxParallelDownloads', gameConfig.loaderMaxParallelDownloads);
2018-02-09 17:56:43 +00:00
/**
* xhr specific global settings (can be overridden on a per-file basis)
*
* @name Phaser.Loader.LoaderPlugin#xhr
2018-03-21 12:03:14 +00:00
* @type {XHRSettingsObject}
2018-02-09 17:56:43 +00:00
* @since 3.0.0
*/
this.xhr = XHRSettings(
GetFastValue(sceneConfig, 'responseType', gameConfig.loaderResponseType),
GetFastValue(sceneConfig, 'async', gameConfig.loaderAsync),
GetFastValue(sceneConfig, 'user', gameConfig.loaderUser),
GetFastValue(sceneConfig, 'password', gameConfig.loaderPassword),
GetFastValue(sceneConfig, 'timeout', gameConfig.loaderTimeout)
);
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* The crossOrigin value applied to loaded images. Very often this needs to be set to 'anonymous'.
2018-02-09 17:56:43 +00:00
*
* @name Phaser.Loader.LoaderPlugin#crossOrigin
* @type {string}
* @since 3.0.0
*/
this.crossOrigin = GetFastValue(sceneConfig, 'crossOrigin', gameConfig.loaderCrossOrigin);
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* The total number of files to load. It may not always be accurate because you may add to the Loader during the process
* of loading, especially if you load a Pack File. Therefore this value can change, but in most cases remains static.
2018-02-09 17:56:43 +00:00
*
* @name Phaser.Loader.LoaderPlugin#totalToLoad
* @type {integer}
2018-02-09 17:56:43 +00:00
* @default 0
* @since 3.0.0
*/
this.totalToLoad = 0;
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* The progress of the current load queue, as a float value between 0 and 1.
* This is updated automatically as files complete loading.
* Note that it is possible for this value to go down again if you add content to the current load queue during a load.
2018-02-09 17:56:43 +00:00
*
* @name Phaser.Loader.LoaderPlugin#progress
* @type {float}
2018-02-09 17:56:43 +00:00
* @default 0
* @since 3.0.0
*/
this.progress = 0;
2018-02-09 17:56:43 +00:00
/**
* Files are placed in this Set when they're added to the Loader via `addFile`.
*
* They are moved to the `inflight` Set when they start loading, and assuming a successful
* load, to the `queue` Set for further processing.
*
* By the end of the load process this Set will be empty.
2018-02-09 17:56:43 +00:00
*
* @name Phaser.Loader.LoaderPlugin#list
2018-03-23 15:54:12 +00:00
* @type {Phaser.Structs.Set.<Phaser.Loader.File>}
2018-02-09 17:56:43 +00:00
* @since 3.0.0
*/
2017-10-04 22:48:16 +00:00
this.list = new CustomSet();
2018-02-09 17:56:43 +00:00
/**
* Files are stored in this Set while they're in the process of being loaded.
*
* Upon a successful load they are moved to the `queue` Set.
*
* By the end of the load process this Set will be empty.
2018-02-09 17:56:43 +00:00
*
* @name Phaser.Loader.LoaderPlugin#inflight
2018-03-23 15:54:12 +00:00
* @type {Phaser.Structs.Set.<Phaser.Loader.File>}
2018-02-09 17:56:43 +00:00
* @since 3.0.0
*/
2017-10-04 22:48:16 +00:00
this.inflight = new CustomSet();
2018-02-09 17:56:43 +00:00
/**
* Files are stored in this Set while they're being processed.
*
* If the process is successful they are moved to their final destination, which could be
* a Cache or the Texture Manager.
*
* At the end of the load process this Set will be empty.
2018-02-09 17:56:43 +00:00
*
* @name Phaser.Loader.LoaderPlugin#queue
2018-03-23 15:54:12 +00:00
* @type {Phaser.Structs.Set.<Phaser.Loader.File>}
2018-02-09 17:56:43 +00:00
* @since 3.0.0
*/
this.queue = new CustomSet();
2018-02-09 17:56:43 +00:00
/**
* A temporary Set in which files are stored after processing,
* awaiting destruction at the end of the load process.
*
* @name Phaser.Loader.LoaderPlugin#_deleteQueue
* @type {Phaser.Structs.Set.<Phaser.Loader.File>}
* @private
* @since 3.7.0
*/
this._deleteQueue = new CustomSet();
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* The total number of files that failed to load during the most recent load.
* This value is reset when you call `Loader.start`.
2018-02-09 17:56:43 +00:00
*
* @name Phaser.Loader.LoaderPlugin#totalFailed
* @type {integer}
* @default 0
* @since 3.7.0
2018-02-09 17:56:43 +00:00
*/
this.totalFailed = 0;
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* The total number of files that successfully loaded during the most recent load.
* This value is reset when you call `Loader.start`.
2018-02-09 17:56:43 +00:00
*
* @name Phaser.Loader.LoaderPlugin#totalComplete
* @type {integer}
* @default 0
* @since 3.7.0
2018-02-09 17:56:43 +00:00
*/
this.totalComplete = 0;
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* The current state of the Loader.
2018-02-09 17:56:43 +00:00
*
* @name Phaser.Loader.LoaderPlugin#state
* @type {integer}
2018-05-04 17:50:10 +00:00
* @readOnly
2018-02-09 17:56:43 +00:00
* @since 3.0.0
*/
this.state = CONST.LOADER_IDLE;
scene.sys.events.once('boot', this.boot, this);
scene.sys.events.on('start', this.pluginStart, this);
},
/**
* This method is called automatically, only once, when the Scene is first created.
* Do not invoke it directly.
*
* @method Phaser.Loader.LoaderPlugin#boot
* @private
* @since 3.5.1
*/
boot: function ()
{
this.systems.events.once('destroy', this.destroy, this);
},
2018-02-09 17:56:43 +00:00
/**
* This method is called automatically by the Scene when it is starting up.
* It is responsible for creating local systems, properties and listening for Scene events.
* Do not invoke it directly.
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#pluginStart
* @private
* @since 3.5.1
2018-02-09 17:56:43 +00:00
*/
pluginStart: function ()
2018-01-19 14:47:16 +00:00
{
this.systems.events.once('shutdown', this.shutdown, this);
2018-01-19 14:47:16 +00:00
},
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* If you want to append a URL before the path of any asset you can set this here.
*
* Useful if allowing the asset base url to be configured outside of the game code.
*
* Once a base URL is set it will affect every file loaded by the Loader from that point on. It does _not_ change any
* file _already_ being loaded. To reset it, call this method with no arguments.
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#setBaseURL
* @since 3.0.0
*
2018-05-04 17:50:10 +00:00
* @param {string} [url] - The URL to use. Leave empty to reset.
2018-02-09 17:56:43 +00:00
*
* @return {Phaser.Loader.LoaderPlugin} This Loader object.
*/
setBaseURL: function (url)
{
if (url === undefined) { url = ''; }
if (url !== '' && url.substr(-1) !== '/')
{
url = url.concat('/');
}
this.baseURL = url;
return this;
},
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* The value of `path`, if set, is placed before any _relative_ file path given. For example:
*
* ```javascript
* this.load.setPath("images/sprites/");
* this.load.image("ball", "ball.png");
* this.load.image("tree", "level1/oaktree.png");
* this.load.image("boom", "http://server.com/explode.png");
* ```
*
* Would load the `ball` file from `images/sprites/ball.png` and the tree from
* `images/sprites/level1/oaktree.png` but the file `boom` would load from the URL
* given as it's an absolute URL.
*
* Please note that the path is added before the filename but *after* the baseURL (if set.)
*
* Once a path is set it will then affect every file added to the Loader from that point on. It does _not_ change any
* file _already_ in the load queue. To reset it, call this method with no arguments.
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#setPath
* @since 3.0.0
*
2018-05-04 17:50:10 +00:00
* @param {string} [path] - The path to use. Leave empty to reset.
2018-02-09 17:56:43 +00:00
*
* @return {Phaser.Loader.LoaderPlugin} This Loader object.
*/
setPath: function (path)
{
if (path === undefined) { path = ''; }
if (path !== '' && path.substr(-1) !== '/')
{
path = path.concat('/');
}
this.path = path;
return this;
},
/**
* An optional prefix that is automatically prepended to the start of every file key.
2018-05-04 17:50:10 +00:00
*
* If prefix was `MENU.` and you load an image with the key 'Background' the resulting key would be `MENU.Background`.
2018-05-04 17:50:10 +00:00
*
* Once a prefix is set it will then affect every file added to the Loader from that point on. It does _not_ change any
* file _already_ in the load queue. To reset it, call this method with no arguments.
*
* @method Phaser.Loader.LoaderPlugin#setPrefix
* @since 3.7.0
*
* @param {string} [prefix] - The prefix to use. Leave empty to reset.
*
* @return {Phaser.Loader.LoaderPlugin} This Loader object.
*/
setPrefix: function (prefix)
{
if (prefix === undefined) { prefix = ''; }
this.prefix = prefix;
return this;
},
2018-02-13 05:54:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* Sets the Cross Origin Resource Sharing value used when loading files.
*
* Files can override this value on a per-file basis by specifying an alternative `crossOrigin` value in their file config.
*
* Once CORs is set it will then affect every file loaded by the Loader from that point on, as long as they don't have
* their own CORs setting. To reset it, call this method with no arguments.
*
* For more details about CORs see https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
2018-02-13 05:54:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#setCORS
* @since 3.0.0
*
2018-05-04 17:50:10 +00:00
* @param {string} [crossOrigin] - The value to use for the `crossOrigin` property in the load request.
2018-02-13 05:54:43 +00:00
*
* @return {Phaser.Loader.LoaderPlugin} This Loader object.
*/
setCORS: function (crossOrigin)
{
this.crossOrigin = crossOrigin;
return this;
},
2018-05-05 10:56:09 +00:00
/**
* This event is fired when a Loader successfully begins to load its queue.
*
* @event Phaser.Loader.LoaderPlugin#addFileEvent
* @param {string} key - The key of the file that was added.
* @param {string} type - The type of the file that was added.
* @param {Phaser.Loader.LoaderPlugin} loader - The Loader that had the file added to it.
* @param {Phaser.Loader.File} loader - The File object that was added to the Loader.
*/
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* Adds a file, or array of files, into the load queue.
*
* The file must be an instance of `Phaser.Loader.File`, or a class that extends it. The Loader will check that the key
* used by the file won't conflict with any other key either in the loader, the inflight queue or the target cache.
* If allowed it will then add the file into the pending list, read for the load to start. Or, if the load has already
* started, ready for the next batch of files to be pulled from the list to the inflight queue.
*
* You should not normally call this method directly, but rather use one of the Loader methods like `image` or `atlas`,
* however you can call this as long as the file given to it is well formed.
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#addFile
2018-05-05 10:56:09 +00:00
* @fires Phaser.Loader.LoaderPlugin#addFileEvent
2018-02-09 17:56:43 +00:00
* @since 3.0.0
*
* @param {(Phaser.Loader.File|Phaser.Loader.File[])} file - The file, or array of files, to be added to the load queue.
2018-02-09 17:56:43 +00:00
*/
addFile: function (file)
{
if (!Array.isArray(file))
{
file = [ file ];
}
for (var i = 0; i < file.length; i++)
{
var item = file[i];
// Does the file already exist in the cache or texture manager?
// Or will it conflict with a file already in the queue or inflight?
if (!this.keyExists(item))
{
this.list.set(item);
2018-05-05 10:56:09 +00:00
this.emit('addfile', item.key, item.type, this, item);
if (this.isLoading())
{
this.totalToLoad++;
this.updateProgress();
}
}
}
},
/**
* Checks the key and type of the given file to see if it will conflict with anything already
* in a Cache, the Texture Manager, or the list or inflight queues.
*
* @method Phaser.Loader.LoaderPlugin#keyExists
* @since 3.7.0
*
* @param {Phaser.Loader.File} file - The file to check the key of.
*
* @return {boolean} `true` if adding this file will cause a cache or queue conflict, otherwise `false`.
*/
keyExists: function (file)
{
var keyConflict = file.hasCacheConflict();
if (!keyConflict)
{
this.list.iterate(function (item)
{
if (item.type === file.type && item.key === file.key)
{
keyConflict = true;
return false;
}
});
}
if (!keyConflict && this.isLoading())
{
this.inflight.iterate(function (item)
{
if (item.type === file.type && item.key === file.key)
{
keyConflict = true;
return false;
}
});
this.queue.iterate(function (item)
{
if (item.type === file.type && item.key === file.key)
{
keyConflict = true;
return false;
}
});
}
return keyConflict;
},
/**
2018-05-04 17:50:10 +00:00
* Takes a well formed, fully parsed pack file object and adds its entries into the load queue. Usually you do not call
* this method directly, but instead use `Loader.pack` and supply a path to a JSON file that holds the
* pack data. However, if you've got the data prepared you can pass it to this method.
*
* You can also provide an optional key. If you do then it will only add the entries from that part of the pack into
* to the load queue. If not specified it will add all entries it finds. For more details about the pack file format
* see the `LoaderPlugin.pack` method.
*
* @method Phaser.Loader.LoaderPlugin#addPack
* @since 3.7.0
*
2018-05-04 17:50:10 +00:00
* @param {any} data - The Pack File data to be parsed and each entry of it to added to the load queue.
* @param {string} [packKey] - An optional key to use from the pack file data.
*
* @return {boolean} `true` if any files were added to the queue, otherwise `false`.
*/
addPack: function (pack, packKey)
{
// if no packKey provided we'll add everything to the queue
if (packKey && pack.hasOwnProperty(packKey))
{
pack = { packKey: pack[packKey] };
}
var total = 0;
// Store the loader settings in case this pack replaces them
var currentBaseURL = this.baseURL;
var currentPath = this.path;
var currentPrefix = this.prefix;
// Here we go ...
for (var key in pack)
{
var config = pack[key];
// Any meta data to process?
var baseURL = GetFastValue(config, 'baseURL', currentBaseURL);
var path = GetFastValue(config, 'path', currentPath);
var prefix = GetFastValue(config, 'prefix', currentPrefix);
var files = GetFastValue(config, 'files', null);
var defaultType = GetFastValue(config, 'defaultType', 'void');
if (Array.isArray(files))
{
this.setBaseURL(baseURL);
this.setPath(path);
this.setPrefix(prefix);
for (var i = 0; i < files.length; i++)
{
var file = files[i];
var type = (file.hasOwnProperty('type')) ? file.type : defaultType;
if (this[type])
{
this[type](file);
total++;
}
}
}
}
// Reset the loader settings
this.setBaseURL(currentBaseURL);
this.setPath(currentPath);
this.setPrefix(currentPrefix);
return (total > 0);
},
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* Is the Loader actively loading, or processing loaded files?
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#isLoading
* @since 3.0.0
*
2018-05-04 17:50:10 +00:00
* @return {boolean} `true` if the Loader is busy loading or processing, otherwise `false`.
2018-02-09 17:56:43 +00:00
*/
isLoading: function ()
{
return (this.state === CONST.LOADER_LOADING || this.state === CONST.LOADER_PROCESSING);
},
2018-02-09 17:56:43 +00:00
/**
* Is the Loader ready to start a new load?
*
* @method Phaser.Loader.LoaderPlugin#isReady
* @since 3.0.0
*
2018-05-04 17:50:10 +00:00
* @return {boolean} `true` if the Loader is ready to start a new load, otherwise `false`.
2018-02-09 17:56:43 +00:00
*/
isReady: function ()
{
return (this.state === CONST.LOADER_IDLE || this.state === CONST.LOADER_COMPLETE);
},
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* This event is fired when a Loader successfully begins to load its queue.
*
* @event Phaser.Loader.LoaderPlugin#startEvent
* @param {Phaser.Loader.LoaderPlugin} loader - The Loader instance that started.
*/
/**
* Starts the Loader running. This will reset the progress and totals and then emit a `start` event.
* If there is nothing in the queue the Loader will immediately complete, otherwise it will start
* loading the first batch of files.
*
* The Loader is started automatically if the queue is populated within your Scenes `preload` method.
*
* However, outside of this, you need to call this method to start it.
*
* If the Loader is already running this method will simply return.
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#start
2018-05-04 17:50:10 +00:00
* @fires Phaser.Loader.LoaderPlugin#startEvent
2018-02-09 17:56:43 +00:00
* @since 3.0.0
*/
start: function ()
{
if (!this.isReady())
{
return;
}
this.progress = 0;
this.totalFailed = 0;
this.totalComplete = 0;
this.totalToLoad = this.list.size;
this.emit('start', this);
if (this.list.size === 0)
{
this.loadComplete();
}
else
{
this.state = CONST.LOADER_LOADING;
this.inflight.clear();
this.queue.clear();
this.updateProgress();
2018-05-04 17:50:10 +00:00
this.checkLoadQueue();
}
},
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* This event is fired when the Loader updates its progress, typically as a result of
* a file having completed loading.
*
* @event Phaser.Loader.LoaderPlugin#progressEvent
* @param {float} progress - The current progress of the load. A value between 0 and 1.
*/
/**
* Called automatically during the load process.
* It updates the `progress` value and then emits a progress event, which you can use to
* display a loading bar in your game.
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#updateProgress
2018-05-04 17:50:10 +00:00
* @fires Phaser.Loader.LoaderPlugin#progressEvent
2018-02-09 17:56:43 +00:00
* @since 3.0.0
*/
updateProgress: function ()
{
this.progress = 1 - ((this.list.size + this.inflight.size) / this.totalToLoad);
this.emit('progress', this.progress);
},
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* An internal method called by the Loader.
*
* It will check to see if there are any more files in the pending list that need loading, and if so it will move
* them from the list Set into the inflight Set, set their CORs flag and start them loading.
*
* It will carrying on doing this for each file in the pending list until it runs out, or hits the max allowed parallel downloads.
2018-02-09 17:56:43 +00:00
*
2018-05-04 17:50:10 +00:00
* @method Phaser.Loader.LoaderPlugin#checkLoadQueue
* @private
* @since 3.7.0
2018-02-09 17:56:43 +00:00
*/
2018-05-04 17:50:10 +00:00
checkLoadQueue: function ()
{
this.list.each(function (file)
{
if (file.state === CONST.FILE_POPULATED || (file.state === CONST.FILE_PENDING && this.inflight.size < this.maxParallelDownloads))
{
2017-10-04 22:48:16 +00:00
this.inflight.set(file);
2017-10-04 22:48:16 +00:00
this.list.delete(file);
// If the file doesn't have its own crossOrigin set,
// we'll use the Loaders (which is undefined by default)
if (!file.crossOrigin)
{
file.crossOrigin = this.crossOrigin;
}
file.load();
}
2017-10-04 22:48:16 +00:00
if (this.inflight.size === this.maxParallelDownloads)
{
// Tells the Set iterator to abort
return false;
}
2017-10-04 22:48:16 +00:00
}, this);
},
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* This event is fired when the a file successfully completes loading, _before_ it is processed.
*
* @event Phaser.Loader.LoaderPlugin#loadEvent
* @param {Phaser.Loader.File} file - The file that has completed loading.
*/
/**
* This event is fired when the a file errors during load.
*
* @event Phaser.Loader.LoaderPlugin#loadErrorEvent
* @param {Phaser.Loader.File} file - The file that has failed to load.
*/
/**
* An internal method called automatically by the XHRLoader belong to a File.
*
* This method will remove the given file from the inflight Set and update the load progress.
* If the file was successful its `onProcess` method is called, otherwise it is added to the delete queue.
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#nextFile
2018-05-04 17:50:10 +00:00
* @fires Phaser.Loader.LoaderPlugin#loadEvent
* @fires Phaser.Loader.LoaderPlugin#loadErrorEvent
2018-02-09 17:56:43 +00:00
* @since 3.0.0
*
* @param {Phaser.Loader.File} file - The File that just finished loading, or errored during load.
* @param {boolean} success - `true` if the file loaded successfully, otherwise `false`.
2018-02-09 17:56:43 +00:00
*/
nextFile: function (file, success)
{
this.inflight.delete(file);
this.updateProgress();
if (success)
{
this.totalComplete++;
this.queue.set(file);
this.emit('load', file);
file.onProcess();
}
else
{
this.totalFailed++;
this._deleteQueue.set(file);
this.emit('loaderror', file);
}
if (this.list.size > 0)
{
2018-05-04 17:50:10 +00:00
this.checkLoadQueue();
}
2016-12-07 00:27:56 +00:00
},
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* An internal method that is called automatically by the File when it has finished processing.
*
* If the process was successful, and the File isn't part of a MultiFile, its `addToCache` method is called.
*
* It this then removed from the queue. If there are more files to load, `checkLoadQueue` is called, otherwise `loadComplete` is.
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#fileProcessComplete
* @since 3.7.0
2018-02-09 17:56:43 +00:00
*
2018-05-04 17:50:10 +00:00
* @param {Phaser.Loader.File} file - The file that has finished processing.
2018-02-09 17:56:43 +00:00
*/
fileProcessComplete: function (file)
2016-12-07 00:27:56 +00:00
{
// This file has failed, so move it to the failed Set
if (file.state === CONST.FILE_ERRORED)
{
2018-05-04 10:33:51 +00:00
if (file.multiFile)
{
2018-05-04 10:33:51 +00:00
file.multiFile.onFileFailed(file);
}
}
else if (file.state === CONST.FILE_COMPLETE)
{
2018-05-04 10:33:51 +00:00
if (file.multiFile)
{
2018-05-04 10:33:51 +00:00
if (file.multiFile.isReadyToProcess())
{
// If we got here then all files the link file needs are ready to add to the cache
2018-05-04 10:33:51 +00:00
file.multiFile.addToCache();
}
}
else
{
// If we got here, then the file processed, so let it add itself to its cache
file.addToCache();
}
}
// Remove it from the queue
this.queue.delete(file);
// Nothing left to do?
if (this.list.size === 0 && this.inflight.size === 0 && this.queue.size === 0)
{
this.loadComplete();
}
else
{
// In case we've added to the list by processing this file
2018-05-04 17:50:10 +00:00
this.checkLoadQueue();
}
},
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* This event is fired when the Loader has finished loading everything and the queue is empty.
* By this point every loaded file will now be in its associated cache and ready for use.
*
* @event Phaser.Loader.LoaderPlugin#completeEvent
* @param {Phaser.Loader.File} file - The file that has failed to load.
*/
/**
* Called at the end when the load queue is exhausted and all files have either loaded or errored.
* By this point every loaded file will now be in its associated cache and ready for use.
*
* Also clears down the Sets, puts progress to 1 and clears the deletion queue.
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#loadComplete
2018-05-04 17:50:10 +00:00
* @fires Phaser.Loader.LoaderPlugin#completeEvent
* @since 3.7.0
2018-02-09 17:56:43 +00:00
*/
loadComplete: function ()
2016-12-07 00:27:56 +00:00
{
this.emit('loadcomplete', this);
this.list.clear();
this.inflight.clear();
this.queue.clear();
this.progress = 1;
this.state = CONST.LOADER_COMPLETE;
// Call 'destroy' on each file ready for deletion
this._deleteQueue.iterateLocal('destroy');
this._deleteQueue.clear();
this.emit('complete', this, this.totalComplete, this.totalFailed);
},
/**
2018-05-04 17:50:10 +00:00
* Adds a File into the pending-deletion queue.
*
* @method Phaser.Loader.LoaderPlugin#flagForRemoval
* @since 3.7.0
*
2018-05-04 17:50:10 +00:00
* @param {Phaser.Loader.File} file - The File to be queued for deletion when the Loader completes.
*/
flagForRemoval: function (file)
{
this._deleteQueue.set(file);
},
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* Converts the given JSON data into a file that the browser then prompts you to download so you can save it locally.
*
* The data must be well formed JSON and ready-parsed, not a JavaScript object.
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#saveJSON
* @since 3.0.0
*
2018-05-04 17:50:10 +00:00
* @param {*} data - The JSON data, ready parsed.
* @param {string} [filename=file.json] - The name to save the JSON file as.
2018-02-09 17:56:43 +00:00
*
2018-03-19 00:54:19 +00:00
* @return {Phaser.Loader.LoaderPlugin} This Loader plugin.
2018-02-09 17:56:43 +00:00
*/
saveJSON: function (data, filename)
{
return this.save(JSON.stringify(data), filename);
},
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* Causes the browser to save the given data as a file to its default Downloads folder.
*
* Creates a DOM level anchor link, assigns it as being a `download` anchor, sets the href
* to be an ObjectURL based on the given data, and then invokes a click event.
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#save
* @since 3.0.0
*
2018-05-04 17:50:10 +00:00
* @param {*} data - The data to be saved. Will be passed through URL.createObjectURL.
* @param {string} [filename=file.json] - The filename to save the file as.
* @param {string} [filetype=application/json] - The file type to use when saving the file. Defaults to JSON.
2018-02-09 17:56:43 +00:00
*
* @return {Phaser.Loader.LoaderPlugin} This Loader plugin.
*/
save: function (data, filename, filetype)
{
if (filename === undefined) { filename = 'file.json'; }
if (filetype === undefined) { filetype = 'application/json'; }
var blob = new Blob([ data ], { type: filetype });
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.download = filename;
a.textContent = 'Download ' + filename;
a.href = url;
a.click();
return this;
},
2018-02-09 17:56:43 +00:00
/**
2018-05-04 17:50:10 +00:00
* Resets the Loader.
*
* This will clear all lists and reset the base URL, path and prefix.
*
* Warning: If the Loader is currently downloading files, or has files in its queue, they will be aborted.
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#reset
* @since 3.0.0
*/
reset: function ()
{
this.list.clear();
this.inflight.clear();
this.queue.clear();
var gameConfig = this.systems.game.config;
var sceneConfig = this.systems.settings.loader;
this.setBaseURL(GetFastValue(sceneConfig, 'baseURL', gameConfig.loaderBaseURL));
this.setPath(GetFastValue(sceneConfig, 'path', gameConfig.loaderPath));
this.setPrefix(GetFastValue(sceneConfig, 'prefix', gameConfig.loaderPrefix));
this.state = CONST.LOADER_IDLE;
},
2018-02-09 17:56:43 +00:00
/**
* The Scene that owns this plugin is shutting down.
* We need to kill and reset all internal properties as well as stop listening to Scene events.
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#shutdown
* @private
2018-02-09 17:56:43 +00:00
* @since 3.0.0
*/
shutdown: function ()
{
this.reset();
this.state = CONST.LOADER_SHUTDOWN;
this.systems.events.off('shutdown', this.shutdown, this);
},
2018-02-09 17:56:43 +00:00
/**
* The Scene that owns this plugin is being destroyed.
* We need to shutdown and then kill off all external references.
2018-02-09 17:56:43 +00:00
*
* @method Phaser.Loader.LoaderPlugin#destroy
* @private
2018-02-09 17:56:43 +00:00
* @since 3.0.0
*/
destroy: function ()
{
this.shutdown();
this.state = CONST.LOADER_DESTROYED;
this.systems.events.off('start', this.pluginStart, this);
this.list = null;
this.inflight = null;
this.queue = null;
this.scene = null;
this.systems = null;
2018-04-25 16:16:39 +00:00
this.textureManager = null;
this.cacheManager = null;
}
});
PluginCache.register('Loader', LoaderPlugin, 'load');
2018-01-19 14:47:16 +00:00
module.exports = LoaderPlugin;