// ===== 车库资源预热模块 ===== // 目的:从战局返回车库时,车库界面要一次性加载大量贴图/图标/坦克模型, // 首次全部现场拉取会明显卡顿。这里在**战局空闲时**后台把这些资源拉进浏览器缓存。 // // 预热两批: // ① /play/ —— 车库/大厅 UI 图片(由 resources.json 资源表枚举,本地直出) // ② /resources/<...> —— res.3dtank.com 上最近访问过的模型/贴图 // 清单由本地 serve.js 的 /__prefetch_list__ 提供(按缓存文件 mtime 倒序) // // 策略: // - 只在捕获到 __gameMode__(已在战局)后 8 秒才开始,避开进入战局的加载高峰 // - 串行 + 间隔限速,避免抢占战局带宽 // - 10 分钟内只跑一次(localStorage 记录),可用 __hornetPrefetch__.stop() 手动停止 // - localStorage.hornetPrefetch = '0' 可永久关闭 (function () { var KEY_AT = 'hornetPrefetchAt'; var KEY_OFF = 'hornetPrefetch'; var GAP_STATIC = 40; // UI 图片间隔(ms) var GAP_RES = 30; // 模型/贴图间隔(ms) var State = { running: false, stopped: false, timers: [] }; function LastRunAt() { try { return Number(localStorage.getItem(KEY_AT) || 0) || 0; } catch (e) { return 0; } } function MarkRun() { try { localStorage.setItem(KEY_AT, String(Date.now())); } catch (e) {} } // 串行限速加载;命中缓存的资源不会产生网络请求 function LoadList(List, Gap, Done) { var Index = 0, Ok = 0; function Next() { if (State.stopped) return; if (Index >= List.length) { if (Ok > 0) console.log('[预热] 完成 ' + Ok + '/' + List.length + ' 项'); if (Done) Done(); return; } var Url = List[Index++]; fetch(Url, { cache: 'force-cache' }) .then(function () { Ok++; }) .catch(function () {}) .then(function () { setTimeout(Next, Gap); }); } Next(); } // ① 车库/大厅 UI 图片:从 webpack 资源表枚举 function PreloadStatic() { fetch('/play/static/js/unpacked/resources.json', { cache: 'force-cache' }) .then(function (R) { return R.json(); }) .then(function (Json) { var Map = (Json && Json.resourceMap) || {}; var Seen = {}; var List = []; for (var K in Map) { if (!Object.prototype.hasOwnProperty.call(Map, K)) continue; var V = Map[K]; if (typeof V !== 'string' || Seen[V]) continue; Seen[V] = 1; List.push('/play/' + V); } console.log('[预热] 车库 UI 资源 ' + List.length + ' 项'); LoadList(List, GAP_STATIC, PreloadRes); }) .catch(function () { PreloadRes(); }); } // ② res.3dtank.com 资源:按本地缓存清单(最近访问优先) function PreloadRes() { fetch('/__prefetch_list__?n=600') .then(function (R) { return R.json(); }) .then(function (Json) { var List = (Json && Json.list) || []; if (!List.length) return; console.log('[预热] 战局/车库资源 ' + List.length + ' 项'); LoadList(List, GAP_RES, null); }) .catch(function () {}); } function Run() { if (State.stopped) return; if (Date.now() - LastRunAt() < 10 * 60 * 1000) return; // 10 分钟内只跑一次 MarkRun(); PreloadStatic(); } function _start() { if (State.running) return; State.running = true; State.stopped = false; try { if (localStorage.getItem(KEY_OFF) === '0') { console.log('[预热] 已被 localStorage.hornetPrefetch=0 关闭'); State.running = false; return; } } catch (e) {} console.log('===== 车库资源预热启动 ====='); // 等进入战局:__gameMode__ 由 hook_gamemode.js 捕获 var Wait = setInterval(function () { if (State.stopped) { clearInterval(Wait); return; } if (!window.__gameMode__) return; clearInterval(Wait); setTimeout(Run, 8000); }, 2000); State.timers.push(Wait); window.__hornetPrefetch__ = { running: true, stop: function () { State.stopped = true; for (var I = 0; I < State.timers.length; I++) clearInterval(State.timers[I]); console.log('[预热] 已停止'); } }; } _start(); })();