初始化

This commit is contained in:
SmallMain
2022-06-25 00:23:03 +08:00
commit ef0589e8e5
2264 changed files with 617829 additions and 0 deletions

View File

@@ -0,0 +1,836 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const preprocess = require('./preprocess');
const fetch = require('./fetch');
const Cache = require('./cache');
const helper = require('./helper');
const releaseManager = require('./releaseManager');
const dependUtil = require('./depend-util');
const load = require('./load');
const Pipeline = require('./pipeline');
const Task = require('./task');
const RequestItem = require('./request-item');
const downloader = require('./downloader');
const parser = require('./parser');
const packManager = require('./pack-manager');
const Bundle = require('./bundle');
const builtins = require('./builtins');
const factory = require('./factory');
const { parse, combine } = require('./urlTransformer');
const { parseParameters, asyncify } = require('./utilities');
const { assets, files, parsed, pipeline, transformPipeline, fetchPipeline, RequestType, bundles, BuiltinBundleName } = require('./shared');
/**
* @module cc
*/
/**
* !#en
* This module controls asset's behaviors and information, include loading, releasing etc. it is a singleton
* All member can be accessed with `cc.assetManager`.
*
* !#zh
* 此模块管理资源的行为和信息,包括加载,释放等,这是一个单例,所有成员能够通过 `cc.assetManager` 调用
*
* @class AssetManager
*/
function AssetManager () {
this._preprocessPipe = preprocess;
this._fetchPipe = fetch;
this._loadPipe = load;
/**
* !#en
* Normal loading pipeline
*
* !#zh
* 正常加载管线
*
* @property pipeline
* @type {Pipeline}
*/
this.pipeline = pipeline.append(preprocess).append(load);
/**
* !#en
* Fetching pipeline
*
* !#zh
* 下载管线
*
* @property fetchPipeline
* @type {Pipeline}
*/
this.fetchPipeline = fetchPipeline.append(preprocess).append(fetch);
/**
* !#en
* Url transformer
*
* !#zh
* Url 转换器
*
* @property transformPipeline
* @type {Pipeline}
*/
this.transformPipeline = transformPipeline.append(parse).append(combine);
/**
* !#en
* The collection of bundle which is already loaded, you can remove cache with {{#crossLink "AssetManager/removeBundle:method"}}{{/crossLink}}
*
* !#zh
* 已加载 bundle 的集合, 你能通过 {{#crossLink "AssetManager/removeBundle:method"}}{{/crossLink}} 来移除缓存
*
* @property bundles
* @type {Cache}
* @typescript
* bundles: AssetManager.Cache<AssetManager.Bundle>
*/
this.bundles = bundles;
/**
* !#en
* The collection of asset which is already loaded, you can remove cache with {{#crossLink "AssetManager/releaseAsset:method"}}{{/crossLink}}
*
* !#zh
* 已加载资源的集合, 你能通过 {{#crossLink "AssetManager/releaseAsset:method"}}{{/crossLink}} 来移除缓存
*
* @property assets
* @type {Cache}
* @typescript
* assets: AssetManager.Cache<cc.Asset>
*/
this.assets = assets;
this._files = files;
this._parsed = parsed;
this.generalImportBase = '';
this.generalNativeBase = '';
/**
* !#en
* Manage relationship between asset and its dependencies
*
* !#zh
* 管理资源依赖关系
*
* @property dependUtil
* @type {DependUtil}
*/
this.dependUtil = dependUtil;
this._releaseManager = releaseManager;
/**
* !#en
* Whether or not cache the loaded asset
*
* !#zh
* 是否缓存已加载的资源
*
* @property cacheAsset
* @type {boolean}
*/
this.cacheAsset = true;
/**
* !#en
* Whether or not load asset forcely, if it is true, asset will be loaded regardless of error
*
* !#zh
* 是否强制加载资源, 如果为 true ,加载资源将会忽略报错
*
* @property force
* @type {boolean}
*/
this.force = false;
/**
* !#en
* Some useful function
*
* !#zh
* 一些有用的方法
*
* @property utils
* @type {Helper}
*/
this.utils = helper;
/**
* !#en
* Manage all downloading task
*
* !#zh
* 管理所有下载任务
*
* @property downloader
* @type {Downloader}
*/
this.downloader = downloader;
/**
* !#en
* Manage all parsing task
*
* !#zh
* 管理所有解析任务
*
* @property parser
* @type {Parser}
*/
this.parser = parser;
/**
* !#en
* Manage internal asset
*
* !#zh
* 管理内置资源
*
* @property builtins
* @type {Builtins}
*/
this.builtins = builtins;
/**
* !#en
* Manage all packed asset
*
* !#zh
* 管理所有合并后的资源
*
* @property packManager
* @type {PackManager}
*/
this.packManager = packManager;
this.factory = factory;
/**
* !#en
* Cache manager is a module which controls all caches downloaded from server in non-web platform.
*
* !#zh
* 缓存管理器是一个模块,在非 WEB 平台上,用于管理所有从服务器上下载下来的缓存
*
* @property cacheManager
* @type {cc.AssetManager.CacheManager}
* @typescript
* cacheManager: cc.AssetManager.CacheManager|null
*/
this.cacheManager = null;
/**
* !#en
* The preset of options
*
* !#zh
* 可选参数的预设集
*
* @property presets
* @type {Object}
* @typescript
* presets: Record<string, Record<string, any>>
*/
this.presets = {
'default': {
priority: 0,
},
'preload': {
maxConcurrency: 2,
maxRequestsPerFrame: 2,
priority: -1,
},
'scene': {
maxConcurrency: 8,
maxRequestsPerFrame: 8,
priority: 1,
},
'bundle': {
maxConcurrency: 8,
maxRequestsPerFrame: 8,
priority: 2,
},
'remote': {
maxRetryCount: 4
},
'script': {
maxConcurrency: 1024,
maxRequestsPerFrame: 1024,
priority: 2
}
}
}
AssetManager.Pipeline = Pipeline;
AssetManager.Task = Task;
AssetManager.Cache = Cache;
AssetManager.RequestItem = RequestItem;
AssetManager.Bundle = Bundle;
AssetManager.BuiltinBundleName = BuiltinBundleName;
AssetManager.prototype = {
constructor: AssetManager,
/**
* !#en
* The builtin 'main' bundle
*
* !#zh
* 内置 main 包
*
* @property main
* @readonly
* @type {Bundle}
*/
get main () {
return bundles.get(BuiltinBundleName.MAIN);
},
/**
* !#en
* The builtin 'resources' bundle
*
* !#zh
* 内置 resources 包
*
* @property resources
* @readonly
* @type {Bundle}
*/
get resources () {
return bundles.get(BuiltinBundleName.RESOURCES);
},
/**
* !#en
* The builtin 'internal' bundle
*
* !#zh
* 内置 internal 包
*
* @property internal
* @readonly
* @type {Bundle}
*/
get internal () {
return bundles.get(BuiltinBundleName.INTERNAL);
},
/**
* !#en
* Initialize assetManager with options
*
* !#zh
* 初始化资源管理器
*
* @method init
* @param {Object} options
*
* @typescript
* init(options: Record<string, any>): void
*/
init (options) {
options = options || Object.create(null);
this._files.clear();
this._parsed.clear();
this._releaseManager.init();
this.assets.clear();
this.bundles.clear();
this.packManager.init();
this.downloader.init(options.bundleVers, options.server);
this.parser.init();
this.dependUtil.init();
this.generalImportBase = options.importBase;
this.generalNativeBase = options.nativeBase;
},
/**
* !#en
* Get the bundle which has been loaded
*
* !#zh
* 获取已加载的分包
*
* @method getBundle
* @param {String} name - The name of bundle
* @return {Bundle} - The loaded bundle
*
* @example
* // ${project}/assets/test1
* cc.assetManager.getBundle('test1');
*
* cc.assetManager.getBundle('resources');
*
* @typescript
* getBundle (name: string): cc.AssetManager.Bundle
*/
getBundle (name) {
return bundles.get(name);
},
/**
* !#en
* Remove this bundle. NOTE: The asset whthin this bundle will not be released automatically, you can call {{#crossLink "Bundle/releaseAll:method"}}{{/crossLink}} manually before remove it if you need
*
* !#zh
* 移除此包, 注意:这个包内的资源不会自动释放, 如果需要的话你可以在摧毁之前手动调用 {{#crossLink "Bundle/releaseAll:method"}}{{/crossLink}} 进行释放
*
* @method removeBundle
* @param {Bundle} bundle - The bundle to be removed
*
* @typescript
* removeBundle(bundle: cc.AssetManager.Bundle): void
*/
removeBundle (bundle) {
bundle._destroy();
bundles.remove(bundle.name);
},
/**
* !#en
* General interface used to load assets with a progression callback and a complete callback. You can achieve almost all effect you want with combination of `requests` and `options`.
* It is highly recommended that you use more simple API, such as `load`, `loadDir` etc. Every custom parameter in `options` will be distribute to each of `requests`.
* if request already has same one, the parameter in request will be given priority. Besides, if request has dependencies, `options` will distribute to dependencies too.
* Every custom parameter in `requests` will be tranfered to handler of `downloader` and `parser` as `options`.
* You can register you own handler downloader or parser to collect these custom parameters for some effect.
*
* Reserved Keyword: `uuid`, `url`, `path`, `dir`, `scene`, `type`, `priority`, `preset`, `audioLoadMode`, `ext`, `bundle`, `onFileProgress`, `maxConcurrency`, `maxRequestsPerFrame`
* `maxRetryCount`, `version`, `responseType`, `withCredentials`, `mimeType`, `timeout`, `header`, `reload`, `cacheAsset`, `cacheEnabled`,
* Please DO NOT use these words as custom options!
*
* !#zh
* 通用加载资源接口,可传入进度回调以及完成回调,通过组合 `request` 和 `options` 参数几乎可以实现和扩展所有想要的加载效果。非常建议你使用更简单的API例如 `load`、`loadDir` 等。
* `options` 中的自定义参数将会分发到 `requests` 的每一项中如果request中已存在同名的参数则以 `requests` 中为准,同时如果有其他
* 依赖资源,则 `options` 中的参数会继续向依赖项中分发。request中的自定义参数都会以 `options` 形式传入加载流程中的 `downloader`, `parser` 的方法中, 你可以
* 扩展 `downloader`, `parser` 收集参数完成想实现的效果。
*
* 保留关键字: `uuid`, `url`, `path`, `dir`, `scene`, `type`, `priority`, `preset`, `audioLoadMode`, `ext`, `bundle`, `onFileProgress`, `maxConcurrency`, `maxRequestsPerFrame`
* `maxRetryCount`, `version`, `responseType`, `withCredentials`, `mimeType`, `timeout`, `header`, `reload`, `cacheAsset`, `cacheEnabled`,
* 请不要使用这些字段为自定义参数!
*
* @method loadAny
* @param {string|string[]|Object|Object[]} requests - The request you want to load
* @param {Object} [options] - Optional parameters
* @param {Function} [onProgress] - Callback invoked when progression change
* @param {Number} onProgress.finished - The number of the items that are already completed
* @param {Number} onProgress.total - The total number of the items
* @param {RequestItem} onProgress.item - The current request item
* @param {Function} [onComplete] - Callback invoked when finish loading
* @param {Error} onComplete.err - The error occured in loading process.
* @param {Object} onComplete.data - The loaded content
*
* @example
* cc.assetManager.loadAny({url: 'http://example.com/a.png'}, (err, img) => cc.log(img));
* cc.assetManager.loadAny(['60sVXiTH1D/6Aft4MRt9VC'], (err, assets) => cc.log(assets));
* cc.assetManager.loadAny([{ uuid: '0cbZa5Y71CTZAccaIFluuZ'}, {url: 'http://example.com/a.png'}], (err, assets) => cc.log(assets));
* cc.assetManager.downloader.register('.asset', (url, options, onComplete) => {
* url += '?userName=' + options.userName + "&password=" + options.password;
* cc.assetManager.downloader.downloadFile(url, null, onComplete);
* });
* cc.assetManager.parser.register('.asset', (file, options, onComplete) => {
* var json = JSON.parse(file);
* var skin = json[options.skin];
* var model = json[options.model];
* onComplete(null, {skin, model});
* });
* cc.assetManager.loadAny({ url: 'http://example.com/my.asset', skin: 'xxx', model: 'xxx', userName: 'xxx', password: 'xxx' });
*
* @typescript
* loadAny(requests: string | string[] | Record<string, any> | Record<string, any>[], options: Record<string, any>, onProgress: (finished: number, total: number, item: cc.AssetManager.RequestItem) => void, onComplete: (err: Error, data: any) => void): void
* loadAny(requests: string | string[] | Record<string, any> | Record<string, any>[], onProgress: (finished: number, total: number, item: cc.AssetManager.RequestItem) => void, onComplete: (err: Error, data: any) => void): void
* loadAny(requests: string | string[] | Record<string, any> | Record<string, any>[], options: Record<string, any>, onComplete: (err: Error, data: any) => void): void
* loadAny(requests: string | string[] | Record<string, any> | Record<string, any>[], onComplete: (err: Error, data: any) => void): void
* loadAny(requests: string | string[] | Record<string, any> | Record<string, any>[], options: Record<string, any>): void
* loadAny(requests: string | string[] | Record<string, any> | Record<string, any>[]): void
*/
loadAny (requests, options, onProgress, onComplete) {
var { options, onProgress, onComplete } = parseParameters(options, onProgress, onComplete);
options.preset = options.preset || 'default';
requests = Array.isArray(requests) ? requests.concat() : requests;
let task = new Task({input: requests, onProgress, onComplete: asyncify(onComplete), options});
pipeline.async(task);
},
/**
* !#en
* General interface used to preload assets with a progression callback and a complete callback.It is highly recommended that you use more simple API, such as `preloadRes`, `preloadResDir` etc.
* Everything about preload is just likes `cc.assetManager.loadAny`, the difference is `cc.assetManager.preloadAny` will only download asset but not parse asset. You need to invoke `cc.assetManager.loadAny(preloadTask)`
* to finish loading asset
*
* !#zh
* 通用预加载资源接口,可传入进度回调以及完成回调,非常建议你使用更简单的 API ,例如 `preloadRes`, `preloadResDir` 等。`preloadAny` 和 `loadAny` 几乎一样,区别在于 `preloadAny` 只会下载资源,不会去解析资源,你需要调用 `cc.assetManager.loadAny(preloadTask)`
* 来完成资源加载。
*
* @method preloadAny
* @param {string|string[]|Object|Object[]} requests - The request you want to preload
* @param {Object} [options] - Optional parameters
* @param {Function} [onProgress] - Callback invoked when progression change
* @param {Number} onProgress.finished - The number of the items that are already completed
* @param {Number} onProgress.total - The total number of the items
* @param {RequestItem} onProgress.item - The current request item
* @param {Function} [onComplete] - Callback invoked when finish preloading
* @param {Error} onComplete.err - The error occured in preloading process.
* @param {RequestItem[]} onComplete.items - The preloaded content
*
* @example
* cc.assetManager.preloadAny('0cbZa5Y71CTZAccaIFluuZ', (err) => cc.assetManager.loadAny('0cbZa5Y71CTZAccaIFluuZ'));
*
* @typescript
* preloadAny(requests: string | string[] | Record<string, any> | Record<string, any>[], options: Record<string, any>, onProgress: (finished: number, total: number, item: cc.AssetManager.RequestItem) => void, onComplete: (err: Error, items: cc.AssetManager.RequestItem[]) => void): void
* preloadAny(requests: string | string[] | Record<string, any> | Record<string, any>[], onProgress: (finished: number, total: number, item: cc.AssetManager.RequestItem) => void, onComplete: (err: Error, items: cc.AssetManager.RequestItem[]) => void): void
* preloadAny(requests: string | string[] | Record<string, any> | Record<string, any>[], options: Record<string, any>, onComplete: (err: Error, items: cc.AssetManager.RequestItem[]) => void): void
* preloadAny(requests: string | string[] | Record<string, any> | Record<string, any>[], onComplete: (err: Error, items: cc.AssetManager.RequestItem[]) => void): void
* preloadAny(requests: string | string[] | Record<string, any> | Record<string, any>[], options: Record<string, any>): void
* preloadAny(requests: string | string[] | Record<string, any> | Record<string, any>[]): void
*/
preloadAny (requests, options, onProgress, onComplete) {
var { options, onProgress, onComplete } = parseParameters(options, onProgress, onComplete);
options.preset = options.preset || 'preload';
requests = Array.isArray(requests) ? requests.concat() : requests;
var task = new Task({input: requests, onProgress, onComplete: asyncify(onComplete), options});
fetchPipeline.async(task);
},
/**
* !#en
* Load native file of asset, if you check the option 'Async Load Assets', you may need to load native file with this before you use the asset
*
* !#zh
* 加载资源的原生文件,如果你勾选了'延迟加载资源'选项,你可能需要在使用资源之前调用此方法来加载原生文件
*
* @method postLoadNative
* @param {Asset} asset - The asset
* @param {Object} [options] - Some optional parameters
* @param {Function} [onComplete] - Callback invoked when finish loading
* @param {Error} onComplete.err - The error occured in loading process.
*
* @example
* cc.assetManager.postLoadNative(texture, (err) => console.log(err));
*
* @typescript
* postLoadNative(asset: cc.Asset, options: Record<string, any>, onComplete: (err: Error) => void): void
* postLoadNative(asset: cc.Asset, onComplete: (err: Error) => void): void
* postLoadNative(asset: cc.Asset, options: Record<string, any>): void
* postLoadNative(asset: cc.Asset): void
*/
postLoadNative (asset, options, onComplete) {
if (!(asset instanceof cc.Asset)) throw new Error('input is not asset');
var { options, onComplete } = parseParameters(options, undefined, onComplete);
if (!asset._native || asset._nativeAsset) {
return asyncify(onComplete)(null);
}
var depend = dependUtil.getNativeDep(asset._uuid);
if (depend) {
if (!bundles.has(depend.bundle)) {
var bundle = bundles.find(function (bundle) {
return bundle.getAssetInfo(asset._uuid);
});
if (bundle) {
depend.bundle = bundle.name;
}
}
this.loadAny(depend, options, function (err, native) {
if (!err) {
if (asset.isValid && !asset._nativeAsset) {
asset._nativeAsset = native
}
}
else {
cc.error(err.message, err.stack);
}
onComplete && onComplete(err);
});
}
},
/**
* !#en
* Load remote asset with url, such as audio, image, text and so on.
*
* !#zh
* 使用 url 加载远程资源,例如音频,图片,文本等等。
*
* @method loadRemote
* @param {string} url - The url of asset
* @param {Object} [options] - Some optional parameters
* @param {cc.AudioClip.LoadMode} [options.audioLoadMode] - Indicate which mode audio you want to load
* @param {string} [options.ext] - If the url does not have a extension name, you can specify one manually.
* @param {Function} [onComplete] - Callback invoked when finish loading
* @param {Error} onComplete.err - The error occured in loading process.
* @param {Asset} onComplete.asset - The loaded texture
*
* @example
* cc.assetManager.loadRemote('http://www.cloud.com/test1.jpg', (err, texture) => console.log(err));
* cc.assetManager.loadRemote('http://www.cloud.com/test2.mp3', (err, audioClip) => console.log(err));
* cc.assetManager.loadRemote('http://www.cloud.com/test3', { ext: '.png' }, (err, texture) => console.log(err));
*
* @typescript
* loadRemote<T extends cc.Asset>(url: string, options: Record<string, any>, onComplete: (err: Error, asset: T) => void): void
* loadRemote<T extends cc.Asset>(url: string, onComplete: (err: Error, asset: T) => void): void
* loadRemote<T extends cc.Asset>(url: string, options: Record<string, any>): void
* loadRemote<T extends cc.Asset>(url: string): void
*/
loadRemote (url, options, onComplete) {
var { options, onComplete } = parseParameters(options, undefined, onComplete);
if (this.assets.has(url)) {
return asyncify(onComplete)(null, this.assets.get(url));
}
options.__isNative__ = true;
options.preset = options.preset || 'remote';
this.loadAny({url}, options, null, function (err, data) {
if (err) {
cc.error(err.message, err.stack);
onComplete && onComplete(err, null);
}
else {
factory.create(url, data, options.ext || cc.path.extname(url), options, function (err, out) {
onComplete && onComplete(err, out);
});
}
});
},
/**
* !#en
* Load script
*
* !#zh
* 加载脚本
*
* @method loadScript
* @param {string|string[]} url - Url of the script
* @param {Object} [options] - Some optional paramters
* @param {boolean} [options.async] - Indicate whether or not loading process should be async
* @param {Function} [onComplete] - Callback when script loaded or failed
* @param {Error} onComplete.err - The occurred error, null indicetes success
*
* @example
* loadScript('http://localhost:8080/index.js', null, (err) => console.log(err));
*
* @typescript
* loadScript(url: string|string[], options: Record<string, any>, onComplete: (err: Error) => void): void
* loadScript(url: string|string[], onComplete: (err: Error) => void): void
* loadScript(url: string|string[], options: Record<string, any>): void
* loadScript(url: string|string[]): void
*/
loadScript (url, options, onComplete) {
var { options, onComplete } = parseParameters(options, undefined, onComplete);
options.__requestType__ = RequestType.URL;
options.preset = options.preset || 'script';
this.loadAny(url, options, onComplete);
},
/**
* !#en
* load bundle
*
* !#zh
* 加载资源包
*
* @method loadBundle
* @param {string} nameOrUrl - The name or root path of bundle
* @param {Object} [options] - Some optional paramter, same like downloader.downloadFile
* @param {string} [options.version] - The version of this bundle, you can check config.json in this bundle
* @param {Function} [onComplete] - Callback when bundle loaded or failed
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {Bundle} onComplete.bundle - The loaded bundle
*
* @example
* loadBundle('http://localhost:8080/test', null, (err, bundle) => console.log(err));
*
* @typescript
* loadBundle(nameOrUrl: string, options: Record<string, any>, onComplete: (err: Error, bundle: cc.AssetManager.Bundle) => void): void
* loadBundle(nameOrUrl: string, onComplete: (err: Error, bundle: cc.AssetManager.Bundle) => void): void
* loadBundle(nameOrUrl: string, options: Record<string, any>): void
* loadBundle(nameOrUrl: string): void
*/
loadBundle (nameOrUrl, options, onComplete) {
var { options, onComplete } = parseParameters(options, undefined, onComplete);
let bundleName = cc.path.basename(nameOrUrl);
if (this.bundles.has(bundleName)) {
return asyncify(onComplete)(null, this.getBundle(bundleName));
}
options.preset = options.preset || 'bundle';
options.ext = 'bundle';
this.loadRemote(nameOrUrl, options, onComplete);
},
/**
* !#en
* Release asset and it's dependencies.
* This method will not only remove the cache of the asset in assetManager, but also clean up its content.
* For example, if you release a texture, the texture asset and its gl texture data will be freed up.
* Notice, this method may cause the texture to be unusable, if there are still other nodes use the same texture, they may turn to black and report gl errors.
*
* !#zh
* 释放资源以及其依赖资源, 这个方法不仅会从 assetManager 中删除资源的缓存引用,还会清理它的资源内容。
* 比如说,当你释放一个 texture 资源,这个 texture 和它的 gl 贴图数据都会被释放。
* 注意,这个函数可能会导致资源贴图或资源所依赖的贴图不可用,如果场景中存在节点仍然依赖同样的贴图,它们可能会变黑并报 GL 错误。
*
* @method releaseAsset
* @param {Asset} asset - The asset to be released
*
* @example
* // release a texture which is no longer need
* cc.assetManager.releaseAsset(texture);
*
* @typescript
* releaseAsset(asset: cc.Asset): void
*/
releaseAsset (asset) {
releaseManager.tryRelease(asset, true);
},
/**
* !#en
* Release all unused assets. Refer to {{#crossLink "AssetManager/releaseAsset:method"}}{{/crossLink}} for detailed informations.
*
* !#zh
* 释放所有没有用到的资源。详细信息请参考 {{#crossLink "AssetManager/releaseAsset:method"}}{{/crossLink}}
*
* @method releaseUnusedAssets
* @private
*
* @typescript
* releaseUnusedAssets(): void
*/
releaseUnusedAssets () {
assets.forEach(function (asset) {
releaseManager.tryRelease(asset);
});
},
/**
* !#en
* Release all assets. Refer to {{#crossLink "AssetManager/releaseAsset:method"}}{{/crossLink}} for detailed informations.
*
* !#zh
* 释放所有资源。详细信息请参考 {{#crossLink "AssetManager/releaseAsset:method"}}{{/crossLink}}
*
* @method releaseAll
*
* @typescript
* releaseAll(): void
*/
releaseAll () {
assets.forEach(function (asset) {
releaseManager.tryRelease(asset, true);
});
if (CC_EDITOR) {
dependUtil._depends.clear();
}
},
_transform (input, options) {
var subTask = Task.create({input, options});
var urls = [];
try {
var result = transformPipeline.sync(subTask);
for (var i = 0, l = result.length; i < l; i++) {
var item = result[i];
var url = item.url;
item.recycle();
urls.push(url);
}
}
catch (e) {
for (var i = 0, l = subTask.output.length; i < l; i++) {
subTask.output[i].recycle();
}
cc.error(e.message, e.stack);
}
subTask.recycle();
return urls.length > 1 ? urls : urls[0];
}
};
cc.AssetManager = AssetManager;
/**
* @module cc
*/
/**
* @property assetManager
* @type {AssetManager}
*/
cc.assetManager = new AssetManager();
Object.defineProperty(cc, 'resources', {
/**
* !#en
* cc.resources is a bundle and controls all asset under assets/resources
*
* !#zh
* cc.resources 是一个 bundle用于管理所有在 assets/resources 下的资源
*
* @property resources
* @readonly
* @type {AssetManager.Bundle}
*/
get () {
return bundles.get(BuiltinBundleName.RESOURCES);
}
});
module.exports = cc.assetManager;
/**
* !#en
* This module controls asset's behaviors and information, include loading, releasing etc.
* All member can be accessed with `cc.assetManager`. All class or enum can be accessed with `cc.AssetManager`
*
* !#zh
* 此模块管理资源的行为和信息,包括加载,释放等,所有成员能够通过 `cc.assetManager` 调用. 所有类型或枚举能通过 `cc.AssetManager` 访问
*
* @module cc.AssetManager
*/

