add hotfix
This commit is contained in:
parent
ff550d5d6a
commit
b7504ff7be
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,7 +7,6 @@ temp/
|
|||||||
local/
|
local/
|
||||||
build/
|
build/
|
||||||
android/
|
android/
|
||||||
hotupdate/
|
|
||||||
keystore/
|
keystore/
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
|
373
assets/scripts/hotUpdate/HotUpdate.ts
Normal file
373
assets/scripts/hotUpdate/HotUpdate.ts
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
import { isTest } from '../Config';
|
||||||
|
import { GIFCache } from '../gif/GIF';
|
||||||
|
import { getSwitch } from '../jcfw/service/jclogin';
|
||||||
|
|
||||||
|
const { ccclass, property } = cc._decorator;
|
||||||
|
|
||||||
|
const UpdateMsg = [
|
||||||
|
'No use hot update!',
|
||||||
|
'No local manifest file found, hot update skipped.',
|
||||||
|
'Fail to download manifest file, hot update skipped.',
|
||||||
|
'Fail to parse manifest file, hot update skipped.',
|
||||||
|
'Asset update error.',
|
||||||
|
'Compression package decoding failed.',
|
||||||
|
'Already up to date with the latest remote version.',
|
||||||
|
'New version found, please try to update.',
|
||||||
|
'Update finished.',
|
||||||
|
'Update failed.',
|
||||||
|
];
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
NONE,
|
||||||
|
ERROR_NO_LOCAL_MANIFEST,
|
||||||
|
ERROR_DOWNLOAD_MANIFEST,
|
||||||
|
ERROR_PARSE_MANIFEST,
|
||||||
|
ERROR_UPDATING,
|
||||||
|
ERROR_DECOMPRESS,
|
||||||
|
ALREADY_UP_TO_DATE,
|
||||||
|
NEW_VERSION_FOUND,
|
||||||
|
UPDATE_FINISHED,
|
||||||
|
UPDATE_FAILED,
|
||||||
|
UPDATE_PROGRESSION,
|
||||||
|
}
|
||||||
|
|
||||||
|
@ccclass
|
||||||
|
export default class HotUpdate extends cc.Component {
|
||||||
|
static State = State;
|
||||||
|
|
||||||
|
@property(cc.Node) loginNode: cc.Node = null;
|
||||||
|
|
||||||
|
@property({
|
||||||
|
type: cc.Asset,
|
||||||
|
tooltip: '当前版本资源记录的manifest文件',
|
||||||
|
})
|
||||||
|
manifestUrl: cc.Asset = null;
|
||||||
|
|
||||||
|
@property(cc.ProgressBar) bar: cc.ProgressBar = null;
|
||||||
|
|
||||||
|
@property(cc.Label) log: cc.Label = null;
|
||||||
|
|
||||||
|
@property(cc.Label) currentProcess: cc.Label = null; //当前进度
|
||||||
|
|
||||||
|
// @property(cc.Node) uiloading: cc.Node = null; // loading小人
|
||||||
|
|
||||||
|
state = State.NONE;
|
||||||
|
|
||||||
|
private _updating = false;
|
||||||
|
private _canRetry = false;
|
||||||
|
|
||||||
|
private _am = null;
|
||||||
|
|
||||||
|
private _failCount = 0;
|
||||||
|
private _totalFilesCount = 0;
|
||||||
|
private _totalBytesCount = 0;
|
||||||
|
|
||||||
|
// use this for initialization
|
||||||
|
init() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// Hot update is only available in Native build
|
||||||
|
if (!cc.sys.isNative) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let storagePath =
|
||||||
|
(jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') +
|
||||||
|
'blackjack-remote-asset';
|
||||||
|
console.log('Storage path for remote asset : ' + storagePath);
|
||||||
|
|
||||||
|
// Setup your own version compare handler, versionA and B is versions in string
|
||||||
|
// if the return value greater than 0, versionA is greater than B,
|
||||||
|
// if the return value equals 0, versionA equals to B,
|
||||||
|
// if the return value smaller than 0, versionA is smaller than B.
|
||||||
|
let versionCompareHandle = (versionA, versionB) => {
|
||||||
|
console.log(
|
||||||
|
'JS Custom Version Compare: version A is ' +
|
||||||
|
versionA +
|
||||||
|
', version B is ' +
|
||||||
|
versionB
|
||||||
|
);
|
||||||
|
let vA = versionA.split('.');
|
||||||
|
let vB = versionB.split('.');
|
||||||
|
for (let i = 0; i < vA.length; ++i) {
|
||||||
|
let a = parseInt(vA[i]);
|
||||||
|
let b = parseInt(vB[i] || 0);
|
||||||
|
if (a === b) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vB.length > vA.length) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Init with empty manifest url for testing custom manifest
|
||||||
|
this._am = new jsb.AssetsManager(
|
||||||
|
'',
|
||||||
|
storagePath,
|
||||||
|
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, asset) {
|
||||||
|
// When asset is compressed, we don't need to check its md5, because zip file have been deleted.
|
||||||
|
let compressed = asset.compressed;
|
||||||
|
// Retrieve the correct md5 value.
|
||||||
|
let expectedMD5 = asset.md5;
|
||||||
|
// asset.path is relative path and path is absolute.
|
||||||
|
let relativePath = asset.path;
|
||||||
|
// The size of asset file, but this value could be absent.
|
||||||
|
let size = asset.size;
|
||||||
|
if (compressed) {
|
||||||
|
console.log('Verification passed : ' + relativePath);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
'Verification passed : ' +
|
||||||
|
relativePath +
|
||||||
|
' (' +
|
||||||
|
expectedMD5 +
|
||||||
|
')'
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 'Hot update is ready, please check or directly update.';
|
||||||
|
|
||||||
|
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.
|
||||||
|
// Max concurrent tasks count have been limited to 2
|
||||||
|
this._am.setMaxConcurrentTask(2);
|
||||||
|
}
|
||||||
|
// this.checkUpdate();
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测线上与服务器的资源版本
|
||||||
|
*/
|
||||||
|
async checkUpdate() {
|
||||||
|
this.log.string = 'checking...';
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!cc.sys.isNative) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this._updating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
|
||||||
|
// Resolve md5 url
|
||||||
|
let url = this.manifestUrl.nativeUrl;
|
||||||
|
if (cc.loader.md5Pipe) {
|
||||||
|
url = cc.loader.md5Pipe.transformURL(url);
|
||||||
|
}
|
||||||
|
this._am.loadLocalManifest(url);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!this._am.getLocalManifest() ||
|
||||||
|
!this._am.getLocalManifest().isLoaded()
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._am.setEventCallback(this.checkUpdateCallback.bind(this));
|
||||||
|
|
||||||
|
this._am.checkUpdate();
|
||||||
|
this._updating = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 将服务器资源更新到本地
|
||||||
|
*/
|
||||||
|
hotUpdate() {
|
||||||
|
// this.uiloading.active = true;
|
||||||
|
this.log.string = 'updating...';
|
||||||
|
if (!cc.sys.isNative) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this._am && !this._updating) {
|
||||||
|
this._am.setEventCallback(this.hotUpdateCallback.bind(this));
|
||||||
|
|
||||||
|
if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
|
||||||
|
// Resolve md5 url
|
||||||
|
let url = this.manifestUrl.nativeUrl;
|
||||||
|
if (cc.loader.md5Pipe) {
|
||||||
|
url = cc.loader.md5Pipe.transformURL(url);
|
||||||
|
}
|
||||||
|
this._am.loadLocalManifest(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._failCount = 0;
|
||||||
|
this._am.update();
|
||||||
|
this._updating = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新下载失败资源
|
||||||
|
*/
|
||||||
|
retry() {
|
||||||
|
if (!cc.sys.isNative) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this._updating && this._canRetry) {
|
||||||
|
this._canRetry = false;
|
||||||
|
// 'Retry failed Assets...';
|
||||||
|
this._am.downloadFailedAssets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkUpdateCallback(event) {
|
||||||
|
let logMsg = '';
|
||||||
|
switch (event.getEventCode()) {
|
||||||
|
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
|
||||||
|
this.state = State.ERROR_NO_LOCAL_MANIFEST;
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
|
||||||
|
this.state = State.ERROR_DOWNLOAD_MANIFEST;
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
|
||||||
|
this.state = State.ERROR_PARSE_MANIFEST;
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
|
||||||
|
this.state = State.ALREADY_UP_TO_DATE;
|
||||||
|
this.loginNode.active = true;
|
||||||
|
this.node.destroy();
|
||||||
|
// 进去游戏
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.NEW_VERSION_FOUND:
|
||||||
|
this.state = State.NEW_VERSION_FOUND;
|
||||||
|
// update
|
||||||
|
this.scheduleOnce(() => {
|
||||||
|
this.hotUpdate();
|
||||||
|
}, 1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._am.setEventCallback(null);
|
||||||
|
this._updating = false;
|
||||||
|
|
||||||
|
logMsg = UpdateMsg[this.state];
|
||||||
|
}
|
||||||
|
|
||||||
|
private hotUpdateCallback(event) {
|
||||||
|
let needRestart = false;
|
||||||
|
let failed = false;
|
||||||
|
|
||||||
|
switch (event.getEventCode()) {
|
||||||
|
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
|
||||||
|
this.state = State.ERROR_NO_LOCAL_MANIFEST;
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.UPDATE_PROGRESSION:
|
||||||
|
this.state = State.UPDATE_PROGRESSION;
|
||||||
|
|
||||||
|
this._totalFilesCount = event.getTotalFiles();
|
||||||
|
this._totalBytesCount = event.getTotalBytes();
|
||||||
|
|
||||||
|
this.bar.progress = event.getPercent();
|
||||||
|
|
||||||
|
// 显示进度 33%
|
||||||
|
this.currentProcess.string = `${Math.round(
|
||||||
|
event.getPercent() * 100
|
||||||
|
).toString()}%`;
|
||||||
|
//
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
|
||||||
|
this.state = State.ERROR_DOWNLOAD_MANIFEST;
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
|
||||||
|
this.state = State.ERROR_PARSE_MANIFEST;
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
|
||||||
|
this.state = State.ALREADY_UP_TO_DATE;
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.UPDATE_FINISHED:
|
||||||
|
this.state = State.UPDATE_FINISHED;
|
||||||
|
|
||||||
|
needRestart = true;
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.UPDATE_FAILED:
|
||||||
|
this.state = State.UPDATE_FAILED;
|
||||||
|
this._updating = false;
|
||||||
|
this._canRetry = true;
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.ERROR_UPDATING:
|
||||||
|
this.state = State.ERROR_UPDATING;
|
||||||
|
|
||||||
|
this._failCount++;
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.ERROR_DECOMPRESS:
|
||||||
|
this.state = State.ERROR_DECOMPRESS;
|
||||||
|
// logMsg = event.getMessage();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failed) {
|
||||||
|
this._am.setEventCallback(null);
|
||||||
|
this._updating = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needRestart) {
|
||||||
|
this._am.setEventCallback(null);
|
||||||
|
|
||||||
|
// Prepend the manifest's search path
|
||||||
|
let searchPaths = jsb.fileUtils.getSearchPaths();
|
||||||
|
let newPaths = this._am.getLocalManifest().getSearchPaths();
|
||||||
|
|
||||||
|
console.log('new search path:', 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);
|
||||||
|
|
||||||
|
// 更新完成 重新开始游戏
|
||||||
|
// this.uiloading.active = false;
|
||||||
|
console.log('hotupdate done!');
|
||||||
|
this.log.string = 'finish';
|
||||||
|
// this.scheduleOnce(() => {
|
||||||
|
// cc.game.restart();
|
||||||
|
// }, 1);
|
||||||
|
setTimeout(() => {
|
||||||
|
cc.game.restart();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onLoad(): void {
|
||||||
|
// GIFCache.getInstance();
|
||||||
|
// 原生且不在审核
|
||||||
|
getSwitch(() => {
|
||||||
|
if (cc.sys.isNative && !isTest) {
|
||||||
|
this.init().then(async () => {
|
||||||
|
await this.checkUpdate();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.loginNode.active = true;
|
||||||
|
this.node.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy() {
|
||||||
|
if (cc.sys.isNative && this._am) {
|
||||||
|
this._am.setEventCallback(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
assets/scripts/hotUpdate/HotUpdate.ts.meta
Normal file
9
assets/scripts/hotUpdate/HotUpdate.ts.meta
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.0.5",
|
||||||
|
"uuid": "6f84f1f8-b5d9-456d-9f11-d0cd76b15f21",
|
||||||
|
"isPlugin": false,
|
||||||
|
"loadPluginInWeb": true,
|
||||||
|
"loadPluginInNative": true,
|
||||||
|
"loadPluginInEditor": false,
|
||||||
|
"subMetas": {}
|
||||||
|
}
|
1
hotupdate/project.manifest
Normal file
1
hotupdate/project.manifest
Normal file
File diff suppressed because one or more lines are too long
1
hotupdate/version.manifest
Normal file
1
hotupdate/version.manifest
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"packageUrl":"https://test.kingsome.cn/game2006/test/slim/","remoteManifestUrl":"https://test.kingsome.cn/game2006/test/slim/project.manifest","remoteVersionUrl":"https://test.kingsome.cn/game2006/test/slim/version.manifest","version":"0.1.0"}
|
Loading…
x
Reference in New Issue
Block a user