Creator 3.x 热更新构建插件 | 热更新 Demo | 热更新 manifest

商店链接 ( 热更新 3.0 | Cocos Store)

功能介绍

  • 官方热更方案融合到 Creator 构建流程中.

  • 插件支持平台: Windows & MacOS.

  • 构建热更支持的平台: iOS & android & windows & mac

  • 构建后自动拷贝资源文件, 生成 version.manifest、project.manifest 文件.

  • 自动在构建后的 main.js 中注入添加搜索目录代码.

  • 支持命令行构建, 进而可支持 Jenkins 等 CI 工具.

测试 Demo

使用教程

  • 手动构建:

    • [菜单->项目->构建发布->选择支持的发布平台] 会看到如下图的设置界面.
    • 勾选生成热更数据选框.
    • 资源服务地址对应 CDN 的域名等.
    • 设置 [热更存储名], 热更文件存储目录, 会自动加入到 searchPath.

  • 命令行构建:

    • 配置格式和说明
    // 命令行构建, packages 对应配置, 对应选项不传则使用默认配置
    {
        "hot-update": {
            // 是否构建热更数据
            "hotUpdateEnable": true,
            // 热更服务地址, CDN 地址...
            "remoteAddress": "http://192.168.123.108/",
            // 热更文件存储目录, 会自动加入到 searchPath,
            "storagePath": "hotupdate_storage",
            // 版本号
            "version": "1.0.0",
            // jenkins buildNum | 或其他构建工具
            "buildNum": 1
        }
    }
    
    • 构建命令示例
    # 构建参数 packages=JSON.stringify(`{"hot-update": {"hotUpdateEnable": true, ...}}`)
    /Applications/CocosCreator/Creator/3.3.0/CocosCreator.app/Contents/MacOS/CocosCreator --project /Volumes/WorkSpace/tutorial-hot-update --build "platform=android;packages={\"hot-update\":{\"hotUpdateEnable\":true,\"remoteAddress\":\"http://192.168.123.108/\",\"version\":\"1.0.0\",\"buildNum\":3}}"
    
  • 注意事项:

    • 创建 AssetsManager 不传比较函数的话, 热更新版本号不要填写超过 3 位数字, 因为插件会把 buildNum 拼到最后一位, C++ 中的默认比较函数只取 版本号的前 4 个数字进行比较.