View File

@@ -0,0 +1,133 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const Cache = require('./cache');
const releaseManager = require('./releaseManager');
const { BuiltinBundleName } = require('./shared');
/**
* @module cc.AssetManager
*/
/**
* !#en
* This module contains the builtin asset, it's a singleton, all member can be accessed with `cc.assetManager.builtins`
*
* !#zh
* 此模块包含内建资源,这是一个单例,所有成员能通过 `cc.assetManager.builtins` 访问
*
* @class Builtins
*/
var builtins = {
_assets: new Cache({ material: new Cache(), effect: new Cache() }), // builtin assets
_loadBuiltins (name, cb) {
let dirname = name + 's';
let builtin = this._assets.get(name);
return cc.assetManager.internal.loadDir(dirname, null, null, (err, assets) => {
if (err) {
cc.error(err.message, err.stack);
}
else {
for (let i = 0; i < assets.length; i++) {
var asset = assets[i];
builtin.add(asset.name, asset.addRef());
}
}
cb();
});
},
/**
* !#en
* Initialize
*
* !#zh
* 初始化
*
* @method init
* @param {Function} cb - Callback when finish loading built-in assets
*
* @typescript
* init (cb: () => void): void
*/
init (cb) {
this.clear();
if (cc.game.renderType === cc.game.RENDER_TYPE_CANVAS || !cc.assetManager.bundles.has(BuiltinBundleName.INTERNAL)) {
return cb && cb();
}
this._loadBuiltins('effect', () => {
this._loadBuiltins('material', cb);
});
},
/**
* !#en
* Get the built-in asset using specific type and name.
*
* !#zh
* 通过特定的类型和名称获取内建资源
*
* @method getBuiltin
* @param {string} [type] - The type of asset, such as `effect`
* @param {string} [name] - The name of asset, such as `phong`
* @return {Asset|Cache} Builtin-assets
*
* @example
* cc.assetManaer.builtins.getBuiltin('effect', 'phone');
*
* @typescript
* getBuiltin(type?: string, name?: string): cc.Asset | Cache<cc.Asset>
*/
getBuiltin (type, name) {
if (arguments.length === 0) return this._assets;
else if (arguments.length === 1) return this._assets.get(type);
else return this._assets.get(type).get(name);
},
/**
* !#en
* Clear all builtin assets
*
* !#zh
* 清空所有内置资源
*
* @method clear
*
* @typescript
* clear(): void
*/
clear () {
this._assets.forEach(function (assets) {
assets.forEach(function (asset) {
releaseManager.tryRelease(asset, true);
});
assets.clear();
});
}
}
module.exports = builtins;

View File

@@ -0,0 +1,614 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const Config = require('./config');
const releaseManager = require('./releaseManager');
const { parseParameters, parseLoadResArgs } = require('./utilities');
const { RequestType, assets, bundles } = require('./shared');
/**
* @module cc.AssetManager
*/
/**
* !#en
* A bundle contains an amount of assets(includes scene), you can load, preload, release asset which is in this bundle
*
* !#zh
* 一个包含一定数量资源(包括场景)的包,你可以加载,预加载,释放此包内的资源
*
* @class Bundle
*/
function Bundle () {
this._config = new Config();
}
Bundle.prototype = {
/**
* !#en
* Create a bundle
*
* !#zh
* 创建一个 bundle
*
* @method constructor
*
* @typescript
* constructor()
*/
constructor: Bundle,
/**
* !#en
* The name of this bundle
*
* !#zh
* 此 bundle 的名称
*
* @property name
* @type {string}
*/
get name () {
return this._config.name;
},
/**
* !#en
* The dependency of this bundle
*
* !#zh
* 此 bundle 的依赖
*
* @property deps
* @type {string[]}
*/
get deps () {
return this._config.deps;
},
/**
* !#en
* The root path of this bundle, such like 'http://example.com/bundle1'
*
* !#zh
* 此 bundle 的根路径, 例如 'http://example.com/bundle1'
*
* @property base
* @type {string}
*/
get base () {
return this._config.base;
},
/**
* !#en
* Get asset's info using path, only valid when asset is in bundle folder.
*
* !#zh
* 使用 path 获取资源的配置信息
*
* @method getInfoWithPath
* @param {string} path - The relative path of asset, such as 'images/a'
* @param {Function} [type] - The constructor of asset, such as `cc.Texture2D`
* @returns {Object} The asset info
*
* @example
* var info = bundle.getInfoWithPath('image/a', cc.Texture2D);
*
* @typescript
* getInfoWithPath (path: string, type?: typeof cc.Asset): Record<string, any>
*/
getInfoWithPath (path, type) {
return this._config.getInfoWithPath(path, type);
},
/**
* !#en
* Get all asset's info within specific folder
*
* !#zh
* 获取在某个指定文件夹下的所有资源信息
*
* @method getDirWithPath
* @param {string} path - The relative path of folder, such as 'images'
* @param {Function} [type] - The constructor should be used to filter paths
* @param {Array} [out] - The output array
* @returns {Object[]} Infos
*
* @example
* var infos = [];
* bundle.getDirWithPath('images', cc.Texture2D, infos);
*
* @typescript
* getDirWithPath (path: string, type: typeof cc.Asset, out: Array<Record<string, any>>): Array<Record<string, any>>
* getDirWithPath (path: string, type: typeof cc.Asset): Array<Record<string, any>>
* getDirWithPath (path: string): Array<Record<string, any>>
*/
getDirWithPath (path, type, out) {
return this._config.getDirWithPath(path, type, out);
},
/**
* !#en
* Get asset's info with uuid
*
* !#zh
* 通过 uuid 获取资源信息
*
* @method getAssetInfo
* @param {string} uuid - The asset's uuid
* @returns {Object} info
*
* @example
* var info = bundle.getAssetInfo('fcmR3XADNLgJ1ByKhqcC5Z');
*
* @typescript
* getAssetInfo (uuid: string): Record<string, any>
*/
getAssetInfo (uuid) {
return this._config.getAssetInfo(uuid);
},
/**
* !#en
* Get scene'info with name
*
* !#zh
* 通过场景名获取场景信息
*
* @method getSceneInfo
* @param {string} name - The name of scene
* @return {Object} info
*
* @example
* var info = bundle.getSceneInfo('first.fire');
*
* @typescript
* getSceneInfo(name: string): Record<string, any>
*/
getSceneInfo (name) {
return this._config.getSceneInfo(name);
},
/**
* !#en
* Initialize this bundle with options
*
* !#zh
* 初始化此 bundle
*
* @method init
* @param {Object} options
*
* @typescript
* init(options: Record<string, any>): void
*/
init (options) {
this._config.init(options);
bundles.add(options.name, this);
},
/**
* !#en
* Load the asset within this bundle by the path which is relative to bundle's path
*
* !#zh
* 通过相对路径加载分包中的资源。路径是相对分包文件夹路径的相对路径
*
* @method load
* @param {String|String[]} paths - Paths of the target assets.The path is relative to the bundle's folder, extensions must be omitted.
* @param {Function} [type] - Only asset of type will be loaded if this argument is supplied.
* @param {Function} [onProgress] - Callback invoked when progression change.
* @param {Number} onProgress.finish - The number of the items that are already completed.
* @param {Number} onProgress.total - The total number of the items.
* @param {RequestItem} onProgress.item - The finished request item.
* @param {Function} [onComplete] - Callback invoked when all assets loaded.
* @param {Error} onComplete.error - The error info or null if loaded successfully.
* @param {Asset|Asset[]} onComplete.assets - The loaded assets.
*
* @example
* // load the texture (${project}/assets/resources/textures/background.jpg) from resources
* cc.resources.load('textures/background', cc.Texture2D, (err, texture) => console.log(err));
*
* // load the audio (${project}/assets/resources/music/hit.mp3) from resources
* cc.resources.load('music/hit', cc.AudioClip, (err, audio) => console.log(err));
*
* // load the prefab (${project}/assets/bundle1/misc/character/cocos) from bundle1 folder
* bundle1.load('misc/character/cocos', cc.Prefab, (err, prefab) => console.log(err));
*
* // load the sprite frame (${project}/assets/some/xxx/bundle2/imgs/cocos.png) from bundle2 folder
* bundle2.load('imgs/cocos', cc.SpriteFrame, null, (err, spriteFrame) => console.log(err));
*
* @typescript
* load<T extends cc.Asset>(paths: string, type: { prototype: T } onProgress: (finish: number, total: number, item: RequestItem) => void, onComplete: (error: Error, assets: T) => void): void
* load<T extends cc.Asset>(paths: string[], type: { prototype: T }, onProgress: (finish: number, total: number, item: RequestItem) => void, onComplete: (error: Error, assets: Array<T>) => void): void
* load<T extends cc.Asset>(paths: string, onProgress: (finish: number, total: number, item: RequestItem) => void, onComplete: (error: Error, assets: T) => void): void
* load<T extends cc.Asset>(paths: string[], onProgress: (finish: number, total: number, item: RequestItem) => void, onComplete: (error: Error, assets: Array<T>) => void): void
* load<T extends cc.Asset>(paths: string, type: { prototype: T }, onComplete?: (error: Error, assets: T) => void): void
* load<T extends cc.Asset>(paths: string[], type: { prototype: T }, onComplete?: (error: Error, assets: Array<T>) => void): void
* load<T extends cc.Asset>(paths: string, onComplete?: (error: Error, assets: T) => void): void
* load<T extends cc.Asset>(paths: string[], onComplete?: (error: Error, assets: Array<T>) => void): void
*/
load (paths, type, onProgress, onComplete) {
var { type, onProgress, onComplete } = parseLoadResArgs(type, onProgress, onComplete);
cc.assetManager.loadAny(paths, { __requestType__: RequestType.PATH, type: type, bundle: this.name, __outputAsArray__: Array.isArray(paths) }, onProgress, onComplete);
},
/**
* !#en
* Preload the asset within this bundle by the path which is relative to bundle's path.
* After calling this method, you still need to finish loading by calling `Bundle.load`.
* It will be totally fine to call `Bundle.load` at any time even if the preloading is not
* yet finished
*
* !#zh
* 通过相对路径预加载分包中的资源。路径是相对分包文件夹路径的相对路径。调用完后,你仍然需要通过 `Bundle.load` 来完成加载。
* 就算预加载还没完成,你也可以直接调用 `Bundle.load`。
*
* @method preload
* @param {String|String[]} paths - Paths of the target asset.The path is relative to bundle folder, extensions must be omitted.
* @param {Function} [type] - Only asset of type will be loaded if this argument is supplied.
* @param {Function} [onProgress] - Callback invoked when progression change.
* @param {Number} onProgress.finish - The number of the items that are already completed.
* @param {Number} onProgress.total - The total number of the items.
* @param {RequestItem} onProgress.item - The finished request item.
* @param {Function} [onComplete] - Callback invoked when the resource loaded.
* @param {Error} onComplete.error - The error info or null if loaded successfully.
* @param {RequestItem[]} onComplete.items - The preloaded items.
*
* @example
* // preload the texture (${project}/assets/resources/textures/background.jpg) from resources
* cc.resources.preload('textures/background', cc.Texture2D);
*
* // preload the audio (${project}/assets/resources/music/hit.mp3) from resources
* cc.resources.preload('music/hit', cc.AudioClip);
* // wait for while
* cc.resources.load('music/hit', cc.AudioClip, (err, audioClip) => {});
*
* * // preload the prefab (${project}/assets/bundle1/misc/character/cocos) from bundle1 folder
* bundle1.preload('misc/character/cocos', cc.Prefab);
*
* // load the sprite frame of (${project}/assets/bundle2/imgs/cocos.png) from bundle2 folder
* bundle2.preload('imgs/cocos', cc.SpriteFrame);
* // wait for while
* bundle2.load('imgs/cocos', cc.SpriteFrame, (err, spriteFrame) => {});
*
* @typescript
* preload(paths: string|string[], type: typeof cc.Asset, onProgress: (finish: number, total: number, item: RequestItem) => void, onComplete: (error: Error, items: RequestItem[]) => void): void
* preload(paths: string|string[], onProgress: (finish: number, total: number, item: RequestItem) => void, onComplete: (error: Error, items: RequestItem[]) => void): void
* preload(paths: string|string[], type: typeof cc.Asset, onComplete: (error: Error, items: RequestItem[]) => void): void
* preload(paths: string|string[], type: typeof cc.Asset): void
* preload(paths: string|string[], onComplete: (error: Error, items: RequestItem[]) => void): void
* preload(paths: string|string[]): void
*/
preload (paths, type, onProgress, onComplete) {
var { type, onProgress, onComplete } = parseLoadResArgs(type, onProgress, onComplete);
cc.assetManager.preloadAny(paths, { __requestType__: RequestType.PATH, type: type, bundle: this.name }, onProgress, onComplete);
},
/**
* !#en
* Load all assets under a folder inside the bundle folder.<br>
* <br>
* Note: All asset paths in Creator use forward slashes, paths using backslashes will not work.
*
* !#zh
* 加载目标文件夹中的所有资源, 注意:路径中只能使用斜杠,反斜杠将停止工作
*
* @method loadDir
* @param {string} dir - path of the target folder.The path is relative to the bundle folder, extensions must be omitted.
* @param {Function} [type] - Only asset of type will be loaded if this argument is supplied.
* @param {Function} [onProgress] - Callback invoked when progression change.
* @param {Number} onProgress.finish - The number of the items that are already completed.
* @param {Number} onProgress.total - The total number of the items.
* @param {Object} onProgress.item - The latest request item
* @param {Function} [onComplete] - A callback which is called when all assets have been loaded, or an error occurs.
* @param {Error} onComplete.error - If one of the asset failed, the complete callback is immediately called with the error. If all assets are loaded successfully, error will be null.
* @param {Asset[]|Asset} onComplete.assets - An array of all loaded assets.
*
* @example
* // load all audios (resources/audios/)
* cc.resources.loadDir('audios', cc.AudioClip, (err, audios) => {});
*
* // load all textures in "resources/imgs/"
* cc.resources.loadDir('imgs', cc.Texture2D, null, function (err, textures) {
* var texture1 = textures[0];
* var texture2 = textures[1];
* });
*
* // load all prefabs (${project}/assets/bundle1/misc/characters/) from bundle1 folder
* bundle1.loadDir('misc/characters', cc.Prefab, (err, prefabs) => console.log(err));
*
* // load all sprite frame (${project}/assets/some/xxx/bundle2/skills/) from bundle2 folder
* bundle2.loadDir('skills', cc.SpriteFrame, null, (err, spriteFrames) => console.log(err));
*
* @typescript
* loadDir<T extends cc.Asset>(dir: string, type: { prototype: T }, onProgress: (finish: number, total: number, item: RequestItem) => void, onComplete: (error: Error, assets: Array<T>) => void): void
* loadDir<T extends cc.Asset>(dir: string, onProgress: (finish: number, total: number, item: RequestItem) => void, onComplete: (error: Error, assets: Array<T>) => void): void
* loadDir<T extends cc.Asset>(dir: string, type: { prototype: T }, onComplete: (error: Error, assets: Array<T>) => void): void
* loadDir<T extends cc.Asset>(dir: string, type: { prototype: T }): void
* loadDir<T extends cc.Asset>(dir: string, onComplete: (error: Error, assets: Array<T>) => void): void
* loadDir<T extends cc.Asset>(dir: string): void
*/
loadDir (dir, type, onProgress, onComplete) {
var { type, onProgress, onComplete } = parseLoadResArgs(type, onProgress, onComplete);
cc.assetManager.loadAny(dir, { __requestType__: RequestType.DIR, type: type, bundle: this.name, __outputAsArray__: true }, onProgress, onComplete);
},
/**
* !#en
* Preload all assets under a folder inside the bundle folder.<br> After calling this method, you still need to finish loading by calling `Bundle.loadDir`.
* It will be totally fine to call `Bundle.loadDir` at any time even if the preloading is not yet finished
*
* !#zh
* 预加载目标文件夹中的所有资源。调用完后,你仍然需要通过 `Bundle.loadDir` 来完成加载。
* 就算预加载还没完成,你也可以直接调用 `Bundle.loadDir`。
*
* @method preloadDir
* @param {string} dir - path of the target folder.The path is relative to the bundle folder, extensions must be omitted.
* @param {Function} [type] - Only asset of type will be preloaded if this argument is supplied.
* @param {Function} [onProgress] - Callback invoked when progression change.
* @param {Number} onProgress.finish - The number of the items that are already completed.
* @param {Number} onProgress.total - The total number of the items.
* @param {Object} onProgress.item - The latest request item
* @param {Function} [onComplete] - A callback which is called when all assets have been loaded, or an error occurs.
* @param {Error} onComplete.error - If one of the asset failed, the complete callback is immediately called with the error. If all assets are preloaded successfully, error will be null.
* @param {RequestItem[]} onComplete.items - An array of all preloaded items.
*
* @example
* // preload all audios (resources/audios/)
* cc.resources.preloadDir('audios', cc.AudioClip);
*
* // preload all textures in "resources/imgs/"
* cc.resources.preloadDir('imgs', cc.Texture2D);
* // wait for while
* cc.resources.loadDir('imgs', cc.Texture2D, (err, textures) => {});
*
* // preload all prefabs (${project}/assets/bundle1/misc/characters/) from bundle1 folder
* bundle1.preloadDir('misc/characters', cc.Prefab);
*
* // preload all sprite frame (${project}/assets/some/xxx/bundle2/skills/) from bundle2 folder
* bundle2.preloadDir('skills', cc.SpriteFrame);
* // wait for while
* bundle2.loadDir('skills', cc.SpriteFrame, (err, spriteFrames) => {});
*
* @typescript
* preloadDir(dir: string, type: typeof cc.Asset, onProgress: (finish: number, total: number, item: RequestItem) => void, onComplete: (error: Error, items: RequestItem[]) => void): void
* preloadDir(dir: string, onProgress: (finish: number, total: number, item: RequestItem) => void, onComplete: (error: Error, items: RequestItem[]) => void): void
* preloadDir(dir: string, type: typeof cc.Asset, onComplete: (error: Error, items: RequestItem[]) => void): void
* preloadDir(dir: string, type: typeof cc.Asset): void
* preloadDir(dir: string, onComplete: (error: Error, items: RequestItem[]) => void): void
* preloadDir(dir: string): void
*/
preloadDir (dir, type, onProgress, onComplete) {
var { type, onProgress, onComplete } = parseLoadResArgs(type, onProgress, onComplete);
cc.assetManager.preloadAny(dir, { __requestType__: RequestType.DIR, type: type, bundle: this.name }, onProgress, onComplete);
},
/**
* !#en
* Loads the scene within this bundle by its name.
*
* !#zh
* 通过场景名称加载分包中的场景。
*
* @method loadScene
* @param {String} sceneName - The name of the scene to load.
* @param {Object} [options] - Some optional parameters
* @param {Function} [onProgress] - Callback invoked when progression change.
* @param {Number} onProgress.finish - The number of the items that are already completed.
* @param {Number} onProgress.total - The total number of the items.
* @param {Object} onProgress.item - The latest request item
* @param {Function} [onComplete] - callback, will be called after scene launched.
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {SceneAsset} onComplete.sceneAsset - The scene asset
*
* @example
* bundle1.loadScene('first', (err, sceneAsset) => cc.director.runScene(sceneAsset));
*
* @typescript
* loadScene(sceneName: string, options: Record<string, any>, onProgress: (finish: number, total: number, item: RequestItem) => void, onComplete: (error: Error, sceneAsset: cc.SceneAsset) => void): void
* loadScene(sceneName: string, onProgress: (finish: number, total: number, item: RequestItem) => void, onComplete: (error: Error, sceneAsset: cc.SceneAsset) => void): void
* loadScene(sceneName: string, options: Record<string, any>, onComplete: (error: Error, sceneAsset: cc.SceneAsset) => void): void
* loadScene(sceneName: string, onComplete: (error: Error, sceneAsset: cc.SceneAsset) => void): void
* loadScene(sceneName: string, options: Record<string, any>): void
* loadScene(sceneName: string): void
*/
loadScene (sceneName, options, onProgress, onComplete) {
var { options, onProgress, onComplete } = parseParameters(options, onProgress, onComplete);
options.preset = options.preset || 'scene';
options.bundle = this.name;
cc.assetManager.loadAny({ 'scene': sceneName }, options, onProgress, function (err, sceneAsset) {
if (err) {
cc.error(err.message, err.stack);
onComplete && onComplete(err);
}
else if (sceneAsset instanceof cc.SceneAsset) {
var scene = sceneAsset.scene;
scene._id = sceneAsset._uuid;
scene._name = sceneAsset._name;
onComplete && onComplete(null, sceneAsset);
}
else {
onComplete && onComplete(new Error('The asset ' + sceneAsset._uuid + ' is not a scene'));
}
});
},
/**
* !#en
* Preloads the scene within this bundle by its name. After calling this method, you still need to finish loading by calling `Bundle.loadScene` or `cc.director.loadScene`.
* It will be totally fine to call `Bundle.loadDir` at any time even if the preloading is not yet finished
*
* !#zh
* 通过场景名称预加载分包中的场景.调用完后,你仍然需要通过 `Bundle.loadScene` 或 `cc.director.loadScene` 来完成加载。
* 就算预加载还没完成,你也可以直接调用 `Bundle.loadScene` 或 `cc.director.loadScene`。
*
* @method preloadScene
* @param {String} sceneName - The name of the scene to preload.
* @param {Object} [options] - Some optional parameters
* @param {Function} [onProgress] - callback, will be called when the load progression change.
* @param {Number} onProgress.finish - The number of the items that are already completed
* @param {Number} onProgress.total - The total number of the items
* @param {RequestItem} onProgress.item The latest request item
* @param {Function} [onComplete] - callback, will be called after scene loaded.
* @param {Error} onComplete.error - null or the error object.
*
* @example
* bundle1.preloadScene('first');
* // wait for a while
* bundle1.loadScene('first', (err, scene) => cc.director.runScene(scene));
*
* @typescript
* preloadScene(sceneName: string, options: Record<string, any>, onProgress: (finish: number, total: number, item: RequestItem) => void, onComplete: (error: Error) => void): void
* preloadScene(sceneName: string, onProgress: (finish: number, total: number, item: RequestItem) => void, onComplete: (error: Error) => void): void
* preloadScene(sceneName: string, options: Record<string, any>, onComplete: (error: Error) => void): void
* preloadScene(sceneName: string, onComplete: (error: Error) => void): void
* preloadScene(sceneName: string, options: Record<string, any>): void
* preloadScene(sceneName: string): void
*/
preloadScene (sceneName, options, onProgress, onComplete) {
var { options, onProgress, onComplete } = parseParameters(options, onProgress, onComplete);
options.bundle = this.name;
cc.assetManager.preloadAny({'scene': sceneName}, options, onProgress, function (err) {
if (err) {
cc.errorID(1210, sceneName, err.message);
}
onComplete && onComplete(err);
});
},
/**
* !#en
* Get asset within this bundle by path and type. <br>
* After you load asset with {{#crossLink "Bundle/load:method"}}{{/crossLink}} or {{#crossLink "Bundle/loadDir:method"}}{{/crossLink}},
* you can acquire them by passing the path to this API.
*
* !#zh
* 通过路径与类型获取资源。在你使用 {{#crossLink "Bundle/load:method"}}{{/crossLink}} 或者 {{#crossLink "Bundle/loadDir:method"}}{{/crossLink}} 之后,
* 你能通过传路径通过这个 API 获取到这些资源。
*
* @method get
* @param {String} path - The path of asset
* @param {Function} [type] - Only asset of type will be returned if this argument is supplied.
* @returns {Asset}
*
* @example
* bundle1.get('music/hit', cc.AudioClip);
*
* @typescript
* get<T extends cc.Asset> (path: string, type?: { prototype: T }): T
*/
get (path, type) {
var info = this.getInfoWithPath(path, type);
return assets.get(info && info.uuid);
},
/**
* !#en
* Release the asset loaded by {{#crossLink "Bundle/load:method"}}{{/crossLink}} or {{#crossLink "Bundle/loadDir:method"}}{{/crossLink}} and it's dependencies.
* Refer to {{#crossLink "AssetManager/releaseAsset:method"}}{{/crossLink}} for detailed informations.
*
* !#zh
* 释放通过 {{#crossLink "Bundle/load:method"}}{{/crossLink}} 或者 {{#crossLink "Bundle/loadDir:method"}}{{/crossLink}} 加载的资源。详细信息请参考 {{#crossLink "AssetManager/releaseAsset:method"}}{{/crossLink}}
*
* @method release
* @param {String} path - The path of asset
* @param {Function} [type] - Only asset of type will be released if this argument is supplied.
*
* @example
* // release a texture which is no longer need
* bundle1.release('misc/character/cocos');
*
* @typescript
* release(path: string, type: typeof cc.Asset): void
* release(path: string): void
*/
release (path, type) {
releaseManager.tryRelease(this.get(path, type), true);
},
/**
* !#en
* Release all unused assets within this bundle. Refer to {{#crossLink "AssetManager/releaseAll:method"}}{{/crossLink}} for detailed informations.
*
* !#zh
* 释放此包中的所有没有用到的资源。详细信息请参考 {{#crossLink "AssetManager/releaseAll:method"}}{{/crossLink}}
*
* @method releaseUnusedAssets
* @private
*
* @example
* // release all unused asset within bundle1
* bundle1.releaseUnusedAssets();
*
* @typescript
* releaseUnusedAssets(): void
*/
releaseUnusedAssets () {
var self = this;
assets.forEach(function (asset) {
let info = self.getAssetInfo(asset._uuid);
if (info && !info.redirect) {
releaseManager.tryRelease(asset);
}
});
},
/**
* !#en
* Release all assets within this bundle. Refer to {{#crossLink "AssetManager/releaseAll:method"}}{{/crossLink}} for detailed informations.
*
* !#zh
* 释放此包中的所有资源。详细信息请参考 {{#crossLink "AssetManager/releaseAll:method"}}{{/crossLink}}
*
* @method releaseAll
*
* @example
* // release all asset within bundle1
* bundle1.releaseAll();
*
* @typescript
* releaseAll(): void
*/
releaseAll () {
var self = this;
assets.forEach(function (asset) {
let info = self.getAssetInfo(asset._uuid);
if (info && !info.redirect) {
releaseManager.tryRelease(asset, true);
}
});
},
_destroy () {
this._config.destroy();
}
};
module.exports = Bundle;

