一个Unity的WebGL的资源更新逻辑

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/stalendp/article/details/82498622

直接贴代码了

// md5 function
!function (n) { "use strict"; function t(n, t) { var r = (65535 & n) + (65535 & t); return (n >> 16) + (t >> 16) + (r >> 16) << 16 | 65535 & r } function r(n, t) { return n << t | n >>> 32 - t } function e(n, e, o, u, c, f) { return t(r(t(t(e, n), t(u, f)), c), o) } function o(n, t, r, o, u, c, f) { return e(t & r | ~t & o, n, t, u, c, f) } function u(n, t, r, o, u, c, f) { return e(t & o | r & ~o, n, t, u, c, f) } function c(n, t, r, o, u, c, f) { return e(t ^ r ^ o, n, t, u, c, f) } function f(n, t, r, o, u, c, f) { return e(r ^ (t | ~o), n, t, u, c, f) } function i(n, r) { n[r >> 5] |= 128 << r % 32, n[14 + (r + 64 >>> 9 << 4)] = r; var e, i, a, d, h, l = 1732584193, g = -271733879, v = -1732584194, m = 271733878; for (e = 0; e < n.length; e += 16)i = l, a = g, d = v, h = m, g = f(g = f(g = f(g = f(g = c(g = c(g = c(g = c(g = u(g = u(g = u(g = u(g = o(g = o(g = o(g = o(g, v = o(v, m = o(m, l = o(l, g, v, m, n[e], 7, -680876936), g, v, n[e + 1], 12, -389564586), l, g, n[e + 2], 17, 606105819), m, l, n[e + 3], 22, -1044525330), v = o(v, m = o(m, l = o(l, g, v, m, n[e + 4], 7, -176418897), g, v, n[e + 5], 12, 1200080426), l, g, n[e + 6], 17, -1473231341), m, l, n[e + 7], 22, -45705983), v = o(v, m = o(m, l = o(l, g, v, m, n[e + 8], 7, 1770035416), g, v, n[e + 9], 12, -1958414417), l, g, n[e + 10], 17, -42063), m, l, n[e + 11], 22, -1990404162), v = o(v, m = o(m, l = o(l, g, v, m, n[e + 12], 7, 1804603682), g, v, n[e + 13], 12, -40341101), l, g, n[e + 14], 17, -1502002290), m, l, n[e + 15], 22, 1236535329), v = u(v, m = u(m, l = u(l, g, v, m, n[e + 1], 5, -165796510), g, v, n[e + 6], 9, -1069501632), l, g, n[e + 11], 14, 643717713), m, l, n[e], 20, -373897302), v = u(v, m = u(m, l = u(l, g, v, m, n[e + 5], 5, -701558691), g, v, n[e + 10], 9, 38016083), l, g, n[e + 15], 14, -660478335), m, l, n[e + 4], 20, -405537848), v = u(v, m = u(m, l = u(l, g, v, m, n[e + 9], 5, 568446438), g, v, n[e + 14], 9, -1019803690), l, g, n[e + 3], 14, -187363961), m, l, n[e + 8], 20, 1163531501), v = u(v, m = u(m, l = u(l, g, v, m, n[e + 13], 5, -1444681467), g, v, n[e + 2], 9, -51403784), l, g, n[e + 7], 14, 1735328473), m, l, n[e + 12], 20, -1926607734), v = c(v, m = c(m, l = c(l, g, v, m, n[e + 5], 4, -378558), g, v, n[e + 8], 11, -2022574463), l, g, n[e + 11], 16, 1839030562), m, l, n[e + 14], 23, -35309556), v = c(v, m = c(m, l = c(l, g, v, m, n[e + 1], 4, -1530992060), g, v, n[e + 4], 11, 1272893353), l, g, n[e + 7], 16, -155497632), m, l, n[e + 10], 23, -1094730640), v = c(v, m = c(m, l = c(l, g, v, m, n[e + 13], 4, 681279174), g, v, n[e], 11, -358537222), l, g, n[e + 3], 16, -722521979), m, l, n[e + 6], 23, 76029189), v = c(v, m = c(m, l = c(l, g, v, m, n[e + 9], 4, -640364487), g, v, n[e + 12], 11, -421815835), l, g, n[e + 15], 16, 530742520), m, l, n[e + 2], 23, -995338651), v = f(v, m = f(m, l = f(l, g, v, m, n[e], 6, -198630844), g, v, n[e + 7], 10, 1126891415), l, g, n[e + 14], 15, -1416354905), m, l, n[e + 5], 21, -57434055), v = f(v, m = f(m, l = f(l, g, v, m, n[e + 12], 6, 1700485571), g, v, n[e + 3], 10, -1894986606), l, g, n[e + 10], 15, -1051523), m, l, n[e + 1], 21, -2054922799), v = f(v, m = f(m, l = f(l, g, v, m, n[e + 8], 6, 1873313359), g, v, n[e + 15], 10, -30611744), l, g, n[e + 6], 15, -1560198380), m, l, n[e + 13], 21, 1309151649), v = f(v, m = f(m, l = f(l, g, v, m, n[e + 4], 6, -145523070), g, v, n[e + 11], 10, -1120210379), l, g, n[e + 2], 15, 718787259), m, l, n[e + 9], 21, -343485551), l = t(l, i), g = t(g, a), v = t(v, d), m = t(m, h); return [l, g, v, m] } function a(n) { var t, r = "", e = 32 * n.length; for (t = 0; t < e; t += 8)r += String.fromCharCode(n[t >> 5] >>> t % 32 & 255); return r } function d(n) { var t, r = []; for (r[(n.length >> 2) - 1] = void 0, t = 0; t < r.length; t += 1)r[t] = 0; var e = 8 * n.length; for (t = 0; t < e; t += 8)r[t >> 5] |= (255 & n.charCodeAt(t / 8)) << t % 32; return r } function h(n) { return a(i(d(n), 8 * n.length)) } function l(n, t) { var r, e, o = d(n), u = [], c = []; for (u[15] = c[15] = void 0, o.length > 16 && (o = i(o, 8 * n.length)), r = 0; r < 16; r += 1)u[r] = 909522486 ^ o[r], c[r] = 1549556828 ^ o[r]; return e = i(u.concat(d(t)), 512 + 8 * t.length), a(i(c.concat(e), 640)) } function g(n) { var t, r, e = ""; for (r = 0; r < n.length; r += 1)t = n.charCodeAt(r), e += "0123456789abcdef".charAt(t >>> 4 & 15) + "0123456789abcdef".charAt(15 & t); return e } function v(n) { return unescape(encodeURIComponent(n)) } function m(n) { return h(v(n)) } function p(n) { return g(m(n)) } function s(n, t) { return l(v(n), v(t)) } function C(n, t) { return g(s(n, t)) } function A(n, t, r) { return t ? r ? s(t, n) : C(t, n) : r ? m(n) : p(n) } "function" == typeof define && define.amd ? define(function () { return A }) : "object" == typeof module && module.exports ? module.exports = A : n.md5 = A }(this);

