Files
TankIC/sockets/probe_railgun.js
T
TermiNexus 2b95e5de28 chore(3DTank): 新增页面内探针的网络转储通道(/__probe__ + probe_railgun.js)
- tools/serve.js: POST /__probe__ 追加写 3DTank/names/probe_railgun.log
- sockets/probe_railgun.js: 电磁炮/武器链路现场探针(组件清单 + 采样 + WS 上行帧),fetch 批量转储,POST 不通自动回落控制台
2026-10-12 10:13:32 +08:00

144 lines
6.2 KiB
JavaScript

// 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)');
})();