View File

@@ -0,0 +1,154 @@
import Cache from './cache';
/**
* @module cc.AssetManager
*/
/**
* !#en
* Cache manager is a module which controls all caches downloaded from server in non-web platform, it is a singleton
* All member can be accessed with `cc.assetManager.cacheManager`.
*
* !#zh
* 缓存管理器是一个模块,在非 WEB 平台上,用于管理所有从服务器上下载下来的缓存,这是一个单例,所有成员能通过 `cc.assetManager.cacheManager` 访问。
*
* @class CacheManager
*/
export abstract class CacheManager {
/**
* !#en
* The name of cacheDir
*
* !#zh
* 缓存目录的名称
*
* @property cacheDir
* @type {String}
* @default 'gamecaches'
*/
public abstract cacheDir: String;
/**
* !#en
* Whether or not cache asset into user's storage space, this property only works on mini-game platforms
*
* !#zh
* 是否缓存资源到用户存储空间,此属性只在小游戏平台有效
*
* @property cacheEnabled
* @type {Boolean}
* @default true
*/
public abstract cacheEnabled: Boolean;
/**
* !#en
* Whether or not auto clear cache when storage ran out, this property only works on mini-game platforms
*
* !#zh
* 是否在存储空间满了后自动清理缓存,此属性只在小游戏平台有效
*
* @property autoClear
* @type {Boolean}
* @default true
*/
public abstract autoClear: Boolean;
/**
* !#en
* The interval between caching resources, this property only works on mini-game platforms, unit: ms
*
* !#zh
* 缓存资源的间隔时间此属性只在小游戏平台有效单位ms
*
* @property cacheInterval
* @type {Number}
* @default 500
*/
public abstract cacheInterval: Number;
/**
* !#en
* The interval between deleting resources, when you use `cleanLRU`, the resources will be deleted as this interval, unit: ms
*
* !#zh
* 清理资源的间隔时间,当你使用 `cleanLRU` 时资源将以此间隔被删除单位ms
*
* @property deleteInterval
* @type {Number}
* @default 500
*/
public abstract deleteInterval: Number;
/**
* !#en
* List of all cached files
*
* !#zh
* 所有缓存文件列表
*
* @property cachedFiles
* @type {Cache}
* @typescript
* cachedFiles: Cache<{ bundle: string, url: string, lastTime: number }>
*/
public abstract cachedFiles: Cache;
/**
* !#en
* Get cached path with origin url
*
* !#zh
* 通过原始 url 获取缓存后的路径
*
* @method getCache
* @param {string} originUrl
* @returns {String} The cached path
*/
public abstract getCache (originUrl: string): string;
/**
* !#en
* Get temporary path with origin url, this method only works on mini-game platforms
*
* !#zh
* 通过原始 url 获取临时文件的路径,此方法只在小游戏平台有效
*
* @method getTemp
* @param {string} originUrl
* @returns {String} The temp path
*/
public abstract getTemp (originUrl: string): string;
/**
* !#en
* Clear all caches, please use with caution, If necessary, we recommend using it before the game is launched
*
* !#zh
* 清空所有缓存,请谨慎使用,如果必要的话,我们建议在游戏启动之前使用
*
* @method clearCache
*/
public abstract clearCache (): void;
/**
* !#en
* Clear part of caches with LRU strategy
*
* !#zh
* 使用 LRU 策略清空部分缓存
*
* @method clearLRU
*/
public abstract clearLRU (): void;
/**
* !#en
* Remove cache with origin url
*
* !#zh
* 通过原始 url 移除缓存
*
* @method removeCache
*/
public abstract removeCache (originUrl: string): void;
}

View File

@@ -0,0 +1,267 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
/**
* @module cc.AssetManager
*/
const js = require('../platform/js');
/**
* !#en
* use to cache something
*
* !#zh
* 用于缓存某些内容
*
* @class Cache
* @typescript Cache<T = any>
*/
function Cache (map) {
if (map) {
this._map = map;
this._count = Object.keys(map).length;
}
else {
this._map = js.createMap(true);
this._count = 0;
}
}
Cache.prototype = {
/**
* !#en
* Create a cache
*
* !#zh
* 创建一个 cache
*
* @method constructor
* @param {Object} [map] - An object used to initialize
*
* @typescript
* constructor(map?: Record<string, T>)
*/
constructor: Cache,
/**
* !#en
* Add Key-Value to cache
*
* !#zh
* 增加键值对到缓存中
*
* @method add
* @param {String} key - The key
* @param {*} val - The value
* @returns {*} The value
*
* @example
* var cache = new Cache();
* cache.add('test', null);
*
* @typescript
* add(key: string, val: T): T
*/
add (key, val) {
if (!(key in this._map)) this._count++;
return this._map[key] = val;
},
/**
* !#en
* Get the cached content by key
*
* !#zh
* 通过 key 获取对应的 value
*
* @method get
* @param {string} key - The key
* @returns {*} The corresponding content
*
* @example
* var cache = new Cache();
* var test = cache.get('test');
*
* @typescript
* get(key: string): T
*/
get (key) {
return this._map[key];
},
/**
* !#en
* Check whether or not content exists by key
*
* !#zh
* 通过 Key 判断是否存在对应的内容
*
* @method has
* @param {string} key - The key
* @returns {boolean} True indecates that content of the key exists
*
* @example
* var cache = new Cache();
* var exist = cache.has('test');
*
* @typescript
* has(key: string): boolean
*/
has (key) {
return key in this._map;
},
/**
* !#en
* Remove the cached content by key
*
* !#zh
* 通过 Key 移除对应的内容
*
* @method remove
* @param {string} key - The key
* @returns {*} The removed content
*
* @example
* var cache = new Cache();
* var content = cache.remove('test');
*
* @typescript
* remove(key: string): T
*/
remove (key) {
var out = this._map[key];
if (key in this._map) {
delete this._map[key];
this._count--;
}
return out;
},
/**
* !#en
* Clear all content
*
* !#zh
* 清除所有内容
*
* @method clear
*
* @example
* var cache = new Cache();
* cache.clear();
*
* @typescript
* clear():void
*/
clear () {
if (this._count !== 0) {
this._map = js.createMap(true);
this._count = 0;
}
},
/**
* !#en
* Enumerate all content and invoke function
*
* !#zh
* 枚举所有内容并执行方法
*
* @method forEach
* @param {Function} func - Function to be invoked
* @param {*} func.val - The value
* @param {String} func.key - The corresponding key
*
* @example
* var cache = new Cache();
* cache.forEach((val, key) => console.log(key));
*
* @typescript
* forEach(func: (val: T, key: string) => void): void
*/
forEach (func) {
for (var key in this._map) {
func(this._map[key], key);
}
},
/**
* !#en
* Enumerate all content to find one element which can fulfill condition
*
* !#zh
* 枚举所有内容,找到一个可以满足条件的元素
*
* @method find
* @param {Function} predicate - The condition
* @returns {*} content
*
* @example
* var cache = new Cache();
* var val = cache.find((val, key) => key === 'test');
*
* @typescript
* find(predicate: (val: T, key: string) => boolean): T
*/
find (predicate) {
for (var key in this._map) {
if (predicate(this._map[key], key)) return this._map[key];
}
return null;
},
/**
* !#en
* The count of cached content
*
* !#zh
* 缓存数量
*
* @property count
* @type {Number}
*/
get count () {
return this._count;
},
/**
* !#en
* Destroy this cache
*
* !#zh
* 销毁这个 cache
*
* @method destroy
*
* @typescript
* destroy(): void
*/
destroy () {
this._map = null;
}
};
module.exports = Cache;

View File

@@ -0,0 +1,257 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const js = require('../platform/js');
const Cache = require('./cache');
const { normalize } = require('./helper');
const { processOptions } = require('./utilities');
function Config () {
this.name = '';
this.base = '';
this.importBase = '';
this.nativeBase = '';
this.deps = null;
this.assetInfos = new Cache();
this.scenes = new Cache();
this.paths = new Cache();
}
Config.prototype = {
constructor: Config,
init: function (options) {
processOptions(options);
this.importBase = options.importBase || '';
this.nativeBase = options.nativeBase || '';
this.base = options.base || '';
this.name = options.name || '';
this.deps = options.deps || [];
// init
this._initUuid(options.uuids);
this._initPath(options.paths);
this._initScene(options.scenes);
this._initPackage(options.packs);
this._initVersion(options.versions);
this._initRedirect(options.redirect);
},
_initUuid: function (uuidList) {
if (!uuidList) return;
this.assetInfos.clear();
for (var i = 0, l = uuidList.length; i < l; i++) {
var uuid = uuidList[i];
this.assetInfos.add(uuid, {uuid});
}
},
_initPath: function (pathList) {
if (!pathList) return;
var paths = this.paths;
paths.clear();
for (var uuid in pathList) {
var info = pathList[uuid];
var path = info[0];
var type = info[1];
var isSubAsset = info.length === 3;
var assetInfo = this.assetInfos.get(uuid);
assetInfo.path = path;
assetInfo.ctor = js._getClassById(type);
if (paths.has(path)) {
if (isSubAsset) {
paths.get(path).push(assetInfo);
}
else {
paths.get(path).unshift(assetInfo);
}
}
else {
paths.add(path, [assetInfo]);
}
}
},
_initScene: function (sceneList) {
if (!sceneList) return;
var scenes = this.scenes;
scenes.clear();
var assetInfos = this.assetInfos;
for (var sceneName in sceneList) {
var uuid = sceneList[sceneName];
var assetInfo = assetInfos.get(uuid);
assetInfo.url = sceneName;
scenes.add(sceneName, assetInfo);
}
},
_initPackage: function (packageList) {
if (!packageList) return;
var assetInfos = this.assetInfos;
for (var packUuid in packageList) {
var uuids = packageList[packUuid];
var pack = {uuid: packUuid, packs: uuids, ext:'.json'};
assetInfos.add(packUuid, pack);
for (var i = 0, l = uuids.length; i < l; i++) {
var uuid = uuids[i];
var assetInfo = assetInfos.get(uuid);
var assetPacks = assetInfo.packs;
if (assetPacks) {
if (l === 1) {
assetPacks.unshift(pack);
}
else {
assetPacks.push(pack);
}
}
else {
assetInfo.packs = [pack];
}
}
}
},
_initVersion: function (versions) {
if (!versions) return;
var assetInfos = this.assetInfos;
var entries = versions.import;
if (entries) {
for (var i = 0, l = entries.length; i < l; i += 2) {
var uuid = entries[i];
var assetInfo = assetInfos.get(uuid);
assetInfo.ver = entries[i + 1];
}
}
entries = versions.native;
if (entries) {
for (var i = 0, l = entries.length; i < l; i += 2) {
var uuid = entries[i];
var assetInfo = assetInfos.get(uuid);
assetInfo.nativeVer = entries[i + 1];
}
}
},
_initRedirect: function (redirect) {
if (!redirect) return;
var assetInfos = this.assetInfos;
for (var i = 0, l = redirect.length; i < l; i += 2) {
var uuid = redirect[i];
var assetInfo = assetInfos.get(uuid);
assetInfo.redirect = redirect[i + 1];
}
},
getInfoWithPath: function (path, type) {
if (!path) {
return null;
}
path = normalize(path);
var items = this.paths.get(path);
if (items) {
if (type) {
for (var i = 0, l = items.length; i < l; i++) {
var assetInfo = items[i];
if (js.isChildClassOf(assetInfo.ctor, type)) {
return assetInfo;
}
}
}
else {
return items[0];
}
}
return null;
},
getDirWithPath: function (path, type, out) {
path = normalize(path);
if (path[path.length - 1] === '/') {
path = path.slice(0, -1);
}
var infos = out || [];
function isMatchByWord (path, test) {
if (path.length > test.length) {
var nextAscii = path.charCodeAt(test.length);
return nextAscii === 47; // '/'
}
return true;
}
this.paths.forEach(function (items, p) {
if ((p.startsWith(path) && isMatchByWord(p, path)) || !path) {
for (var i = 0, l = items.length; i < l; i++) {
var entry = items[i];
if (!type || js.isChildClassOf(entry.ctor, type)) {
infos.push(entry);
}
}
}
});
return infos;
},
getAssetInfo: function (uuid) {
return this.assetInfos.get(uuid);
},
getSceneInfo: function (name) {
if (!name.endsWith('.fire')) {
name += '.fire';
}
if (name[0] !== '/' && !name.startsWith('db://')) {
name = '/' + name; // 使用全名匹配
}
// search scene
var info = this.scenes.find(function (val, key) {
return key.endsWith(name);
});
return info;
},
destroy: function () {
this.paths.destroy();
this.scenes.destroy();
this.assetInfos.destroy();
}
};
if (CC_TEST) {
cc._Test.Config = Config;
}
module.exports = Config;

View File

@@ -0,0 +1,246 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const Cache = require('./cache');
const deserialize = require('./deserialize');
const { files, parsed } = require('./shared');
import { hasNativeDep , getDependUuidList } from '../platform/deserialize-compiled';
import deserializeForCompiled from '../platform/deserialize-compiled';
/**
* @module cc.AssetManager
*/
/**
* !#en
* Control asset's dependency list, it is a singleton. All member can be accessed with `cc.assetManager.dependUtil`
*
* !#zh
* 控制资源的依赖列表,这是一个单例, 所有成员能通过 `cc.assetManager.dependUtil` 访问
*
* @class DependUtil
*/
var dependUtil = {
_depends: new Cache(),
init () {
this._depends.clear();
},
/**
* !#en
* Get asset's native dependency. For example, Texture's native dependency is image.
*
* !#zh
* 获取资源的原生依赖,例如 Texture 的原生依赖是图片
*
* @method getNativeDep
* @param {string} uuid - asset's uuid
* @returns {Object} native dependency
*
* @example
* var dep = dependUtil.getNativeDep('fcmR3XADNLgJ1ByKhqcC5Z');
*
* @typescript
* getNativeDep(uuid: string): Record<string, any>
*/
getNativeDep (uuid) {
let depend = this._depends.get(uuid);
if (depend) return depend.nativeDep && Object.assign({}, depend.nativeDep);
return null;
},
/**
* !#en
* Get asset's direct referencing non-native dependency list. For example, Material's non-native dependencies are Texture.
*
* !#zh
* 获取资源直接引用的非原生依赖列表,例如,材质的非原生依赖是 Texture
*
* @method getDeps
* @param {string} uuid - asset's uuid
* @returns {string[]} direct referencing non-native dependency list
*
* @example
* var deps = dependUtil.getDeps('fcmR3XADNLgJ1ByKhqcC5Z');
*
* @typescript
* getDeps(uuid: string): string[]
*/
getDeps (uuid) {
if (this._depends.has(uuid)) {
return this._depends.get(uuid).deps;
}
return [];
},
/**
* !#en
* Get non-native dependency list of the loaded asset, include indirect reference.
* The returned array stores the dependencies with their uuid, after retrieve dependencies,
*
* !#zh
* 获取某个已经加载好的资源的所有非原生依赖资源列表,包括间接引用的资源,并保存在数组中返回。
* 返回的数组将仅保存依赖资源的 uuid。
*
* @method getDependsRecursively
* @param {String} uuid - The asset's uuid
* @returns {string[]} non-native dependency list
*
* @example
* var deps = dependUtil.getDepsRecursively('fcmR3XADNLgJ1ByKhqcC5Z');
*
* @typescript
* getDepsRecursively(uuid: string): string[]
*/
getDepsRecursively (uuid) {
var exclude = Object.create(null), depends = [];
this._descend(uuid, exclude, depends);
return depends;
},
_descend (uuid, exclude, depends) {
var deps = this.getDeps(uuid);
for (var i = 0; i < deps.length; i++) {
var depend = deps[i];
if ( !exclude[depend] ) {
exclude[depend] = true;
depends.push(depend);
this._descend(depend, exclude, depends);
}
}
},
remove (uuid) {
this._depends.remove(uuid);
},
/**
* !#en
* Extract dependency list from serialized data or asset and then store in cache.
*
* !#zh
* 从序列化数据或资源中提取出依赖列表,并且存储在缓存中。
*
* @param {string} uuid - The uuid of serialized data or asset
* @param {Object} json - Serialized data or asset
* @returns {Object} dependency list, include non-native and native dependency
*
* @example
* downloader.downloadFile('test.json', {responseType: 'json'}, null, (err, file) => {
* var dependencies = parse('fcmR3XADNLgJ1ByKhqcC5Z', file);
* });
*
* @typescript
* parse(uuid: string, json: any): { deps?: string[], nativeDep?: any }
*/
parse (uuid, json) {
var out = null;
if (Array.isArray(json) || json.__type__) {
if (out = this._depends.get(uuid)) return out;
if (Array.isArray(json) && (!(CC_BUILD || deserializeForCompiled.isCompiledJson(json)) || !hasNativeDep(json))) {
out = {
deps: this._parseDepsFromJson(json),
};
}
else {
try {
var asset = deserialize(json);
out = this._parseDepsFromAsset(asset);
out.nativeDep && (out.nativeDep.uuid = uuid);
parsed.add(uuid + '@import', asset);
}
catch (e) {
files.remove(uuid + '@import');
out = { deps: [] };
}
}
}
// get deps from an existing asset
else {
if (!CC_EDITOR && (out = this._depends.get(uuid)) && out.parsedFromExistAsset) return out;
out = this._parseDepsFromAsset(json);
}
// cache dependency list
this._depends.add(uuid, out);
return out;
},
_parseDepsFromAsset: function (asset) {
var out = {
deps: [],
parsedFromExistAsset: true,
preventPreloadNativeObject: asset.constructor.preventPreloadNativeObject,
preventDeferredLoadDependents: asset.constructor.preventDeferredLoadDependents
};
let deps = asset.__depends__;
for (var i = 0, l = deps.length; i < l; i++) {
var dep = deps[i].uuid;
out.deps.push(dep);
}
if (asset.__nativeDepend__) {
out.nativeDep = asset._nativeDep;
}
return out;
},
_parseDepsFromJson: CC_EDITOR || CC_PREVIEW ? function (json) {
if (deserializeForCompiled.isCompiledJson(json)) {
let depends = getDependUuidList(json);
depends.forEach((uuid, index) => depends[index] = cc.assetManager.utils.decodeUuid(uuid));
return depends;
}
var depends = [];
function parseDependRecursively (data, out) {
if (!data || typeof data !== 'object' || data.__id__) return;
var uuid = data.__uuid__;
if (Array.isArray(data)) {
for (let i = 0, l = data.length; i < l; i++) {
parseDependRecursively(data[i], out);
}
}
else if (uuid) {
out.push(cc.assetManager.utils.decodeUuid(uuid));
}
else {
for (var prop in data) {
parseDependRecursively(data[prop], out);
}
}
}
parseDependRecursively(json, depends);
return depends;
} : function (json) {
let depends = getDependUuidList(json);
depends.forEach((uuid, index) => depends[index] = cc.assetManager.utils.decodeUuid(uuid));
return depends;
}
};
module.exports = dependUtil;

View File

