426 lines
19 KiB
TypeScript
426 lines
19 KiB
TypeScript
|
import BusinessTypeSetting from "../../_BusinessTypeSetting/BusinessTypeSetting";
|
|||
|
import Enum_Loading from "../HUDV2/Enum_Loading";
|
|||
|
import AssetBundleMamager from "./AssetBundleMamager";
|
|||
|
|
|||
|
const { ccclass, property } = cc._decorator;
|
|||
|
|
|||
|
/** HUDManager */
|
|||
|
@ccclass
|
|||
|
export default class HUDM extends cc.Component {
|
|||
|
|
|||
|
//#region static 屬性
|
|||
|
|
|||
|
private static _instance: HUDM = null;
|
|||
|
public static get Instance(): HUDM { return HUDM._instance; }
|
|||
|
|
|||
|
//#endregion
|
|||
|
|
|||
|
//#region static 屬性
|
|||
|
|
|||
|
public BundleName: string = "";
|
|||
|
|
|||
|
//#endregion
|
|||
|
|
|||
|
//#region private 屬性
|
|||
|
|
|||
|
private _am: jsb.AssetsManager;
|
|||
|
private _onFileProgress: (finish: number, total: number, item: string) => void;
|
|||
|
private _updateListener: any;
|
|||
|
private _checkListener: any;
|
|||
|
private _versionCompareHandle: any = null;
|
|||
|
private _needUpdateData: Enum_Loading.NeedUpdateDataObj = null;
|
|||
|
private _updateingData: Enum_Loading.UpdateingDataObj = null;
|
|||
|
private _updating: boolean = false;
|
|||
|
private _canRetry: boolean = false;
|
|||
|
private _isChangeUrl: boolean = false;
|
|||
|
private _path: string = "Bundle";
|
|||
|
private _customManifest: string = "";
|
|||
|
private _storagePath: string = "";
|
|||
|
|
|||
|
//#endregion
|
|||
|
|
|||
|
//#region Lifecycle
|
|||
|
|
|||
|
constructor(...params: any[]) {
|
|||
|
super();
|
|||
|
|
|||
|
if (!cc.sys.isNative) {
|
|||
|
return;
|
|||
|
} else if (params.length === 0) {
|
|||
|
return;
|
|||
|
}
|
|||
|
HUDM._instance = this;
|
|||
|
|
|||
|
this.BundleName = params[0];
|
|||
|
// let packageUrl: string = params[1];
|
|||
|
// let BundleData: Enum_Loading.BundleDataObj = AssetBundleMamager.Instance.RemoteVerList[this.BundleName];
|
|||
|
// let packageUrl: string = BundleData.BundleUrl;
|
|||
|
let packageUrl: string = `${BusinessTypeSetting.UsePatch}${BusinessTypeSetting.FolderUrlBundle}${this.BundleName}`;
|
|||
|
|
|||
|
this._customManifest = JSON.stringify({
|
|||
|
"packageUrl": packageUrl,
|
|||
|
"remoteManifestUrl": `${packageUrl}/project.manifest`,
|
|||
|
"remoteVersionUrl": `${packageUrl}/version.json`,
|
|||
|
"version": "0.0.0",
|
|||
|
});
|
|||
|
|
|||
|
this._storagePath = `${(jsb.fileUtils ? jsb.fileUtils.getWritablePath() : "./")}${this._path}/${this.BundleName}`;
|
|||
|
|
|||
|
// 本地熱更目錄下已存在project.manifest,則直接修改已存在的project.manifest
|
|||
|
if (AssetBundleMamager.Instance.IsChangeBundleUrl) {
|
|||
|
if (jsb.fileUtils.isFileExist(this._storagePath + "/project.manifest")) {
|
|||
|
this._isChangeUrl = true;
|
|||
|
this._modifyAppLoadUrlForManifestFile(this._storagePath, packageUrl);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
this._versionCompareHandle = function (versionA: string, versionB: string): number {
|
|||
|
// console.log("Ver A " + versionA + "VerB " + versionB);
|
|||
|
var vA: string[] = versionA.split(".");
|
|||
|
var vB: string[] = versionB.split(".");
|
|||
|
|
|||
|
// 長度不相等,則進行更新
|
|||
|
if (vA.length !== vB.length) {
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
for (var i: number = 0; i < vA.length; ++i) {
|
|||
|
var a: number = +vA[i];
|
|||
|
var b: number = +vB[i] || 0;
|
|||
|
if (a === b) {
|
|||
|
// 數字相同,則跳過
|
|||
|
continue;
|
|||
|
} else {
|
|||
|
// 數字不同,則進行更新
|
|||
|
return -1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 長度相等且數字相等,則不更新
|
|||
|
return 0;
|
|||
|
};
|
|||
|
this._initAssetManaget();
|
|||
|
}
|
|||
|
private _initAssetManaget(): void {
|
|||
|
//
|
|||
|
this._am = new jsb.AssetsManager("", this._storagePath, this._versionCompareHandle);
|
|||
|
|
|||
|
// Setup the verification callback, but we don't have md5 check function yet, so only print some message
|
|||
|
// Return true if the verification passed, otherwise return false
|
|||
|
this._am.setVerifyCallback(function (path: any, asset: { compressed: any; md5: any; path: any; size: any; }): boolean {
|
|||
|
// When asset is compressed, we don't need to check its md5, because zip file have been deleted.
|
|||
|
var compressed: any = asset.compressed;
|
|||
|
// Retrieve the correct md5 value.
|
|||
|
var expectedMD5: string = asset.md5;
|
|||
|
// asset.path is relative path and path is absolute.
|
|||
|
var relativePath: string = asset.path;
|
|||
|
// The size of asset file, but this value could be absent.
|
|||
|
var size: any = asset.size;
|
|||
|
if (compressed) {
|
|||
|
// panel.info.string = "Verification passed : " + relativePath;
|
|||
|
// cc.log("onLoad -> Verification passed : " + relativePath);
|
|||
|
return true;
|
|||
|
} else {
|
|||
|
// panel.info.string = "Verification passed : " + relativePath + ' (' + expectedMD5 + ')';
|
|||
|
// cc.log("onLoad -> setVerifyCallbackVerification passed : " + relativePath + " (" + expectedMD5 + ")");
|
|||
|
return true;
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
if (cc.sys.os === cc.sys.OS_ANDROID) {
|
|||
|
// Some Android device may slow down the download process when concurrent tasks is too much.
|
|||
|
// The value may not be accurate, please do more test and find what's most suitable for your game.
|
|||
|
// this._am.setMaxConcurrentTask(10);
|
|||
|
this._am["setMaxConcurrentTask"](10);
|
|||
|
// this.panel.info.string = "Max concurrent tasks count have been limited to 2";
|
|||
|
// cc.log("onLoad -> Max concurrent tasks count have been limited to 10");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private _modifyAppLoadUrlForManifestFile(filePath: string, newBundleUrl: string): void {
|
|||
|
let allpath: string[] = [filePath, filePath + "_temp"];
|
|||
|
let manifestname: string[] = ["project.manifest", "project.manifest.temp"];
|
|||
|
for (var i: number = 0; i < allpath.length; ++i) {
|
|||
|
let path: string = `${allpath[i]}/${manifestname[i]}`;
|
|||
|
if (jsb.fileUtils.isFileExist(path)) {
|
|||
|
// console.log(`[HUD] modifyAppLoadUrlForManifestFile: 有下載的manifest文件,直接修改熱更地址`);
|
|||
|
// 修改project.manifest
|
|||
|
let projectManifest: string = jsb.fileUtils.getStringFromFile(path);
|
|||
|
let projectManifestObj: any = JSON.parse(projectManifest);
|
|||
|
projectManifestObj.packageUrl = newBundleUrl;
|
|||
|
projectManifestObj.remoteManifestUrl = newBundleUrl + "/project.manifest";
|
|||
|
projectManifestObj.remoteVersionUrl = newBundleUrl + "/version.json";
|
|||
|
let afterString: string = JSON.stringify(projectManifestObj);
|
|||
|
let isWrittenProject: boolean = jsb.fileUtils.writeStringToFile(afterString, path);
|
|||
|
// // 更新數據庫中的新請求地址,下次如果檢測到不一致就重新修改 manifest 文件
|
|||
|
// if (isWrittenProject) {
|
|||
|
// LocalStorageData.Instance.BundleUrl = BusinessTypeSetting.UsePatch;
|
|||
|
// }
|
|||
|
// console.log("[HUD] 修改是否成功,project.manifest:", isWrittenProject);
|
|||
|
// console.log("[HUD] 修改後文件:", projectManifestObj.packageUrl, projectManifestObj.remoteManifestUrl, projectManifestObj.remoteVersionUrl);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//#endregion
|
|||
|
|
|||
|
|
|||
|
public *CheckUpdate(): IterableIterator<any> {
|
|||
|
this._needUpdateData = null;
|
|||
|
if (this._updating) {
|
|||
|
// this.panel.info.string = 'Checking or updating ...';
|
|||
|
console.error("checkUpdate -> Checking or updating ...");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
|
|||
|
let manifest: jsb.Manifest = new jsb.Manifest(this._customManifest, this._storagePath);
|
|||
|
this._am.loadLocalManifest(manifest, this._storagePath);
|
|||
|
}
|
|||
|
if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
|
|||
|
// this.tipsLabel.string = "Failed to load local manifest ...";
|
|||
|
console.error("checkUpdate -> Failed to load local manifest ...");
|
|||
|
return;
|
|||
|
}
|
|||
|
this._am.setEventCallback(this.checkCb.bind(this));
|
|||
|
|
|||
|
this._am.checkUpdate();
|
|||
|
this._updating = true;
|
|||
|
|
|||
|
while (this._needUpdateData === null) {
|
|||
|
yield null;
|
|||
|
}
|
|||
|
|
|||
|
let newBundleUrl: string = `${BusinessTypeSetting.UsePatch}${BusinessTypeSetting.FolderUrlBundle}${this.BundleName}`;
|
|||
|
this._modifyAppLoadUrlForManifestFile(this._storagePath, newBundleUrl);
|
|||
|
this._initAssetManaget();
|
|||
|
|
|||
|
let manifest: jsb.Manifest = new jsb.Manifest(this._customManifest, this._storagePath);
|
|||
|
this._am.loadLocalManifest(manifest, this._storagePath);
|
|||
|
if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
|
|||
|
// this.tipsLabel.string = "Failed to load local manifest ...";
|
|||
|
console.error("checkUpdate -> Failed to load local manifest ...");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// 更新動態路徑後再跑一次
|
|||
|
this._am.setEventCallback(this.checkCb.bind(this));
|
|||
|
|
|||
|
this._needUpdateData = null;
|
|||
|
this._am.checkUpdate();
|
|||
|
this._updating = true;
|
|||
|
while (this._needUpdateData === null) {
|
|||
|
yield null;
|
|||
|
}
|
|||
|
if (this._isChangeUrl && (!this._needUpdateData.IsNeedUpdate || this._needUpdateData.TotalBytes === "0 B")) {
|
|||
|
if (jsb.fileUtils.isFileExist(this._storagePath)) {
|
|||
|
let isremoveDirectory: boolean = jsb.fileUtils.removeDirectory(this._storagePath);
|
|||
|
let isremoveDirectory_temp: boolean = jsb.fileUtils.removeDirectory(this._storagePath + "_temp");
|
|||
|
if (isremoveDirectory_temp) {
|
|||
|
console.log(`removeDirectory: ${this._storagePath}_temp`);
|
|||
|
}
|
|||
|
if (isremoveDirectory) {
|
|||
|
console.log(`removeDirectory: ${this._storagePath}`);
|
|||
|
this._needUpdateData = null;
|
|||
|
this._initAssetManaget();
|
|||
|
this._needUpdateData = yield* this.CheckUpdate();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return this._needUpdateData;
|
|||
|
}
|
|||
|
|
|||
|
private checkCb(event: jsb.EventAssetsManager): void {
|
|||
|
var failed: boolean = false;
|
|||
|
switch (event.getEventCode()) {
|
|||
|
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
|
|||
|
// this.tipsLabel.string = "No local manifest file found, HUD skipped.";
|
|||
|
console.error("checkCb -> No local manifest file found, HUD skipped.");
|
|||
|
failed = true;
|
|||
|
break;
|
|||
|
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
|
|||
|
case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
|
|||
|
// this.tipsLabel.string = "Fail to download manifest file, HUD skipped.";
|
|||
|
console.error("checkCb -> Fail to download manifest file, HUD skipped.");
|
|||
|
failed = true;
|
|||
|
break;
|
|||
|
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
|
|||
|
// this.tipsLabel.string = "Already up to date with the latest remote version.";
|
|||
|
// cc.log("checkCb -> Already up to date with the latest remote version.");
|
|||
|
this._needUpdateData = new Enum_Loading.NeedUpdateDataObj(false);
|
|||
|
break;
|
|||
|
case jsb.EventAssetsManager.NEW_VERSION_FOUND:
|
|||
|
// this.downloadLabel.node.active = true;
|
|||
|
// this.downloadLabel.string = "New version found, please try to update." + event.getTotalBytes();
|
|||
|
// this.panel.checkBtn.active = false;
|
|||
|
// this.panel.fileProgress.progress = 0;
|
|||
|
// this.panel.byteProgress.progress = 0;
|
|||
|
// cc.log("checkCb -> New version found, please try to update." + event.getTotalBytes());
|
|||
|
this._needUpdateData = new Enum_Loading.NeedUpdateDataObj(true, this._bytesToSize(event.getTotalBytes()));
|
|||
|
break;
|
|||
|
default:
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
this._am.setEventCallback(null);
|
|||
|
this._checkListener = null;
|
|||
|
this._updating = false;
|
|||
|
|
|||
|
if (failed) {
|
|||
|
//
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public *HUD(onFileProgress?: (finish: number, total: number, item: string) => void): IterableIterator<any> {
|
|||
|
this._updateingData = null;
|
|||
|
if (this._am && !this._updating) {
|
|||
|
this._am.setEventCallback(this._updateCb.bind(this));
|
|||
|
|
|||
|
if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
|
|||
|
let manifest: jsb.Manifest = new jsb.Manifest(this._customManifest, this._storagePath);
|
|||
|
this._am.loadLocalManifest(manifest, this._storagePath);
|
|||
|
}
|
|||
|
|
|||
|
this._onFileProgress = onFileProgress ? onFileProgress : null;
|
|||
|
this._am.update();
|
|||
|
this._updating = true;
|
|||
|
|
|||
|
while (this._updateingData === null) {
|
|||
|
yield null;
|
|||
|
}
|
|||
|
|
|||
|
return this._updateingData;
|
|||
|
} else {
|
|||
|
return new Enum_Loading.UpdateingDataObj(false);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private _updateCb(event: jsb.EventAssetsManager): void {
|
|||
|
var needRestart: boolean = false;
|
|||
|
var failed: boolean = false;
|
|||
|
switch (event.getEventCode()) {
|
|||
|
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
|
|||
|
// this.panel.info.string = 'No local manifest file found, HUD skipped.';
|
|||
|
cc.log("updateCb -> No local manifest file found, HUD skipped.");
|
|||
|
failed = true;
|
|||
|
break;
|
|||
|
case jsb.EventAssetsManager.UPDATE_PROGRESSION:
|
|||
|
// this.panel.byteProgress.progress = event.getPercent();
|
|||
|
// this.panel.fileProgress.progress = event.getPercentByFile();
|
|||
|
// this.panel.fileLabel.string = event.getDownloadedFiles() + ' / ' + event.getTotalFiles();
|
|||
|
// this.tipsLabel.string = event.getDownloadedBytes() + " / " + event.getTotalBytes();
|
|||
|
|
|||
|
// cc.log("updateCb -> " + event.getDownloadedBytes() + " / " + event.getTotalBytes());
|
|||
|
// var msg: string = event.getMessage();
|
|||
|
// if (msg) {
|
|||
|
// // this.panel.info.string = 'Updated file: ' + msg;
|
|||
|
// cc.log("updateCb -> Updated file: " + msg);
|
|||
|
// console.log("updateCb -> " + event.getPercent() / 100 + "% : " + msg);
|
|||
|
// }
|
|||
|
|
|||
|
var msg: string = event.getMessage();
|
|||
|
if (this._onFileProgress) {
|
|||
|
this._onFileProgress(event.getDownloadedBytes(), event.getTotalBytes(), msg ? msg : "");
|
|||
|
}
|
|||
|
break;
|
|||
|
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
|
|||
|
case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
|
|||
|
// this.panel.info.string = 'Fail to download manifest file, HUD skipped.';
|
|||
|
console.error("updateCb -> Fail to download manifest file, HUD skipped.");
|
|||
|
failed = true;
|
|||
|
break;
|
|||
|
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
|
|||
|
// this.panel.info.string = 'Already up to date with the latest remote version.';
|
|||
|
console.error("updateCb -> Already up to date with the latest remote version.");
|
|||
|
failed = true;
|
|||
|
break;
|
|||
|
case jsb.EventAssetsManager.UPDATE_FINISHED:
|
|||
|
// this.tipsLabel.string = "更新完成. " + event.getMessage();
|
|||
|
// cc.log("updateCb -> 更新完成. " + event.getMessage());
|
|||
|
this._updateingData = new Enum_Loading.UpdateingDataObj(true);
|
|||
|
needRestart = true;
|
|||
|
break;
|
|||
|
case jsb.EventAssetsManager.UPDATE_FAILED:
|
|||
|
// this.panel.info.string = 'Update failed. ' + event.getMessage();
|
|||
|
console.error("updateCb -> Update failed. " + event.getMessage());
|
|||
|
// this.panel.retryBtn.active = true;
|
|||
|
this._canRetry = true;
|
|||
|
this._updateingData = new Enum_Loading.UpdateingDataObj(false);
|
|||
|
this._updating = false;
|
|||
|
break;
|
|||
|
case jsb.EventAssetsManager.ERROR_UPDATING:
|
|||
|
// this.panel.info.string = 'Asset update error: ' + event.getAssetId() + ', ' + event.getMessage();
|
|||
|
console.error("updateCb -> Asset update error: " + event.getAssetId() + ", " + event.getMessage());
|
|||
|
break;
|
|||
|
case jsb.EventAssetsManager.ERROR_DECOMPRESS:
|
|||
|
// this.panel.info.string = event.getMessage();
|
|||
|
console.error("updateCb -> " + event.getMessage());
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (failed) {
|
|||
|
this._am.setEventCallback(null);
|
|||
|
this._updateListener = null;
|
|||
|
this._updating = false;
|
|||
|
}
|
|||
|
|
|||
|
// 測試先不restart 之後看情況
|
|||
|
// if (needRestart) {
|
|||
|
// this._am.setEventCallback(null);
|
|||
|
// this._updateListener = null;
|
|||
|
// // Prepend the manifest's search path
|
|||
|
// var searchPaths: string[] = jsb.fileUtils.getSearchPaths();
|
|||
|
|
|||
|
// // var newPaths = this._am.getLocalManifest().getSearchPaths();
|
|||
|
// // cc.log("newPath."+JSON.stringify(newPaths));
|
|||
|
// // Array.prototype.unshift.apply(searchPaths, newPaths);
|
|||
|
|
|||
|
// cc.sys.localStorage.setItem("HUDSearchPaths", JSON.stringify(searchPaths));
|
|||
|
|
|||
|
// jsb.fileUtils.setSearchPaths(searchPaths);
|
|||
|
|
|||
|
// cc.audioEngine.stopAll();
|
|||
|
// cc.game.restart();
|
|||
|
// }
|
|||
|
}
|
|||
|
|
|||
|
public *RetryDownLoadFailedAssets(): IterableIterator<any> {
|
|||
|
if (!this._updating && this._canRetry) {
|
|||
|
this._updateingData = null;
|
|||
|
// this.panel.retryBtn.active = false;
|
|||
|
this._canRetry = false;
|
|||
|
|
|||
|
// this.panel.info.string = 'Retry failed Assets...';
|
|||
|
// cc.log("retry -> Retry failed Assets...");
|
|||
|
this._am.downloadFailedAssets();
|
|||
|
|
|||
|
while (this._updateingData === null) {
|
|||
|
yield null;
|
|||
|
}
|
|||
|
|
|||
|
return this._updateingData;
|
|||
|
} else {
|
|||
|
console.error(`retry -> error updating: ${this._updating}, canRetry: ${this._canRetry}`);
|
|||
|
this._updateingData = new Enum_Loading.UpdateingDataObj(false);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private _bytesToSize(bytes: number): string {
|
|||
|
if (bytes === 0) {
|
|||
|
return "0 B";
|
|||
|
}
|
|||
|
let k: number = 1024;
|
|||
|
let sizes: string[] = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|||
|
let i: number = Math.floor(Math.log(bytes) / Math.log(k));
|
|||
|
return (bytes / Math.pow(k, i)).toPrecision(3) + " " + sizes[i];
|
|||
|
}
|
|||
|
|
|||
|
protected onDestroy(): void {
|
|||
|
if (this._updateListener) {
|
|||
|
this._am.setEventCallback(null);
|
|||
|
this._updateListener = null;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|