fix(mirror): ws 抓包开关改为 serve.js --ws-hook(不碰 URL),避免 hash 破坏游戏路由

- 实测:URL 里任何 hash(包括 #hook/#nohook)都会让游戏进不去战场(游戏用 location.hash 做路由/状态),所以弃用 hash 开关

- serve.js --ws-hook:服务 HTML 时注入 /ws_hook.js;Start-LocalAndRelay.ps1 加 -Hook 开关;ws_hook.js 支持 localStorage.hornetWsHook=0 临时关闭

- index.html(本地)移除静态 ws_hook 引入
This commit is contained in:
TermiNexus committed 2026-10-11 21:43:12 +08:00
1 parent d6758b14e1
commit ae4d4ee055
3 files changed
+27 -11

No files matched your search

+4 -2
View File
@@ -31,7 +31,8 @@ param(
[switch]$FetchAssets, # 先跑 fetch_assets.js 镜像资源(首次或资源缺失时用)
[switch]$KillExisting, # 端口被占用时,先结束占用进程
[switch]$OpenBrowser, # 启动后打开 http://localhost:<MirrorPort>/
[switch]$WithRelay, # 启动本地中继(默认**不启动**:页面直连真实服务器 wss://c1.3dtank.com:9090/)
[switch]$WithRelay, # 启动本地中继(默认**不启动**:页面直连真实服务器 wss://c1.3dtank.com:9090/)
[switch]$Hook, # 本地镜像:注入页面内 ws 抓包(不用改 URL;游戏用 hash 做路由,加 hash 会进不去战场)
[int]$DumpEvery = 1, # 中继:每 N 帧写一个 .bin(默认 1 = 每帧都写;0 = 不写抓包文件)
[switch]$NoLog, # 中继:不写 log.txt(默认写)
[switch]$NoHex, # 中继:log.txt 不写十六进制(日志体积大幅减小)
@@ -151,7 +152,8 @@ if ($WithRelay) {
# 走中继(-WithRelay)时不加它,改用 local/config.xml 把客户端指向本机 127.0.0.1:<RelayPort>
$MirrorArgs = @('serve.js', '--port', "$MirrorPort")
if (-not $WithRelay) { $MirrorArgs += '--real-config' }
Write-Host ("[镜像] serve.js http://localhost:{0}/ {1}" -f $MirrorPort, ($(if ($WithRelay) {'(config → 本地中继)'} else {'(config → 真实服务器,直连)'}))) -ForegroundColor Green
if ($Hook) { $MirrorArgs += '--ws-hook' }
Write-Host ("[镜像] serve.js http://localhost:{0}/ {1}{2}" -f $MirrorPort, ($(if ($WithRelay) {'(config → 本地中继)'} else {'(config → 真实服务器,直连)'})), ($(if ($Hook) {' + ws 抓包'} else {''}))) -ForegroundColor Green
$MirrorProc = Start-Process -FilePath 'cmd.exe' `
-ArgumentList (@('/k', 'node') + $MirrorArgs) `
-WorkingDirectory $Tools -PassThru
+17 -1
View File
@@ -31,6 +31,10 @@ const VERBOSE = Argv.indexOf('--verbose') >= 0;
// 加 --real-config 可强制走真实服务器
const LOCAL_CONFIG = path.join(PROJECT, 'local', 'config.xml');
const REAL_CONFIG = Argv.indexOf('--real-config') >= 0;
// --ws-hook:服务 index.html 时注入页面内 ws 抓包脚本(sockets/ws_hook.js)。
// 刻意用**启动参数**而不是 URL 开关:游戏用 location.hash 做路由/状态,
// 任何 hash(哪怕只是 #hook)都会让它进不去战场。
const WS_HOOK = Argv.indexOf('--ws-hook') >= 0;
// 本机路径 → 真实站点(StripPrefix: 去掉本机前缀再拼到 Target 上;KeepQuery: 是否保留查询串)
const PROXY = [
@@ -259,8 +263,19 @@ const Server = http.createServer((Req, Res) => {
Res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' }).end('Not Found: ' + UrlPath);
return;
}
const Ext = path.extname(File).toLowerCase();
const Type = MIME[Ext] || 'application/octet-stream';
// --ws-hook:HTML 里注入抓包脚本(在主脚本之前 → 先于游戏创建 WebSocket)
if (WS_HOOK && (Ext === '.html' || Ext === '.htm')) {
const Html = fs.readFileSync(File, 'utf8').replace(/<\/head>/i, '<script src="/ws_hook.js"></script></head>');
const Buf = Buffer.from(Html, 'utf8');
Res.writeHead(200, { 'Content-Type': Type, 'Content-Length': Buf.length, 'Cache-Control': 'no-cache' });
if (VERBOSE) console.log('[200] ' + Buf.length + '\t' + UrlPath + ' (已注入 ws_hook)');
Res.end(Buf);
return;
}
Res.writeHead(200, {
'Content-Type': MIME[path.extname(File).toLowerCase()] || 'application/octet-stream',
'Content-Type': Type,
'Content-Length': Stat.size,
'Cache-Control': 'no-cache',
});
@@ -277,4 +292,5 @@ Server.listen(PORT, () => {
console.log(' 切割版 JS : ' + JS_DIR);
console.log(' 404 记录 : ' + MISSING_LOG);
console.log(' config.xml: ' + (!REAL_CONFIG && fs.existsSync(LOCAL_CONFIG) ? '本地覆盖 ' + LOCAL_CONFIG : '回源 c1.3dtank.com(--real-config)'));
console.log(' ws 抓包 : ' + (WS_HOOK ? '已开启(HTML 注入 /ws_hook.js;保存用 __ws_save__())' : '关闭(要抓包加 --ws-hook,勿用 URL hash)'));
});
+6 -8
View File
@@ -12,18 +12,16 @@
// 3) **热路径不编码**:send/onmessage 里只做一次内存拷贝,base64 推迟到 __ws_save__() 时才做,
// 避免给战斗入口的收发包增加延迟。
//
// 挂载:serve.js 提供 /ws_hook.js;web/index.html 在主脚本之前引入。
// 开关:**默认不启用**;URL hash 加 #hook 才抓(#nohook 强制关)。
// 挂载 / 开关(**不碰 URL**):
// 游戏用 location.hash 做路由/状态,URL 里出现任何 hash(哪怕 #hook)都会让它进不去战场,
// 所以开关改用启动参数:`serve.js --ws-hook` 时才把本脚本注入 HTML;不注入就是干净页面。
// 临时关闭(不改启动参数):控制台执行 localStorage.hornetWsHook='0' 后刷新;'1' 或删除则恢复。
// 产物:window.__ws_capture__ 内存抓包;__ws_save__() 保存到 3DTank/names/(离开页面自动再存一份)
// ============================================================
(function () {
if (window.__ws_hooked__) return;
if (!/#hook/.test(location.hash) || /#nohook/.test(location.hash)) {
console.log('[WS Hook] 未启用(默认关闭;URL 加 #hook 开启抓包)');
window.__ws_save__ = function () {
console.warn('[WS Hook] 未启用:请在 URL 末尾加 #hook(如 http://localhost:8080/#hook)后整页刷新,再调用 __ws_save__()');
return Promise.resolve(null);
};
if (localStorage.getItem('hornetWsHook') === '0') {
console.log('[WS Hook] 已被 localStorage.hornetWsHook=0 关闭');
return;
}
window.__ws_hooked__ = true;