@@ -0,0 +1,793 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const js = require('../platform/js');
require('../CCDirector');
const utilities = require('./utilities');
const dependUtil = require('./depend-util');
const releaseManager = require('./releaseManager');
const downloader = require('./downloader');
const factory = require('./factory');
const helper = require('./helper');
const ImageFmts = ['.png', '.jpg', '.bmp', '.jpeg', '.gif', '.ico', '.tiff', '.webp', '.image', '.pvr', '.pkm'];
const AudioFmts = ['.mp3', '.ogg', '.wav', '.m4a'];
function GetTrue () { return true; }
const md5Pipe = {
transformURL (url) {
let uuid = helper.getUuidFromURL(url);
if (!uuid) { return url; }
let bundle = cc.assetManager.bundles.find((b) => {
return !!b.getAssetInfo(uuid);
});
if (!bundle) { return url; }
let hashValue = '';
let info = bundle.getAssetInfo(uuid);
if (url.startsWith(bundle.base + bundle._config.nativeBase)) {
hashValue = info.nativeVer || '';
}
else {
hashValue = info.ver || '';
}
if (!hashValue || url.indexOf(hashValue) !== -1) { return url; }
let hashPatchInFolder = false;
if (cc.path.extname(url) === '.ttf') {
hashPatchInFolder = true;
}
if (hashPatchInFolder) {
let dirname = cc.path.dirname(url);
let basename = cc.path.basename(url);
url = `${dirname}.${hashValue}/${basename}`;
} else {
url = url.replace(/.*[/\\][0-9a-fA-F]{2}[/\\]([0-9a-fA-F-]{8,})/, (match, uuid) => {
return match + '.' + hashValue;
});
}
return url;
},
};
/**
* `cc.loader` is deprecated, please backup your project and upgrade to {{#crossLink "AssetManager"}}{{/crossLink}}
*
* @class loader
* @static
* @deprecated cc.loader is deprecated, please backup your project and upgrade to cc.assetManager
*/
const loader = {
/**
* `cc.loader.onProgress` is deprecated, please transfer onProgress to API as a parameter
* @property onProgress
* @deprecated cc.loader.onProgress is deprecated, please transfer onProgress to API as a parameter
*/
onProgress: null,
_autoReleaseSetting: Object.create(null),
get _cache () {
return cc.assetManager.assets._map;
},
/**
* `cc.loader.load` is deprecated, please use {{#crossLink "AssetManager/loadAny:method"}}{{/crossLink}} instead
*
* @deprecated cc.loader.load is deprecated, please use cc.assetManager.loadAny instead
*
* @method load
* @param {String|String[]|Object} resources - Url list in an array
* @param {Function} [progressCallback] - Callback invoked when progression change
* @param {Number} progressCallback.completedCount - The number of the items that are already completed
* @param {Number} progressCallback.totalCount - The total number of the items
* @param {Object} progressCallback.item - The latest item which flow out the pipeline
* @param {Function} [completeCallback] - Callback invoked when all resources loaded
* @typescript
* load(resources: string|string[]|{uuid?: string, url?: string, type?: string}, completeCallback?: Function): void
* load(resources: string|string[]|{uuid?: string, url?: string, type?: string}, progressCallback: (completedCount: number, totalCount: number, item: any) => void, completeCallback: Function|null): void
*/
load (resources, progressCallback, completeCallback) {
if (completeCallback === undefined) {
if (progressCallback !== undefined) {
completeCallback = progressCallback;
progressCallback = null;
}
}
resources = Array.isArray(resources) ? resources : [resources];
for (var i = 0; i < resources.length; i++) {
var item = resources[i];
if (typeof item === 'string') {
resources[i] = { url: item, __isNative__: true};
}
else {
if (item.type) {
item.ext = '.' + item.type;
item.type = undefined;
}
if (item.url) {
item.__isNative__ = true;
}
}
}
var images = [];
var audios = [];
cc.assetManager.loadAny(resources, null, (finish, total, item) => {
if (item.content) {
if (ImageFmts.includes(item.ext)) {
images.push(item.content);
}
else if (AudioFmts.includes(item.ext)) {
audios.push(item.content);
}
}
progressCallback && progressCallback(finish, total, item);
}, (err, native) => {
var res = null;
if (!err) {
native = Array.isArray(native) ? native : [native];
for (var i = 0; i < native.length; i++) {
var item = native[i];
if (!(item instanceof cc.Asset)) {
var asset = item;
var url = resources[i].url;
if (images.includes(asset)) {
factory.create(url, item, '.png', null, (err, image) => {
asset = native[i] = image;
});
}
else if (audios.includes(asset)) {
factory.create(url, item, '.mp3', null, (err, audio) => {
asset = native[i] = audio;
});
}
cc.assetManager.assets.add(url, asset);
}
}
if (native.length > 1) {
var map = Object.create(null);
native.forEach(function (asset) {
map[asset._uuid] = asset;
});
res = { isCompleted: GetTrue, _map: map };
}
else {
res = native[0];
}
}
completeCallback && completeCallback(err, res);
});
},
/**
* `cc.loader.getXMLHttpRequest` is deprecated, please use `XMLHttpRequest` directly
*
* @method getXMLHttpRequest
* @deprecated cc.loader.getXMLHttpRequest is deprecated, please use XMLHttpRequest directly
* @returns {XMLHttpRequest}
*/
getXMLHttpRequest () {
return new XMLHttpRequest();
},
_parseLoadResArgs: utilities.parseLoadResArgs,
/**
* `cc.loader.getItem` is deprecated, please use `cc.assetManager.asset.get` instead
*
* @method getItem
* @param {Object} id The id of the item
* @return {Object}
* @deprecated cc.loader.getItem is deprecated, please use cc.assetManager.assets.get instead
*/
getItem (key) {
return cc.assetManager.assets.has(key) ? { content: cc.assetManager.assets.get(key) } : null;
},
/**
* `cc.loader.loadRes` is deprecated, please use {{#crossLink "Bundle/load:method"}}{{/crossLink}} instead
*
* @deprecated cc.loader.loadRes is deprecated, please use cc.resources.load instead
* @method loadRes
* @param {String} url - Url of the target resource.
* The url is relative to the "resources" folder, extensions must be omitted.
* @param {Function} [type] - Only asset of type will be loaded if this argument is supplied.
* @param {Function} [progressCallback] - Callback invoked when progression change.
* @param {Number} progressCallback.completedCount - The number of the items that are already completed.
* @param {Number} progressCallback.totalCount - The total number of the items.
* @param {Object} progressCallback.item - The latest item which flow out the pipeline.
* @param {Function} [completeCallback] - Callback invoked when the resource loaded.
* @param {Error} completeCallback.error - The error info or null if loaded successfully.
* @param {Object} completeCallback.resource - The loaded resource if it can be found otherwise returns null.
*
* @typescript
* loadRes(url: string, type: typeof cc.Asset, progressCallback: (completedCount: number, totalCount: number, item: any) => void, completeCallback: ((error: Error, resource: any) => void)|null): void
* loadRes(url: string, type: typeof cc.Asset, completeCallback: (error: Error, resource: any) => void): void
* loadRes(url: string, type: typeof cc.Asset): void
* loadRes(url: string, progressCallback: (completedCount: number, totalCount: number, item: any) => void, completeCallback: ((error: Error, resource: any) => void)|null): void
* loadRes(url: string, completeCallback: (error: Error, resource: any) => void): void
* loadRes(url: string): void
*/
loadRes (url, type, progressCallback, completeCallback) {
var { type, onProgress, onComplete } = this._parseLoadResArgs(type, progressCallback, completeCallback);
var extname = cc.path.extname(url);
if (extname) {
// strip extname
url = url.slice(0, - extname.length);
}
cc.resources.load(url, type, onProgress, onComplete);
},
/**
* `cc.loader.loadResArray` is deprecated, please use {{#crossLink "Bundle/load:method"}}{{/crossLink}} instead
*
* @deprecated cc.loader.loadResArray is deprecated, please use cc.resources.load instead
* @method loadResArray
* @param {String[]} urls - Array of URLs of the target resource.
* The url is relative to the "resources" folder, extensions must be omitted.
* @param {Function} [type] - Only asset of type will be loaded if this argument is supplied.
* @param {Function} [progressCallback] - Callback invoked when progression change.
* @param {Number} progressCallback.completedCount - The number of the items that are already completed.
* @param {Number} progressCallback.totalCount - The total number of the items.
* @param {Object} progressCallback.item - The latest item which flow out the pipeline.
* @param {Function} [completeCallback] - A callback which is called when all assets have been loaded, or an error occurs.
* @param {Error} completeCallback.error - If one of the asset failed, the complete callback is immediately called
* with the error. If all assets are loaded successfully, error will be null.
* @param {Asset[]|Array} completeCallback.assets - An array of all loaded assets.
* If nothing to load, assets will be an empty array.
* @typescript
* loadResArray(url: string[], type: typeof cc.Asset, progressCallback: (completedCount: number, totalCount: number, item: any) => void, completeCallback: ((error: Error, resource: any[]) => void)|null): void
* loadResArray(url: string[], type: typeof cc.Asset, completeCallback: (error: Error, resource: any[]) => void): void
* loadResArray(url: string[], type: typeof cc.Asset): void
* loadResArray(url: string[], progressCallback: (completedCount: number, totalCount: number, item: any) => void, completeCallback: ((error: Error, resource: any[]) => void)|null): void
* loadResArray(url: string[], completeCallback: (error: Error, resource: any[]) => void): void
* loadResArray(url: string[]): void
* loadResArray(url: string[], type: typeof cc.Asset[]): void
*/
loadResArray (urls, type, progressCallback, completeCallback) {
var { type, onProgress, onComplete } = this._parseLoadResArgs(type, progressCallback, completeCallback);
urls.forEach((url, i) => {
var extname = cc.path.extname(url);
if (extname) {
// strip extname
urls[i] = url.slice(0, - extname.length);
}
})
cc.resources.load(urls, type, onProgress, onComplete);
},
/**
* `cc.loader.loadResDir` is deprecated, please use {{#crossLink "Bundle/loadDir:method"}}{{/crossLink}} instead
*
* @deprecated cc.loader.loadResDir is deprecated, please use cc.resources.loadDir instead
* @method loadResDir
* @param {String} url - Url of the target folder.
* The url is relative to the "resources" folder, extensions must be omitted.
* @param {Function} [type] - Only asset of type will be loaded if this argument is supplied.
* @param {Function} [progressCallback] - Callback invoked when progression change.
* @param {Number} progressCallback.completedCount - The number of the items that are already completed.
* @param {Number} progressCallback.totalCount - The total number of the items.
* @param {Object} progressCallback.item - The latest item which flow out the pipeline.
* @param {Function} [completeCallback] - A callback which is called when all assets have been loaded, or an error occurs.
* @param {Error} completeCallback.error - If one of the asset failed, the complete callback is immediately called
* with the error. If all assets are loaded successfully, error will be null.
* @param {Asset[]|Array} completeCallback.assets - An array of all loaded assets.
* If nothing to load, assets will be an empty array.
* @param {String[]} completeCallback.urls - An array that lists all the URLs of loaded assets.
*
* @typescript
* loadResDir(url: string, type: typeof cc.Asset, progressCallback: (completedCount: number, totalCount: number, item: any) => void, completeCallback: ((error: Error, resource: any[], urls: string[]) => void)|null): void
* loadResDir(url: string, type: typeof cc.Asset, completeCallback: (error: Error, resource: any[], urls: string[]) => void): void
* loadResDir(url: string, type: typeof cc.Asset): void
* loadResDir(url: string, progressCallback: (completedCount: number, totalCount: number, item: any) => void, completeCallback: ((error: Error, resource: any[], urls: string[]) => void)|null): void
* loadResDir(url: string, completeCallback: (error: Error, resource: any[], urls: string[]) => void): void
* loadResDir(url: string): void
*/
loadResDir (url, type, progressCallback, completeCallback) {
var { type, onProgress, onComplete } = this._parseLoadResArgs(type, progressCallback, completeCallback);
cc.resources.loadDir(url, type, onProgress, function (err, assets) {
var urls = [];
if (!err) {
var infos = cc.resources.getDirWithPath(url, type);
urls = infos.map(function (info) {
return info.path;
});
}
onComplete && onComplete(err, assets, urls);
});
},
/**
* `cc.loader.getRes` is deprecated, please use {{#crossLink "Bundle/get:method"}}{{/crossLink}} instead
*
* @method getRes
* @param {String} url
* @param {Function} [type] - Only asset of type will be returned if this argument is supplied.
* @returns {*}
* @deprecated cc.loader.getRes is deprecated, please use cc.resources.get instead
*/
getRes (url, type) {
return cc.assetManager.assets.has(url) ? cc.assetManager.assets.get(url) : cc.resources.get(url, type);
},
getResCount () {
return cc.assetManager.assets.count;
},
/**
* `cc.loader.getDependsRecursively` is deprecated, please use use {{#crossLink "DependUtil/getDepsRecursively:method"}}{{/crossLink}} instead
*
* @deprecated cc.loader.getDependsRecursively is deprecated, please use use cc.assetManager.dependUtil.getDepsRecursively instead
* @method getDependsRecursively
* @param {Asset|String} owner - The owner asset or the resource url or the asset's uuid
* @returns {Array}
*/
getDependsRecursively (owner) {
if (!owner) return [];
return dependUtil.getDepsRecursively(typeof owner === 'string' ? owner : owner._uuid).concat([ owner._uuid ]);
},
/**
* `cc.loader.assetLoader` was removed, assetLoader and md5Pipe were merged into {{#crossLink "AssetManager/transformPipeline:property"}}{{/crossLink}}
*
* @property assetLoader
* @deprecated cc.loader.assetLoader was removed, assetLoader and md5Pipe were merged into cc.assetManager.transformPipeline
* @type {Object}
*/
get assetLoader () {
if (CC_DEBUG) {
cc.error('cc.loader.assetLoader was removed, assetLoader and md5Pipe were merged into cc.assetManager.transformPipeline');
}
},
/**
* `cc.loader.md5Pipe` is deprecated, assetLoader and md5Pipe were merged into {{#crossLink "AssetManager/transformPipeline:property"}}{{/crossLink}}
*
* @property md5Pipe
* @deprecated cc.loader.md5Pipe is deprecated, assetLoader and md5Pipe were merged into cc.assetManager.transformPipeline
* @type {Object}
*/
get md5Pipe () {
return md5Pipe;
},
/**
* `cc.loader.downloader` is deprecated, please use {{#crossLink "AssetManager/downloader:property"}}{{/crossLink}} instead
*
* @deprecated cc.loader.downloader is deprecated, please use cc.assetManager.downloader instead
* @property downloader
* @type {Object}
*/
get downloader () {
return cc.assetManager.downloader;
},
/**
* `cc.loader.loader` is deprecated, please use {{#crossLink "AssetManager/parser:property"}}{{/crossLink}} instead
*
* @property loader
* @type {Object}
* @deprecated cc.loader.loader is deprecated, please use cc.assetManager.parser instead
*/
get loader () {
return cc.assetManager.parser;
},
/**
* `cc.loader.addDownloadHandlers` is deprecated, please use `cc.assetManager.downloader.register` instead
*
* @method addDownloadHandlers
* @param {Object} extMap Custom supported types with corresponded handler
* @deprecated cc.loader.addDownloadHandlers is deprecated, please use cc.assetManager.downloader.register instead
*/
addDownloadHandlers (extMap) {
if (CC_DEBUG) {
cc.warn('`cc.loader.addDownloadHandlers` is deprecated, please use `cc.assetManager.downloader.register` instead');
}
var handler = Object.create(null);
for (var type in extMap) {
var func = extMap[type];
handler['.' + type] = function (url, options, onComplete) {
func({url}, onComplete);
};
}
cc.assetManager.downloader.register(handler);
},
/**
* `cc.loader.addLoadHandlers` is deprecated, please use `cc.assetManager.parser.register` instead
*
* @method addLoadHandlers
* @param {Object} extMap Custom supported types with corresponded handler
* @deprecated cc.loader.addLoadHandlers is deprecated, please use cc.assetManager.parser.register instead
*/
addLoadHandlers (extMap) {
if (CC_DEBUG) {
cc.warn('`cc.loader.addLoadHandlers` is deprecated, please use `cc.assetManager.parser.register` instead');
}
var handler = Object.create(null);
for (var type in extMap) {
var func = extMap[type];
handler['.' + type] = function (file, options, onComplete) {
func({content: file}, onComplete);
};
}
cc.assetManager.parser.register(handler);
},
flowInDeps () {
if (CC_DEBUG) {
cc.error('cc.loader.flowInDeps was removed');
}
},
/**
* `cc.loader.release` is deprecated, please use {{#crossLink "AssetManager/releaseAsset:method"}}{{/crossLink}} instead
*
* @method release
* @param {Asset|String|Array} asset
* @deprecated cc.loader.release is deprecated, please use cc.assetManager.releaseAsset instead
*/
release (asset) {
if (Array.isArray(asset)) {
for (let i = 0; i < asset.length; i++) {
var key = asset[i];
if (typeof key === 'string') key = cc.assetManager.assets.get(key);
let isBuiltin = cc.assetManager.builtins._assets.find(function (assets) {
return assets.find(builtinAsset => builtinAsset === key);
});
if (isBuiltin) continue;
cc.assetManager.releaseAsset(key);
}
}
else if (asset) {
if (typeof asset === 'string') asset = cc.assetManager.assets.get(asset);
let isBuiltin = cc.assetManager.builtins._assets.find(function (assets) {
return assets.find(builtinAsset => builtinAsset === asset);
});
if (isBuiltin) return;
cc.assetManager.releaseAsset(asset);
}
},
/**
* `cc.loader.releaseAsset` is deprecated, please use {{#crossLink "AssetManager/releaseAsset:method"}}{{/crossLink}} instead
*
* @deprecated cc.loader.releaseAsset is deprecated, please use cc.assetManager.releaseAsset instead
* @method releaseAsset
* @param {Asset} asset
*/
releaseAsset (asset) {
cc.assetManager.releaseAsset(asset);
},
/**
* `cc.loader.releaseRes` is deprecated, please use {{#crossLink "AssetManager/releaseRes:method"}}{{/crossLink}} instead
*
* @deprecated cc.loader.releaseRes is deprecated, please use cc.assetManager.releaseRes instead
* @method releaseRes
* @param {String} url
* @param {Function} [type] - Only asset of type will be released if this argument is supplied.
*/
releaseRes (url, type) {
cc.resources.release(url, type);
},
/**
* `cc.loader.releaseResDir` was removed, please use {{#crossLink "AssetManager/releaseRes:method"}}{{/crossLink}} instead
*
* @deprecated cc.loader.releaseResDir was removed, please use cc.assetManager.releaseRes instead
* @method releaseResDir
*/
releaseResDir () {
if (CC_DEBUG) {
cc.error('cc.loader.releaseResDir was removed, please use cc.assetManager.releaseAsset instead');
}
},
/**
* `cc.loader.releaseAll` is deprecated, please use {{#crossLink "AssetManager/releaseAll:method"}}{{/crossLink}} instead
*
* @deprecated cc.loader.releaseAll is deprecated, please use cc.assetManager.releaseAll instead
* @method releaseAll
*/
releaseAll () {
cc.assetManager.releaseAll();
cc.assetManager.assets.clear();
},
/**
* `cc.loader.removeItem` is deprecated, please use `cc.assetManager.assets.remove` instead
*
* @deprecated cc.loader.removeItem is deprecated, please use cc.assetManager.assets.remove instead
* @method removeItem
* @param {Object} id The id of the item
* @return {Boolean} succeed or not
*/
removeItem (key) {
cc.assetManager.assets.remove(key);
},
/**
* `cc.loader.setAutoRelease` is deprecated, if you want to prevent some asset from auto releasing, please use {{#crossLink "Asset/addRef:method"}}{{/crossLink}} instead
*
* @deprecated cc.loader.setAutoRelease is deprecated, if you want to prevent some asset from auto releasing, please use cc.Asset.addRef instead
* @method setAutoRelease
* @param {Asset|String} assetOrUrlOrUuid - asset object or the raw asset's url or uuid
* @param {Boolean} autoRelease - indicates whether should release automatically
*/
setAutoRelease (asset, autoRelease) {
if (typeof asset === 'object') asset = asset._uuid;
this._autoReleaseSetting[asset] = !!autoRelease;
},
/**
* `cc.loader.setAutoReleaseRecursively` is deprecated, if you want to prevent some asset from auto releasing, please use {{#crossLink "Asset/addRef:method"}}{{/crossLink}} instead
*
* @method setAutoReleaseRecursively
* @param {Asset|String} assetOrUrlOrUuid - asset object or the raw asset's url or uuid
* @param {Boolean} autoRelease - indicates whether should release automatically
* @deprecated cc.loader.setAutoReleaseRecursively is deprecated, if you want to prevent some asset from auto releasing, please use cc.Asset.addRef instead
*/
setAutoReleaseRecursively (asset, autoRelease) {
if (typeof asset === 'object') asset = asset._uuid;
autoRelease = !!autoRelease;
this._autoReleaseSetting[asset] = autoRelease;
var depends = dependUtil.getDepsRecursively(asset);
for (var i = 0; i < depends.length; i++) {
var depend = depends[i];
this._autoReleaseSetting[depend] = autoRelease;
}
},
/**
* `cc.loader.isAutoRelease` is deprecated
*
* @method isAutoRelease
* @param {Asset|String} assetOrUrl - asset object or the raw asset's url
* @returns {Boolean}
* @deprecated cc.loader.isAutoRelease is deprecated
*/
isAutoRelease (asset) {
if (typeof asset === 'object') asset = asset._uuid;
return !!this._autoReleaseSetting[asset];
}
};
/**
* @class Downloader
*/
/**
* `cc.loader.downloader.loadSubpackage` is deprecated, please use {{#crossLink "AssetManager/loadBundle:method"}}{{/crossLink}} instead
*
* @deprecated cc.loader.downloader.loadSubpackage is deprecated, please use AssetManager.loadBundle instead
* @method loadSubpackage
* @param {String} name - Subpackage name
* @param {Function} [completeCallback] - Callback invoked when subpackage loaded
* @param {Error} completeCallback.error - error information
*/
downloader.loadSubpackage = function (name, completeCallback) {
cc.assetManager.loadBundle(name, null, completeCallback);
};
/**
* @deprecated cc.AssetLibrary is deprecated, please backup your project and upgrade to cc.assetManager
*/
var AssetLibrary = {
/**
* @deprecated cc.AssetLibrary.init is deprecated, please use cc.assetManager.init instead
*/
init (options) {
options.importBase = options.libraryPath;
options.nativeBase = CC_BUILD ? options.rawAssetsBase : options.libraryPath;
cc.assetManager.init(options);
if (options.rawAssets) {
var resources = new cc.AssetManager.Bundle();
resources.init({
name: cc.AssetManager.BuiltinBundleName.RESOURCES,
importBase: options.importBase,
nativeBase: options.nativeBase,
paths: options.rawAssets.assets,
uuids: Object.keys(options.rawAssets.assets),
});
}
},
/**
* @deprecated cc.AssetLibrary is deprecated, please use cc.assetManager.loadAny instead
*/
loadAsset (uuid, onComplete) {
cc.assetManager.loadAny(uuid, onComplete);
},
getLibUrlNoExt () {
if (CC_DEBUG) {
cc.error('cc.AssetLibrary.getLibUrlNoExt was removed, if you want to transform url, please use cc.assetManager.utils.getUrlWithUuid instead');
}
},
queryAssetInfo () {
if (CC_DEBUG) {
cc.error('cc.AssetLibrary.queryAssetInfo was removed, only available in the editor by using cc.assetManager.editorExtend.queryAssetInfo');
}
}
};
/**
* `cc.url` is deprecated
*
* @deprecated cc.url is deprecated
* @class url
* @static
*/
cc.url = {
normalize (url) {
cc.warnID(1400, 'cc.url.normalize', 'cc.assetManager.utils.normalize');
return cc.assetManager.utils.normalize(url);
},
/**
* `cc.url.raw` is deprecated, please use `cc.resources.load` directly, or use `Asset.nativeUrl` instead.
*
* @deprecated cc.url.raw is deprecated, please use cc.resources.load directly, or use Asset.nativeUrl instead.
* @method raw
* @param {String} url
* @return {String}
*/
raw (url) {
cc.warnID(1400, 'cc.url.raw', 'cc.resources.load');
if (url.startsWith('resources/')) {
return cc.assetManager._transform({'path': cc.path.changeExtname(url.substr(10)), bundle: cc.AssetManager.BuiltinBundleName.RESOURCES, __isNative__: true, ext: cc.path.extname(url)});
}
return '';
}
};
let onceWarns = {
loader: true,
assetLibrary: true,
};
Object.defineProperties(cc, {
loader: {
get () {
if (CC_DEBUG) {
if (onceWarns.loader) {
onceWarns.loader = false;
cc.log('cc.loader is deprecated, use cc.assetManager instead please. See https://docs.cocos.com/creator/manual/zh/release-notes/asset-manager-upgrade-guide.html');
}
}
return loader;
}
},
AssetLibrary: {
get () {
if (CC_DEBUG) {
if (onceWarns.assetLibrary) {
onceWarns.assetLibrary = false;
cc.log('cc.AssetLibrary is deprecated, use cc.assetManager instead please. See https://docs.cocos.com/creator/manual/zh/release-notes/asset-manager-upgrade-guide.html');
}
}
return AssetLibrary;
}
},
/**
* `cc.LoadingItems` was removed, please use {{#crossLink "Task"}}{{/crossLink}} instead
*
* @deprecated cc.LoadingItems was removed, please use cc.AssetManager.Task instead
* @class LoadingItems
*/
LoadingItems: {
get () {
cc.warnID(1400, 'cc.LoadingItems', 'cc.AssetManager.Task');
return cc.AssetManager.Task;
}
},
Pipeline: {
get () {
cc.warnID(1400, 'cc.Pipeline', 'cc.AssetManager.Pipeline');
return cc.AssetManager.Pipeline;
}
}
});
js.obsolete(cc, 'cc.RawAsset', 'cc.Asset');
/**
* @class Asset
*/
/**
* `cc.Asset.url` is deprecated, please use {{#crossLink "Asset/nativeUrl:property"}}{{/crossLink}} instead
* @property url
* @type {String}
* @deprecated cc.Asset.url is deprecated, please use cc.Asset.nativeUrl instead
*/
js.obsolete(cc.Asset.prototype, 'cc.Asset.url', 'nativeUrl');
/**
* @class macro
* @static
*/
Object.defineProperties(cc.macro, {
/**
* `cc.macro.DOWNLOAD_MAX_CONCURRENT` is deprecated now, please use {{#crossLink "Downloader/maxConcurrency:property"}}{{/crossLink}} instead
*
* @property DOWNLOAD_MAX_CONCURRENT
* @type {Number}
* @deprecated cc.macro.DOWNLOAD_MAX_CONCURRENT is deprecated now, please use cc.assetManager.downloader.maxConcurrency instead
*/
DOWNLOAD_MAX_CONCURRENT: {
get () {
return cc.assetManager.downloader.maxConcurrency;
},
set (val) {
cc.assetManager.downloader.maxConcurrency = val;
}
}
});
Object.assign(cc.director, {
_getSceneUuid (sceneName) {
cc.assetManager.main.getSceneInfo(sceneName);
}
});
Object.defineProperties(cc.game, {
_sceneInfos: {
get () {
var scenes = [];
cc.assetManager.main._config.scenes.forEach(function (val) {
scenes.push(val);
});
return scenes;
}
}
});
var parseParameters = utilities.parseParameters;
utilities.parseParameters = function (options, onProgress, onComplete) {
var result = parseParameters(options, onProgress, onComplete);
result.onProgress = result.onProgress || loader.onProgress;
return result;
};
var autoRelease = releaseManager._autoRelease;
releaseManager._autoRelease = function () {
autoRelease.apply(this, arguments);
var releaseSettings = loader._autoReleaseSetting;
var keys = Object.keys(releaseSettings);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
if (releaseSettings[key] === true) {
var asset = cc.assetManager.assets.get(key);
asset && releaseManager.tryRelease(asset);
}
}
};

View File

@@ -0,0 +1,103 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const helper = require('./helper');
const MissingClass = CC_EDITOR && Editor.require('app://editor/page/scene-utils/missing-class-reporter').MissingClass;
require('../platform/deserialize');
function deserialize (json, options) {
var classFinder, missingClass;
if (CC_EDITOR) {
missingClass = MissingClass;
classFinder = function (type, data, owner, propName) {
var res = missingClass.classFinder(type, data, owner, propName);
if (res) {
return res;
}
return cc._MissingScript;
};
classFinder.onDereferenced = missingClass.classFinder.onDereferenced;
}
else {
classFinder = cc._MissingScript.safeFindClass;
}
let pool = null;
if (!CC_PREVIEW) {
pool = cc.deserialize.Details.pool;
}
else {
let { default: deserializeForCompiled } = require('../platform/deserialize-compiled');
let deserializeForEditor = require('../platform/deserialize-editor');
if (deserializeForCompiled.isCompiledJson(json)) {
pool = deserializeForCompiled.Details.pool;
}
else {
pool = deserializeForEditor.Details.pool;
}
}
var tdInfo = pool.get();
var asset;
try {
asset = cc.deserialize(json, tdInfo, {
classFinder: classFinder,
customEnv: options
});
}
catch (e) {
pool.put(tdInfo);
throw e;
}
if (CC_EDITOR && missingClass) {
missingClass.reportMissingClass(asset);
missingClass.reset();
}
var uuidList = tdInfo.uuidList;
var objList = tdInfo.uuidObjList;
var propList = tdInfo.uuidPropList;
var depends = [];
for (var i = 0; i < uuidList.length; i++) {
var dependUuid = uuidList[i];
depends[i] = {
uuid: helper.decodeUuid(dependUuid),
owner: objList[i],
prop: propList[i]
};
}
// non-native deps
asset.__depends__ = depends;
// native dep
asset._native && (asset.__nativeDepend__ = true);
pool.put(tdInfo);
return asset;
}
module.exports = deserialize;

