搜索过论坛了,没有找到相关问题。
最近刚开始写TS脚本,TS版本的热更新应该怎么写?用论坛现有的JS版本试着翻译了下,但是里面有很多代码在TS环境报错,找不到相应的函数。很多东西也不知道怎么翻译,不知道有没有TS版本的热更新实例可以学习一下?
你这手伸的有点长,Js 改成ts,你看十几分钟官方文档中的ts写法教程就会了
真心没改出来,一堆报错,还有类型不知道该怎么改。现在ts的class可以直接用吗?还是必须得用装饰器的ccclass
你要挂载到节点上就必须用类装饰器。不挂载,那就随便用
我在10.3上写过
仅供参考,不是完整工程,当时测试是通过的。
const {ccclass, property} = cc._decorator;
@ccclass
export default class HotUpdate extends cc.Component {
@property(cc.ProgressBar)
private updateProgressBar: cc.ProgressBar = null;
@property(cc.Label)
private tipUpLabel: cc.Label = null;
@property(cc.Label)
private progressLabel: cc.Label = null;
@property(cc.Label)
private tipDownLabel: cc.Label = null;
@property({type: cc.Asset})
private manifestUrl: cc.Asset = null;
private _checkCallBack: (err: any, result?: any) => void = null;
private _storagePath: string = "";
private _assetsManager: jsb.AssetsManager = null;
private _updating: boolean = false;
private _canRetry: boolean = false;
private _checkListener: jsb.EventListenerAssetsManager = null;
private _updateListener: jsb.EventListenerAssetsManager = null;
public async checkUpdate(callback: (err: any, result?: any) => void) {
this._checkCallBack = callback;
if (this._updating) {
return;
}
this.tipUpLabel.string = "check update";
if (this._assetsManager.getState() === jsb.AssetsManager.State.UNINITED) {
let url = this.manifestUrl.nativeUrl;
console.info("check manifestUrl is: ", url);
const loader = cc.loader as any;
if (loader.md5Pipe) {
url = loader.md5Pipe.transformURL(url);
}
this._assetsManager.loadLocalManifest(url);
}
if (!this._assetsManager.getLocalManifest() || !this._assetsManager.getLocalManifest().isLoaded()) {
// TODO: 错误处理
console.error("Failed to load local manifest ...");
return;
}
this._checkListener = new jsb.EventListenerAssetsManager(this._assetsManager, this.innerCheckCallBack.bind(this));
cc.eventManager.addListener(this._checkListener, 1);
this._assetsManager.checkUpdate();
this._updating = true;
}
protected onLoad() {
if (!cc.sys.isNative) {
return;
}
this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : "/") + "bingo_remote_assets");
console.info("Storage path for remote assets : " + this._storagePath);
this._assetsManager = new jsb.AssetsManager("", this._storagePath, this.versionCompare.bind(this));
this._assetsManager.setVerifyCallback((path: string, asset: any) => {
// When asset is compressed, we don"t need to check its md5, because zip file have been deleted.
const compressed = asset.compressed;
// Retrieve the correct md5 value.
const expectedMD5 = asset.md5;
// asset.path is relative path and path is absolute.
const relativePath = asset.path;
// The size of asset file, but this value could be absent.
// const size = asset.size;
if (compressed) {
console.info("Verification passed : " + relativePath);
return true;
} else {
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._assetsManager.setMaxConcurrentTask(2);
}
}
protected start() {
//
}
protected onDestroy() {
if (this._updateListener) {
cc.eventManager.removeListener(this._updateListener);
this._updateListener = null;
}
if (this._checkListener) {
cc.eventManager.removeListener(this._checkListener);
this._checkListener = null;
}
}
private retryDownLoadFailedAssets() {
if (!this._updating && this._canRetry) {
// TODO: 弹框提示重试, 回调执行重试
console.info("hot update retry");
this._canRetry = false;
this._assetsManager.downloadFailedAssets();
}
}
/**
* 启动热更
*
* @private
* @memberof HotUpdate
*/
private hotUpdate() {
if (this._assetsManager && !this._updating) {
this._updateListener = new jsb.EventListenerAssetsManager(this._assetsManager, this.updateCallBack.bind(this));
cc.eventManager.addListener(this._updateListener, 1);
if (this._assetsManager.getState() === jsb.AssetsManager.State.UNINITED) {
let url = this.manifestUrl.nativeUrl;
console.info("update manifestUrl is: ", url);
const loader = cc.loader as any;
if (loader.md5Pipe) {
url = loader.md5Pipe.transformURL(url);
}
this._assetsManager.loadLocalManifest(url);
}
this._assetsManager.update();
this._updating = true;
this.tipUpLabel.string = "updating...";
}
}
/**
* 版本比较
*
* @private
* @param {string} localVersion
* @param {string} remoteVersion
* @returns
* @memberof HotUpdate
*/
private versionCompare(localVersion: string, remoteVersion: string): number {
console.info("localVersion is :", localVersion, "remoteVersion is :", remoteVersion);
const localVer: string[] = localVersion.split(".");
const remoteVer: string[] = remoteVersion.split(".");
for (let i = 0; i < localVer.length; i++) {
const lv = parseInt(localVer[i], 10);
const rv = parseInt(remoteVer[i] || "0", 10);
if (lv === rv) {
continue;
} else {
return lv - rv;
}
}
if (localVer.length > remoteVer.length) {
return -1;
} else {
return 0;
}
}
/**
* 检查热更新回调
*
* @private
* @param {cc.Event.EventTouch} event
* @returns
* @memberof HotUpdate
*/
private innerCheckCallBack(event: any): void {
let haveNewVersion = false;
console.info("check code: " + event.getEventCode());
switch (event.getEventCode()) {
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
console.info("No local manifest file found, hot update skipped.");
break;
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
console.info("Fail to download manifest file, hot update skipped.");
break;
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
console.info("Already up to date with the latest remote version.");
break;
case jsb.EventAssetsManager.NEW_VERSION_FOUND:
haveNewVersion = true;
console.info("New version found, begin to update.");
break;
default:
return;
}
cc.eventManager.removeListener(this._checkListener);
this._checkListener = null;
this._updating = false;
if (!haveNewVersion) {
if (this._checkCallBack) {
this._checkCallBack(null);
}
} else {
this.hotUpdate();
this.updateProgressBar.progress = 0;
}
}
/**
* 更新回调
*
* @private
* @param {*} event
* @memberof HotUpdate
*/
private updateCallBack(event: any): void {
let needRestart = false;
let failed = false;
console.info("update code: " + event.getEventCode());
switch (event.getEventCode()) {
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
console.info("No local manifest file found, hot update skipped.");
failed = true;
break;
case jsb.EventAssetsManager.UPDATE_PROGRESSION:
this.updateProgressBar.progress = event.getPercent(); // byteProgress
const downloadedMegabyte = event.getDownloadedBytes() / (1024 * 1024);
const totalMegabyte = event.getTotalBytes() / (1024 * 1024);
// 以 MB 为单位
this.progressLabel.string = ` ${downloadedMegabyte.toFixed(2)}MB / ${totalMegabyte.toFixed(2)}MB`;
break;
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
console.info("Fail to download manifest file, hot update skipped.");
failed = true;
break;
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
console.info("Already up to date with the latest remote version.");
failed = true;
break;
case jsb.EventAssetsManager.UPDATE_FINISHED:
console.info("Update finished. " + event.getMessage());
needRestart = true;
break;
case jsb.EventAssetsManager.UPDATE_FAILED:
console.info("Update failed. " + event.getMessage());
this._updating = false;
this._canRetry = true;
this.retryDownLoadFailedAssets();
break;
case jsb.EventAssetsManager.ERROR_UPDATING:
console.info("Asset update error: " + event.getAssetId() + ", " + event.getMessage());
break;
case jsb.EventAssetsManager.ERROR_DECOMPRESS:
console.info(event.getMessage());
break;
default:
break;
}
if (failed) {
cc.eventManager.removeListener(this._updateListener);
this._updateListener = null;
this._updating = false;
}
if (needRestart) {
cc.eventManager.removeListener(this._updateListener);
this._updateListener = null;
// Prepend the manifest"s search path
const searchPaths = jsb.fileUtils.getSearchPaths();
console.info("befare restart searchPaths:", JSON.stringify(searchPaths));
const newPaths = this._assetsManager.getLocalManifest().getSearchPaths();
console.info("befare restart newPaths:", 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.
// !!! 搜索路径记得 main.js 里也要写,否则不好用
cc.sys.localStorage.setItem("HOT_UPDATE_SEARCH_PATHS", JSON.stringify(searchPaths));
jsb.fileUtils.setSearchPaths(searchPaths);
cc.audioEngine.stopAll();
cc.game.restart();
}
}
// TODO: 二进制包更新后,清理热更新目录
// https://github.com/pandamicro/creator-docs/blob/v1.4/source/zh/advanced-topics/assets-manager.md
// jsb.fileUtils.removeDirectory(storagePath);
}
const customManifestStr = JSON.stringify({
“version”: “0.0.0.0”,
“packageUrl”: “http://192.168.2.164:12547”,
“remoteManifestUrl”: “http://192.168.2.164:12547/project.manifest”,
“remoteVersionUrl”: “http://192.168.2.164:12547/version.manifest”,
“assets”: {},
“searchPaths”: []
});
const VERSION_MANIFEST = ‘version.manifest’;
const PROJECT_MANIFEST = ‘project.manifest’;
export class HotUpdateMgr {
protected _storagePath: string;
protected _assetMgr : jsb.AssetsManager;
public manifestUrl: cc.Asset;
protected _localVersion: GVersion = null;
/** state/check/handler */
protected _checkListener;
protected _updateListener;
protected _updating: boolean = false;
protected _canRetry: boolean = false;
protected _failCount: number = 0;
protected _isNewApp: boolean = false;
protected _handler: HotUpdateInterface = null;
public prepare(manifesurl: cc.Asset, handler: HotUpdateInterface) {
this.manifestUrl = manifesurl;
this._handler = handler;
if(!cc.sys.isNative)
return false;
this._storagePath = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'tcg-remote-asset';
cc.log('Storage path for remote asset : ' + this._storagePath);
// Init asset manager;
let url = this.manifestUrl.nativeUrl;
if(cc.loader.md5Pipe) {
url = cc.loader.md5Pipe.transformURL(url);
}
this._assetMgr = new jsb.AssetsManager(url, this._storagePath, this.versionCompareCb.bind(this));
// 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._assetMgr.setVerifyCallback(this.verifyCb.bind(this));
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._assetMgr.setMaxConcurrentTask(2);
}
}
/**
*
* @param versionA 旧版本
* @param versionB 新版本
*/
protected versionCompareCb(versionA: string, versionB: string) {
cc.log("JS Custom Version Compare: version A is " + versionA + ', version B is ' + versionB);
var vA = versionA.split('.');
var vB = versionB.split('.');
for (var i = 0; i < vA.length; ++i) {
var a = parseInt(vA[i]);
var b = parseInt(vB[i] || '0');
if (a === b) {
continue;
}
else {
if(i == 0) { //'a.b.c.d', if diff a, app verison change, to get a new version of the app.
this._isNewApp = true;
return 0;
}
return a - b;
}
}
if (vB.length > vA.length) {
return -1;
}
else {
return 0;
}
}
/**
* 资源比对,是否下载成功
* @param path 资源路径
* @param asset 资源数据
*/
protected verifyCb(path: string, asset: jsb.ManifestAsset) {
console.log('path', path, 'setVerifyCallback' + JSON.stringify(asset));
var compressed = asset.compressed;
/**
* 计算md5
* @param {*} filePath
*/
let calMD5OfFile = function (filePath) {
return md5(jsb.fileUtils.getDataFromFile(filePath));
};
if (compressed) {
return true;
} else {
var resMD5 = calMD5OfFile(path);
console.log('resMD%=', resMD5, 'asset md5=', asset.md5);
if (asset.md5 == resMD5) {
return true;
}
jsb.fileUtils.removeFile(path);
return false;
}
}
/**
* 更新检测回调
* @param event
*/
protected checkCb(event: jsb.EventAssetsManager) {
let eventCode = event.getEventCode();
switch (eventCode)
{
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST: {
this._handler.up_check_err(eventCode, 'ERROR_NO_LOCAL_MANIFEST', "No local manifest file found, hot update skipped.");
break;
}
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:{
this._handler.up_check_err(eventCode, 'ERROR_DOWNLOAD_MANIFEST', "Fail to download manifest file, hot update skipped.");
break;
}
case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:{
this._handler.up_check_err(eventCode, 'ERROR_PARSE_MANIFEST', "Fail to parse manifest file, hot update skipped.");
break;
}
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE: {
this._isNewApp ? this._handler.new_app_version_handler(this.getRemoteStrVersion()) : this._handler.up_to_newest_version(this.getLocalVersion());
break;
}
case jsb.EventAssetsManager.NEW_VERSION_FOUND: {
this._handler.new_res_version_handler(this.getRemoteStrVersion());
break;
}
default: {
cc.log('Update check event code: ' + eventCode);
return;
}
}
this._assetMgr.setEventCallback(null);
this._checkListener = null;
this._updating = false;
};
protected updateCb(event: jsb.EventAssetsManager) {
var needRestart = false;
var failed = false;
let eventCode = event.getEventCode();
switch (eventCode)
{
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST: {
this._handler.up_check_err(eventCode, 'ERROR_NO_LOCAL_MANIFEST', "No local manifest file found, hot update skipped.");
failed = true;
break;
}
case jsb.EventAssetsManager.UPDATE_PROGRESSION: {
let progressData : ProgressionData = {
msg : event.getMessage(),
byteProgress : event.getPercent(),
fileProgress : event.getPercentByFile(),
sucFiles : event.getDownloadedFiles(),
totalFiles : event.getTotalFiles(),
sucBytes : event.getDownloadedBytes(),
totalBytes : event.getTotalBytes(),
}
this._handler.up_progression(progressData);
break;
}
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:{
this._handler.up_check_err(eventCode, 'ERROR_DOWNLOAD_MANIFEST', "Fail to download manifest file, hot update skipped.");
failed = true;
break;
}
case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:{
this._handler.up_check_err(eventCode, 'ERROR_PARSE_MANIFEST', "Fail to parse manifest file, hot update skipped.");
failed = true;
break;
}
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE: {
this._handler.up_check_err(eventCode, 'ALREADY_UP_TO_DATE', "the res version is newest! you need checking the logic.");
failed = true;
break;
}
case jsb.EventAssetsManager.UPDATE_FINISHED: {
this._handler.up_finished();
needRestart = true;
break;
}
case jsb.EventAssetsManager.UPDATE_FAILED: {
// it need retry to download failed files!
this._canRetry = true;
this._updating = false;
this._handler.up_failed_files(event.getMessage());
break;
}
case jsb.EventAssetsManager.ERROR_UPDATING: {
this._handler.up_file_error(event.getAssetId(), event.getMessage());
break;
}
case jsb.EventAssetsManager.ERROR_DECOMPRESS: {
this._handler.unable_decompress_file(event.getMessage());
break;
}
default:
break;
}
if (failed) {
this._assetMgr.setEventCallback(null);
this._updateListener = null;
this._updating = false;
}
if (needRestart) {
this._assetMgr.setEventCallback(null);
this._updateListener = null;
// Prepend the manifest's search path
var searchPaths = jsb.fileUtils.getSearchPaths();
var newPaths = this._assetMgr.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();
}
}
protected loadCustomManifest () {
if (this._assetMgr.getState() === jsb.AssetsManager.State.UNINITED) {
var manifest = new jsb.Manifest(customManifestStr, this._storagePath);
this._assetMgr.loadLocalManifest(manifest, this._storagePath);
// this.panel.info.string = 'Using custom manifest';
}
}
public retry() {
if (!this._updating && this._canRetry) {
// this.panel.retryBtn.active = false;
this._canRetry = false;
// this.panel.info.string = 'Retry failed Assets...';
this._assetMgr.downloadFailedAssets();
}
}
public checkUpdate() {
if (this._updating) {
// this.panel.info.string = 'Checking or updating ...';
return;
}
if (this._assetMgr.getState() === jsb.AssetsManager.State.UNINITED) {
// Resolve md5 url
var url = this.manifestUrl.nativeUrl;
if (cc.loader.md5Pipe) {
url = cc.loader.md5Pipe.transformURL(url);
}
this._assetMgr.loadLocalManifest(url);
}
if (!this._assetMgr.getLocalManifest() || !this._assetMgr.getLocalManifest().isLoaded()) {
// this.panel.info.string = 'Failed to load local manifest ...';
return;
}
this._assetMgr.setEventCallback(this.checkCb.bind(this));
this._assetMgr.checkUpdate();
this._updating = true;
}
public hotUpdate() {
if (this._assetMgr && !this._updating) {
this._assetMgr.setEventCallback(this.updateCb.bind(this));
if (this._assetMgr.getState() === jsb.AssetsManager.State.UNINITED) {
// Resolve md5 url
var url = this.manifestUrl.nativeUrl;
if (cc.loader.md5Pipe) {
url = cc.loader.md5Pipe.transformURL(url);
}
this._assetMgr.loadLocalManifest(url);
}
this._failCount = 0;
this._assetMgr.update();
// this.panel.updateBtn.active = false;
this._updating = true;
}
}
protected show() {
// if (this.updateUI.active === false) {
// this.updateUI.active = true;
// }
}
/** 获取本地版本号 */
public getLocalVersion() : GVersion {
if(this._assetMgr.getState() == jsb.AssetsManager.State.UNINITED) return null;
if(this._localVersion) {
return this._localVersion;
}
let version = this._assetMgr.getLocalManifest().getVersion();
let splites = version.split('.');
if(splites.length != 4) return null;
this._localVersion = this.genVersion(version);
return this._localVersion;
}
/** 获取远端版本号 */
public getRemoteStrVersion(): string {
let remote = this._assetMgr.getRemoteManifest();
if(!remote) return;
return remote.getVersion();
}
/** 将字符串版本号转为 GVersion */
public genVersion(version: string) : GVersion {
let splites = version.split('.');
if(splites.length != 4) return null;
return {
ver: version,
app: parseInt(splites[0]),
res: `${splites[1]}.${splites[2]}.${splites[3]}`
}
}
public destroy() {
if (this._updateListener) {
!!this._assetMgr && this._assetMgr.setEventCallback(null);
this._updateListener = null;
}
}
}文字缩进4格
interface GVersion {
ver: string;
app: number;
res: string;
}
interface ProgressionData {
msg : string;
byteProgress : number;
fileProgress : number;
sucFiles : number;
totalFiles : number;
sucBytes : number;
totalBytes : number;
}
interface HotUpdateInterface {
/** discovery new res verison */
new_res_version_handler: {(version: string) : void};
/** disconvery new app verison */
new_app_version_handler: {(version: string) : void};
/** update to newest app and res verison */
up_to_newest_version: {(version: GVersion) : void};
/** update the res progression call back */
up_progression: {(proData: ProgressionData): void};
/** update the res finished call back! */
up_finished: {(): void};
/** update failed call back */
up_failed_files: {(errMsg: string): void};
/** update file error! */
up_file_error: { (assetId: string, errMsg: string): void};
/** unable to decompress file */
unable_decompress_file: {(errMsg: string): void };
up_check_err: {(errCode: number, errMsg: string, detail: string)};
}
文字缩进4格
感谢楼上的各位大神,根据你们给的例子我应该可以把JS版本翻译过来了
兄弟 分享下呗~
2.4上面不成功,检查更新直接说ALREADY_UP_TO_DATE
你2.4的 jsb.EventAssetsManager和jsb.AssetsManager不会报错吗
那肯定需要你根据新的文档相关的api调整一下了