mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-26 11:48:56 +00:00
509 lines
17 KiB
JavaScript
509 lines
17 KiB
JavaScript
|
/****************************************************************************
|
||
|
Copyright (c) 2017 Chukong Technologies Inc.
|
||
|
|
||
|
http://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.
|
||
|
Chukong Aipu 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 ID = 'WXDownloader';
|
||
|
const wxFsUtils = require('./wx-fs-utils');
|
||
|
|
||
|
const REGEX = /^\w+:\/\/.*/;
|
||
|
|
||
|
var packageFiles = null;
|
||
|
var cachedFiles = null;
|
||
|
var writeCacheFileList = null;
|
||
|
var cacheQueue = null;
|
||
|
var checkNextPeriod = false;
|
||
|
var errTest = /the maximum size of the file storage/;
|
||
|
|
||
|
var _newAssets = {};
|
||
|
var WXDownloader = window.WXDownloader = function () {
|
||
|
this.id = ID;
|
||
|
this.async = true;
|
||
|
this.pipeline = null;
|
||
|
this.REMOTE_SERVER_ROOT = '';
|
||
|
this.SUBCONTEXT_ROOT = '';
|
||
|
|
||
|
this.totalBytesExpectedToWriteAlreadyCounted = new Map(); // Keyed by "remoteUrl".
|
||
|
this.totalBytesExpectedToWriteForAllTasks = 0;
|
||
|
this.totalBytesWrittenForAllTasks = 0;
|
||
|
|
||
|
this.immediateHandleItemCount = 0;
|
||
|
this.immediateHandleItemCompleteCount = 0;
|
||
|
|
||
|
this.immediateReadFromLocalCount = 0;
|
||
|
this.immediateReadFromLocalCompleteCount = 0;
|
||
|
|
||
|
this.immediatePackDownloaderCount = 0;
|
||
|
this.immediatePackDownloaderCompleteCount = 0;
|
||
|
};
|
||
|
WXDownloader.ID = ID;
|
||
|
|
||
|
WXDownloader.prototype.init = function () {
|
||
|
if (!CC_WECHATGAMESUB) {
|
||
|
this.cacheDir = wx.env.USER_DATA_PATH + '/gamecaches';
|
||
|
this.cachedFileName = 'cacheList.json';
|
||
|
// whether or not cache asset into user's storage space
|
||
|
this.cacheAsset = true;
|
||
|
// cache one per cycle
|
||
|
this.cachePeriod = 100;
|
||
|
// whether or not storage space is run out of
|
||
|
this.outOfStorage = false;
|
||
|
|
||
|
this.writeFilePeriod = 1000;
|
||
|
|
||
|
cacheQueue = {};
|
||
|
packageFiles = {};
|
||
|
|
||
|
var cacheFilePath = this.cacheDir + '/' + this.cachedFileName;
|
||
|
cachedFiles = wxFsUtils.readJsonSync(cacheFilePath);
|
||
|
if (cachedFiles instanceof Error) {
|
||
|
cachedFiles = {};
|
||
|
wxFsUtils.makeDirSync(this.cacheDir, true);
|
||
|
wxFsUtils.writeFileSync(cacheFilePath, JSON.stringify(cachedFiles), 'utf8');
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
WXDownloader.prototype.handle = function (item, callback) {
|
||
|
if (item.type === 'js') {
|
||
|
return null;
|
||
|
}
|
||
|
var immediateHandleItemCompleteCallback = (errors, results) => {
|
||
|
wxDownloader.immediateHandleItemCompleteCount += 1;
|
||
|
callback(errors, results);
|
||
|
};
|
||
|
var immediateReadFromLocalCompleteCallback = (errors, results) => {
|
||
|
wxDownloader.immediateReadFromLocalCompleteCount += 1;
|
||
|
callback(errors, results);
|
||
|
};
|
||
|
var immediatePackerDownloaderCompleteCallback = (errors, results) => {
|
||
|
wxDownloader.immediatePackDownloaderCompleteCount += 1;
|
||
|
callback(errors, results);
|
||
|
};
|
||
|
if (item.type === 'uuid') {
|
||
|
wxDownloader.immediatePackDownloaderCount += 1;
|
||
|
var result = cc.Pipeline.Downloader.PackDownloader.load(item, immediatePackerDownloaderCompleteCallback);
|
||
|
if (undefined === result) {
|
||
|
// When "undefined" is returned, the above "PackDownloader.load(...)" has finished running synchronously and won't trigger "theWrappedCompleteCallback".
|
||
|
wxDownloader.immediatePackDownloaderCompleteCount += 1;
|
||
|
return;
|
||
|
} else if (null === result) {
|
||
|
// When "null" is returned, the above "PackDownloader.load(...)" has been running asynchronously and will trigger "theWrappedCompleteCallback".
|
||
|
return;
|
||
|
} else {
|
||
|
// When "non-empty" is returned, the above "PackDownloader.load(...)" has finished running synchronously and won't trigger "theWrappedCompleteCallback".
|
||
|
wxDownloader.immediatePackDownloaderCompleteCount += 1;
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (CC_WECHATGAMESUB) {
|
||
|
// if wx.getFileSystemManager is undefined, need to skip
|
||
|
if (REGEX.test(item.url)) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
item.url = this.SUBCONTEXT_ROOT + '/' + item.url;
|
||
|
if (wxFsUtils.checkFsValid()) return null;
|
||
|
wxDownloader.immediateHandleItemCount += 1;
|
||
|
|
||
|
handleItem(item, immediateHandleItemCompleteCallback);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
function seek (inPackage) {
|
||
|
if (inPackage) {
|
||
|
wxDownloader.immediateHandleItemCount += 1;
|
||
|
handleItem(item, immediateHandleItemCompleteCallback);
|
||
|
}
|
||
|
else {
|
||
|
wxDownloader.immediateReadFromLocalCount += 1;
|
||
|
readFromLocal(item, immediateReadFromLocalCompleteCallback);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (item.url in packageFiles) {
|
||
|
seek(packageFiles[item.url]);
|
||
|
}
|
||
|
else {
|
||
|
wxFsUtils.exists(item.url, function (existance) {
|
||
|
packageFiles[item.url] = existance;
|
||
|
seek(existance);
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
WXDownloader.prototype.cleanOldAssets = function () {
|
||
|
cc.warn('wxDownloader.cleanOldAssets has been deprecated, please use wxDownloader.cleanOldCaches instead!');
|
||
|
return this.cleanOldCaches();
|
||
|
};
|
||
|
|
||
|
WXDownloader.prototype.cleanOldCaches = function () {
|
||
|
this.cleanAllCaches(_newAssets, function (err) {
|
||
|
if (err) {
|
||
|
cc.warn(err);
|
||
|
}
|
||
|
else {
|
||
|
for (var path in _newAssets) {
|
||
|
cc.log('reserve local file: ' + path);
|
||
|
}
|
||
|
cc.log('Clean old Assets successfully!');
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
|
||
|
function handleItem (item, callback) {
|
||
|
if (item.type && !shouldReadFile(item.type)) {
|
||
|
callback(null, null);
|
||
|
}
|
||
|
else {
|
||
|
readFile(item, callback);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
WXDownloader.prototype.getCacheName = function (filePath) {
|
||
|
var cacheUrlReg = /\//g;
|
||
|
return filePath.replace(cacheUrlReg, '-');
|
||
|
};
|
||
|
|
||
|
WXDownloader.prototype.getCachedFileList = function () {
|
||
|
return cachedFiles;
|
||
|
};
|
||
|
|
||
|
WXDownloader.prototype.cleanCache = function (filePath) {
|
||
|
if (filePath in cachedFiles) {
|
||
|
var self = this;
|
||
|
delete cachedFiles[filePath];
|
||
|
wxFsUtils.writeFileSync(this.cacheDir + '/' + this.cachedFileName, JSON.stringify(cachedFiles), 'utf8');
|
||
|
wxFsUtils.deleteFile(this.cacheDir + '/' + filePath, function (err) {
|
||
|
if (!err) self.outOfStorage = false;
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
WXDownloader.prototype.cleanAllAssets = function () {
|
||
|
cc.warn('wxDownloader.cleanAllAssets has been deprecated, please use cleanAllCaches instead!');
|
||
|
this.cleanAllCaches(null, function (err) {
|
||
|
if (err) cc.error(err.message);
|
||
|
});
|
||
|
};
|
||
|
|
||
|
WXDownloader.prototype.cleanAllCaches = function (exclude, callback) {
|
||
|
exclude = exclude || {};
|
||
|
var self = this;
|
||
|
var result = wxFsUtils.readDir(self.cacheDir, function (err, list) {
|
||
|
if (err) {
|
||
|
callback && callback(err);
|
||
|
return;
|
||
|
}
|
||
|
var toDelete = [];
|
||
|
for (var i = 0, l = list.length; i < l; i ++) {
|
||
|
var path = list[i];
|
||
|
if (path === self.cachedFileName) continue;
|
||
|
if (path in exclude) continue;
|
||
|
if (path in cacheQueue) {
|
||
|
delete cacheQueue[path];
|
||
|
continue;
|
||
|
}
|
||
|
delete cachedFiles[path];
|
||
|
toDelete.push(path);
|
||
|
}
|
||
|
wxFsUtils.writeFileSync(self.cacheDir + '/' + self.cachedFileName, JSON.stringify(cachedFiles), 'utf8');
|
||
|
var count = 0;
|
||
|
for (var i = 0, l = toDelete.length; i < l; i ++) {
|
||
|
wxFsUtils.deleteFile(self.cacheDir + '/' + toDelete[i], function (err) {
|
||
|
if (!err) self.outOfStorage = false;
|
||
|
count++;
|
||
|
if (count === l) callback && callback(null);
|
||
|
})
|
||
|
}
|
||
|
});
|
||
|
if (result) callback(result);
|
||
|
};
|
||
|
|
||
|
var wxDownloader = window.wxDownloader = new WXDownloader();
|
||
|
|
||
|
function registerFailHandler (item, cachePath) {
|
||
|
var queue = cc.LoadingItems.getQueue(item);
|
||
|
queue.addListener(item.id, function (item) {
|
||
|
if (item.error) {
|
||
|
if (item.url in cacheQueue) {
|
||
|
delete cacheQueue[item.url];
|
||
|
}
|
||
|
else {
|
||
|
wxDownloader.cleanCache(cachePath);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function readFile (item, callback) {
|
||
|
var url = item.url;
|
||
|
var func = wxFsUtils.readText;
|
||
|
if (getFileType(item.type) === FileType.BIN) func = wxFsUtils.readArrayBuffer;
|
||
|
var result = func(url, function (err, data) {
|
||
|
if (err) {
|
||
|
callback(err);
|
||
|
return;
|
||
|
}
|
||
|
if (data) {
|
||
|
item.states[cc.loader.downloader.id] = cc.Pipeline.ItemState.COMPLETE;
|
||
|
callback(null, data);
|
||
|
}
|
||
|
else {
|
||
|
callback(new Error("Empty file: " + url));
|
||
|
}
|
||
|
});
|
||
|
if (result) callback(result);
|
||
|
}
|
||
|
|
||
|
function readFromLocal (item, callback) {
|
||
|
var result = wxFsUtils.checkFsValid();
|
||
|
if (result) {
|
||
|
callback(result);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var cachedPath = wxDownloader.getCacheName(item.url);
|
||
|
var localPath = wxDownloader.cacheDir + '/' + cachedPath;
|
||
|
|
||
|
if (cachedPath in cachedFiles) {
|
||
|
// cache new asset
|
||
|
_newAssets[cachedPath] = true;
|
||
|
item.url = localPath;
|
||
|
registerFailHandler(item, cachedPath);
|
||
|
handleItem(item, callback);
|
||
|
}
|
||
|
else {
|
||
|
if (!wxDownloader.REMOTE_SERVER_ROOT) {
|
||
|
callback(null, null);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
downloadRemoteFile(item, callback);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function cacheFile (url, isCopy, cachePath) {
|
||
|
cacheQueue[url] = { isCopy, cachePath };
|
||
|
|
||
|
if (!checkNextPeriod) {
|
||
|
checkNextPeriod = true;
|
||
|
function cache () {
|
||
|
checkNextPeriod = false;
|
||
|
for (var srcUrl in cacheQueue) {
|
||
|
if (!wxDownloader.outOfStorage) {
|
||
|
var item = cacheQueue[srcUrl]
|
||
|
var localPath = wxDownloader.cacheDir + '/' + item.cachePath;
|
||
|
var func = wxFsUtils.copyFile;
|
||
|
if (!item.isCopy) func = wxFsUtils.downloadFile;
|
||
|
func(srcUrl, localPath, function (err) {
|
||
|
if (err) {
|
||
|
errTest.test(err.message) && (wxDownloader.outOfStorage = true);
|
||
|
return;
|
||
|
}
|
||
|
cachedFiles[item.cachePath] = 1;
|
||
|
writeCacheFile();
|
||
|
});
|
||
|
delete cacheQueue[srcUrl];
|
||
|
}
|
||
|
if (!cc.js.isEmptyObject(cacheQueue) && !checkNextPeriod) {
|
||
|
checkNextPeriod = true;
|
||
|
setTimeout(cache, wxDownloader.cachePeriod);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
};
|
||
|
setTimeout(cache, wxDownloader.cachePeriod);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function downloadRemoteFile (item, callback) {
|
||
|
// Download from remote server
|
||
|
var relatUrl = item.url;
|
||
|
|
||
|
// filter protocol url (E.g: https:// or http:// or ftp://)
|
||
|
if (REGEX.test(relatUrl)) {
|
||
|
callback(null, null);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var remoteUrl = wxDownloader.REMOTE_SERVER_ROOT + '/' + relatUrl;
|
||
|
item.url = remoteUrl;
|
||
|
var cachePath = wxDownloader.getCacheName(relatUrl);
|
||
|
if (cc.sys.os === cc.sys.OS_ANDROID && item.type && getFileType(item.type) === FileType.IMAGE) {
|
||
|
if (wxDownloader.cacheAsset) {
|
||
|
cacheFile(remoteUrl, false, cachePath);
|
||
|
registerFailHandler(item, cachePath);
|
||
|
}
|
||
|
callback(null, null);
|
||
|
}
|
||
|
else {
|
||
|
wxFsUtils.downloadFile(remoteUrl, undefined, function (err, path) {
|
||
|
if (err) {
|
||
|
callback(err, null);
|
||
|
return;
|
||
|
}
|
||
|
item.url = path;
|
||
|
if (wxDownloader.cacheAsset) {
|
||
|
cacheFile(path, true, cachePath);
|
||
|
registerFailHandler(item, cachePath);
|
||
|
}
|
||
|
handleItem(item, callback);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
function writeCacheFile () {
|
||
|
function write () {
|
||
|
writeCacheFileList = null;
|
||
|
wxFsUtils.writeFile(wxDownloader.cacheDir + '/' + wxDownloader.cachedFileName, JSON.stringify(cachedFiles), 'utf8');
|
||
|
}
|
||
|
!writeCacheFileList && (writeCacheFileList = setTimeout(write, wxDownloader.writeFilePeriod));
|
||
|
}
|
||
|
|
||
|
function shouldReadFile (type) {
|
||
|
return getFileType(type) >= FileType.LOADABLE_MIN;
|
||
|
}
|
||
|
|
||
|
function getFileType (type) {
|
||
|
return (map[type] || FileType.DEFAULT);
|
||
|
}
|
||
|
|
||
|
var FileType = {
|
||
|
'IMAGE': 1,
|
||
|
'FONT': 2,
|
||
|
'AUDIO': 3,
|
||
|
'SCRIPT': 4,
|
||
|
'TEXT': 5,
|
||
|
'BIN': 6,
|
||
|
'DEFAULT': 7,
|
||
|
'LOADABLE_MIN': 5
|
||
|
};
|
||
|
|
||
|
var map = {
|
||
|
// JS
|
||
|
'js' : FileType.SCRIPT,
|
||
|
|
||
|
// Images
|
||
|
'png' : FileType.IMAGE,
|
||
|
'jpg' : FileType.IMAGE,
|
||
|
'bmp' : FileType.IMAGE,
|
||
|
'jpeg' : FileType.IMAGE,
|
||
|
'gif' : FileType.IMAGE,
|
||
|
'ico' : FileType.IMAGE,
|
||
|
'tiff' : FileType.IMAGE,
|
||
|
'webp' : FileType.IMAGE,
|
||
|
'image' : FileType.IMAGE,
|
||
|
|
||
|
// Audio
|
||
|
'mp3' : FileType.AUDIO,
|
||
|
'ogg' : FileType.AUDIO,
|
||
|
'wav' : FileType.AUDIO,
|
||
|
'm4a' : FileType.AUDIO,
|
||
|
|
||
|
// Txt
|
||
|
'txt' : FileType.TEXT,
|
||
|
'xml' : FileType.TEXT,
|
||
|
'vsh' : FileType.TEXT,
|
||
|
'fsh' : FileType.TEXT,
|
||
|
'atlas' : FileType.TEXT,
|
||
|
|
||
|
'tmx' : FileType.TEXT,
|
||
|
'tsx' : FileType.TEXT,
|
||
|
|
||
|
'json' : FileType.TEXT,
|
||
|
'ExportJson' : FileType.TEXT,
|
||
|
'plist' : FileType.TEXT,
|
||
|
|
||
|
'fnt' : FileType.TEXT,
|
||
|
|
||
|
// Font
|
||
|
'font' : FileType.FONT,
|
||
|
'eot' : FileType.FONT,
|
||
|
'ttf' : FileType.FONT,
|
||
|
'woff' : FileType.FONT,
|
||
|
'svg' : FileType.FONT,
|
||
|
'ttc' : FileType.FONT,
|
||
|
|
||
|
// Binary
|
||
|
'binary' : FileType.BIN,
|
||
|
'dbbin' : FileType.BIN,
|
||
|
'bin': FileType.BIN,
|
||
|
'pvr': FileType.BIN,
|
||
|
'pkm': FileType.BIN
|
||
|
};
|
||
|
// function downloadRemoteTextFile (item, callback) {
|
||
|
// // Download from remote server
|
||
|
// var relatUrl = item.url;
|
||
|
// var remoteUrl = wxDownloader.REMOTE_SERVER_ROOT + '/' + relatUrl;
|
||
|
// item.url = remoteUrl;
|
||
|
// wx.request({
|
||
|
// url: remoteUrl,
|
||
|
// success: function(res) {
|
||
|
// if (res.data) {
|
||
|
// if (res.statusCode === 200 || res.statusCode === 0) {
|
||
|
// var data = res.data;
|
||
|
// item.states[cc.loader.downloader.ID] = cc.Pipeline.ItemState.COMPLETE;
|
||
|
// if (data) {
|
||
|
// if (typeof data !== 'string' && !(data instanceof ArrayBuffer)) {
|
||
|
// // Should we check if item.type is json ? If not, loader behavior could be different
|
||
|
// item.states[cc.loader.loader.ID] = cc.Pipeline.ItemState.COMPLETE;
|
||
|
// callback(null, data);
|
||
|
// data = JSON.stringify(data);
|
||
|
// }
|
||
|
// else {
|
||
|
// callback(null, data);
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// // Save to local path
|
||
|
// var localPath = wx.env.USER_DATA_PATH + '/' + relatUrl;
|
||
|
// // Should recursively mkdir first
|
||
|
// fs.writeFile({
|
||
|
// filePath: localPath,
|
||
|
// data: data,
|
||
|
// encoding: 'utf8',
|
||
|
// success: function (res) {
|
||
|
// cc.log('Write file to ' + res.savedFilePath + ' successfully!');
|
||
|
// },
|
||
|
// fail: function (res) {
|
||
|
// // undone implementation
|
||
|
// }
|
||
|
// });
|
||
|
// } else {
|
||
|
// cc.warn("Download text file failed: " + remoteUrl);
|
||
|
// callback({
|
||
|
// status:0,
|
||
|
// errorMessage: res && res.errMsg ? res.errMsg : "Download text file failed: " + remoteUrl
|
||
|
// });
|
||
|
// }
|
||
|
// }
|
||
|
// },
|
||
|
// fail: function (res) {
|
||
|
// // Continue to try download with downloader, most probably will also fail
|
||
|
// callback(null, null);
|
||
|
// }
|
||
|
// });
|
||
|
// }
|