View File

@@ -0,0 +1,68 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
var __audioSupport = cc.sys.__audioSupport;
const { parseParameters } = require('./utilities');
function downloadDomAudio (url, options, onComplete) {
var { options, onComplete } = parseParameters(options, undefined, onComplete);
var dom = document.createElement('audio');
dom.src = url;
var clearEvent = function () {
clearTimeout(timer);
dom.removeEventListener("canplaythrough", success, false);
dom.removeEventListener("error", failure, false);
if(__audioSupport.USE_LOADER_EVENT)
dom.removeEventListener(__audioSupport.USE_LOADER_EVENT, success, false);
};
var timer = setTimeout(function () {
if (dom.readyState === 0)
failure();
else
success();
}, 8000);
var success = function () {
clearEvent();
onComplete && onComplete(null, dom);
};
var failure = function () {
clearEvent();
var message = 'load audio failure - ' + url;
cc.log(message);
onComplete && onComplete(new Error(message));
};
dom.addEventListener("canplaythrough", success, false);
dom.addEventListener("error", failure, false);
if(__audioSupport.USE_LOADER_EVENT)
dom.addEventListener(__audioSupport.USE_LOADER_EVENT, success, false);
return dom;
}
module.exports = downloadDomAudio;

View File

@@ -0,0 +1,56 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const { parseParameters } = require('./utilities');
function downloadDomImage (url, options, onComplete) {
var { options, onComplete } = parseParameters(options, undefined, onComplete);
var img = new Image();
if (window.location.protocol !== 'file:') {
img.crossOrigin = 'anonymous';
}
function loadCallback () {
img.removeEventListener('load', loadCallback);
img.removeEventListener('error', errorCallback);
onComplete && onComplete(null, img);
}
function errorCallback () {
img.removeEventListener('load', loadCallback);
img.removeEventListener('error', errorCallback);
onComplete && onComplete(new Error(cc.debug.getError(4930, url)));
}
img.addEventListener('load', loadCallback);
img.addEventListener('error', errorCallback);
img.src = url;
return img;
}
module.exports = downloadDomImage;

View File

@@ -0,0 +1,79 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const { parseParameters } = require('./utilities');
function downloadFile (url, options, onProgress, onComplete) {
var { options, onProgress, onComplete } = parseParameters(options, onProgress, onComplete);
var xhr = new XMLHttpRequest(), errInfo = 'download failed: ' + url + ', status: ';
xhr.open('GET', url, true);
if (options.responseType !== undefined) xhr.responseType = options.responseType;
if (options.withCredentials !== undefined) xhr.withCredentials = options.withCredentials;
if (options.mimeType !== undefined && xhr.overrideMimeType ) xhr.overrideMimeType(options.mimeType);
if (options.timeout !== undefined) xhr.timeout = options.timeout;
if (options.header) {
for (var header in options.header) {
xhr.setRequestHeader(header, options.header[header]);
}
}
xhr.onload = function () {
if ( xhr.status === 200 || xhr.status === 0 ) {
onComplete && onComplete(null, xhr.response);
} else {
onComplete && onComplete(new Error(errInfo + xhr.status + '(no response)'));
}
};
if (onProgress) {
xhr.onprogress = function (e) {
if (e.lengthComputable) {
onProgress(e.loaded, e.total);
}
};
}
xhr.onerror = function(){
onComplete && onComplete(new Error(errInfo + xhr.status + '(error)'));
};
xhr.ontimeout = function(){
onComplete && onComplete(new Error(errInfo + xhr.status + '(time out)'));
};
xhr.onabort = function(){
onComplete && onComplete(new Error(errInfo + xhr.status + '(abort)'));
};
xhr.send(null);
return xhr;
}
module.exports = downloadFile;

View File

@@ -0,0 +1,66 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const { parseParameters } = require('./utilities');
const downloaded = {};
function downloadScript (url, options, onComplete) {
var { options, onComplete } = parseParameters(options, undefined, onComplete);
// no need to load script again
if (downloaded[url]) {
return onComplete && onComplete(null);
}
var d = document, s = document.createElement('script');
if (window.location.protocol !== 'file:') {
s.crossOrigin = 'anonymous';
}
s.async = options.async;
s.src = url;
function loadHandler () {
s.parentNode.removeChild(s);
s.removeEventListener('load', loadHandler, false);
s.removeEventListener('error', errorHandler, false);
downloaded[url] = true;
onComplete && onComplete(null);
}
function errorHandler() {
s.parentNode.removeChild(s);
s.removeEventListener('load', loadHandler, false);
s.removeEventListener('error', errorHandler, false);
onComplete && onComplete(new Error(cc.debug.getError(4928, url)));
}
s.addEventListener('load', loadHandler, false);
s.addEventListener('error', errorHandler, false);
d.body.appendChild(s);
}
module.exports = downloadScript;

View File

@@ -0,0 +1,601 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
/**
* @module cc.AssetManager
*/
const js = require('../platform/js');
const debug = require('../CCDebug');
const { loadFont } = require('./font-loader');
const callInNextTick = require('../platform/utils').callInNextTick;
const downloadDomImage = require('./download-dom-image');
const downloadDomAudio = require('./download-dom-audio');
const downloadFile = require('./download-file');
const downloadScript = require('./download-script.js');
const Cache = require('./cache');
const { files } = require('./shared');
const { __audioSupport, capabilities } = require('../platform/CCSys');
const { urlAppendTimestamp, retry } = require('./utilities');
const REGEX = /^(?:\w+:\/\/|\.+\/).+/;
var formatSupport = __audioSupport.format || [];
var unsupported = function (url, options, onComplete) {
onComplete(new Error(debug.getError(4927)));
}
var downloadAudio = function (url, options, onComplete) {
// web audio need to download file as arrayBuffer
if (options.audioLoadMode !== cc.AudioClip.LoadMode.DOM_AUDIO) {
downloadArrayBuffer(url, options, onComplete);
}
else {
downloadDomAudio(url, options, onComplete);
}
};
var downloadAudio = (!CC_EDITOR || !Editor.isMainProcess) ? (formatSupport.length === 0 ? unsupported : (__audioSupport.WEB_AUDIO ? downloadAudio : downloadDomAudio)) : null;
var downloadImage = function (url, options, onComplete) {
// if createImageBitmap is valid, we can transform blob to ImageBitmap. Otherwise, just use HTMLImageElement to load
var func = capabilities.imageBitmap && cc.macro.ALLOW_IMAGE_BITMAP ? downloadBlob : downloadDomImage;
func.apply(this, arguments);
};
var downloadBlob = function (url, options, onComplete) {
options.responseType = "blob";
downloadFile(url, options, options.onFileProgress, onComplete);
};
var downloadJson = function (url, options, onComplete) {
options.responseType = "json";
downloadFile(url, options, options.onFileProgress, function (err, data) {
if (!err && typeof data === 'string') {
try {
data = JSON.parse(data);
}
catch (e) {
err = e;
}
}
onComplete && onComplete(err, data);
});
};
var downloadArrayBuffer = function (url, options, onComplete) {
options.responseType = "arraybuffer";
downloadFile(url, options, options.onFileProgress, onComplete);
};
var downloadText = function (url, options, onComplete) {
options.responseType = "text";
downloadFile(url, options, options.onFileProgress, onComplete);
};
var downloadVideo = function (url, options, onComplete) {
onComplete(null, url);
};
var downloadBundle = function (nameOrUrl, options, onComplete) {
let bundleName = cc.path.basename(nameOrUrl);
let url = nameOrUrl;
if (!REGEX.test(url)) url = 'assets/' + bundleName;
var version = options.version || downloader.bundleVers[bundleName];
var count = 0;
var config = `${url}/config.${version ? version + '.' : ''}json`;
let out = null, error = null;
downloadJson(config, options, function (err, response) {
if (err) {
error = err;
}
out = response;
out && (out.base = url + '/');
count++;
if (count === 2) {
onComplete(error, out);
}
});
var js = `${url}/index.${version ? version + '.' : ''}js`;
downloadScript(js, options, function (err) {
if (err) {
error = err;
}
count++;
if (count === 2) {
onComplete(error, out);
}
});
};
var _downloading = new Cache();
var _queue = [];
var _queueDirty = false;
// the number of loading thread
var _totalNum = 0;
// the number of request that launched in this period
var _totalNumThisPeriod = 0;
// last time, if now - lastTime > period, refresh _totalNumThisPeriod.
var _lastDate = -1;
// if _totalNumThisPeriod equals max, move request to next period using setTimeOut.
var _checkNextPeriod = false;
var updateTime = function () {
var now = Date.now();
// use deltaTime as interval
let interval = cc.director._deltaTime > downloader._maxInterval ? downloader._maxInterval : cc.director._deltaTime;
if (now - _lastDate > interval * 1000) {
_totalNumThisPeriod = 0;
_lastDate = now;
}
};
// handle the rest request in next period
var handleQueue = function (maxConcurrency, maxRequestsPerFrame) {
_checkNextPeriod = false;
updateTime();
while (_queue.length > 0 && _totalNum < maxConcurrency && _totalNumThisPeriod < maxRequestsPerFrame) {
if (_queueDirty) {
_queue.sort(function (a, b) {
return a.priority - b.priority;
});
_queueDirty = false;
}
var nextOne = _queue.pop();
if (!nextOne) {
break;
}
_totalNum++;
_totalNumThisPeriod++;
nextOne.invoke();
}
if (_queue.length > 0 && _totalNum < maxConcurrency) {
callInNextTick(handleQueue, maxConcurrency, maxRequestsPerFrame);
_checkNextPeriod = true;
}
}
/**
* !#en
* Control all download process, it is a singleton. All member can be accessed with `cc.assetManager.downloader` , it can download several types of files:
* 1. Text
* 2. Image
* 3. Audio
* 4. Assets
* 5. Scripts
*
* !#zh
* 管理所有下载过程downloader 是个单例,所有成员能通过 `cc.assetManager.downloader` 访问,它能下载以下几种类型的文件:
* 1. 文本
* 2. 图片
* 3. 音频
* 4. 资源
* 5. 脚本
*
* @class Downloader
*/
var downloader = {
_remoteServerAddress: '',
_maxInterval: 1 / 30,
/**
* !#en
* The address of remote server
*
* !#zh
* 远程服务器地址
*
* @property remoteServerAddress
* @type {string}
* @default ''
*/
get remoteServerAddress () {
return this._remoteServerAddress;
},
/**
* !#en
* The maximum number of concurrent when downloading
*
* !#zh
* 下载时的最大并发数
*
* @property maxConcurrency
* @type {number}
* @default 6
*/
maxConcurrency: 6,
/**
* !#en
* The maximum number of request can be launched per frame when downloading
*
* !#zh
* 下载时每帧可以启动的最大请求数
*
* @property maxRequestsPerFrame
* @type {number}
* @default 6
*/
maxRequestsPerFrame: 6,
/**
* !#en
* The max number of retries when fail
*
* !#zh
* 失败重试次数
*
* @property maxRetryCount
* @type {Number}
*/
maxRetryCount: 3,
appendTimeStamp: false,
limited: true,
/**
* !#en
* Wait for while before another retry, unit: ms
*
* !#zh
* 重试的间隔时间
*
* @property retryInterval
* @type {Number}
*/
retryInterval: 2000,
bundleVers: null,
/*
* !#en
* Use Image element to download image
*
* !#zh
* 使用 Image 元素来下载图片
*
* @method downloadDomImage
* @param {string} url - Url of the image
* @param {Object} [options] - Some optional paramters
* @param {Function} [onComplete] - Callback when image loaded or failed
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {HTMLImageElement} onComplete.img - The loaded Image element, null if error occurred
* @returns {HTMLImageElement} The image element
*
* @example
* downloadDomImage('http://example.com/test.jpg', null, (err, img) => console.log(err));
*
* @typescript
* downloadDomImage(url: string, options?: Record<string, any> , onComplete?: (err: Error, img: HTMLImageElement) => void): HTMLImageElement
* downloadDomImage(url: string, onComplete?: (err: Error, img: HTMLImageElement) => void): HTMLImageElement
*/
downloadDomImage: downloadDomImage,
/*
* !#en
* Use audio element to download audio
*
* !#zh
* 使用 Audio 元素来下载音频
*
* @method downloadDomAudio
* @param {string} url - Url of the audio
* @param {Object} [options] - Some optional paramters
* @param {Function} [onComplete] - Callback invoked when audio loaded or failed
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {HTMLAudioElement} onComplete.audio - The loaded audio element, null if error occurred
* @returns {HTMLAudioElement} The audio element
*
* @example
* downloadDomAudio('http://example.com/test.mp3', null, (err, audio) => console.log(err));
*
* @typescript
* downloadDomAudio(url: string, options?: Record<string, any>, onComplete?: (err: Error, audio: HTMLAudioElement) => void): HTMLAudioElement
* downloadDomAudio(url: string, onComplete?: (err: Error, audio: HTMLAudioElement) => void): HTMLAudioElement
*/
downloadDomAudio: downloadDomAudio,
/*
* !#en
* Use XMLHttpRequest to download file
*
* !#zh
* 使用 XMLHttpRequest 来下载文件
*
* @method downloadFile
* @param {string} url - Url of the file
* @param {Object} [options] - Some optional paramters
* @param {string} [options.responseType] - Indicate which type of content should be returned
* @param {boolean} [options.withCredentials] - Indicate whether or not cross-site Access-Contorl requests should be made using credentials
* @param {string} [options.mimeType] - Indicate which type of content should be returned. In some browsers, responseType does't work, you can use mimeType instead
* @param {Number} [options.timeout] - Represent the number of ms a request can take before being terminated.
* @param {Object} [options.header] - The header should be tranferred to server
* @param {Function} [onFileProgress] - Callback continuously during download is processing
* @param {Number} onFileProgress.loaded - Size of downloaded content.
* @param {Number} onFileProgress.total - Total size of content.
* @param {Function} [onComplete] - Callback when file loaded or failed
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {*} onComplete.response - The loaded content, null if error occurred, type of content can be indicated by options.responseType
* @returns {XMLHttpRequest} The xhr to be send
*
* @example
* downloadFile('http://example.com/test.bin', {responseType: 'arraybuffer'}, null, (err, arrayBuffer) => console.log(err));
*
* @typescript
* downloadFile(url: string, options?: Record<string, any>, onFileProgress?: (loaded: Number, total: Number) => void, onComplete?: (err: Error, response: any) => void): XMLHttpRequest
* downloadFile(url: string, onFileProgress?: (loaded: Number, total: Number) => void, onComplete?: (err: Error, response: any) => void): XMLHttpRequest
* downloadFile(url: string, options?: Record<string, any>, onComplete?: (err: Error, response: any) => void): XMLHttpRequest
* downloadFile(url: string, onComplete?: (err: Error, response: any) => void): XMLHttpRequest
*/
downloadFile: downloadFile,
/*
* !#en
* Load script
*
* !#zh
* 加载脚本
*
* @method downloadScript
* @param {string} url - Url of the script
* @param {Object} [options] - Some optional paramters
* @param {boolean} [options.isAsync] - Indicate whether or not loading process should be async
* @param {Function} [onComplete] - Callback when script loaded or failed
* @param {Error} onComplete.err - The occurred error, null indicetes success
*
* @example
* downloadScript('http://localhost:8080/index.js', null, (err) => console.log(err));
*
* @typescript
* downloadScript(url: string, options?: Record<string, any>, onComplete?: (err: Error) => void): void
* downloadScript(url: string, onComplete?: (err: Error) => void): void
*/
downloadScript: downloadScript,
init (bundleVers, remoteServerAddress) {
_downloading.clear();
_queue.length = 0;
this._remoteServerAddress = remoteServerAddress || '';
this.bundleVers = bundleVers || Object.create(null);
},
/**
* !#en
* Register custom handler if you want to change default behavior or extend downloader to download other format file
*
* !#zh
* 当你想修改默认行为或者拓展 downloader 来下载其他格式文件时可以注册自定义的 handler
*
* @method register
* @param {string|Object} type - Extension likes '.jpg' or map likes {'.jpg': jpgHandler, '.png': pngHandler}
* @param {Function} [handler] - handler
* @param {string} handler.url - url
* @param {Object} handler.options - some optional paramters will be transferred to handler.
* @param {Function} handler.onComplete - callback when finishing downloading
*
* @example
* downloader.register('.tga', (url, options, onComplete) => onComplete(null, null));
* downloader.register({'.tga': (url, options, onComplete) => onComplete(null, null), '.ext': (url, options, onComplete) => onComplete(null, null)});
*
* @typescript
* register(type: string, handler: (url: string, options: Record<string, any>, onComplete: (err: Error, content: any) => void) => void): void
* register(map: Record<string, (url: string, options: Record<string, any>, onComplete: (err: Error, content: any) => void) => void>): void
*/
register (type, handler) {
if (typeof type === 'object') {
js.mixin(downloaders, type);
}
else {
downloaders[type] = handler;
}
},
/**
* !#en
* Use corresponding handler to download file under limitation
*
* !#zh
* 在限制下使用对应的 handler 来下载文件
*
* @method download
* @param {string} url - The url should be downloaded
* @param {string} type - The type indicates that which handler should be used to download, such as '.jpg'
* @param {Object} options - some optional paramters will be transferred to the corresponding handler.
* @param {Function} [options.onFileProgress] - progressive callback will be transferred to handler.
* @param {Number} [options.maxRetryCount] - How many times should retry when download failed
* @param {Number} [options.maxConcurrency] - The maximum number of concurrent when downloading
* @param {Number} [options.maxRequestsPerFrame] - The maximum number of request can be launched per frame when downloading
* @param {Number} [options.priority] - The priority of this url, default is 0, the greater number is higher priority.
* @param {Function} onComplete - callback when finishing downloading
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {*} onComplete.contetnt - The downloaded file
*
* @example
* download('http://example.com/test.tga', '.tga', {onFileProgress: (loaded, total) => console.lgo(loaded/total)}, onComplete: (err) => console.log(err));
*
* @typescript
* download(id: string, url: string, type: string, options: Record<string, any>, onComplete: (err: Error, content: any) => void): void
*/
download (id, url, type, options, onComplete) {
let func = downloaders[type] || downloaders['default'];
let self = this;
// if it is downloaded, don't download again
let file, downloadCallbacks;
if (file = files.get(id)) {
onComplete(null, file);
}
else if (downloadCallbacks = _downloading.get(id)) {
downloadCallbacks.push(onComplete);
for (let i = 0, l = _queue.length; i < l; i++) {
var item = _queue[i];
if (item.id === id) {
var priority = options.priority || 0;
if (item.priority < priority) {
item.priority = priority;
_queueDirty = true;
}
return;
}
}
}
else {
// if download fail, should retry
var maxRetryCount = typeof options.maxRetryCount !== 'undefined' ? options.maxRetryCount : this.maxRetryCount;
var maxConcurrency = typeof options.maxConcurrency !== 'undefined' ? options.maxConcurrency : this.maxConcurrency;
var maxRequestsPerFrame = typeof options.maxRequestsPerFrame !== 'undefined' ? options.maxRequestsPerFrame : this.maxRequestsPerFrame;
function process (index, callback) {
if (index === 0) {
_downloading.add(id, [onComplete]);
}
if (!self.limited) return func(urlAppendTimestamp(url), options, callback);
// refresh
updateTime();
function invoke () {
func(urlAppendTimestamp(url), options, function () {
// when finish downloading, update _totalNum
_totalNum--;
if (!_checkNextPeriod && _queue.length > 0) {
callInNextTick(handleQueue, maxConcurrency, maxRequestsPerFrame);
_checkNextPeriod = true;
}
callback.apply(this, arguments);
});
}
if (_totalNum < maxConcurrency && _totalNumThisPeriod < maxRequestsPerFrame) {
invoke();
_totalNum++;
_totalNumThisPeriod++;
}
else {
// when number of request up to limitation, cache the rest
_queue.push({ id, priority: options.priority || 0, invoke });
_queueDirty = true;
if (!_checkNextPeriod && _totalNum < maxConcurrency) {
callInNextTick(handleQueue, maxConcurrency, maxRequestsPerFrame);
_checkNextPeriod = true;
}
}
}
// when retry finished, invoke callbacks
function finale (err, result) {
if (!err) files.add(id, result);
var callbacks = _downloading.remove(id);
for (let i = 0, l = callbacks.length; i < l; i++) {
callbacks[i](err, result);
}
}
retry(process, maxRetryCount, this.retryInterval, finale);
}
}
};
// dafault handler map
var downloaders = {
// Images
'.png' : downloadImage,
'.jpg' : downloadImage,
'.bmp' : downloadImage,
'.jpeg' : downloadImage,
'.gif' : downloadImage,
'.ico' : downloadImage,
'.tiff' : downloadImage,
'.webp' : downloadImage,
'.image' : downloadImage,
'.pvr': downloadArrayBuffer,
'.pkm': downloadArrayBuffer,
// Audio
'.mp3' : downloadAudio,
'.ogg' : downloadAudio,
'.wav' : downloadAudio,
'.m4a' : downloadAudio,
// Txt
'.txt' : downloadText,
'.xml' : downloadText,
'.vsh' : downloadText,
'.fsh' : downloadText,
'.atlas' : downloadText,
'.tmx' : downloadText,
'.tsx' : downloadText,
'.json' : downloadJson,
'.ExportJson' : downloadJson,
'.plist' : downloadText,
'.fnt' : downloadText,
// font
'.font' : loadFont,
'.eot' : loadFont,
'.ttf' : loadFont,
'.woff' : loadFont,
'.svg' : loadFont,
'.ttc' : loadFont,
// Video
'.mp4': downloadVideo,
'.avi': downloadVideo,
'.mov': downloadVideo,
'.mpg': downloadVideo,
'.mpeg': downloadVideo,
'.rm': downloadVideo,
'.rmvb': downloadVideo,
// Binary
'.binary' : downloadArrayBuffer,
'.bin': downloadArrayBuffer,
'.dbbin': downloadArrayBuffer,
'.skel': downloadArrayBuffer,
'.js': downloadScript,
'bundle': downloadBundle,
'default': downloadText
};
downloader._downloaders = downloaders;
module.exports = downloader;

View File