构建结果

  • 构建后在项目目录下自动生成热更新文件, 目录结构如下:

    .
    ├── hotupdate-assets                // 热更新文件根目录, 插件创建
    │   └── android                     // 对应构建平台, 上传 CDN 需从此目录开始上传
    │       ├── 1.0.0.1                 // 版本号 + buildNum
    │       │   ├── assets
    │       │   ├── jsb-adapter
    │       │   ├── project.manifest
    │       │   └── src
    │       ├── 1.0.0.3
    │       │   ├── assets
    │       │   ├── jsb-adapter
    │       │   ├── project.manifest
    │       │   └── src
    │       ├── project.manifest       // 每次都会被替换为最新一次构建结果
    │       └── version.manifest       // 每次都会被替换为最新一次构建结果
    ├── package.json
    └── tsconfig.json
    
  • project.manifest 构建结果示例

    {
        // 实际下载资源的 url 为: packageUrl + assets[key]
        // 例如 http://192.168.123.108/android/1.0.0.3/src/application.js
        "packageUrl": "http://192.168.123.108/android/1.0.0.3",
        "version": "1.0.0.3",
        "searchPaths": [
            "hotupdate_storage"
        ],
        "remoteManifestUrl": "http://192.168.123.108/android/project.manifest",
        "remoteVersionUrl": "http://192.168.123.108/android/version.manifest",
        "assets": {
            "src/application.js": {
                "size": 5738,
                "md5": "aaf093a660c5fa8e6559e264a0eaeed3"
            },
            "其余资源文件省略": {}
        }
    }
    
  • main.js 脚本注入热更相关代码,添加搜索路径, 主要逻辑参考官方热更 Demo

    // inject by extensions hot-update ---- start ----
    jsb.fileUtils.addSearchPath(jsb.fileUtils.getWritablePath() + "hotupdate_storage", true);
    var fileList = [];
    var storagePath = "hotupdate_storage";
    var tempPath = storagePath + "_temp/";
    var baseOffset = tempPath.length;
    
    if (jsb.fileUtils.isDirectoryExist(tempPath) && !jsb.fileUtils.isFileExist(tempPath + 'project.manifest.temp')) {
        jsb.fileUtils.listFilesRecursively(tempPath, fileList);
        fileList.forEach(srcPath => {
            var relativePath = srcPath.substr(baseOffset);
            var dstPath = storagePath + relativePath;
            if (srcPath[srcPath.length - 1] === "/") {
                jsb.fileUtils.createDirectory(dstPath)
            } else {
                if (jsb.fileUtils.isFileExist(dstPath)) {
                    jsb.fileUtils.removeFile(dstPath)
                }
                jsb.fileUtils.renameFile(srcPath, dstPath);
            }
        })
        jsb.fileUtils.removeDirectory(tempPath);
    }
    // inject by extensions hot-update ---- end ----
    
    // SystemJS support.
    window.self = window;
    require("src/system.bundle.js");
    
    const importMapJson = jsb.fileUtils.getStringFromFile("src/import-map.json");
    const importMap = JSON.parse(importMapJson);
    System.warmup({
        importMap,
        importMapUrl: 'src/import-map.json',
        defaultHandler: (urlNoSchema) => {
            require(urlNoSchema.startsWith('/') ? urlNoSchema.substr(1) : urlNoSchema);
        },
    });
    
    System.import('./src/application.js').then(({ createApplication }) => {
        return createApplication({
            loadJsListFile: (url) => require(url),
        });
    }).then((application) => {
        return application.import('cc').then((cc) => {
            require('jsb-adapter/jsb-engine.js');
            cc.macro.CLEANUP_IMAGE_CACHE = false;
        }).then(() => {
            return application.start({
                settings: window._CCSettings,
                findCanvas: () => {
                    var container = document.createElement('div');
                    var frame = document.documentElement;
                    var canvas = window.__canvas;
                    return { frame, canvas, container };
                },
            });
        });
    }).catch((err) => {
        console.error(err.toString());
    });
    
3赞

MArk!!!!!!!

2赞

使用上遇到问题,或者想要什么功能, 可以直接在这个帖子上回复

支持楼主。楼主万岁~

mark,我们解决方案改成zip热更包了,学习下

请问怎么解决远程资源缓存的问题喃
我这边替换了资源,但是构建出来的资源名还和以前一样,没有使用到新资源

理论上只要 MD5 变了,都会更新的,不知道你这边遇到的是不是 cdn 缓存的问题;可以详细描述下,看看时使用上的问题,还是插件有 Bug?

我这边是使用的3.2.1,我更新了一个mp3资源,热更上去发现版本更新了,但是资源没发生改变。
排查后发现是引擎构建的bug,没有勾选MD5缓存选项,资源不会更新,我这边手动替换了,再热更,发现使用的是上次的缓存文件(旧的mp3),删除了重新安装之后,从第一个版本热更过去才是正确的资源(新的mp3)
我这边暂时不确定是不是构建bug引起的

您说的MD5 变了,都会更新的,是指插件会去对比文件的MD5值,判断文件是否更新吗

差不多是这个意思, 另外一般热更新不需要勾选 MD5 缓存, 印象中 MD5 缓存是为了发布到浏览器版本及时能更新准备的

我是希望能尽量减小包体,所以做了远程包,资源都放远程。但是热更没办法选择MD5,资源都是同名的,替换了资源,如果有缓存,会使用缓存,没办法拉取新资源