JM_KA/assets/Script/HUD/HUDM.ts
2022-09-04 13:13:06 +08:00

449 lines
17 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { CoroutineV2 } from "../Engine/CatanEngine/CoroutineV2/CoroutineV2";
import LocalStorageData from "../Engine/Data/LocalStorageData";
import UpdatePanel from "../UpdatePanel";
import BusinessTypeSetting from "../_BusinessTypeSetting/BusinessTypeSetting";
import Enum_HUDM from "./Enum_HUDM";
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 private 屬性
private _updatePanel: UpdatePanel;
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_HUDM.NeedUpdateDataObj = null;
private _updateingData: Enum_HUDM.UpdateingDataObj = null;
private _updating: boolean = false;
private _canRetry: boolean = false;
private _isChangeUrl: boolean = false;
private _isNewBundle: boolean = false;
private _path: string = "Bundle";
private _customManifest: string = "";
private _storagePath: string = "";
//#endregion
//#region Lifecycle
constructor(...params: any[]) {
super();
if (!cc.sys.isNative) {
return;
}
HUDM._instance = this;
this._updatePanel = params[0];
this._isNewBundle = params[1];
// let packageUrl: string = `https://jianmiau.tk/Resources/App/JMKA/update/remote-assets/${BusinessTypeSetting.COMPILE_VERSION}`;
let packageUrl: string = BusinessTypeSetting.UsePatch;
this.CheckCompileVersion();
this.CheckChangePatchUrl();
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}`;
// 本地熱更目錄下已存在project.manifest則直接修改已存在的project.manifest
if (this._isChangeUrl) {
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);
let vA: string[] = versionA.split(".");
let vB: string[] = versionB.split(".");
// 長度不相等,則進行更新
if (vA.length !== vB.length) {
return -1;
}
for (let i: number = 0; i < vA.length; ++i) {
let a: number = +vA[i];
let b: number = +vB[i] || 0;
if (a === b) {
// 數字相同,則跳過
continue;
} else {
// 數字不同,則進行更新
return -1;
}
}
// 長度相等且數字相等,則不更新
return 0;
};
this._initAssetManaget();
}
private _initAssetManaget(): void {
let self: this = this;
//
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.
let compressed: any = asset.compressed;
// Retrieve the correct md5 value.
let expectedMD5: string = asset.md5;
// asset.path is relative path and path is absolute.
let relativePath: string = asset.path;
// The size of asset file, but this value could be absent.
let size: any = asset.size;
if (compressed) {
self._updatePanel.info.string = "Verification passed : " + relativePath;
// console.log("onLoad -> Verification passed : " + relativePath);
return true;
} else {
self._updatePanel.info.string = "Verification passed : " + relativePath + " (" + expectedMD5 + ")";
// console.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._updatePanel.info.string = "Max concurrent tasks count have been limited to 2";
// console.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);
jsb.fileUtils.writeStringToFile(afterString, path);
}
}
}
//#endregion
public *CheckUpdate(): IterableIterator<any> {
this._needUpdateData = null;
if (this._updating) {
this._updatePanel.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;
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._isNewBundle) && ((!this._needUpdateData.IsNeedUpdate && this._needUpdateData.TotalBytes !== "failed") || 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 {
let failed: boolean = false;
switch (event.getEventCode()) {
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
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:
console.error("checkCb -> Fail to download manifest file, HUD skipped.");
failed = true;
break;
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
console.log("checkCb -> Already up to date with the latest remote version.");
this._needUpdateData = new Enum_HUDM.NeedUpdateDataObj(false);
break;
case jsb.EventAssetsManager.NEW_VERSION_FOUND:
this._updatePanel.checkBtn.active = false;
this._updatePanel.fileProgress.progress = 0;
this._updatePanel.byteProgress.progress = 0;
this._updatePanel.info.string = "發現新版本,請嘗試更新。 " + this._bytesToSize(event.getTotalBytes());
console.log("checkCb -> New version found, please try to update." + event.getTotalBytes());
this._needUpdateData = new Enum_HUDM.NeedUpdateDataObj(true, this._bytesToSize(event.getTotalBytes()));
break;
default:
return;
}
this._am.setEventCallback(null);
this._checkListener = null;
this._updating = false;
if (failed) {
this._needUpdateData = new Enum_HUDM.NeedUpdateDataObj(false, "failed");
}
}
public *HUD(onFileProgress?: (finish: number, total: number, item: string) => void): IterableIterator<any> {
this._updatePanel.updateBtn.active = false;
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_HUDM.UpdateingDataObj(false);
}
}
private _updateCb(event: jsb.EventAssetsManager): void {
let self: this = this;
let needRestart: boolean = false;
let failed: boolean = false;
switch (event.getEventCode()) {
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
this._updatePanel.info.string = "No local manifest file found, HUD skipped.";
console.log("updateCb -> No local manifest file found, HUD skipped.");
failed = true;
break;
case jsb.EventAssetsManager.UPDATE_PROGRESSION:
this._updatePanel.byteProgress.progress = event.getPercent();
this._updatePanel.fileProgress.progress = event.getPercentByFile();
this._updatePanel.fileLabel.string = event.getDownloadedFiles() + " / " + event.getTotalFiles();
// this.tipsLabel.string = event.getDownloadedBytes() + " / " + event.getTotalBytes();
// console.log("updateCb -> " + event.getDownloadedBytes() + " / " + event.getTotalBytes());
// let msg: string = event.getMessage();
// if (msg) {
// this._updatePanel.info.string = 'Updated file: ' + msg;
// console.log("updateCb -> Updated file: " + msg);
// console.log("updateCb -> " + event.getPercent() / 100 + "% : " + msg);
// }
let 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._updatePanel.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._updatePanel.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();
console.log("updateCb -> 更新完成. " + event.getMessage());
this._updateingData = new Enum_HUDM.UpdateingDataObj(true);
needRestart = true;
break;
case jsb.EventAssetsManager.UPDATE_FAILED:
this._updatePanel.info.string = "Update failed. " + event.getMessage();
console.error("updateCb -> Update failed. " + event.getMessage());
this._updatePanel.retryBtn.active = true;
this._canRetry = true;
this._updateingData = new Enum_HUDM.UpdateingDataObj(false);
this._updating = false;
break;
case jsb.EventAssetsManager.ERROR_UPDATING:
this._updatePanel.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._updatePanel.info.string = event.getMessage();
console.error("updateCb -> " + event.getMessage());
break;
default:
break;
}
if (failed) {
this._am.setEventCallback(null);
this._updateListener = null;
this._updating = false;
}
if (needRestart) {
let AsyncFunction: () => IterableIterator<any> = function* (): IterableIterator<any> {
self._updatePanel.info.string = "更新完成 即將重啟";
// 卡個一幀不然都看不到100%的畫面
yield CoroutineV2.WaitTime(5 / cc.game.getFrameRate()).Start();
self._am.setEventCallback(null);
self._updateListener = null;
// Prepend the manifest's search path
let searchPaths: string[] = jsb.fileUtils.getSearchPaths();
let newPaths: [string] = self._am.getLocalManifest().getSearchPaths();
console.log(JSON.stringify(newPaths));
Array.prototype.unshift.apply(searchPaths, newPaths);
// This value will be retrieved and appended to the default search path during game startup,
// please refer to samples/js-tests/main.js for detailed usage.
// !!! Re-add the search paths in main.js is very important, otherwise, new scripts won't take effect.
cc.sys.localStorage.setItem("HotUpdateSearchPaths", JSON.stringify(searchPaths));
jsb.fileUtils.setSearchPaths(searchPaths);
cc.audioEngine.stopAll();
cc.game.restart();
};
CoroutineV2.Single(AsyncFunction()).Start();
}
}
public *RetryDownLoadFailedAssets(): IterableIterator<any> {
if (!this._updating && this._canRetry) {
this._updateingData = null;
this._updatePanel.retryBtn.active = false;
this._canRetry = false;
this._updatePanel.info.string = "Retry failed Assets...";
console.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_HUDM.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;
}
}
//#region 清除資料
/** 判斷更改編譯版號.清除BUNDLE記錄 */
public CheckCompileVersion(): void {
let oldCompileVersion: string = LocalStorageData.Instance.CompileVersion;
let newCompileVersion: string = BusinessTypeSetting.COMPILE_VERSION;
if (oldCompileVersion && oldCompileVersion !== newCompileVersion) {
// this.ClearBundleData();
console.warn(`change compile version. ${oldCompileVersion} -> ${newCompileVersion}`);
}
LocalStorageData.Instance.CompileVersion = BusinessTypeSetting.COMPILE_VERSION;
}
/** 判斷更改PATCH環境.清除BUNDLE記錄 */
public CheckChangePatchUrl(): void {
let oldBundleUrl: string = LocalStorageData.Instance.BundleUrl;
let newBundleUrl: string = BusinessTypeSetting.UsePatch;
if (oldBundleUrl && oldBundleUrl !== newBundleUrl) {
// this.ClearBundleData();
console.warn(`change patch url. ${oldBundleUrl} -> ${newBundleUrl}`);
this._isChangeUrl = true;
}
LocalStorageData.Instance.BundleUrl = BusinessTypeSetting.UsePatch;
}
//#endregion
}