@@ -0,0 +1,201 @@
/****************************************************************************
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const Bundle = require('./bundle');
const Cache = require('./cache');
const { assets, bundles } = require('./shared');
const _creating = new Cache();
function createTexture (id, data, options, onComplete) {
let out = null, err = null;
try {
out = new cc.Texture2D();
out._nativeUrl = id;
out._nativeAsset = data;
}
catch (e) {
err = e;
}
onComplete && onComplete(err, out);
}
function createAudioClip (id, data, options, onComplete) {
let out = new cc.AudioClip();
out._nativeUrl = id;
out._nativeAsset = data;
out.duration = data.duration;
onComplete && onComplete(null, out);
}
function createVideoClip (id, data, options, onComplete) {
let out = new cc.VideoClip();
out._nativeUrl = id;
out._nativeAsset = data;
onComplete && onComplete(null, out);
}
function createJsonAsset (id, data, options, onComplete) {
let out = new cc.JsonAsset();
out.json = data;
onComplete && onComplete(null, out);
}
function createTextAsset (id, data, options, onComplete) {
let out = new cc.TextAsset();
out.text = data;
onComplete && onComplete(null, out);
}
function createFont (id, data, options, onComplete) {
let out = new cc.TTFFont();
out._nativeUrl = id;
out._nativeAsset = data;
onComplete && onComplete(null, out);
}
function createBufferAsset (id, data, options, onComplete) {
let out = new cc.BufferAsset();
out._nativeUrl = id;
out._nativeAsset = data;
onComplete && onComplete(null, out);
}
function createAsset (id, data, options, onComplete) {
let out = new cc.Asset();
out._nativeUrl = id;
out._nativeAsset = data;
onComplete && onComplete(null, out);
}
function createBundle (id, data, options, onComplete) {
let bundle = bundles.get(data.name);
if (!bundle) {
bundle = new Bundle();
data.base = data.base || id + '/';
bundle.init(data);
}
onComplete && onComplete(null, bundle);
}
const factory = {
register (type, handler) {
if (typeof type === 'object') {
cc.js.mixin(producers, type);
}
else {
producers[type] = handler;
}
},
create (id, data, type, options, onComplete) {
var func = producers[type] || producers['default'];
let asset, creating;
if (asset = assets.get(id)) {
onComplete(null, asset);
}
else if (creating = _creating.get(id)) {
creating.push(onComplete);
}
else {
_creating.add(id, [onComplete]);
func(id, data, options, function (err, data) {
if (!err && data instanceof cc.Asset) {
data._uuid = id;
assets.add(id, data);
}
let callbacks = _creating.remove(id);
for (let i = 0, l = callbacks.length; i < l; i++) {
callbacks[i](err, data);
}
});
}
}
};
const producers = {
// Images
'.png' : createTexture,
'.jpg' : createTexture,
'.bmp' : createTexture,
'.jpeg' : createTexture,
'.gif' : createTexture,
'.ico' : createTexture,
'.tiff' : createTexture,
'.webp' : createTexture,
'.image' : createTexture,
'.pvr': createTexture,
'.pkm': createTexture,
// Audio
'.mp3' : createAudioClip,
'.ogg' : createAudioClip,
'.wav' : createAudioClip,
'.m4a' : createAudioClip,
// Video
'.mp4' : createVideoClip,
'.avi' : createVideoClip,
'.mov' : createVideoClip,
'.mpg' : createVideoClip,
'.mpeg': createVideoClip,
'.rm' : createVideoClip,
'.rmvb': createVideoClip,
// Txt
'.txt' : createTextAsset,
'.xml' : createTextAsset,
'.vsh' : createTextAsset,
'.fsh' : createTextAsset,
'.atlas' : createTextAsset,
'.tmx' : createTextAsset,
'.tsx' : createTextAsset,
'.fnt' : createTextAsset,
'.json' : createJsonAsset,
'.ExportJson' : createJsonAsset,
// font
'.font' : createFont,
'.eot' : createFont,
'.ttf' : createFont,
'.woff' : createFont,
'.svg' : createFont,
'.ttc' : createFont,
// Binary
'.binary': createBufferAsset,
'.bin': createBufferAsset,
'.dbbin': createBufferAsset,
'.skel': createBufferAsset,
'bundle': createBundle,
'default': createAsset
};
module.exports = factory;

View File

@@ -0,0 +1,129 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const packManager = require('./pack-manager');
const Task = require('./task');
const { getDepends, clear, forEach } = require('./utilities');
const { assets, fetchPipeline } = require('./shared');
function fetch (task, done) {
let firstTask = false;
if (!task.progress) {
task.progress = { finish: 0, total: task.input.length, canInvoke: true };
firstTask = true;
}
let options = task.options, depends = [], progress = task.progress, total = progress.total;
options.__exclude__ = options.__exclude__ || Object.create(null);
task.output = [];
forEach(task.input, function (item, cb) {
if (!item.isNative && assets.has(item.uuid)) {
var asset = assets.get(item.uuid);
asset.addRef();
handle(item, task, asset, null, asset.__asyncLoadAssets__, depends, total, done);
return cb();
}
packManager.load(item, task.options, function (err, data) {
if (err) {
if (!task.isFinish) {
if (!cc.assetManager.force || firstTask) {
cc.error(err.message, err.stack);
progress.canInvoke = false;
done(err);
}
else {
handle(item, task, null, null, false, depends, total, done);
}
}
}
else {
if (!task.isFinish) handle(item, task, null, data, !item.isNative, depends, total, done);
}
cb();
});
}, function () {
if (task.isFinish) {
clear(task, true);
return task.dispatch('error');
}
if (depends.length > 0) {
// stage 2 , download depend asset
let subTask = Task.create({
name: task.name + ' dependencies',
input: depends,
progress,
options,
onProgress: task.onProgress,
onError: Task.prototype.recycle,
onComplete: function (err) {
if (!err) {
task.output.push.apply(task.output, this.output);
subTask.recycle();
}
if (firstTask) decreaseRef(task);
done(err);
},
});
fetchPipeline.async(subTask);
return;
}
if (firstTask) decreaseRef(task);
done();
});
}
function decreaseRef (task) {
let output = task.output;
for (let i = 0, l = output.length; i < l; i++) {
output[i].content && output[i].content.decRef(false);
}
}
function handle (item, task, content, file, loadDepends, depends, last, done) {
var exclude = task.options.__exclude__;
var progress = task.progress;
item.content = content;
item.file = file;
task.output.push(item);
if (loadDepends) {
exclude[item.uuid] = true;
getDepends(item.uuid, file || content, exclude, depends, true, false, item.config);
progress.total = last + depends.length;
}
progress.canInvoke && task.dispatch('progress', ++progress.finish, progress.total, item);
}
module.exports = fetch;

View File

@@ -0,0 +1,226 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const textUtils = require('../utils/text-utils');
let _canvasContext = null;
// letter symbol number CJK
let _testString = "BES bswy:->@123\u4E01\u3041\u1101";
let _fontFaces = Object.create(null);
let _intervalId = -1;
let _loadingFonts = [];
// 3 seconds timeout
let _timeout = 3000;
// Refer to https://github.com/typekit/webfontloader/blob/master/src/core/fontwatcher.js
let useNativeCheck = (function () {
var nativeCheck = undefined;
return function () {
if (nativeCheck === undefined) {
if (!!window.FontFace) {
var match = /Gecko.*Firefox\/(\d+)/.exec(window.navigator.userAgent);
var safari10Match = /OS X.*Version\/10\..*Safari/.exec(window.navigator.userAgent) && /Apple/.exec(window.navigator.vendor);
if (match) {
nativeCheck = parseInt(match[1], 10) > 42;
}
else if (safari10Match) {
nativeCheck = false;
}
else {
nativeCheck = true;
}
} else {
nativeCheck = false;
}
}
return nativeCheck;
}
})();
function _checkFontLoaded () {
let allFontsLoaded = true;
let now = Date.now();
for (let i = _loadingFonts.length - 1; i >= 0; i--) {
let fontLoadHandle = _loadingFonts[i];
let fontFamily = fontLoadHandle.fontFamilyName;
// load timeout
if (now - fontLoadHandle.startTime > _timeout) {
cc.warnID(4933, fontFamily);
fontLoadHandle.onComplete(null, fontFamily);
_loadingFonts.splice(i, 1);
continue;
}
let oldWidth = fontLoadHandle.refWidth;
let fontDesc = '40px ' + fontFamily;
_canvasContext.font = fontDesc;
let newWidth = textUtils.safeMeasureText(_canvasContext, _testString, fontDesc);
// loaded successfully
if (oldWidth !== newWidth) {
_loadingFonts.splice(i, 1);
fontLoadHandle.onComplete(null, fontFamily);
}
else {
allFontsLoaded = false;
}
}
if (allFontsLoaded) {
clearInterval(_intervalId);
_intervalId = -1;
}
}
// refer to https://github.com/typekit/webfontloader/blob/master/src/core/nativefontwatchrunner.js
function nativeCheckFontLoaded (start, font, callback) {
var loader = new Promise(function (resolve, reject) {
var check = function () {
var now = Date.now();
if (now - start >= _timeout) {
reject();
}
else {
document.fonts.load('40px ' + font).then(function (fonts) {
if (fonts.length >= 1) {
resolve();
}
else {
setTimeout(check, 100);
}
}, function () {
reject();
});
}
};
check();
});
var timeoutId = null,
timer = new Promise(function (resolve, reject) {
timeoutId = setTimeout(reject, _timeout);
});
Promise.race([timer, loader]).then(function () {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
callback(null, font);
}, function () {
cc.warnID(4933, font);
callback(null, font);
});
}
var fontLoader = {
loadFont: function (url, options, onComplete) {
let fontFamilyName = fontLoader._getFontFamily(url);
// Already loaded fonts
if (_fontFaces[fontFamilyName]) {
return onComplete(null, fontFamilyName);
}
if (!_canvasContext) {
let labelCanvas = document.createElement('canvas');
labelCanvas.width = 100;
labelCanvas.height = 100;
_canvasContext = labelCanvas.getContext('2d');
}
// Default width reference to test whether new font is loaded correctly
let fontDesc = '40px ' + fontFamilyName;
_canvasContext.font = fontDesc;
let refWidth = textUtils.safeMeasureText(_canvasContext, _testString, fontDesc);
// Setup font face style
let fontStyle = document.createElement("style");
fontStyle.type = "text/css";
let fontStr = "";
if (isNaN(fontFamilyName - 0))
fontStr += "@font-face { font-family:" + fontFamilyName + "; src:";
else
fontStr += "@font-face { font-family:'" + fontFamilyName + "'; src:";
fontStr += "url('" + url + "');";
fontStyle.textContent = fontStr + "}";
document.body.appendChild(fontStyle);
// Preload font with div
let preloadDiv = document.createElement("div");
let divStyle = preloadDiv.style;
divStyle.fontFamily = fontFamilyName;
preloadDiv.innerHTML = ".";
divStyle.position = "absolute";
divStyle.left = "-100px";
divStyle.top = "-100px";
document.body.appendChild(preloadDiv);
if (useNativeCheck()) {
nativeCheckFontLoaded(Date.now(), fontFamilyName, onComplete);
}
else {
// Save loading font
let fontLoadHandle = {
fontFamilyName,
refWidth,
onComplete,
startTime: Date.now()
}
_loadingFonts.push(fontLoadHandle);
if (_intervalId === -1) {
_intervalId = setInterval(_checkFontLoaded, 100);
}
}
_fontFaces[fontFamilyName] = fontStyle;
},
_getFontFamily: function (fontHandle) {
var ttfIndex = fontHandle.lastIndexOf(".ttf");
if (ttfIndex === -1) return fontHandle;
var slashPos = fontHandle.lastIndexOf("/");
var fontFamilyName;
if (slashPos === -1) {
fontFamilyName = fontHandle.substring(0, ttfIndex) + "_LABEL";
} else {
fontFamilyName = fontHandle.substring(slashPos + 1, ttfIndex) + "_LABEL";
}
if (fontFamilyName.indexOf(' ') !== -1) {
fontFamilyName = '"' + fontFamilyName + '"';
}
return fontFamilyName;
}
};
module.exports = fontLoader

View File

@@ -0,0 +1,174 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const { bundles } = require('./shared');
/**
* @module cc.AssetManager
*/
/**
* !#en
* Provide some helpful function, it is a singleton. All member can be accessed with `cc.assetManager.utils`
*
* !#zh
* 提供一些辅助方法helper 是一个单例, 所有成员能通过 `cc.assetManager.utils` 访问
*
* @class Helper
*/
var helper = {
/**
* !#en
* Decode uuid, returns the original uuid
*
* !#zh
* 解码 uuid返回原始 uuid
*
* @method decodeUuid
* @param {String} base64 - the encoded uuid
* @returns {String} the original uuid
*
* @example
* var uuid = 'fcmR3XADNLgJ1ByKhqcC5Z';
* var originalUuid = decodeUuid(uuid); // fc991dd7-0033-4b80-9d41-c8a86a702e59
*
* @typescript
* decodeUuid(base64: string): string
*/
decodeUuid: require('../utils/decode-uuid'),
/**
* !#en
* Extract uuid from url
*
* !#zh
* 从 url 中提取 uuid
*
* @method getUuidFromURL
* @param {String} url - url
* @returns {String} the uuid parsed from url
*
* @example
* var url = 'assets/main/import/fc/fc991dd7-0033-4b80-9d41-c8a86a702e59.json';
* var uuid = getUuidFromURL(url); // fc991dd7-0033-4b80-9d41-c8a86a702e59
*
* @typescript
* getUuidFromURL(url: string): string
*/
getUuidFromURL: (function () {
var _uuidRegex = /.*[/\\][0-9a-fA-F]{2}[/\\]([0-9a-fA-F-]{8,})/;
return function (url) {
var matches = url.match(_uuidRegex);
if (matches) {
return matches[1];
}
return '';
}
})(),
/**
* !#en
* Transform uuid to url
*
* !#zh
* 转换 uuid 为 url
*
* @method getUrlWithUuid
* @param {string} uuid - The uuid of asset
* @param {Object} [options] - Some optional parameters
* @param {Boolean} [options.isNative] - Indicates whether the path you want is a native resource path
* @param {string} [options.nativeExt] - Extension of the native resource path, it is required when isNative is true
* @returns {string} url
*
* @example
* // json path, 'assets/main/import/fc/fc991dd7-0033-4b80-9d41-c8a86a702e59.json';
* var url = getUrlWithUuid('fcmR3XADNLgJ1ByKhqcC5Z', {isNative: false});
*
* // png path, 'assets/main/native/fc/fc991dd7-0033-4b80-9d41-c8a86a702e59.png';
* var url = getUrlWithUuid('fcmR3XADNLgJ1ByKhqcC5Z', {isNative: true, nativeExt: '.png'});
*
* @typescript
* getUrlWithUuid(uuid: string, options?: Record<string, any>): string
*/
getUrlWithUuid: function (uuid, options) {
options = options || Object.create(null);
options.__isNative__ = options.isNative;
options.ext = options.nativeExt;
var bundle = bundles.find(function (bundle) {
return bundle.getAssetInfo(uuid);
});
if (bundle) {
options.bundle = bundle.name;
}
return cc.assetManager._transform(uuid, options);
},
/**
* !#en
* Check if the type of asset is scene
*
* !#zh
* 检查资源类型是否是场景
*
* @method isScene
* @param {*} asset - asset
* @returns {boolean} - whether or not type is cc.SceneAsset
*
* @typescript
* isScene(asset: any): boolean
*/
isScene: function (asset) {
return asset && (asset.constructor === cc.SceneAsset || asset instanceof cc.Scene);
},
/**
* !#en
* Normalize url, strip './' and '/'
*
* !#zh
* 标准化 url ,去除 './' 和 '/'
*
* @method normalize
* @param {string} url - url
* @returns {string} - The normalized url
*
* @typescript
* normalize(url: string): string
*/
normalize: function (url) {
if (url) {
if (url.charCodeAt(0) === 46 && url.charCodeAt(1) === 47) {
// strip './'
url = url.slice(2);
}
else if (url.charCodeAt(0) === 47) {
// strip '/'
url = url.slice(1);
}
}
return url;
}
};
module.exports = helper;

View File

@@ -0,0 +1,27 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
require('./deprecated');
require('./CCAssetManager');

View File

@@ -0,0 +1,244 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const packManager = require('./pack-manager');
const Pipeline = require('./pipeline');
const parser = require('./parser');
const { getDepends, cache, gatherAsset, setProperties, forEach, clear, checkCircleReference } = require('./utilities');
const { assets, files, parsed, pipeline } = require('./shared');
const Task = require('./task');
function load (task, done) {
let firstTask = false;
if (!task.progress) {
task.progress = { finish: 0, total: task.input.length, canInvoke: true };
firstTask = true;
}
var options = task.options, progress = task.progress;
options.__exclude__ = options.__exclude__ || Object.create(null);
task.output = [];
forEach(task.input, function (item, cb) {
let subTask = Task.create({
input: item,
onProgress: task.onProgress,
options,
progress,
onComplete: function (err, item) {
if (err && !task.isFinish) {
if (!cc.assetManager.force || firstTask) {
if (!CC_EDITOR) {
cc.error(err.message, err.stack);
}
progress.canInvoke = false;
done(err);
}
else {
progress.canInvoke && task.dispatch('progress', ++progress.finish, progress.total, item);
}
}
task.output.push(item);
subTask.recycle();
cb();
}
});
loadOneAssetPipeline.async(subTask);
}, function () {
options.__exclude__ = null;
if (task.isFinish) {
clear(task, true);
return task.dispatch('error');
}
gatherAsset(task);
clear(task, true);
done();
});
}
var loadOneAssetPipeline = new Pipeline('loadOneAsset', [
function fetch (task, done) {
var item = task.output = task.input;
var { options, isNative, uuid, file } = item;
var { reload } = options;
if (file || (!reload && !isNative && assets.has(uuid))) return done();
packManager.load(item, task.options, function (err, data) {
item.file = data;
done(err);
});
},
function parse (task, done) {
var item = task.output = task.input, progress = task.progress, exclude = task.options.__exclude__;
var { id, file, options } = item;
if (item.isNative) {
parser.parse(id, file, item.ext, options, function (err, asset) {
if (err) return done(err);
item.content = asset;
progress.canInvoke && task.dispatch('progress', ++progress.finish, progress.total, item);
files.remove(id);
parsed.remove(id);
done();
});
}
else {
var { uuid } = item;
if (uuid in exclude) {
var { finish, content, err, callbacks } = exclude[uuid];
progress.canInvoke && task.dispatch('progress', ++progress.finish, progress.total, item);
if (finish || checkCircleReference(uuid, uuid, exclude) ) {
content && content.addRef && content.addRef();
item.content = content;
done(err);
}
else {
callbacks.push({ done, item });
}
}
else {
if (!options.reload && assets.has(uuid)) {
var asset = assets.get(uuid);
if (options.__asyncLoadAssets__ || !asset.__asyncLoadAssets__) {
item.content = asset.addRef();
progress.canInvoke && task.dispatch('progress', ++progress.finish, progress.total, item);
done();
}
else {
loadDepends(task, asset, done, false);
}
}
else {
parser.parse(id, file, 'import', options, function (err, asset) {
if (err) return done(err);
asset._uuid = uuid;
loadDepends(task, asset, done, true);
});
}
}
}
}
]);
function loadDepends (task, asset, done, init) {
var item = task.input, progress = task.progress;
var { uuid, id, options, config } = item;
var { __asyncLoadAssets__, cacheAsset } = options;
var depends = [];
// add reference avoid being released during loading dependencies
asset.addRef && asset.addRef();
getDepends(uuid, asset, Object.create(null), depends, false, __asyncLoadAssets__, config);
progress.canInvoke && task.dispatch('progress', ++progress.finish, progress.total += depends.length, item);
var repeatItem = task.options.__exclude__[uuid] = { content: asset, finish: false, callbacks: [{ done, item }] };
let subTask = Task.create({
input: depends,
options: task.options,
onProgress: task.onProgress,
onError: Task.prototype.recycle,
progress,
onComplete: function (err) {
asset.decRef && asset.decRef(false);
asset.__asyncLoadAssets__ = __asyncLoadAssets__;
repeatItem.finish = true;
repeatItem.err = err;
if (!err) {
var assets = Array.isArray(subTask.output) ? subTask.output : [subTask.output];
var map = Object.create(null);
for (let i = 0, l = assets.length; i < l; i++) {
var dependAsset = assets[i];
dependAsset && (map[dependAsset instanceof cc.Asset ? dependAsset._uuid + '@import' : uuid + '@native'] = dependAsset);
}
if (!init) {
if (asset.__nativeDepend__ && !asset._nativeAsset) {
var missingAsset = setProperties(uuid, asset, map);
if (!missingAsset && !asset.__onLoadInvoked__) {
try {
asset.onLoad && asset.onLoad();
asset.__onLoadInvoked__ = true;
}
catch (e) {
cc.error(e.message, e.stack);
}
}
}
}
else {
var missingAsset = setProperties(uuid, asset, map);
if (!missingAsset && !asset.__onLoadInvoked__) {
try {
asset.onLoad && asset.onLoad();
asset.__onLoadInvoked__ = true;
}
catch (e) {
cc.error(e.message, e.stack);
}
}
files.remove(id);
parsed.remove(id);
cache(uuid, asset, cacheAsset !== undefined ? cacheAsset : cc.assetManager.cacheAsset);
}
subTask.recycle();
}
var callbacks = repeatItem.callbacks;
for (var i = 0, l = callbacks.length; i < l; i++) {
var cb = callbacks[i];
asset.addRef && asset.addRef();
cb.item.content = asset;
cb.done(err);
}
callbacks.length = 0;
}
});
pipeline.async(subTask);
}
module.exports = load;

View File

@@ -0,0 +1,266 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
import { unpackJSONs, packCustomObjData } from '../platform/deserialize-compiled';
const downloader = require('./downloader');
const Cache = require('./cache');
const js = require('../platform/js');
const { files } = require('./shared');
var _loading = new Cache();
function isLoading (val) {
return _loading.has(val.uuid);
}
/**
* @module cc.AssetManager
*/
/**
* !#en
* Handle the packed asset, include unpacking, loading, cache and so on. It is a singleton. All member can be accessed with `cc.assetManager.packManager`
*
* !#zh
* 处理打包资源,包括拆包,加载,缓存等等,这是一个单例, 所有成员能通过 `cc.assetManager.packManager` 访问
*
* @class PackManager
*/
var packManager = {
/**
* !#en
* Unpack the json, revert to what it was before packing
*
* !#zh
* 拆解 json 包,恢复为打包之前的内容
*
* @method unpackJson
* @param {String[]} pack - The pack
* @param {Object} json - The content of pack
* @param {Object} options - Some optional parameters
* @param {Function} onComplete - Callback when finish unpacking
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {Object} onComplete.content - The unpacked assets
*
* @example
* downloader.downloadFile('pack.json', {responseType: 'json'}, null, (err, file) => {
* packManager.unpackJson(['a', 'b'], file, null, (err, data) => console.log(err));
* });
*
* @typescript
* unpackJson(pack: string[], json: any, options: Record<string, any>, onComplete?: (err: Error, content: any) => void): void
*/
unpackJson (pack, json, options, onComplete) {
var out = js.createMap(true), err = null;
if (Array.isArray(json)) {
json = unpackJSONs(json);
if (json.length !== pack.length) {
cc.errorID(4915);
}
for (let i = 0; i < pack.length; i++) {
var key = pack[i] + '@import';
out[key] = json[i];
}
}
else {
const textureType = js._getClassId(cc.Texture2D);
if (json.type === textureType) {
if (json.data) {
var datas = json.data.split('|');
if (datas.length !== pack.length) {
cc.errorID(4915);
}
for (let i = 0; i < pack.length; i++) {
out[pack[i] + '@import'] = packCustomObjData(textureType, datas[i], true);
}
}
}
else {
err = new Error('unmatched type pack!');
out = null;
}
}
onComplete && onComplete(err, out);
},
init () {
_loading.clear();
},
/**
* !#en
* Register custom handler if you want to change default behavior or extend packManager to unpack other format pack
*
* !#zh
* 当你想修改默认行为或者拓展 packManager 来拆分其他格式的包时可以注册自定义的 handler
*
* @method register
* @param {string|Object} type - Extension likes '.bin' or map likes {'.bin': binHandler, '.ab': abHandler}
* @param {Function} [handler] - handler
* @param {string} handler.packUuid - The uuid of pack
* @param {*} handler.data - The content of pack
* @param {Object} handler.options - Some optional parameters
* @param {Function} handler.onComplete - Callback when finishing unpacking
*
* @example
* packManager.register('.bin', (packUuid, file, options, onComplete) => onComplete(null, null));
* packManager.register({'.bin': (packUuid, file, options, onComplete) => onComplete(null, null), '.ab': (packUuid, file, options, onComplete) => onComplete(null, null)});
*
* @typescript
* register(type: string, handler: (packUuid: string, data: any, options: Record<string, any>, onComplete: (err: Error, content: any) => void) => void): void
* register(map: Record<string, (packUuid: string, data: any, options: Record<string, any>, onComplete: (err: Error, content: any) => void) => void>): void
*/
register (type, handler) {
if (typeof type === 'object') {
js.mixin(unpackers, type);
}
else {
unpackers[type] = handler;
}
},
/**
* !#en
* Use corresponding handler to unpack package
*
* !#zh
* 用对应的 handler 来进行解包
*
* @method unpack
* @param {String[]} pack - The uuid of packed assets
* @param {*} data - The packed data
* @param {string} type - The type indicates that which handler should be used to download, such as '.jpg'
* @param {Object} options - Some optional parameter
* @param {Function} onComplete - callback when finishing unpacking
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {*} onComplete.data - Original assets
*
* @example
* downloader.downloadFile('pack.json', {responseType: 'json'}, null, (err, file) => {
* packManager.unpack(['2fawq123d', '1zsweq23f'], file, '.json', null, (err, data) => console.log(err));
* });
*
* @typescript
* unpack(pack: string[], data: any, type: string, options: Record<string, any>, onComplete?: (err: Error, data: any) => void): void
*/
unpack (pack, data, type, options, onComplete) {
if (!data) {
onComplete && onComplete(new Error('package data is wrong!'));
return;
}
var unpacker = unpackers[type];
unpacker(pack, data, options, onComplete);
},
/**
* !#en
* Download request item, If item is not in any package, download as usual. Otherwise, download the corresponding package and unpack it.
* And then retrieve the corresponding content form it.
*
* !#zh
* 下载请求对象,如果请求对象不在任何包内,则正常下载,否则下载对应的 package 并进行拆解,并取回包内对应的内容
*
* @method load
* @param {RequestItem} item - Some item you want to download
* @param {Object} options - Some optional parameters
* @param {Function} onComplete - Callback when finished
* @param {Err} onComplete.err - The occurred error, null indicetes success
* @param {*} onComplete.data - The unpacked data retrieved from package
*
* @example
* var requestItem = cc.AssetManager.RequestItem.create();
* requestItem.uuid = 'fcmR3XADNLgJ1ByKhqcC5Z';
* requestItem.info = config.getAssetInfo('fcmR3XADNLgJ1ByKhqcC5Z');
* packManager.load(requestItem, null, (err, data) => console.log(err));
*
* @typescript
* load(item: RequestItem, options: Record<string, any>, onComplete: (err: Error, data: any) => void): void
*
*/
load (item, options, onComplete) {
// if not in any package, download as uausl
if (item.isNative || !item.info || !item.info.packs) return downloader.download(item.id, item.url, item.ext, item.options, onComplete);
if (files.has(item.id)) return onComplete(null, files.get(item.id));
var packs = item.info.packs;
// find a loading package
var pack = packs.find(isLoading);
if (pack) return _loading.get(pack.uuid).push({ onComplete, id: item.id });
// download a new package
pack = packs[0];
_loading.add(pack.uuid, [{ onComplete, id: item.id }]);
let url = cc.assetManager._transform(pack.uuid, {ext: pack.ext, bundle: item.config.name});
downloader.download(pack.uuid, url, pack.ext, item.options, function (err, data) {
files.remove(pack.uuid);
if (err) {
cc.error(err.message, err.stack);
}
// unpack package
packManager.unpack(pack.packs, data, pack.ext, item.options, function (err, result) {
if (!err) {
for (var id in result) {
files.add(id, result[id]);
}
} else {
err.message = `unpack ${url} failed! details: ${err.message}`;
}
var callbacks = _loading.remove(pack.uuid);
for (var i = 0, l = callbacks.length; i < l; i++) {
var cb = callbacks[i];
if (err) {
cb.onComplete(err);
continue;
}
var data = result[cb.id];
if (!data) {
cb.onComplete(new Error('can not retrieve data from package'));
}
else {
cb.onComplete(null, data);
}
}
});
});
}
};
var unpackers = {
'.json': packManager.unpackJson
};
module.exports = packManager;

