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

Posted stalendp

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个Unity的WebGL的资源更新逻辑相关的知识,希望对你有一定的参考价值。

直接贴代码了

// 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 <iz@onicos.co.jp>
     * 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();
            
        
    ;
)();

 

 

以上是关于一个Unity的WebGL的资源更新逻辑的主要内容,如果未能解决你的问题,请参考以下文章

更新到 Unity 2019.3 后无法编译 Unity WebGL

Unity使用 Addressables 预加载所有资源,提现加载资源,发布webgl加载缓慢问题

Unity的WebGL发布在IIS上并加载资源的若干问题

unitywebgl允许http没有响应

Unity 远程加载资源以及本地缓存相关

unity 发布webgl怎么加载assetbundle