chore(3DTank): 新增页面内探针的网络转储通道(/__probe__ + probe_railgun.js)
- tools/serve.js: POST /__probe__ 追加写 3DTank/names/probe_railgun.log - sockets/probe_railgun.js: 电磁炮/武器链路现场探针(组件清单 + 采样 + WS 上行帧),fetch 批量转储,POST 不通自动回落控制台
This commit is contained in:
1 parent
0c55021479
commit
2b95e5de28
2 files changed
+166
No files matched your search
@@ -24,6 +24,8 @@ const PORT = Number(Arg('port', '8080'));
|
||||
const WEB_DIR = path.resolve(Arg('web', path.join(PROJECT, 'web')));
|
||||
const JS_DIR = path.join(ROOT, '3dtank.com', 'play', 'static', 'js');
|
||||
const MISSING_LOG = path.join(PROJECT, 'names', 'missing.txt');
|
||||
// 页面内探针(sockets/probe_railgun.js)的网络转储落盘位置:POST /__probe__ 追加写这里
|
||||
const PROBE_LOG = path.join(PROJECT, 'names', 'probe_railgun.log');
|
||||
const CACHE_DIR = path.join(PROJECT, 'cache');
|
||||
const NO_PROXY = Argv.indexOf('--no-proxy') >= 0;
|
||||
const VERBOSE = Argv.indexOf('--verbose') >= 0;
|
||||
@@ -230,6 +232,27 @@ const Server = http.createServer((Req, Res) => {
|
||||
}).end(Hook);
|
||||
return;
|
||||
}
|
||||
// /__probe__:页面内探针的**网络转储**入口(控制台刷屏太扰,改为 POST 落盘)
|
||||
// 探针(sockets/probe_railgun.js)把组件清单与采样分批 POST 到这里,
|
||||
// 这里追加写入 3DTank/names/probe_railgun.log,供离线分析。
|
||||
if (UrlPath.split('?')[0] === '/__probe__') {
|
||||
if (Req.method !== 'POST') {
|
||||
Res.writeHead(405, { 'Content-Type': 'text/plain; charset=utf-8' }).end('POST only');
|
||||
return;
|
||||
}
|
||||
const Chunks = [];
|
||||
Req.on('data', (C) => Chunks.push(C));
|
||||
Req.on('end', () => {
|
||||
const Body = Buffer.concat(Chunks).toString('utf8');
|
||||
try {
|
||||
fs.appendFileSync(PROBE_LOG, Body.endsWith('\n') ? Body : Body + '\n');
|
||||
} catch (Err) {
|
||||
console.warn('[probe] 落盘失败: ' + Err.message);
|
||||
}
|
||||
Res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8', 'Cache-Control': 'no-cache' }).end('ok');
|
||||
});
|
||||
return;
|
||||
}
|
||||
// /config.xml 本地覆盖:存在 local/config.xml 时用它,把游戏指向本地 wss_server
|
||||
if (!REAL_CONFIG && UrlPath.split('?')[0] === '/config.xml' && fs.existsSync(LOCAL_CONFIG)) {
|
||||
const Conf = fs.readFileSync(LOCAL_CONFIG);
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// probe_railgun.js (v4) —— 电磁炮链路探针:**网络转储**版(不再刷控制台)
|
||||
//
|
||||
// 数据流向:探针 → POST /__probe__ → serve.js 追加写 3DTank/names/probe_railgun.log
|
||||
// 分析时直接读那个文件即可(离线 grep)。
|
||||
//
|
||||
// 内容:
|
||||
// ① 全量组件清单:玩家坦克 oyt_1.c2a_1 里每个组件(#id + 构造函数头 + 字段名)
|
||||
// ② 现场采样:每 300ms 只记录**发生变化**的采样对象(z4t / PYt / 模式对象 vWt·pWt 等)
|
||||
// ③ WS 上行:包装 WebSocket.prototype.send,记录帧长 + 前 16 字节(用于对齐开火时刻)
|
||||
//
|
||||
// 用法:进战场后整段粘到控制台。停:__probeRailgun__(false)
|
||||
// 若 POST 不通会自动回落到 console(并有 [probe] 提示)。
|
||||
(function () {
|
||||
'use strict';
|
||||
var ENDPOINT = '/__probe__';
|
||||
var T0 = (window.performance && performance.now) ? performance.now() : Date.now();
|
||||
var Now = function () { return +(((window.performance && performance.now) ? performance.now() : Date.now()) - T0).toFixed(1); };
|
||||
var Src = function (o) { try { return String(o).replace(/\s+/g, ' '); } catch (e) { return '?'; } };
|
||||
var Ctor = function (o) { try { return Src(o.constructor).slice(0, 90); } catch (e) { return '?'; } };
|
||||
var Num = function (v) {
|
||||
if (v === null) return 'null';
|
||||
if (v === undefined) return 'undef';
|
||||
if (typeof v === 'number') return Math.round(v * 1000) / 1000;
|
||||
if (typeof v === 'boolean') return v;
|
||||
if (typeof v === 'object') { try { return 'obj:' + Src(v.constructor).slice(9, 24); } catch (e) { return 'obj'; } }
|
||||
return String(v).slice(0, 24);
|
||||
};
|
||||
|
||||
// ---- 转储队列 ----
|
||||
var Buf = [];
|
||||
var Fallback = false;
|
||||
var flush = function () {
|
||||
if (!Buf.length) return;
|
||||
var text = Buf.join('\n') + '\n';
|
||||
Buf = [];
|
||||
if (Fallback) { console.log('[probe] ' + text); return; }
|
||||
try {
|
||||
fetch(ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'text/plain;charset=utf-8' }, body: text })
|
||||
.then(function (R) { if (!R.ok) throw new Error('HTTP ' + R.status); })
|
||||
.catch(function (E) {
|
||||
Fallback = true;
|
||||
console.warn('[probe] POST 失败,回落控制台: ' + (E && E.message));
|
||||
Buf.push(text.trim());
|
||||
flush();
|
||||
});
|
||||
} catch (e) { Fallback = true; }
|
||||
};
|
||||
var LOG = function () {
|
||||
var s = '[+' + Now() + 'ms] ' + [].slice.call(arguments).map(function (a) { return typeof a === 'string' ? a : JSON.stringify(a); }).join(' ');
|
||||
Buf.push(s);
|
||||
if (Buf.length >= 40) flush();
|
||||
};
|
||||
|
||||
// ---- ③ WS 上行记录 ----
|
||||
try {
|
||||
var W = window.WebSocket;
|
||||
if (W && W.prototype && !W.prototype.__probeWrapped__) {
|
||||
var Orig = W.prototype.send;
|
||||
W.prototype.send = function (d) {
|
||||
try {
|
||||
if (d && d.byteLength !== undefined) {
|
||||
var u = new Uint8Array(d.slice ? d.slice(0, 16) : d, 0, Math.min(16, d.byteLength));
|
||||
var h = '';
|
||||
for (var i = 0; i < u.length; i++) h += (u[i] < 16 ? '0' : '') + u[i].toString(16);
|
||||
Buf.push('[+' + Now() + 'ms] SEND len=' + d.byteLength + ' hex=' + h);
|
||||
}
|
||||
} catch (e) {}
|
||||
return Orig.apply(this, arguments);
|
||||
};
|
||||
W.prototype.__probeWrapped__ = true;
|
||||
Buf.push('WS send 已挂(记帧长+前16字节)');
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
var Gm = window.__gameMode__;
|
||||
if (!Gm) { LOG('__gameMode__ 未就绪'); flush(); return; }
|
||||
LOG('location=' + location.href + ' 87072含c18v_1=' + (String(window.__modules__ && window.__modules__[87072] || '').indexOf('c18v_1') >= 0));
|
||||
|
||||
// ---- 找玩家坦克(取组件数最多者)----
|
||||
var Tank = null, Best = -1;
|
||||
[Gm.y100_1, Gm.x100_1, Gm.fzr_1, Gm.ezr_1].forEach(function (t) {
|
||||
if (!t || !t.oyt_1 || !t.oyt_1.c2a_1) return;
|
||||
var n = 0;
|
||||
try {
|
||||
var m = t.oyt_1.c2a_1;
|
||||
if (typeof m.forEach === 'function' && !(m instanceof Array)) m.forEach(function () { n++; });
|
||||
else n = Object.keys(m).length;
|
||||
} catch (e) {}
|
||||
if (n > Best) { Best = n; Tank = t; }
|
||||
});
|
||||
if (!Tank) { LOG('没找到带 oyt_1.c2a_1 的坦克'); flush(); return; }
|
||||
LOG('玩家坦克组件数=' + Best + ' ctor=' + Ctor(Tank));
|
||||
|
||||
// ---- ① 全量清单 ----
|
||||
var Entries = [];
|
||||
(function () {
|
||||
var m = Tank.oyt_1.c2a_1;
|
||||
if (typeof m.forEach === 'function' && !(m instanceof Array)) m.forEach(function (v, k) { Entries.push([k, v]); });
|
||||
else Object.keys(m).forEach(function (k) { Entries.push([k, m[k]]); });
|
||||
})();
|
||||
LOG('===== 全部组件 ' + Entries.length + ' 个 =====');
|
||||
Entries.forEach(function (e) { LOG('#' + e[0] + ' ' + Ctor(e[1]) + ' ▸ ' + (e[1] ? Object.keys(e[1]).join(',') : '(无)')); });
|
||||
LOG('===== 组件清单结束 =====');
|
||||
|
||||
// ---- ② 采样 ----
|
||||
var Watch = [];
|
||||
Entries.forEach(function (e) {
|
||||
var v = e[1];
|
||||
if (!v) return;
|
||||
var s = Src(v.constructor);
|
||||
if (/function (z4t|PYt|h_n|vWt|pWt|xen)\(/.test(s) || (typeof v.j3t === 'function' && typeof v.p10g === 'function') ||
|
||||
('l1ah_1' in v) || ('c18v_1' in v) || ('q1bx_1' in v) || ('h1bh_1' in v)) {
|
||||
Watch.push({ id: e[0], o: v });
|
||||
}
|
||||
});
|
||||
LOG('采样对象 ' + Watch.length + ' 个: ' + Watch.map(function (w) { return '#' + w.id + ' ' + Ctor(w.o).slice(9, 32); }).join(' | '));
|
||||
|
||||
var Last = '';
|
||||
var Timer = setInterval(function () {
|
||||
var Out = {};
|
||||
Watch.forEach(function (w) {
|
||||
var o = w.o, d = {};
|
||||
Object.keys(o).forEach(function (k) {
|
||||
var v = o[k];
|
||||
if (typeof v === 'number' || typeof v === 'boolean' || v === null) d[k] = Num(v);
|
||||
else if (v && typeof v === 'object' && typeof v.f20_1 === 'number') d[k] = 'V(' + Num(v.f20_1) + ',' + Num(v.g20_1) + ',' + Num(v.h20_1) + ')';
|
||||
});
|
||||
Out['#' + w.id + ' ' + Ctor(o).slice(9, 32)] = d;
|
||||
});
|
||||
var cur = JSON.stringify(Out);
|
||||
if (cur !== Last) { Last = cur; LOG('SAMPLE ' + cur); }
|
||||
flush();
|
||||
}, 300);
|
||||
|
||||
window.addEventListener('beforeunload', flush);
|
||||
window.__probeRailgun__ = function (a) {
|
||||
if (a === false) { clearInterval(Timer); flush(); return 'stopped'; }
|
||||
if (a === 'flush') { flush(); return 'flushed'; }
|
||||
return 'running';
|
||||
};
|
||||
flush();
|
||||
console.log('[probe] 已启动:网络转储 → ' + ENDPOINT + '(落盘 3DTank/names/probe_railgun.log)。停:__probeRailgun__(false)');
|
||||
})();
|
||||
Reference in new issue
Block a user