View File

@@ -0,0 +1,440 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
/**
* @module cc.AssetManager
*/
const plistParser = require('../platform/CCSAXParser').plistParser;
const js = require('../platform/js');
const deserialize = require('./deserialize');
const Cache = require('./cache');
const { isScene } = require('./helper');
const { parsed, files } = require('./shared');
const { __audioSupport, capabilities } = require('../platform/CCSys');
var _parsing = new Cache();
/**
* !#en
* Parse the downloaded file, it's a singleton, all member can be accessed with `cc.assetManager.parser`
*
* !#zh
* 解析已下载的文件parser 是一个单例, 所有成员能通过 `cc.assetManaager.parser` 访问
*
* @class Parser
*/
var parser = {
/*
* !#en
* Parse image file
*
* !#zh
* 解析图片文件
*
* @method parseImage
* @param {Blob} file - The downloaded file
* @param {Object} options - Some optional paramters
* @param {Function} [onComplete] - callback when finish parsing.
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {ImageBitmap|HTMLImageElement} onComplete.img - The parsed content
*
* @example
* downloader.downloadFile('test.jpg', {responseType: 'blob'}, null, (err, file) => {
* parser.parseImage(file, null, (err, img) => console.log(err));
* });
*
* @typescript
* parseImage(file: Blob, options: Record<string, any>, onComplete?: (err: Error, img: ImageBitmap|HTMLImageElement) => void): void
*/
parseImage (file, options, onComplete) {
if (capabilities.imageBitmap && file instanceof Blob) {
let imageOptions = {};
imageOptions.imageOrientation = options.__flipY__ ? 'flipY' : 'none';
imageOptions.premultiplyAlpha = options.__premultiplyAlpha__ ? 'premultiply' : 'none';
createImageBitmap(file, imageOptions).then(function (result) {
result.flipY = !!options.__flipY__;
result.premultiplyAlpha = !!options.__premultiplyAlpha__;
onComplete && onComplete(null, result);
}, function (err) {
onComplete && onComplete(err, null);
});
}
else {
onComplete && onComplete(null, file);
}
},
/*
* !#en
* Parse audio file
*
* !#zh
* 解析音频文件
*
* @method parseAudio
* @param {ArrayBuffer|HTMLAudioElement} file - The downloaded file
* @param {Object} options - Some optional paramters
* @param {Function} onComplete - Callback when finish parsing.
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {AudioBuffer|HTMLAudioElement} onComplete.audio - The parsed content
*
* @example
* downloader.downloadFile('test.mp3', {responseType: 'arraybuffer'}, null, (err, file) => {
* parser.parseAudio(file, null, (err, audio) => console.log(err));
* });
*
* @typescript
* parseAudio(file: ArrayBuffer|HTMLAudioElement, options: Record<string, any>, onComplete?: (err: Error, audio: AudioBuffer|HTMLAudioElement) => void): void
*/
parseAudio (file, options, onComplete) {
if (file instanceof ArrayBuffer) {
__audioSupport.context.decodeAudioData(file, function (buffer) {
onComplete && onComplete(null, buffer);
}, function(e){
onComplete && onComplete(e, null);
});
}
else {
onComplete && onComplete(null, file);
}
},
/*
* !#en
* Parse pvr file
*
* !#zh
* 解析压缩纹理格式 pvr 文件
*
* @method parsePVRTex
* @param {ArrayBuffer|ArrayBufferView} file - The downloaded file
* @param {Object} options - Some optional paramters
* @param {Function} onComplete - Callback when finish parsing.
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {Object} onComplete.pvrAsset - The parsed content
*
* @example
* downloader.downloadFile('test.pvr', {responseType: 'arraybuffer'}, null, (err, file) => {
* parser.parsePVRTex(file, null, (err, pvrAsset) => console.log(err));
* });
*
* @typescript
* parsePVRTex(file: ArrayBuffer|ArrayBufferView, options: Record<string, any>, onComplete: (err: Error, pvrAsset: {_data: Uint8Array, _compressed: boolean, width: number, height: number}) => void): void
*/
parsePVRTex : (function () {
//===============//
// PVR constants //
//===============//
// https://github.com/toji/texture-tester/blob/master/js/webgl-texture-util.js#L424
const PVR_HEADER_LENGTH = 13; // The header length in 32 bit ints.
const PVR_MAGIC = 0x03525650; //0x50565203;
// Offsets into the header array.
const PVR_HEADER_MAGIC = 0;
const PVR_HEADER_FORMAT = 2;
const PVR_HEADER_HEIGHT = 6;
const PVR_HEADER_WIDTH = 7;
const PVR_HEADER_MIPMAPCOUNT = 11;
const PVR_HEADER_METADATA = 12;
return function (file, options, onComplete) {
let err = null, out = null;
try {
let buffer = file instanceof ArrayBuffer ? file : file.buffer;
// Get a view of the arrayBuffer that represents the DDS header.
let header = new Int32Array(buffer, 0, PVR_HEADER_LENGTH);
// Do some sanity checks to make sure this is a valid DDS file.
if(header[PVR_HEADER_MAGIC] != PVR_MAGIC) {
throw new Error("Invalid magic number in PVR header");
}
// Gather other basic metrics and a view of the raw the DXT data.
let width = header[PVR_HEADER_WIDTH];
let height = header[PVR_HEADER_HEIGHT];
let dataOffset = header[PVR_HEADER_METADATA] + 52;
let pvrtcData = new Uint8Array(buffer, dataOffset);
out = {
_data: pvrtcData,
_compressed: true,
width: width,
height: height,
};
}
catch (e) {
err = e;
}
onComplete && onComplete(err, out);
};
})(),
/*
* !#en
* Parse pkm file
*
* !#zh
* 解析压缩纹理格式 pkm 文件
*
* @method parsePKMTex
* @param {ArrayBuffer|ArrayBufferView} file - The downloaded file
* @param {Object} options - Some optional paramters
* @param {Function} onComplete - Callback when finish parsing.
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {Object} onComplete.etcAsset - The parsed content
*
* @example
* downloader.downloadFile('test.pkm', {responseType: 'arraybuffer'}, null, (err, file) => {
* parser.parsePKMTex(file, null, (err, etcAsset) => console.log(err));
* });
*
* @typescript
* parsePKMTex(file: ArrayBuffer|ArrayBufferView, options: Record<string, any>, onComplete: (err: Error, etcAsset: {_data: Uint8Array, _compressed: boolean, width: number, height: number}) => void): void
*/
parsePKMTex: (function () {
//===============//
// ETC constants //
//===============//
const ETC_PKM_HEADER_SIZE = 16;
const ETC_PKM_FORMAT_OFFSET = 6;
const ETC_PKM_ENCODED_WIDTH_OFFSET = 8;
const ETC_PKM_ENCODED_HEIGHT_OFFSET = 10;
const ETC_PKM_WIDTH_OFFSET = 12;
const ETC_PKM_HEIGHT_OFFSET = 14;
const ETC1_RGB_NO_MIPMAPS = 0;
const ETC2_RGB_NO_MIPMAPS = 1;
const ETC2_RGBA_NO_MIPMAPS = 3;
function readBEUint16(header, offset) {
return (header[offset] << 8) | header[offset+1];
}
return function (file, options, onComplete) {
let err = null, out = null;
try {
let buffer = file instanceof ArrayBuffer ? file : file.buffer;
let header = new Uint8Array(buffer);
let format = readBEUint16(header, ETC_PKM_FORMAT_OFFSET);
if (format !== ETC1_RGB_NO_MIPMAPS && format !== ETC2_RGB_NO_MIPMAPS && format !== ETC2_RGBA_NO_MIPMAPS) {
return new Error("Invalid magic number in ETC header");
}
let width = readBEUint16(header, ETC_PKM_WIDTH_OFFSET);
let height = readBEUint16(header, ETC_PKM_HEIGHT_OFFSET);
let encodedWidth = readBEUint16(header, ETC_PKM_ENCODED_WIDTH_OFFSET);
let encodedHeight = readBEUint16(header, ETC_PKM_ENCODED_HEIGHT_OFFSET);
let etcData = new Uint8Array(buffer, ETC_PKM_HEADER_SIZE);
out = {
_data: etcData,
_compressed: true,
width: width,
height: height
};
}
catch (e) {
err = e;
}
onComplete && onComplete(err, out);
}
})(),
/*
* !#en
* Parse plist file
*
* !#zh
* 解析 plist 文件
*
* @method parsePlist
* @param {string} file - The downloaded file
* @param {Object} options - Some optional paramters
* @param {Function} onComplete - Callback when finish parsing
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {*} onComplete.data - The parsed content
*
* @example
* downloader.downloadFile('test.plist', {responseType: 'text'}, null, (err, file) => {
* parser.parsePlist(file, null, (err, data) => console.log(err));
* });
*
* @typescript
* parsePlist(file: string, options: Record<string, any>, onComplete?: (err: Error, data: any) => void): void
*/
parsePlist (file, options, onComplete) {
var err = null;
var result = plistParser.parse(file);
if (!result) err = new Error('parse failed');
onComplete && onComplete(err, result);
},
/*
* !#en
* Deserialize asset file
*
* !#zh
* 反序列化资源文件
*
* @method parseImport
* @param {Object} file - The serialized json
* @param {Object} options - Some optional paramters
* @param {Function} onComplete - Callback when finish parsing
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {Asset} onComplete.asset - The parsed content
*
* @example
* downloader.downloadFile('test.json', {responseType: 'json'}, null, (err, file) => {
* parser.parseImport(file, null, (err, data) => console.log(err));
* });
*
* @typescript
* parseImport (file: any, options: Record<string, any>, onComplete?: (err: Error, asset: cc.Asset) => void): void
*/
parseImport (file, options, onComplete) {
if (!file) return onComplete && onComplete(new Error('Json is empty'));
var result, err = null;
try {
result = deserialize(file, options);
}
catch (e) {
err = e;
}
onComplete && onComplete(err, result);
},
init () {
_parsing.clear();
},
/**
* !#en
* Register custom handler if you want to change default behavior or extend parser to parse other format file
*
* !#zh
* 当你想修改默认行为或者拓展 parser 来解析其他格式文件时可以注册自定义的handler
*
* @method register
* @param {string|Object} type - Extension likes '.jpg' or map likes {'.jpg': jpgHandler, '.png': pngHandler}
* @param {Function} [handler] - The corresponding handler
* @param {*} handler.file - File
* @param {Object} handler.options - Some optional paramter
* @param {Function} handler.onComplete - callback when finishing parsing
*
* @example
* parser.register('.tga', (file, options, onComplete) => onComplete(null, null));
* parser.register({'.tga': (file, options, onComplete) => onComplete(null, null), '.ext': (file, options, onComplete) => onComplete(null, null)});
*
* @typescript
* register(type: string, handler: (file: any, options: Record<string, any>, onComplete: (err: Error, data: any) => void) => void): void
* register(map: Record<string, (file: any, options: Record<string, any>, onComplete: (err: Error, data: any) => void) => void>): void
*/
register (type, handler) {
if (typeof type === 'object') {
js.mixin(parsers, type);
}
else {
parsers[type] = handler;
}
},
/**
* !#en
* Use corresponding handler to parse file
*
* !#zh
* 使用对应的handler来解析文件
*
* @method parse
* @param {string} id - The id of file
* @param {*} file - File
* @param {string} type - The corresponding type of file, likes '.jpg'.
* @param {Object} options - Some optional paramters will be transferred to the corresponding handler.
* @param {Function} onComplete - callback when finishing downloading
* @param {Error} onComplete.err - The occurred error, null indicetes success
* @param {*} onComplete.contetnt - The parsed file
*
* @example
* downloader.downloadFile('test.jpg', {responseType: 'blob'}, null, (err, file) => {
* parser.parse('test.jpg', file, '.jpg', null, (err, img) => console.log(err));
* });
*
* @typescript
* parse(id: string, file: any, type: string, options: Record<string, any>, onComplete: (err: Error, content: any) => void): void
*/
parse (id, file, type, options, onComplete) {
let parsedAsset, parsing, parseHandler;
if (parsedAsset = parsed.get(id)) {
onComplete(null, parsedAsset);
}
else if (parsing = _parsing.get(id)){
parsing.push(onComplete);
}
else if (parseHandler = parsers[type]){
_parsing.add(id, [onComplete]);
parseHandler(file, options, function (err, data) {
if (err) {
files.remove(id);
}
else if (!isScene(data)){
parsed.add(id, data);
}
let callbacks = _parsing.remove(id);
for (let i = 0, l = callbacks.length; i < l; i++) {
callbacks[i](err, data);
}
});
}
else {
onComplete(null, file);
}
}
};
var parsers = {
'.png' : parser.parseImage,
'.jpg' : parser.parseImage,
'.bmp' : parser.parseImage,
'.jpeg' : parser.parseImage,
'.gif' : parser.parseImage,
'.ico' : parser.parseImage,
'.tiff' : parser.parseImage,
'.webp' : parser.parseImage,
'.image' : parser.parseImage,
'.pvr' : parser.parsePVRTex,
'.pkm' : parser.parsePKMTex,
// Audio
'.mp3' : parser.parseAudio,
'.ogg' : parser.parseAudio,
'.wav' : parser.parseAudio,
'.m4a' : parser.parseAudio,
// plist
'.plist' : parser.parsePlist,
'import' : parser.parseImport
};
module.exports = parser;

View File

@@ -0,0 +1,330 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
/**
* @module cc.AssetManager
*/
const Task = require('./task');
var _pipelineId = 0;
/**
* !#en
* Pipeline can execute the task for some effect.
*
* !#zh
* 管线能执行任务达到某个效果
*
* @class Pipeline
*/
function Pipeline (name, funcs) {
if (!Array.isArray(funcs)) {
cc.warn('funcs must be an array');
return;
}
/**
* !#en
* The id of pipeline
*
* !#zh
* 管线的 id
*
* @property id
* @type {Number}
*/
this.id = _pipelineId++;
/**
* !#en
* The name of pipeline
*
* !#zh
* 管线的名字
*
* @property name
* @type {String}
*/
this.name = name;
/**
* !#en
* All pipes of pipeline
*
* !#zh
* 所有的管道
*
* @property pipes
* @type {Function[]}
*/
this.pipes = [];
for (var i = 0, l = funcs.length; i < l; i++) {
if (typeof funcs[i] === 'function') {
this.pipes.push(funcs[i]);
}
}
}
Pipeline.prototype = {
/**
* !#en
* Create a new pipeline
*
* !#zh
* 创建一个管线
*
* @method constructor
* @param {string} name - The name of pipeline
* @param {Function[]} funcs - The array of pipe, every pipe must be function which take two parameters, the first is a `Task` flowed in pipeline, the second is complete callback
*
* @example
* var pipeline = new Pipeline('download', [
* (task, done) => {
* var url = task.input;
* cc.assetManager.downloader.downloadFile(url, null, null, (err, result) => {
* task.output = result;
* done(err);
* });
* },
* (task, done) => {
* var text = task.input;
* var json = JSON.stringify(text);
* task.output = json;
* done();
* }
* ]);
*
* @typescript
* constructor(name: string, funcs: Array<(task: Task, done?: (err: Error) => void) => void>)
*/
constructor: Pipeline,
/**
* !#en
* At specific point insert a new pipe to pipeline
*
* !#zh
* 在某个特定的点为管线插入一个新的 pipe
*
* @method insert
* @param {Function} func - The new pipe
* @param {Task} func.task - The task handled with pipeline will be transferred to this function
* @param {Function} [func.callback] - Callback you need to invoke manually when this pipe is finished. if the pipeline is synchronous, callback is unnecessary.
* @param {number} index - The specific point you want to insert at.
* @return {Pipeline} pipeline
*
* @example
* var pipeline = new Pipeline('test', []);
* pipeline.insert((task, done) => {
* // do something
* done();
* }, 0);
*
* @typescript
* insert(func: (task: Task, callback?: (err: Error) => void) => void, index: number): Pipeline
*/
insert (func, index) {
if (typeof func !== 'function' || index > this.pipes.length) {
cc.warnID(4921);
return;
}
this.pipes.splice(index, 0, func);
return this;
},
/**
* !#en
* Append a new pipe to the pipeline
*
* !#zh
* 添加一个管道到管线中
*
* @method append
* @param {Function} func - The new pipe
* @param {Task} func.task - The task handled with pipeline will be transferred to this function
* @param {Function} [func.callback] - Callback you need to invoke manually when this pipe is finished. if the pipeline is synchronous, callback is unnecessary.
* @return {Pipeline} pipeline
*
* @example
* var pipeline = new Pipeline('test', []);
* pipeline.append((task, done) => {
* // do something
* done();
* });
*
* @typescript
* append(func: (task: Task, callback?: (err: Error) => void) => void): Pipeline
*/
append (func) {
if (typeof func !== 'function') {
return;
}
this.pipes.push(func);
return this;
},
/**
* !#en
* Remove pipe which at specific point
*
* !#zh
* 移除特定位置的管道
*
* @method remove
* @param {number} index - The specific point
* @return {Pipeline} pipeline
*
* @example
* var pipeline = new Pipeline('test', (task, done) => {
* // do something
* done();
* });
* pipeline.remove(0);
*
* @typescript
* remove(index: number): Pipeline
*/
remove (index) {
if (typeof index !== 'number') {
return;
}
this.pipes.splice(index, 1);
return this;
},
/**
* !#en
* Execute task synchronously
*
* !#zh
* 同步执行任务
*
* @method sync
* @param {Task} task - The task will be executed
* @returns {*} result
*
* @example
* var pipeline = new Pipeline('sync', [(task) => {
* let input = task.input;
* task.output = doSomething(task.input);
* }]);
*
* var task = new Task({input: 'test'});
* console.log(pipeline.sync(task));
*
* @typescript
* sync(task: Task): any
*/
sync (task) {
var pipes = this.pipes;
if (!(task instanceof Task) || pipes.length === 0) return;
if (task.output != null) {
task.input = task.output;
task.output = null;
}
task._isFinish = false;
for (var i = 0, l = pipes.length; i < l;) {
var pipe = pipes[i];
var result = pipe(task);
if (result) {
task._isFinish = true;
return result;
}
i++;
if (i !== l) {
task.input = task.output;
task.output = null;
}
}
task._isFinish = true;
return task.output;
},
/**
* !#en
* Execute task asynchronously
*
* !#zh
* 异步执行任务
*
* @method async
* @param {Task} task - The task will be executed
*
* @example
* var pipeline = new Pipeline('sync', [(task, done) => {
* let input = task.input;
* task.output = doSomething(task.input);
* done();
* }]);
* var task = new Task({input: 'test', onComplete: (err, result) => console.log(result)});
* pipeline.async(task);
*
* @typescript
* async(task: Task): void
*/
async (task) {
var pipes = this.pipes;
if (!(task instanceof Task) || pipes.length === 0) return;
if (task.output != null) {
task.input = task.output;
task.output = null;
}
task._isFinish = false;
this._flow(0, task);
},
_flow (index, task) {
var self = this;
var pipe = this.pipes[index];
pipe(task, function (result) {
if (result) {
task._isFinish = true;
task.onComplete && task.onComplete(result);
}
else {
index++;
if (index < self.pipes.length) {
// move output to input
task.input = task.output;
task.output = null;
self._flow(index, task);
}
else {
task._isFinish = true;
task.onComplete && task.onComplete(result, task.output);
}
}
});
}
};
module.exports = Pipeline;

View File

@@ -0,0 +1,78 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const Task = require('./task');
const { transformPipeline, RequestType } = require('./shared');
function preprocess (task, done) {
var options = task.options, subOptions = Object.create(null), leftOptions = Object.create(null);
for (var op in options) {
switch (op) {
// can't set these attributes in options
case RequestType.PATH:
case RequestType.UUID:
case RequestType.DIR:
case RequestType.SCENE:
case RequestType.URL : break;
// only need these attributes to transform url
case '__requestType__':
case '__isNative__':
case 'ext' :
case 'type':
case '__nativeName__':
case 'audioLoadMode':
case 'bundle':
subOptions[op] = options[op];
break;
// other settings, left to next pipe
case '__exclude__':
case '__outputAsArray__':
leftOptions[op] = options[op];
break;
default:
subOptions[op] = options[op];
leftOptions[op] = options[op];
break;
}
}
task.options = leftOptions;
// transform url
let subTask = Task.create({input: task.input, options: subOptions});
var err = null;
try {
task.output = task.source = transformPipeline.sync(subTask);
}
catch (e) {
err = e;
for (var i = 0, l = subTask.output.length; i < l; i++) {
subTask.output[i].recycle();
}
}
subTask.recycle();
done(err);
}
module.exports = preprocess;

View File

@@ -0,0 +1,239 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const dependUtil = require('./depend-util');
const Cache = require('./cache');
require('../assets/CCAsset');
const { assets } = require('./shared');
const { callInNextTick } = require('../platform/utils');
function visitAsset (asset, deps) {
// Skip assets generated programmatically or by user (e.g. label texture)
if (!asset._uuid) {
return;
}
deps.push(asset._uuid);
}
function visitComponent (comp, deps) {
var props = Object.getOwnPropertyNames(comp);
for (let i = 0; i < props.length; i++) {
var propName = props[i];
if (propName === 'node' || propName === '__eventTargets') continue;
var value = comp[propName];
if (typeof value === 'object' && value) {
if (Array.isArray(value)) {
for (let j = 0; j < value.length; j++) {
let val = value[j];
if (val instanceof cc.Asset) {
visitAsset(val, deps);
}
}
}
else if (!value.constructor || value.constructor === Object) {
let keys = Object.getOwnPropertyNames(value);
for (let j = 0; j < keys.length; j++) {
let val = value[keys[j]];
if (val instanceof cc.Asset) {
visitAsset(val, deps);
}
}
}
else if (value instanceof cc.Asset) {
visitAsset(value, deps);
}
}
}
}
let _temp = [];
function visitNode (node, deps) {
for (let i = 0; i < node._components.length; i++) {
visitComponent(node._components[i], deps);
}
for (let i = 0; i < node._children.length; i++) {
visitNode(node._children[i], deps);
}
}
function descendOpRef (asset, refs, exclude, op) {
exclude.push(asset._uuid);
var depends = dependUtil.getDeps(asset._uuid);
for (let i = 0, l = depends.length; i < l; i++) {
var dependAsset = assets.get(depends[i]);
if (dependAsset) {
let uuid = dependAsset._uuid;
if (!(uuid in refs)) {
refs[uuid] = dependAsset.refCount + op;
}
else {
refs[uuid] += op;
}
if (exclude.includes(uuid)) continue;
descendOpRef(dependAsset, refs, exclude, op);
}
}
}
function checkCircularReference (asset) {
// check circular reference
var refs = Object.create(null);
refs[asset._uuid] = asset.refCount;
descendOpRef(asset, refs, _temp, -1);
_temp.length = 0;
if (refs[asset._uuid] !== 0) return refs[asset._uuid];
for (let uuid in refs) {
if (refs[uuid] !== 0) {
descendOpRef(assets.get(uuid), refs, _temp, 1);
}
}
_temp.length = 0;
return refs[asset._uuid];
}
var _persistNodeDeps = new Cache();
var _toDelete = new Cache();
var eventListener = false;
function freeAssets () {
eventListener = false;
_toDelete.forEach(function (asset) {
releaseManager._free(asset);
});
_toDelete.clear();
}
var releaseManager = {
init () {
_persistNodeDeps.clear();
_toDelete.clear();
},
_addPersistNodeRef (node) {
var deps = [];
visitNode(node, deps);
for (let i = 0, l = deps.length; i < l; i++) {
var dependAsset = assets.get(deps[i]);
if (dependAsset) {
dependAsset.addRef();
}
}
_persistNodeDeps.add(node.uuid, deps);
},
_removePersistNodeRef (node) {
if (_persistNodeDeps.has(node.uuid)) {
var deps = _persistNodeDeps.get(node.uuid);
for (let i = 0, l = deps.length; i < l; i++) {
var dependAsset = assets.get(deps[i]);
if (dependAsset) {
dependAsset.decRef();
}
}
_persistNodeDeps.remove(node.uuid);
}
},
// do auto release
_autoRelease (oldScene, newScene, persistNodes) {
if (oldScene) {
var childs = dependUtil.getDeps(oldScene._id);
for (let i = 0, l = childs.length; i < l; i++) {
let asset = assets.get(childs[i]);
asset && asset.decRef(CC_TEST || oldScene.autoReleaseAssets);
}
var dependencies = dependUtil._depends.get(oldScene._id);
if (dependencies && dependencies.persistDeps) {
var persistDeps = dependencies.persistDeps;
for (let i = 0, l = persistDeps.length; i < l; i++) {
let asset = assets.get(persistDeps[i]);
asset && asset.decRef(CC_TEST || oldScene.autoReleaseAssets);
}
}
oldScene._id !== newScene._id && dependUtil.remove(oldScene._id);
}
var sceneDeps = dependUtil._depends.get(newScene._id);
sceneDeps && (sceneDeps.persistDeps = []);
// transfer refs from persist nodes to new scene
for (let key in persistNodes) {
var node = persistNodes[key];
var deps = _persistNodeDeps.get(node.uuid);
for (let i = 0, l = deps.length; i < l; i++) {
var dependAsset = assets.get(deps[i]);
if (dependAsset) {
dependAsset.addRef();
}
}
if (sceneDeps) {
sceneDeps.persistDeps.push.apply(sceneDeps.persistDeps, deps);
}
}
},
_free (asset, force) {
_toDelete.remove(asset._uuid);
if (!cc.isValid(asset, true)) return;
if (!force) {
if (asset.refCount > 0) {
if (checkCircularReference(asset) > 0) return;
}
}
// remove from cache
assets.remove(asset._uuid);
var depends = dependUtil.getDeps(asset._uuid);
for (let i = 0, l = depends.length; i < l; i++) {
var dependAsset = assets.get(depends[i]);
if (dependAsset) {
dependAsset.decRef(false);
releaseManager._free(dependAsset, false);
}
}
asset.destroy();
dependUtil.remove(asset._uuid);
},
tryRelease (asset, force) {
if (!(asset instanceof cc.Asset)) return;
if (force) {
releaseManager._free(asset, force);
}
else {
_toDelete.add(asset._uuid, asset);
if (!eventListener) {
eventListener = true;
callInNextTick(freeAssets);
}
}
}
};
module.exports = releaseManager;

