// probe_chassis.js (v1) —— 十字军 / 悬浮底盘「旋转 + 移动」控制链路探针(网络转储版) // // 数据流向:探针 → POST /__probe__?f=chassis → serve.js 追加写 3DTank/names/probe_chassis.log // 分析时直接读文件(离线 grep),不刷控制台。 // // 采什么(对应 module_87072 里的四段链路): // ① 组件清单定位:Cyt(输入组件,持 j14k_1)/ Zmt(悬浮物理,持 r14f_1·q14f_1) // / Eyt(上行桥,持 k14l_1=serverInterface)/ Iyt(注入通道,持 t14l_1) // ② Hook Cyt.wak —— 本地输入合成结果 j14k_1(forward / right / turnLeft) 三轴 // ③ Hook Zmt.wak —— 悬浮物理每帧:r14f_1 / q14f_1 三轴(变化即记) // ④ Hook serverInterface.s14j —— 悬浮控制上行真实参数(时间 + 三轴)★核心 // ⑤ Hook serverInterface.p14n —— 履带控制上行(对照,确认量纲差异) // ⑥ 扫描 inputSettingsService.ua2() 的前进/侧移刻度(ja3_1) 与转向刻度(ka3_1) // // 用法:加载器会自动引入;进战场后推杆走几下 / 转个圈 / 侧移一下,日志即落盘。 // 手动全量:__probeChassis__('dump') 停止:__probeChassis__(false) (function () { 'use strict'; var ENDPOINT = '/__probe__?f=chassis'; 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 === 'string') return v.slice(0, 24); 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-chassis] ' + 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-chassis] 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(); }; // ---- 状态 ---- var Tank = null, Cyt = null, Zmt = null, Eyt = null, Iyt = null, InputS = null, SI = null, Mount = null, Turret = null; var LastSeen = {}; var Once = function (key, val) { var s = JSON.stringify(val); if (LastSeen[key] === s) return false; LastSeen[key] = s; return true; }; // Qmt 三轴:k149_1=前进 l149_1=侧移 m149_1=转向 var Axes = function (o) { if (!o) return null; return { fwd: Num(o.k149_1), right: Num(o.l149_1), turn: Num(o.m149_1) }; }; var Shallow = function (o) { var d = {}; try { Object.keys(o).forEach(function (k) { var v = o[k]; if (typeof v === 'number' || typeof v === 'boolean' || typeof v === 'string' || v === null) d[k] = Num(v); else if (v && typeof v.length === 'number' && (v instanceof Array || typeof v.slice === 'function')) d[k] = 'arr[' + v.length + ']=' + [].slice.call(v, 0, 16).map(Num).join('/'); }); } catch (e) {} return d; }; // ---- 定位(可重试;坦克会随进战场/重建而变化)---- var CompMapOf = function (t) { if (!t) return null; if (t.oyt_1 && t.oyt_1.c2a_1) return t.oyt_1.c2a_1; // 新版组件表 if (t.pyt_1 && t.pyt_1.s2a_1) return t.pyt_1.s2a_1; // 旧版组件表 return null; }; var Locate = function () { var Gm = window.__gameMode__; if (!Gm) return false; var Best = -1, Found = null; [Gm.y100_1, Gm.x100_1, Gm.fzr_1, Gm.ezr_1].forEach(function (t) { var m = CompMapOf(t); if (!m) return; var n = 0; try { 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; Found = t; } }); if (!Found) return false; var Entries = []; (function () { var m = CompMapOf(Found); 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]]); }); })(); var c = null, z = null, e = null, iy = null, isv = null, mt = null, tu = null; Entries.forEach(function (en) { var v = en[1]; if (!v) return; if (!c && 'j14k_1' in v && 'n14k_1' in v) c = v; // 输入组件 if (!z && 'r14f_1' in v && 'q14f_1' in v && 's14f_1' in v) z = v; // 悬浮物理 if (!e && 'k14l_1' in v && 'l14l_1' in v) e = v; // 上行桥 if (!iy && 't14l_1' in v && 'r14l_1' in v && 'u14l_1' in v) iy = v; // 注入通道 if (!isv && typeof v.ua2 === 'function') isv = v; // inputSettingsService if (!mt && 'azi_1' in v && 'dzi_1' in v && 'ezi_1' in v && 'mzi_1' in v) mt = v; // weaponMount(炮塔朝向矩阵) if (!tu && 'tzp_1' in v && 'pzp_1' in v && 'rzp_1' in v && 'lzp_1' in v) tu = v; // oxt(炮塔控制器) }); // 只在有实质变化时更新(避免坦克重建后仍指向旧对象) if (Found !== Tank) { Tank = Found; LOG('玩家坦克组件数=' + Best + ' ctor=' + Ctor(Tank)); } Cyt = c; Zmt = z; Eyt = e; Iyt = iy; InputS = isv; Mount = mt; Turret = tu; return !!(Cyt || Zmt); }; var Dump = function () { LOG('===== 底盘组件定位 ====='); LOG('Cyt ' + (Cyt ? Ctor(Cyt) + ' ▸ ' + Object.keys(Cyt).join(',') : 'null')); LOG('Zmt ' + (Zmt ? Ctor(Zmt) + ' ▸ ' + Object.keys(Zmt).join(',') : 'null')); LOG('Eyt ' + (Eyt ? Ctor(Eyt) + ' ▸ ' + Object.keys(Eyt).join(',') : 'null')); LOG('Iyt ' + (Iyt ? Ctor(Iyt) + ' ▸ ' + Object.keys(Iyt).join(',') : 'null')); LOG('Mount ' + (Mount ? Ctor(Mount) + ' ▸ ' + Object.keys(Mount).join(',') : 'null')); LOG('Turret ' + (Turret ? Ctor(Turret) + ' ▸ ' + Object.keys(Turret).join(',') : 'null')); if (Cyt) LOG('Cyt.j14k_1 ctor=' + Ctor(Cyt.j14k_1) + ' 三轴=' + JSON.stringify(Axes(Cyt.j14k_1))); if (Zmt) LOG('Zmt.r14f_1 三轴=' + JSON.stringify(Axes(Zmt.r14f_1)) + ' q14f_1 三轴=' + JSON.stringify(Axes(Zmt.q14f_1))); if (InputS) { LOG('inputSettings ctor=' + Ctor(InputS)); try { LOG('ua2()=' + JSON.stringify(Shallow(InputS.ua2()))); } catch (err) { LOG('ua2() 读取失败 ' + err.message); } } else { LOG('inputSettingsService 未在坦克组件表里找到(可能在场景服务层)'); } LOG('===== 定位结束 ====='); }; // ---- Hook(实例自有属性覆盖原型方法,去重记录)---- var Report = function (tag, self, args) { if (tag === 'CYTAWAK') { var a = Axes(self.j14k_1); if (Once('cyt', a)) LOG('Cyt.wak j14k_1=' + JSON.stringify(a) + ' k14k_1=' + Num(self.k14k_1) + ' l14k_1=' + Num(self.l14k_1) + ' m14k_1=' + Num(self.m14k_1)); } else if (tag === 'ZMTAWAK') { var b = Axes(self.r14f_1); if (Once('zmt', b)) LOG('Zmt.wak r14f_1=' + JSON.stringify(b) + ' q14f_1=' + JSON.stringify(Axes(self.q14f_1))); } else if (tag === 'S14J') { LOG('s14j 悬浮上行 t=' + Num(args[0]) + ' ctrl=' + JSON.stringify(Axes(args[1]))); } else if (tag === 'P14N') { var c = args[1]; LOG('p14n 履带上行 t=' + Num(args[0]) + ' fwd=' + Num(c && c.m14b_1) + ' turn=' + Num(c && c.n14b_1) + ' revTurn=' + Num(c && c.o14b_1)); } else if (tag === 'OXTWAK') { // 炮塔控制器每帧:azi_1=目标角,mode=输入模式,rzp_1=输入,lzp_1=当前转速 var z = Num(self.azi_1); if (Once('oxt', z + '|' + Num(self.pzp_1 && self.pzp_1.k3_1))) { LOG('oxt.wak azi_1=' + z + ' mode=' + Num(self.pzp_1 && self.pzp_1.k3_1) + ' rzp_1=' + Num(self.rzp_1) + ' lzp_1=' + Num(self.lzp_1) + ' jzp_1=' + Num(self.jzp_1)); } } else if (tag === 'QYR') { // 炮塔矩阵每帧插值:azi_1=目标角,dzi_1=显示角,czi_1=弹簧补偿,args[0]=渲染插值系数 var R2 = function (x) { return typeof x === 'number' ? Math.round(x * 100) / 100 : Num(x); }; var Key = R2(self.azi_1) + '|' + R2(self.dzi_1) + '|' + R2(self.czi_1) + '|' + R2(args[0]); if (Once('qyr', Key)) { LOG('qyr t=' + R2(args[0]) + ' azi_1=' + R2(self.azi_1) + ' dzi_1=' + R2(self.dzi_1) + ' czi_1=' + R2(self.czi_1)); } } }; var Wrap = function (obj, name, tag) { if (!obj || typeof obj[name] !== 'function' || obj['__hk_' + name]) return false; var Orig = obj[name]; obj[name] = function () { var R = Orig.apply(this, arguments); try { Report(tag, this, arguments); } catch (e) {} return R; }; obj['__hk_' + name] = true; return true; }; window.addEventListener('beforeunload', flush); LOG('===== probe_chassis v1 启动 location=' + location.href + ' ====='); // ---- 主循环:定位 → 挂 Hook → 采样 ---- var Hooked = false, Tries = 0; var R = function () { Tries++; if (!Hooked) { if (Locate()) { Dump(); var Hit = []; if (Wrap(Cyt, 'wak', 'CYTAWAK')) Hit.push('Cyt.wak'); if (Wrap(Zmt, 'wak', 'ZMTAWAK')) Hit.push('Zmt.wak'); if (Wrap(Turret, 'wak', 'OXTWAK')) Hit.push('oxt.wak(炮塔控制器)'); if (Wrap(Mount, 'qyr', 'QYR')) Hit.push('weaponMount.qyr(炮塔矩阵)'); Hooked = true; LOG('已挂 Hook: ' + (Hit.join(' | ') || '(无)')); } else if (Tries % 15 === 0) { LOG('等待玩家坦克 / 底盘组件… 第 ' + Tries + ' 次'); } } else { Locate(); // 坦克重建后刷新引用 } // serverInterface(Eyt.k14l_1)通常在进战场后才赋值,延后补挂 if (!SI && Eyt && Eyt.k14l_1) { SI = Eyt.k14l_1; var Hit2 = []; if (Wrap(SI, 's14j', 'S14J')) Hit2.push('serverInterface.s14j(悬浮)'); if (Wrap(SI, 'p14n', 'P14N')) Hit2.push('serverInterface.p14n(履带)'); LOG('已挂 Hook: ' + (Hit2.join(' | ') || '(无)')); } // 兜底采样 if (Cyt && Once('cyt-sample', Axes(Cyt.j14k_1))) LOG('SAMPLE Cyt.j14k_1=' + JSON.stringify(Axes(Cyt.j14k_1))); if (Zmt && Once('zmt-sample', Axes(Zmt.r14f_1))) LOG('SAMPLE Zmt.r14f_1=' + JSON.stringify(Axes(Zmt.r14f_1))); flush(); if (Tries > 900) { clearInterval(Timer); LOG('探针自动停止(已运行 900 拍)'); } }; var Timer = setInterval(R, 200); R(); window.__probeChassis__ = function (a) { if (a === false) { clearInterval(Timer); flush(); return 'stopped'; } if (a === 'flush') { flush(); return 'flushed'; } if (a === 'dump') { Locate(); Dump(); flush(); return 'dumped'; } return 'running'; }; console.log('[probe-chassis] 已启动:网络转储 → ' + ENDPOINT + '(落盘 3DTank/names/probe_chassis.log)。停:__probeChassis__(false)'); })();