const GameConfig = (function () {
    let _baseUrl = window.location.href.replace(/(https?.*)\/[^\/]*/, "$1");
    let _bundleUrl = `${_baseUrl}/StreamingAssets/AssetsBundles`;
    let _DB_NAME = '/idbfs';
    let _DB_STORE = "FILE_DATA";
    let _BASE_DIR = `/idbfs/${md5(_baseUrl)}`;
    let _bundleDir = `${_BASE_DIR}/AssetsBundles`;

    return {
        get baseUrl() { return _baseUrl; },
        get bundleUrl() { return _bundleUrl; },

        get DB_NAME() { return _DB_NAME; },
        get DB_STORE() { return _DB_STORE; },
        get DB_BASE_DIR() { return _BASE_DIR; },
        get DB_BundleDir() { return _bundleDir; }
    };
})();


let EmUtil = (function () {

    // download files from net 
    function _netGet(url, type, param) {
        // Return a new promise.
        return new Promise(function (resolve, reject) {
            // Do the usual XHR stuff
            let req = new XMLHttpRequest();
            req.open('GET', url);
            req.responseType = type || "arraybuffer";  // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType
            req.onload = function () {
                // This is called even on 404 etc
                // so check the status
                if (req.status == 200) {
                    // Resolve the promise with the response text
                    if (param) {
                        resolve({ val: req.response, param: param });
                    } else {
                        resolve(req.response);
                    }
                }
                else {
                    // Otherwise reject with the status text
                    // which will hopefully be a meaningful error
                    reject(Error(req.statusText));
                }
            };

            // Handle network errors
            req.onerror = function () {
                reject(Error("Network Error"));
            };

            // Make the request
            req.send();
        });
    }

    // http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt

    /* utf.js - UTF-8 <=> UTF-16 convertion
     *
     * Copyright (C) 1999 Masanao Izumo <[email protected]>
     * Version: 1.0
     * LastModified: Dec 25 1999
     * This library is free.  You can redistribute it and/or modify it.
     */
    function _bin2Str(array) {
        let out, i, len, c;
        let char2, char3;

        out = "";
        len = array.length;
        i = 0;
        while (i < len) {
            c = array[i++];
            switch (c >> 4) {
                case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
                    // 0xxxxxxx
                    out += String.fromCharCode(c);
                    break;
                case 12: case 13:
                    // 110x xxxx   10xx xxxx
                    char2 = array[i++];
                    out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
                    break;
                case 14:
                    // 1110 xxxx  10xx xxxx  10xx xxxx
                    char2 = array[i++];
                    char3 = array[i++];
                    out += String.fromCharCode(((c & 0x0F) << 12) |
                        ((char2 & 0x3F) << 6) |
                        ((char3 & 0x3F) << 0));
                    break;
            }
        }

        return out;
    }

    function b64EncodeUnicode(str) {
        // first we use encodeURIComponent to get percent-encoded UTF-8,
        // then we convert the percent encodings into raw bytes which
        // can be fed into btoa.
        return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
            function toSolidBytes(match, p1) {
                return String.fromCharCode('0x' + p1);
            }));
    }

    function b64DecodeUnicode(str) {
        // Going backwards: from bytestream, to percent-encoding, to original string.
        return decodeURIComponent(atob(str).split('').map(function (c) {
            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
        }).join(''));
    }

    let _setCookie = (cname, cvalue, exdays) => {
        let d = new Date();
        d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
        cname = `${cname}_${webconfig.compileType}`;
        cvalue = b64EncodeUnicode(cvalue);
        document.cookie = `${cname}=${cvalue};expires=${d.toUTCString()};secure;path=/`;
    }

    let _getCookie = cname => {
        cname = `${cname}_${webconfig.compileType}`;
        let val = document.cookie.replace(new RegExp(`(?:(?:^|.*;\\s*)${cname}\\s*=\\s*([^;]*).*$)|^.*$`, 'i'), "$1");
        return b64DecodeUnicode(val);
    }
    let _delCookie = cname => {
        document.cookie = `${cname}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
    }

    return {
        netGet: _netGet,
        bin2Str: _bin2Str,
        setCookie: _setCookie,
        getCookie: _getCookie,
        delCookie: _delCookie
    }
})();

// Database Manager
// refer: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
DBManager = (function () {
    const READONLY = "readonly";
    const READWRITE = "readwrite";
    let db = null;
    let _open = () => {
        if (db != null) {
            return Promise.resolve(db);
        }
        return new Promise((resolve, reject) => {
            this.req = window.indexedDB.open(GameConfig.DB_NAME);

            req.onupgradeneeded = evt => {
                db = evt.currentTarget.result;
                let store = db.createObjectStore(GameConfig.DB_STORE);
                store.transaction.oncomplete = function (event) {
                    let tmp = db.transaction(GameConfig.DB_STORE, READWRITE).objectStore(GameConfig.DB_STORE);
                    // create default folders 
                    tmp.add({ timestamp: new Date(), mode: 16877 }, GameConfig.DB_BASE_DIR);   // 40755
                    tmp.add({ timestamp: new Date(), mode: 16895 }, GameConfig.DB_BundleDir); // 40777
                };
            };
            req.onsuccess = function () {
                db = this.result;
                resolve(db);
            };
            req.onerror = evt => {
                reject(Error("Error openDB:" + evt.target.errorCode))
            };
        });
    };

    return {
        iterateAll: async callback => {
            let db = await _open();
            return new Promise((resolve, reject) => {
                let tx = db.transaction(GameConfig.DB_STORE, READONLY);
                // tx.oncomplete = resolve;
                tx.onabort = reject;
                tx.onerror = reject;

                let store = tx.objectStore(GameConfig.DB_STORE);
                let cHandler = store.openCursor();
                cHandler.onsuccess = function (event) {
                    let cursor = event.target.result;
                    if (cursor && callback(cursor)) {
                        cursor.continue();
                    } else {
                        resolve("No more entries!");
                    }
                };
                cHandler.onerror = evt => {
                    reject(Error("Cursor error!"));
                };
            });
        },
        doRead: async arr => {
            let db = await _open();
            return new Promise((resolve, reject) => {
                let tx = db.transaction(GameConfig.DB_STORE, READONLY);
                tx.oncomplete = resolve;
                tx.onabort = reject;
                tx.onerror = reject;

                let store = tx.objectStore(GameConfig.DB_STORE);
                let doJob = e => {
                    let req = store.get(e.path);
                    req.onsuccess = evt => {
                        e.onDBRead(evt.target.result);
                    };
                    req.onerror = evt => {
                        e.onDBError(evt);
                    };
                };
                if (arr.constructor === Array) {  // array
                    arr.forEach(e => {
                        doJob(e);
                    });
                } else if (arr.constructor === Entity) {  // single 
                    doJob(arr);
                } else {
                    throw "fail to read!"
                }
            });
        },
        doInsert: async arr => {
            let db = await _open();
            return new Promise((resolve, reject) => {
                let tx = db.transaction(GameConfig.DB_STORE, READWRITE);
                tx.oncomplete = resolve;
                tx.onabort = reject;
                tx.onerror = reject;

                let store = tx.objectStore(GameConfig.DB_STORE);
                if (arr.constructor === Array) {  // array
                    arr.forEach(e => {
                        store.add(e.Value, e.Path);
                    });
                } else if (arr.constructor === Entity) {  // single 
                    let e = arr;
                    store.add(e.Value, e.Path);
                } else {
                    throw "fail to read!"
                }
            });
        },
        doInsertOrUpdate: async arr => {
            let db = await _open();
            return new Promise((resolve, reject) => {
                let tx = db.transaction(GameConfig.DB_STORE, READWRITE);
                tx.oncomplete = resolve;
                tx.onabort = reject;
                tx.onerror = reject;

                let store = tx.objectStore(GameConfig.DB_STORE);
                if (arr.constructor === Array) {  // array
                    arr.forEach(e => {
                        store.put(e.Value, e.Path);
                    });
                } else if (arr.constructor === Entity) {  // single 
                    let e = arr;
                    store.put(e.Value, e.Path);
                } else {
                    throw "fail to insertOrUpdate!"
                }
            });
        },
        doDelete: async arr => {
            let db = await _open();
            return new Promise((resolve, reject) => {
                let tx = db.transaction(GameConfig.DB_STORE, READWRITE);
                tx.oncomplete = resolve;
                tx.onabort = reject;
                tx.onerror = reject;

                let store = tx.objectStore(GameConfig.DB_STORE);
                if (arr.constructor === Array) {  // array
                    arr.forEach(e => {
                        store.delete(e.Path);
                    });
                } else if (arr.constructor === Entity) {  // single 
                    let e = arr;
                    store.delete(e.Path);
                } else {
                    throw "fail to delete!"
                }
            });
        },
        close: () => {
            if (db) {
                db.close();
                db = null;
            }
        },
    };
})();

class Entity {
    static genBundle(name, content) {
        return new Entity(name,
            `${GameConfig.DB_BundleDir}/${name}`,
            content && { timestamp: new Date(), mode: 33206, contents: content },
            content ? content.byteLength : 1,
            true);
    }

    constructor(name, path, value, size, isBundle) {
        this.name = name;
        this.path = path;
        this.value = value;
        this.isBundle = isBundle;
        this.size = size;
    }

    async checkUpdated(md5Str) {
        await DBManager.doRead(this);
        if (md5Str != md5(this.ContentAsStr)) {
            let tmp = await EmUtil.netGet(`${GameConfig.bundleUrl}/${this.name}`);
            let val = new Uint8Array(tmp);
            if (md5Str != md5(EmUtil.bin2Str(val))) {
                throw new Error(`Fail to update ${entity.name}`);
            }
            this.value = { timestamp: new Date(), mode: 33206, contents: val };
            return false;
        }
        return true;
    }

    onDBRead(vv) { this.value = vv; }
    onDBWrite(vv) { }
    onDBError(vv) { console.error(vv); }

    get Size() { return this.size; }
    get Name() { return this.name; }
    get Path() { return this.path; }
    get Value() { return this.value; }
    get Content() { return this.value && this.value.contents; }
    get ContentAsStr() { return this.value && this.value.contents && EmUtil.bin2Str(new Uint8Array(this.value.contents)); }
}

class DBInsertJob {
    constructor(condCount, condSize) {
        this.condCount = condCount || Number.MAX_SAFE_INTEGER;
        this.condSize = condSize || Number.MAX_SAFE_INTEGER;
        this.batches = [];
        this.curBatch = [];
        this.curSize = 0;
        this.isWorking = false;
    }

    insert(entity) {
        if (entity) {
            this.curSize += entity.Size;
            this.curBatch.push(entity);
            if (this._formBatch()) {
                this._doJob();
            }
        }
    }

    _formBatch(isForce) {
        if (this.curBatch.length > 0 &&
            (isForce || this.curBatch.length >= this.condCount || this.curSize > this.condSize)) {  // meet the batch condition
            this.batches.push(this.curBatch);
            this.curBatch = [];
            this.curSize = 0;
            return true;
        }
        return false;
    }

    _doJob(doneEvent) {
        if (this.batches.length > 0 && !this.isWorking) {  // do job
            this.isWorking = true;
            (async () => {
                while (this.batches.length > 0) {
                    await DBManager.doInsertOrUpdate(this.batches.shift());
                }
                this.isWorking = false;
                if (doneEvent) {
                    doneEvent();
                }
            })();
        }
    }

    flush() {
        return new Promise((resolve, reject) => {
            if (this._formBatch(true)) {
                this._doJob(resolve);
            } else {
                resolve();
            }
        });
    }

    get IsWorking() {
        return this.isWorking;
    }
}

function EmBundleMap(_name) {
    let entity = Entity.genBundle(_name);
    let resList = null;

    return {
        get name() {
            return entity && entity.name;
        },
        get entity() {
            return entity;
        },
        get resList() {
            return resList;
        },
        updateMap: async (md5Str, dbJob) => {
            let isUpdated = await entity.checkUpdated(md5Str);
            if (!isUpdated) {
                dbJob.insert(entity);
            }
            resList = JSON.parse(entity.ContentAsStr);
        },
        updateBundles: async dict => {
            try {
                let reg = /^AssetsBundles\/(.*)$/;
                let upds = [];
                resList.forEach(r => {
                    let name = r.path.replace(reg, "$1");
                    let info = dict[name];
                    if (info.mStatus != 1 || info.bStatus != 1) {
                        upds.push(info);
                    }
                });
                // alrady updated!!
                if (upds.length == 0) {
                    console.log(`${entity && entity.name} is updated!`);
                    return true;
                }
                // start update 
                let dbJob = new DBInsertJob(20, 10 * 1024 * 1024); // 20 entities or 10M as a batch!
                let waitList = [];
                upds.forEach(j => {
                    let name = j.val.path.replace(reg, "$1");
                    waitList.push(new Promise((resolve, reject) => {
                        EmUtil.netGet(`${GameConfig.bundleUrl}/${name}`).then(val => {
                            dbJob.insert(Entity.genBundle(name, new Uint8Array(val)));
                            resolve();
                        }).catch(ex => {
                            reject(ex);
                        });
                    }));

                    waitList.push(new Promise((resolve, reject) => {
                        EmUtil.netGet(`${GameConfig.bundleUrl}/${name}.manifest`).then(val => {
                            dbJob.insert(Entity.genBundle(`${name}.manifest`, new Uint8Array(val)));
                            resolve();
                        }).catch(ex => {
                            reject(ex);
                        });
                    }));
                });
                await Promise.all(waitList);
                await dbJob.flush();

                return true;
            } catch (ex) {
                console.error(ex);
                return false;
            }
        },
    };
};

let AllBundle = (() => {
    let bundleMd5 = `${GameConfig.bundleUrl}/BundleMD5.txt`;

    let local = new EmBundleMap("LocalBundleMap.txt");
    let patch = new EmBundleMap("BundleMap.txt");
    let sprite = new EmBundleMap("SpriteSortingLayers.txt");

    let fixDatabase = async () => {  // delete old resource, pareparing for updating
        let dict = {};
        let dels = new Set();
        let reg = /^AssetsBundles\/(.*)$/;
        local.resList.forEach(r => { dict[r.path.replace(reg, "$1")] = { bStatus: 0, mStatus: 0, val: r }; });
        patch.resList.forEach(r => { dict[r.path.replace(reg, "$1")] = { bStatus: 0, mStatus: 0, val: r }; });
        let reg1 = /AssetsBundles\/(?:([^\/\.]+?)\.manifest|([^\/\.]+))$/;
        let reg2 = /[^]+CRC: (\d+)[^]+/i;
        await DBManager.iterateAll(c => {
            let m = c.key.match(reg1);
            if (m) {
                let mName = m[1];
                let bName = m[2];
                if (mName) {   // found manifest
                    let info = dict[mName];
                    if (info) {
                        let isValid = (c.value && c.value.contents
                            && EmUtil.bin2Str(new Uint8Array(c.value.contents.slice(0, 50))).replace(reg2, "$1") == info.val.crc);
                        info.mStatus = isValid ? 1 : -1;
                    } else {
                        dels.add(`${mName}.manifest`);
                    }
                } else if (bName) {  // found bundle
                    let info = dict[bName];
                    if (info) {
                        let isValid = c.value && c.value.contents;
                        info.bStatus = isValid ? 1 : -1;
                    } else {
                        dels.add(bName);
                    }
                } else {
                    throw `error when validating bundles: ${c.key}`;
                }
            }
            return true;
        });

        Object.values(dict).filter(info => info.mStatus != 1 || info.bStatus != 1).forEach(info => {
            if (info.mStatus != 0) {
                info.mStatus = 0;
                dels.add(`${info.val.path.replace(reg, "$1")}.manifest`);
            }
            if (info.bStatus != 0) {
                info.bStatus = 0;
                dels.add(info.val.path.replace(reg, "$1"));
            }
        });

        if (dels.size > 0) {
            let tmp = [];
            dels.forEach(d => {
                tmp.push(new Entity(d));
            });
            await DBManager.doDelete(tmp);
        }
        return dict;
    };

    return {
        checkDB: async () => {  // validate database!
            let baseFolders = [
                new Entity("baseDir", GameConfig.DB_BASE_DIR),
                new Entity("bundleDir", GameConfig.DB_BundleDir),
            ];
            await DBManager.doRead(baseFolders);

            if (baseFolders.filter(e => !e.Value).length > 0) {
                await DBManager.doInsertOrUpdate([
                    new Entity("baseDir", GameConfig.DB_BASE_DIR, { timestamp: new Date(), mode: 16877 }, 1, false),
                    new Entity("bundleDir", GameConfig.DB_BundleDir, { timestamp: new Date(), mode: 16895 }, 1, false),
                ]);
            }
        },
        updateMap: async () => { // update bundleMaps !
            let res = await EmUtil.netGet(bundleMd5, "text");
            let mm = res.split(",");
            if (mm.length != 3) {
                throw new Error("The format of BundleMD5.txt is not correct!");
            }
            let dbJob = new DBInsertJob();
            await Promise.all([
                local.updateMap(mm[0], dbJob),
                patch.updateMap(mm[1], dbJob),
                sprite.updateMap(mm[2], dbJob)
            ]);
            await dbJob.flush();
            return await fixDatabase();
        },
        updateLocal: async dict => {
            let retryTime = 0;
            while (retryTime++ < 3) {
                let isOK = await local.updateBundles(dict);
                if (isOK) {
                    break;
                } else {
                    console.warn(`failed to update ${local.name} at retryTime: ${retryTime}`);
                }
            }
        },
        updatePatch: async dict => {
            let retryTime = 0;
            while (retryTime++ < 3) {
                let isOK = await patch.updateBundles(dict);
                if (isOK) {
                    break;
                } else {
                    console.warn(`failed to update ${patch.name} at retryTime: ${retryTime}`);
                }
            }
        }
    };
})();

let UpdateTool = (function () {
    return {
        start: async () => {
            try {
                await AllBundle.checkDB();
                let dict = await AllBundle.updateMap();
                await AllBundle.updateLocal(dict);
                await AllBundle.updatePatch(dict);
            } catch (ex) {
                console.error(ex);
            } finally {
                DBManager.close();
            }
        }
    };
})();

猜你喜欢

转载自blog.csdn.net/stalendp/article/details/82498622