View File

@@ -0,0 +1,232 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
/**
* @module cc.AssetManager
*/
var MAX_DEAD_NUM = 500;
var _deadPool = [];
/**
* !#en
* A collection of information about a request
*
* !#zh
* 请求的相关信息集合
*
* @class RequestItem
*/
function RequestItem () {
this._id = '';
/**
* !#en
* The uuid of request
*
* !#zh
* 请求资源的uuid
*
* @property uuid
* @type {String}
*/
this.uuid = '';
/**
* !#en
* The final url of request
*
* !#zh
* 请求的最终url
*
* @property url
* @type {String}
*/
this.url = '';
/**
* !#en
* The extension name of asset
*
* !#zh
* 资源的扩展名
*
* @property ext
* @type {String}
*/
this.ext = '.json';
/**
* !#en
* The content of asset
*
* !#zh
* 资源的内容
*
* @property content
* @type {*}
*/
this.content = null;
/**
* !#en
* The file of asset
*
* !#zh
* 资源的文件
*
* @property file
* @type {*}
*/
this.file = null;
/**
* !#en
* The information of asset
*
* !#zh
* 资源的相关信息
*
* @property info
* @type {Object}
*/
this.info = null;
this.config = null;
/**
* !#en
* Whether or not it is native asset
*
* !#zh
* 资源是否是原生资源
*
* @property isNative
* @type {Boolean}
*/
this.isNative = false;
/**
* !#en
* Custom options
*
* !#zh
* 自定义参数
*
* @property options
* @type {Object}
*/
this.options = Object.create(null);
}
RequestItem.prototype = {
/**
* !#en
* Create a request item
*
* !#zh
* 创建一个 request item
*
* @method constructor
*
* @typescript
* constructor()
*/
constructor: RequestItem,
/**
* !#en
* The id of request, combined from uuid and isNative
*
* !#zh
* 请求的 id, 由 uuid 和 isNative 组合而成
*
* @property id
* @type {String}
*/
get id () {
if (!this._id) {
this._id = this.uuid + '@' + (this.isNative ? 'native' : 'import');
}
return this._id;
},
/**
* !#en
* Recycle this for reuse
*
* !#zh
* 回收 requestItem 用于复用
*
* @method recycle
*
* @typescript
* recycle(): void
*/
recycle () {
if (_deadPool.length === MAX_DEAD_NUM) return;
this._id = '';
this.uuid = '';
this.url = '';
this.ext = '.json';
this.content = null;
this.file = null;
this.info = null;
this.config = null;
this.isNative = false;
this.options = Object.create(null);
_deadPool.push(this);
}
};
/**
* !#en
* Create a new request item from pool
*
* !#zh
* 从对象池中创建 requestItem
*
* @static
* @method create
* @returns {RequestItem} requestItem
*
* @typescript
* create(): RequestItem
*/
RequestItem.create = function () {
var out = null;
if (_deadPool.length !== 0) {
out = _deadPool.pop();
}
else {
out = new RequestItem();
}
return out;
};
module.exports = RequestItem;

View File

@@ -0,0 +1,116 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const Cache = require('./cache');
const Pipeline = require('./pipeline');
var assets = new Cache();
var files = new Cache();
var parsed = new Cache();
var bundles = new Cache();
var pipeline = new Pipeline('normal load', []);
var fetchPipeline = new Pipeline('fetch', []);
var transformPipeline = new Pipeline('transform url', []);
/**
* @module cc.AssetManager
*/
var RequestType = {
UUID: 'uuid',
PATH: 'path',
DIR: 'dir',
URL: 'url',
SCENE: 'scene'
};
/**
* !#en
* The builtin bundles
*
* !#zh
* 内置 bundle
*
* @enum BuiltinBundleName
*/
var BuiltinBundleName = {
/**
* !#en
* The builtin bundle corresponds to 'assets/resources'.
*
* !#zh
* 内置 bundle, 对应 'assets/resources' 目录
*
* @property RESOURCES
* @readonly
* @type {String}
*/
RESOURCES: 'resources',
/**
* !#en
* The builtin bundle corresponds to 'internal/resources'.
*
* !#zh
* 内置 bundle, 对应 'internal/resources' 目录
*
* @property INTERNAL
* @readonly
* @type {String}
*/
INTERNAL: 'internal',
/**
* !#en
* The builtin bundle
*
* !#zh
* 内置 bundle
*
* @property MAIN
* @readonly
* @type {String}
*/
MAIN: 'main',
/**
* !#en
* The builtin bundle, exists when Start Scene asset bundle is checked on the project building panel
*
* !#zh
* 内置 bundle, 如果构建面板开启了首场景分包,则会有 START_SCENE bundle
*
* @property START_SCENE
* @readonly
* @type {String}
*/
START_SCENE: 'start-scene',
};
module.exports = { assets, files, parsed, pipeline, fetchPipeline, transformPipeline, RequestType, bundles, BuiltinBundleName };

View File

@@ -0,0 +1,328 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
/**
* @module cc.AssetManager
*/
var _taskId = 0;
var MAX_DEAD_NUM = 500;
var _deadPool = [];
/**
* !#en
* Task is used to run in the pipeline for some effect
*
* !#zh
* 任务用于在管线中运行以达成某种效果
*
* @class Task
*/
function Task (options) {
/**
* !#en
* The id of task
*
* !#zh
* 任务id
*
* @property id
* @type {Number}
*/
this.id = _taskId++;
this._isFinish = true;
/**
* !#en
* The callback when task is completed
*
* !#zh
* 完成回调
*
* @property onComplete
* @type {Function}
*/
this.onComplete = null;
/**
* !#en
* The callback of progression
*
* !#zh
* 进度回调
*
* @property onProgress
* @type {Function}
*/
this.onProgress = null;
/**
* !#en
* The callback when something goes wrong
*
* !#zh
* 错误回调
*
* @property onError
* @type {Function}
*/
this.onError = null;
/**
* !#en
* The source of task
*
* !#zh
* 任务的源
*
* @property source
* @type {*}
*/
this.source = null;
/**
* !#en
* The output of task
*
* !#zh
* 任务的输出
*
* @property output
* @type {*}
*/
this.output = null
/**
* !#en
* The input of task
*
* !#zh
* 任务的输入
*
* @property input
* @type {*}
*/
this.input = null;
/**
* !#en
* The progression of task
*
* !#zh
* 任务的进度
*
* @property progress
* @type {*}
*/
this.progress = null;
/**
* !#en
* Custom options
*
* !#zh
* 自定义参数
*
* @property options
* @type {Object}
*/
this.options = null;
this.set(options);
};
Task.prototype = {
/**
* !#en
* Create a new Task
*
* !#zh
* 创建一个任务
*
* @method constructor
* @param {Object} [options] - Some optional paramters
* @param {Function} [options.onComplete] - Callback when the task is completed, if the pipeline is synchronous, onComplete is unnecessary.
* @param {Function} [options.onProgress] - Continuously callback when the task is runing, if the pipeline is synchronous, onProgress is unnecessary.
* @param {Function} [options.onError] - Callback when something goes wrong, if the pipeline is synchronous, onError is unnecessary.
* @param {*} options.input - Something will be handled with pipeline
* @param {*} [options.progress] - Progress information, you may need to assign it manually when multiple pipeline share one progress
* @param {Object} [options.options] - Custom parameters
*
* @typescript
* constructor(options?: {onComplete?: (err: Error, result: any) => void, onError?: () => void, onProgress?: Function, input: any, progress?: any, options?: Record<string, any>})
*/
constructor: Task,
/**
* !#en
* Set paramters of this task
*
* !#zh
* 设置任务的参数
*
* @method set
* @param {Object} [options] - Some optional paramters
* @param {Function} [options.onComplete] - Callback when the task complete, if the pipeline is synchronous, onComplete is unnecessary.
* @param {Function} [options.onProgress] - Continuously callback when the task is runing, if the pipeline is synchronous, onProgress is unnecessary.
* @param {Function} [options.onError] - Callback when something goes wrong, if the pipeline is synchronous, onError is unnecessary.
* @param {*} options.input - Something will be handled with pipeline
* @param {*} [options.progress] - Progress information, you may need to assign it manually when multiple pipeline share one progress
* @param {Object} [options.options] - Custom parameters
*
* @example
* var task = new Task();
* task.set({input: ['test'], onComplete: (err, result) => console.log(err), onProgress: (finish, total) => console.log(finish / total)});
*
* @typescript
* set(options?: {onComplete?: (err: Error, result: any) => void, onError?: () => void, onProgress?: Function, input: any, progress?: any, options?: Record<string, any>}): void
*/
set (options) {
options = options || Object.create(null);
this.onComplete = options.onComplete;
this.onProgress = options.onProgress;
this.onError = options.onError;
this.source = this.input = options.input;
this.output = null;
this.progress = options.progress;
// custom data
this.options = options.options || Object.create(null);
},
/**
* !#en
* Dispatch event
*
* !#zh
* 发布事件
*
* @method dispatch
* @param {string} event - The event name
* @param {*} param1 - Parameter 1
* @param {*} param2 - Parameter 2
* @param {*} param3 - Parameter 3
* @param {*} param4 - Parameter 4
*
* @example
* var task = Task.create();
* Task.onComplete = (msg) => console.log(msg);
* Task.dispatch('complete', 'hello world');
*
* @typescript
* dispatch(event: string, param1?: any, param2?: any, param3?: any, param4?: any): void
*/
dispatch (event, param1, param2, param3, param4) {
switch (event) {
case 'complete' :
this.onComplete && this.onComplete(param1, param2, param3, param4);
break;
case 'progress':
this.onProgress && this.onProgress(param1, param2, param3, param4);
break;
case 'error':
this.onError && this.onError(param1, param2, param3, param4);
break;
default:
var str = 'on' + event[0].toUpperCase() + event.substr(1);
if (typeof this[str] === 'function') {
this[str](param1, param2, param3, param4);
}
break;
}
},
/**
* !#en
* Recycle this for reuse
*
* !#zh
* 回收 task 用于复用
*
* @method recycle
*
* @typescript
* recycle(): void
*/
recycle () {
if (_deadPool.length === MAX_DEAD_NUM) return;
this.onComplete = null;
this.onProgress = null;
this.onError = null;
this.source = this.output = this.input = null;
this.progress = null;
this.options = null;
_deadPool.push(this);
},
/**
* !#en
* Whether or not this task is completed
*
* !#zh
* 此任务是否已经完成
*
* @property isFinish
* @type {Boolean}
*/
get isFinish () {
return this._isFinish;
}
};
/**
* !#en
* Create a new task from pool
*
* !#zh
* 从对象池中创建 task
*
* @static
* @method create
* @param {Object} [options] - Some optional paramters
* @param {Function} [options.onComplete] - Callback when the task complete, if the pipeline is synchronous, onComplete is unnecessary.
* @param {Function} [options.onProgress] - Continuously callback when the task is runing, if the pipeline is synchronous, onProgress is unnecessary.
* @param {Function} [options.onError] - Callback when something goes wrong, if the pipeline is synchronous, onError is unnecessary.
* @param {*} options.input - Something will be handled with pipeline
* @param {*} [options.progress] - Progress information, you may need to assign it manually when multiple pipeline share one progress
* @param {Object} [options.options] - Custom parameters
* @returns {Task} task
*
* @typescript
* create(options?: {onComplete?: (err: Error, result: any) => void, onError?: () => void, onProgress?: Function, input: any, progress?: any, options?: Record<string, any>}): Task
*/
Task.create = function (options) {
var out = null;
if (_deadPool.length !== 0) {
out = _deadPool.pop();
out.set(options);
}
else {
out = new Task(options);
}
return out;
};
module.exports = Task;

View File

@@ -0,0 +1,183 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const { decodeUuid } = require('./helper');
const RequestItem = require('./request-item');
const { RequestType, bundles } = require('./shared');
function parse (task) {
var input = task.input, options = task.options;
input = Array.isArray(input) ? input : [ input ];
task.output = [];
for (var i = 0; i < input.length; i ++ ) {
var item = input[i];
var out = RequestItem.create();
if (typeof item === 'string') {
item = Object.create(null);
item[options.__requestType__ || RequestType.UUID] = input[i];
}
if (typeof item === 'object') {
// local options will overlap glabal options
cc.js.addon(item, options);
if (item.preset) {
cc.js.addon(item, cc.assetManager.presets[item.preset]);
}
for (var key in item) {
switch (key) {
case RequestType.UUID:
var uuid = out.uuid = decodeUuid(item.uuid);
if (bundles.has(item.bundle)) {
var config = bundles.get(item.bundle)._config;
var info = config.getAssetInfo(uuid);
if (info && info.redirect) {
if (!bundles.has(info.redirect)) throw new Error(`Please load bundle ${info.redirect} first`);
config = bundles.get(info.redirect)._config;
info = config.getAssetInfo(uuid);
}
out.config = config;
out.info = info;
}
out.ext = item.ext || '.json';
break;
case '__requestType__':
case 'ext':
case 'bundle':
case 'preset':
case 'type': break;
case RequestType.DIR:
if (bundles.has(item.bundle)) {
var infos = [];
bundles.get(item.bundle)._config.getDirWithPath(item.dir, item.type, infos);
for (let i = 0, l = infos.length; i < l; i++) {
var info = infos[i];
input.push({uuid: info.uuid, __isNative__: false, ext: '.json', bundle: item.bundle});
}
}
out.recycle();
out = null;
break;
case RequestType.PATH:
if (bundles.has(item.bundle)) {
var config = bundles.get(item.bundle)._config;
var info = config.getInfoWithPath(item.path, item.type);
if (info && info.redirect) {
if (!bundles.has(info.redirect)) throw new Error(`you need to load bundle ${info.redirect} first`);
config = bundles.get(info.redirect)._config;
info = config.getAssetInfo(info.uuid);
}
if (!info) {
out.recycle();
throw new Error(`Bundle ${item.bundle} doesn't contain ${item.path}`);
}
out.config = config;
out.uuid = info.uuid;
out.info = info;
}
out.ext = item.ext || '.json';
break;
case RequestType.SCENE:
if (bundles.has(item.bundle)) {
var config = bundles.get(item.bundle)._config;
var info = config.getSceneInfo(item.scene);
if (info && info.redirect) {
if (!bundles.has(info.redirect)) throw new Error(`you need to load bundle ${info.redirect} first`);
config = bundles.get(info.redirect)._config;
info = config.getAssetInfo(info.uuid);
}
if (!info) {
out.recycle();
throw new Error(`Bundle ${config.name} doesn't contain scene ${item.scene}`);
}
out.config = config;
out.uuid = info.uuid;
out.info = info;
}
break;
case '__isNative__':
out.isNative = item.__isNative__;
break;
case RequestType.URL:
out.url = item.url;
out.uuid = item.uuid || item.url;
out.ext = item.ext || cc.path.extname(item.url);
out.isNative = item.__isNative__ !== undefined ? item.__isNative__ : true;
break;
default: out.options[key] = item[key];
}
if (!out) break;
}
}
if (!out) continue;
task.output.push(out);
if (!out.uuid && !out.url) throw new Error('Can not parse this input:' + JSON.stringify(item));
}
return null;
}
function combine (task) {
var input = task.output = task.input;
for (var i = 0; i < input.length; i++) {
var item = input[i];
if (item.url) continue;
var url = '', base = '';
var config = item.config;
if (item.isNative) {
base = (config && config.nativeBase) ? (config.base + config.nativeBase) : cc.assetManager.generalNativeBase;
}
else {
base = (config && config.importBase) ? (config.base + config.importBase) : cc.assetManager.generalImportBase;
}
let uuid = item.uuid;
var ver = '';
if (item.info) {
if (item.isNative) {
ver = item.info.nativeVer ? ('.' + item.info.nativeVer) : '';
}
else {
ver = item.info.ver ? ('.' + item.info.ver) : '';
}
}
// ugly hack, WeChat does not support loading font likes 'myfont.dw213.ttf'. So append hash to directory
if (item.ext === '.ttf') {
url = `${base}/${uuid.slice(0, 2)}/${uuid}${ver}/${item.options.__nativeName__}`;
}
else {
url = `${base}/${uuid.slice(0, 2)}/${uuid}${ver}${item.ext}`;
}
item.url = url;
}
return null;
}
module.exports = { parse, combine };

View File

@@ -0,0 +1,350 @@
/****************************************************************************
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
const dependUtil = require('./depend-util');
const { isScene, decodeUuid } = require('./helper');
const { assets } = require('./shared');
const { callInNextTick } = require('../platform/utils');
const MissingObjectReporter = CC_EDITOR && Editor.require('app://editor/page/scene-utils/missing-object-reporter');
require('../assets/CCAsset');
var utils = {
processOptions (options) {
if (CC_EDITOR) return;
var uuids = options.uuids;
var paths = options.paths;
var types = options.types;
var bundles = options.deps;
var realEntries = options.paths = Object.create(null);
if (options.debug === false) {
for (let i = 0, l = uuids.length; i < l; i++) {
uuids[i] = decodeUuid(uuids[i]);
}
for (let id in paths) {
let entry = paths[id];
let type = entry[1];
entry[1] = types[type];
}
}
else {
var out = Object.create(null);
for (let i = 0, l = uuids.length; i < l; i++) {
let uuid = uuids[i];
uuids[i] = out[uuid] = decodeUuid(uuid);
}
uuids = out;
}
for (let id in paths) {
let entry = paths[id];
realEntries[uuids[id]] = entry;
}
var scenes = options.scenes;
for (let name in scenes) {
let uuid = scenes[name];
scenes[name] = uuids[uuid];
}
var packs = options.packs;
for (let packId in packs) {
let packedIds = packs[packId];
for (let j = 0; j < packedIds.length; ++j) {
packedIds[j] = uuids[packedIds[j]];
}
}
var versions = options.versions;
if (versions) {
for (let folder in versions) {
var entries = versions[folder];
for (let i = 0; i < entries.length; i += 2) {
let uuid = entries[i];
entries[i] = uuids[uuid] || uuid;
}
}
}
var redirect = options.redirect;
if (redirect) {
for (let i = 0; i < redirect.length; i += 2) {
redirect[i] = uuids[redirect[i]];
redirect[i + 1] = bundles[redirect[i + 1]];
}
}
},
clear (task, clearRef) {
for (var i = 0, l = task.input.length; i < l; i++) {
var item = task.input[i];
if (clearRef) {
!item.isNative && item.content && item.content.decRef && item.content.decRef(false);
}
item.recycle();
}
task.input = null;
},
urlAppendTimestamp (url) {
if (cc.assetManager.downloader.appendTimeStamp && typeof url === 'string') {
if (/\?/.test(url))
return url + '&_t=' + (new Date() - 0);
else
return url + '?_t=' + (new Date() - 0);
}
return url;
},
retry (process, times, wait, onComplete, index) {
index = index || 0;
process(index, function (err, result) {
index++;
if (!err || index > times) {
onComplete && onComplete(err, result);
}
else {
setTimeout(function () {
utils.retry(process, times, wait, onComplete, index);
}, wait);
}
});
},
getDepends (uuid, data, exclude, depends, preload, asyncLoadAssets, config) {
try {
var info = dependUtil.parse(uuid, data);
var includeNative = true;
if (data instanceof cc.Asset && (!data.__nativeDepend__ || data._nativeAsset)) includeNative = false;
if (!preload) {
asyncLoadAssets = !CC_EDITOR && (!!data.asyncLoadAssets || (asyncLoadAssets && !info.preventDeferredLoadDependents));
for (let i = 0, l = info.deps.length; i < l; i++) {
let dep = info.deps[i];
if (!(dep in exclude)) {
exclude[dep] = true;
depends.push({uuid: dep, __asyncLoadAssets__: asyncLoadAssets, bundle: config && config.name});
}
}
if (includeNative && !asyncLoadAssets && !info.preventPreloadNativeObject && info.nativeDep) {
config && (info.nativeDep.bundle = config.name);
depends.push(Object.assign({}, info.nativeDep));
}
} else {
for (let i = 0, l = info.deps.length; i < l; i++) {
let dep = info.deps[i];
if (!(dep in exclude)) {
exclude[dep] = true;
depends.push({uuid: dep, bundle: config && config.name});
}
}
if (includeNative && info.nativeDep) {
config && (info.nativeDep.bundle = config.name);
depends.push(Object.assign({}, info.nativeDep));
}
}
}
catch (e) {
cc.error(e.message, e.stack);
}
},
cache (id, asset, cacheAsset) {
if (!asset) return;
var _isScene = isScene(asset);
if (!_isScene && cacheAsset) {
assets.add(id, asset);
}
if (_isScene) {
if (CC_EDITOR && !asset.scene) {
Editor.error('Sorry, the scene data of "%s" is corrupted!', asset._uuid);
}
}
},
setProperties (uuid, asset, assetsMap) {
var missingAsset = false;
let depends = asset.__depends__;
if (depends) {
var missingAssetReporter = null;
for (var i = 0, l = depends.length; i < l; i++) {
var depend = depends[i];
var dependAsset = assetsMap[depend.uuid + '@import'];
if (!dependAsset) {
if (CC_EDITOR) {
!missingAssetReporter && (missingAssetReporter = new MissingObjectReporter(asset));
missingAssetReporter.stashByOwner(depend.owner, depend.prop, Editor.serialize.asAsset(depend.uuid));
}
else {
cc.error('The asset ' + depend.uuid + ' is missing!');
}
missingAsset = true;
}
else {
depend.owner[depend.prop] = dependAsset.addRef();
}
}
missingAssetReporter && missingAssetReporter.reportByOwner();
asset.__depends__ = undefined;
}
if (asset.__nativeDepend__) {
if (!asset._nativeAsset) {
if (assetsMap[uuid + '@native']) {
asset._nativeAsset = assetsMap[uuid + '@native'];
}
else {
missingAsset = true;
if (CC_EDITOR) {
console.error(`the native asset of ${uuid} is missing!`);
}
}
}
asset.__nativeDepend__ = undefined;
}
return missingAsset;
},
gatherAsset (task) {
let source = task.source;
if (!task.options.__outputAsArray__ && source.length === 1) {
task.output = source[0].content;
}
else {
let output = task.output = [];
for (var i = 0, l = source.length; i < l; i++) {
output.push(source[i].content);
}
}
},
forEach (array, process, onComplete) {
var count = 0;
var errs = [];
if (array.length === 0) onComplete && onComplete(errs);
for (var i = 0, l = array.length; i < l; i++) {
process(array[i], function (err) {
if (err) {
errs.push(err);
}
count ++;
if (count === l) {
onComplete && onComplete(errs);
}
});
}
},
parseParameters (options, onProgress, onComplete) {
if (onComplete === undefined) {
var isCallback = typeof options === 'function';
if (onProgress) {
onComplete = onProgress;
if (!isCallback) {
onProgress = null;
}
}
else if (onProgress === undefined && isCallback) {
onComplete = options;
options = null;
onProgress = null;
}
if (onProgress !== undefined && isCallback) {
onProgress = options;
options = null;
}
}
options = options || Object.create(null);
return { options, onProgress, onComplete };
},
parseLoadResArgs (type, onProgress, onComplete) {
if (onComplete === undefined) {
var isValidType = cc.js.isChildClassOf(type, cc.Asset);
if (onProgress) {
onComplete = onProgress;
if (isValidType) {
onProgress = null;
}
}
else if (onProgress === undefined && !isValidType) {
onComplete = type;
onProgress = null;
type = null;
}
if (onProgress !== undefined && !isValidType) {
onProgress = type;
type = null;
}
}
return { type, onProgress, onComplete };
},
checkCircleReference (owner, uuid, map, checked) {
if (!checked) {
checked = Object.create(null);
}
let item = map[uuid];
if (!item || checked[uuid]) {
return false;
}
checked[uuid] = true;
var result = false;
var deps = dependUtil.getDeps(uuid);
if (deps) {
for (var i = 0, l = deps.length; i < l; i++) {
var dep = deps[i];
if (dep === owner || utils.checkCircleReference(owner, dep, map, checked)) {
result = true;
break;
}
}
}
return result;
},
asyncify (cb) {
return function (p1, p2) {
if (!cb) return;
let refs = [];
if (Array.isArray(p2)) {
p2.forEach(x => x instanceof cc.Asset && refs.push(x.addRef()));
} else {
p2 instanceof cc.Asset && refs.push(p2.addRef());
}
callInNextTick(() => {
refs.forEach(x => x.decRef(false));
cb(p1, p2);
});
}
}
};
module.exports = utils;