diff --git a/3DTank/Start-LocalAndRelay.ps1 b/3DTank/Start-LocalAndRelay.ps1 index 045ccb6..7a60c17 100644 --- a/3DTank/Start-LocalAndRelay.ps1 +++ b/3DTank/Start-LocalAndRelay.ps1 @@ -31,7 +31,8 @@ param( [switch]$FetchAssets, # 先跑 fetch_assets.js 镜像资源(首次或资源缺失时用) [switch]$KillExisting, # 端口被占用时,先结束占用进程 [switch]$OpenBrowser, # 启动后打开 http://localhost:/ - [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: $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 diff --git a/3DTank/tools/serve.js b/3DTank/tools/serve.js index aff3550..9401fe7 100644 --- a/3DTank/tools/serve.js +++ b/3DTank/tools/serve.js @@ -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, ''); + 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)')); }); diff --git a/sockets/ws_hook.js b/sockets/ws_hook.js index 3e2842d..d98b221 100644 --- a/sockets/ws_hook.js +++ b/sockets/ws_hook.js @@ -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;