- hornet 工具链:无 ght 启动;透视冻结节点拖拽跟随、金箱标记、性能优化;雷达死亡红叉;鼠标稳定器;窗口布局持久化 - 本地镜像:切割与单包加载(pack_unpacked.js)+ SW 单包缓存 + F5 兜底刷新;入口更新为 main.9f4e7a89.js - 中继 wss_server.js:转发改 socket 反压的原样直发(零等待零缓存);落盘改单条有序异步队列(默认开启);新增反压看门狗(默认 60s)
163 lines
5.9 KiB
JavaScript
163 lines
5.9 KiB
JavaScript
// ===== 3DTank Hornet Service Worker =====
|
||
// 两个职责:
|
||
//
|
||
// ① 单包本地缓存(主用途):把 /play/static/js/unpacked/ 下的散装小文件
|
||
// (126 个 module_*.js + resources.json)用单个 unpacked.pack.json 就地命中,
|
||
// 不再逐个走磁盘小文件。包由 tools/pack_unpacked.js 生成。
|
||
// 作用域必须是 /(serve.js 用 Service-Worker-Allowed: / 放行),这样首页 / 发起的
|
||
// /play/... 请求才能被接管;加载器会自动注册它。
|
||
//
|
||
// ② main.*.js 网络层注入(备用):无法改 main 文件时,往 __webpack_require__ 注入
|
||
// window.__wp_require__ 捕获行,并追加 hornet_bundle.js 加载。
|
||
//
|
||
// 卸载:navigator.serviceWorker.getRegistration().then(r => r.unregister()).then(() => location.reload())
|
||
|
||
var SW_DEBUG = '[Hornet SW]';
|
||
var MAIN_JS_PATTERN = /\/main\.[a-f0-9]+\.js$/;
|
||
var INJECT_LINE = 'window.__wp_require__=__webpack_require__;';
|
||
// 在入口模块加载后注入 hornet_bundle.js
|
||
var BUNDLE_MARKER = '__webpack_require__(78231)';
|
||
var BUNDLE_INJECT = ';var __hb=document.createElement("script");__hb.src="/play/static/js/unpacked/hornet_bundle.js";document.head.appendChild(__hb);';
|
||
|
||
// ===== 单包缓存 =====
|
||
var PACK_URL = '/play/static/js/unpacked.pack.json';
|
||
var PACK_PATH_RE = /^\/play\/static\/js\/unpacked\/(module_\d+\.js|resources\.json)$/;
|
||
var PackPromise = null;
|
||
var PackMap = null;
|
||
var PackVersion = '';
|
||
|
||
function LoadPack() {
|
||
if (PackPromise) return PackPromise;
|
||
PackPromise = fetch(PACK_URL, { cache: 'no-cache' })
|
||
.then(function (Resp) {
|
||
if (!Resp.ok) throw new Error('pack HTTP ' + Resp.status);
|
||
return Resp.json();
|
||
})
|
||
.then(function (Pack) {
|
||
PackMap = Pack.files || {};
|
||
PackVersion = Pack.version || '';
|
||
console.log(SW_DEBUG, '单包已载入: ' + (Pack.count || 0) + ' 个文件 版本=' + PackVersion);
|
||
return PackMap;
|
||
})
|
||
.catch(function (Err) {
|
||
console.warn(SW_DEBUG, '单包载入失败(回退逐文件):', Err && Err.message);
|
||
PackMap = null;
|
||
PackPromise = null; // 允许下次重试
|
||
return null;
|
||
});
|
||
return PackPromise;
|
||
}
|
||
|
||
function PackResponse(Name) {
|
||
if (!PackMap) return null;
|
||
var Text = PackMap[Name];
|
||
if (typeof Text !== 'string') return null;
|
||
var Type = /\.json$/.test(Name)
|
||
? 'application/json; charset=utf-8'
|
||
: 'application/javascript; charset=utf-8';
|
||
return new Response(Text, {
|
||
status: 200,
|
||
headers: {
|
||
'Content-Type': Type,
|
||
'X-Hornet-Pack': PackVersion,
|
||
'Cache-Control': 'no-cache',
|
||
},
|
||
});
|
||
}
|
||
|
||
self.addEventListener('install', function(event) {
|
||
self.skipWaiting();
|
||
});
|
||
|
||
self.addEventListener('activate', function(event) {
|
||
event.waitUntil(self.clients.claim());
|
||
});
|
||
|
||
self.addEventListener('fetch', function(event) {
|
||
var url = event.request.url;
|
||
var PathName = '';
|
||
try { PathName = new URL(url).pathname; } catch (Err) {}
|
||
|
||
// ① 单包命中:散装小文件(126 个模块 + resources.json)一次读包解决
|
||
if (PACK_PATH_RE.test(PathName)) {
|
||
var Name = PathName.slice(PathName.lastIndexOf('/') + 1);
|
||
event.respondWith(
|
||
LoadPack().then(function () {
|
||
return PackResponse(Name) || fetch(event.request); // 包里没有的才回退网络
|
||
}),
|
||
);
|
||
return;
|
||
}
|
||
|
||
// ② 拦截 main.*.js
|
||
if (MAIN_JS_PATTERN.test(url)) {
|
||
event.respondWith(
|
||
fetch(event.request).then(function(response) {
|
||
if (!response.ok) return response;
|
||
return response.text().then(function(text) {
|
||
// 在 __webpack_require__ 函数定义后注入捕获行
|
||
// 匹配模式:function __webpack_require__(t) { ... }
|
||
var marker = 'function __webpack_require__(';
|
||
var idx = text.indexOf(marker);
|
||
if (idx === -1) {
|
||
// 尝试 webpack 5 格式
|
||
marker = '__webpack_require__ = function';
|
||
idx = text.indexOf(marker);
|
||
}
|
||
if (idx !== -1) {
|
||
// 找到函数体的第一个 { 后注入捕获行
|
||
var braceIdx = text.indexOf('{', idx);
|
||
if (braceIdx !== -1) {
|
||
var injectPos = braceIdx + 1;
|
||
var modified = text.slice(0, injectPos) + INJECT_LINE + text.slice(injectPos);
|
||
|
||
// 在入口模块加载后注入 hornet_bundle.js
|
||
var bundleIdx = modified.indexOf(BUNDLE_MARKER);
|
||
if (bundleIdx !== -1) {
|
||
var afterMarker = bundleIdx + BUNDLE_MARKER.length;
|
||
modified = modified.slice(0, afterMarker) + BUNDLE_INJECT + modified.slice(afterMarker);
|
||
console.log(SW_DEBUG, '注入 __wp_require__ + bundle 加载到', url);
|
||
} else {
|
||
console.log(SW_DEBUG, '注入 __wp_require__ 捕获行(未找到 bundle marker)', url);
|
||
}
|
||
|
||
return new Response(modified, {
|
||
status: response.status,
|
||
statusText: response.statusText,
|
||
headers: {
|
||
'Content-Type': 'application/javascript; charset=utf-8',
|
||
'Cache-Control': 'no-cache'
|
||
}
|
||
});
|
||
}
|
||
}
|
||
console.warn(SW_DEBUG, '未找到 __webpack_require__ 定义,返回原始响应');
|
||
return new Response(text, {
|
||
status: response.status,
|
||
statusText: response.statusText,
|
||
headers: response.headers
|
||
});
|
||
});
|
||
}).catch(function(err) {
|
||
console.error(SW_DEBUG, '拦截失败:', err);
|
||
return fetch(event.request);
|
||
})
|
||
);
|
||
return;
|
||
}
|
||
|
||
// 其他请求正常处理
|
||
event.respondWith(fetch(event.request));
|
||
});
|
||
|
||
// 监听消息:手动加载 hornet_bundle
|
||
self.addEventListener('message', function(event) {
|
||
if (event.data === 'load_bundle') {
|
||
self.clients.matchAll().then(function(clients) {
|
||
clients.forEach(function(client) {
|
||
client.postMessage('load_bundle');
|
||
});
|
||
});
|
||
}
|
||
});
|