- tools/serve.js: POST /__probe__ 追加写 3DTank/names/probe_railgun.log - sockets/probe_railgun.js: 电磁炮/武器链路现场探针(组件清单 + 采样 + WS 上行帧),fetch 批量转储,POST 不通自动回落控制台
144 lines
6.2 KiB
JavaScript
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)');
|
|
})();
|