feat(hornet): 十字军底盘控制探针 + 中继 config.xml 动态改写
- 新增 sockets/probe_chassis.js:采样 Cyt.j14k_1 / Zmt.r14f_1 三轴并 hook Cyt.wak / Zmt.wak,转储到 names/probe_chassis.log - serve.js:新增 /probe_chassis.js 路由与 /__probe__?f= 落盘选择;新增 --relay-port 动态改写 config.xml 指向本机中继(不落盘、不依赖 local/config.xml) - main.9f4e7a89.js:探针块改为自动加载 railgun + chassis 两个探针 - Start-LocalAndRelay.ps1:-WithRelay 改传 --relay-port,修复「前端未走中继」的根因
This commit is contained in:
1 parent
12e547f32f
commit
512c718d38
4 files changed
+323
-20
No files matched your search
@@ -5,7 +5,7 @@
|
||||
.DESCRIPTION
|
||||
- 本地镜像:把官方 web 客户端在 http://localhost:<MirrorPort>/ 跑起来
|
||||
· web/ 提供首页与镜像资源;/play/static/js/** 直接挂 3dtank.com 的切割版 JS
|
||||
· /config.xml 优先用 3DTank/local/config.xml(指向本地中继 9091),否则代理真实 config
|
||||
· /config.xml:-WithRelay 时由 serve.js 动态改写为 ws://localhost:<RelayPort>/(不落盘);否则代理真实 config
|
||||
- 中继:客户端 → ws://localhost:<RelayPort>/ → 真实服务器(抓包落盘 + 实时解密/解析)
|
||||
· 产物目录:3DTank/packets/<label>/session_00N/
|
||||
- 依赖:中继需要 ws(Node 自带的是客户端 WebSocket,服务端不够用),脚本会自动装
|
||||
@@ -149,9 +149,9 @@ if ($WithRelay) {
|
||||
|
||||
# ---------- 5) 启动本地镜像 ----------
|
||||
# 默认加 --real-config:/config.xml 取真实站点,游戏因此直连 wss://c1.3dtank.com:9090/
|
||||
# 走中继(-WithRelay)时不加它,改用 local/config.xml 把客户端指向本机 127.0.0.1:<RelayPort>
|
||||
# 走中继(-WithRelay)时改用 --relay-port:serve.js 动态把 config 改写为 ws://localhost:<RelayPort>/(不落盘)
|
||||
$MirrorArgs = @('serve.js', '--port', "$MirrorPort")
|
||||
if (-not $WithRelay) { $MirrorArgs += '--real-config' }
|
||||
if ($WithRelay) { $MirrorArgs += @('--relay-port', "$RelayPort") } else { $MirrorArgs += '--real-config' }
|
||||
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' `
|
||||
|
||||
+73
-6
@@ -24,8 +24,11 @@ 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');
|
||||
// 页面内探针的网络转储落盘位置:POST /__probe__?f=<name> 追加写对应文件(不传 f 默认 railgun)
|
||||
const PROBE_LOGS = {
|
||||
railgun: path.join(PROJECT, 'names', 'probe_railgun.log'),
|
||||
chassis: path.join(PROJECT, 'names', 'probe_chassis.log'),
|
||||
};
|
||||
const CACHE_DIR = path.join(PROJECT, 'cache');
|
||||
const NO_PROXY = Argv.indexOf('--no-proxy') >= 0;
|
||||
const VERBOSE = Argv.indexOf('--verbose') >= 0;
|
||||
@@ -33,6 +36,9 @@ 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;
|
||||
// 中继模式:--relay-port <n> 时,/config.xml **动态改写**为指向本机 ws://localhost:<n>/
|
||||
// (每次实时生成,不落盘、不依赖 local/config.xml;address→localhost、wsProtocol→WS、port→n)
|
||||
const RELAY_PORT = Number(Arg('relay-port', '0')) || 0;
|
||||
// --ws-hook:服务 index.html 时注入页面内 ws 抓包脚本(sockets/ws_hook.js)。
|
||||
// 刻意用**启动参数**而不是 URL 开关:游戏用 location.hash 做路由/状态,
|
||||
// 任何 hash(哪怕只是 #hook)都会让它进不去战场。
|
||||
@@ -169,6 +175,39 @@ async function HandleProxy(Rule, Req, Res) {
|
||||
}
|
||||
}
|
||||
|
||||
// 中继模式:取真实 config.xml(本地缓存优先,否则回源 c1),把 address/wsProtocol/port 改写成
|
||||
// 指向本机中继 ws://localhost:<RELAY_PORT>/,每次实时生成返回给客户端(不落盘)。
|
||||
async function ServeRewrittenConfig(Res) {
|
||||
const CacheFile = path.join(CACHE_DIR, 'c1.3dtank.com', 'config.xml');
|
||||
let Xml = '';
|
||||
try {
|
||||
if (fs.existsSync(CacheFile)) {
|
||||
Xml = fs.readFileSync(CacheFile, 'utf8');
|
||||
} else {
|
||||
const Up = await fetch('https://c1.3dtank.com/config.xml', { headers: UPSTREAM_HEADERS, redirect: 'follow' });
|
||||
if (Up.ok) Xml = await Up.text();
|
||||
}
|
||||
} catch (Err) {
|
||||
console.log('[relay-config] 取真实 config 失败: ' + Err.message);
|
||||
}
|
||||
if (!Xml || Xml.indexOf('<server') < 0) {
|
||||
// 兜底模板(与 c1 实际内容同构)
|
||||
Xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><cfg xmlns="http://alternativaplatform.com/core/config.xsd"><server address="c1.3dtank.com" version="" mode="simple" wsProtocol="WSS"><ports/><webSocketPorts><port>9090</port></webSocketPorts></server><status>normal</status></cfg>';
|
||||
}
|
||||
const Out = Xml
|
||||
.replace(/(<server\b[^>]*\baddress=")[^"]*(")/, '$1localhost$2')
|
||||
.replace(/(<server\b[^>]*\bwsProtocol=")[^"]*(")/, '$1WS$2')
|
||||
.replace(/(<webSocketPorts>\s*<port>)\d+(<\/port>)/, '$1' + RELAY_PORT + '$2');
|
||||
const Buf = Buffer.from(Out, 'utf8');
|
||||
Res.writeHead(200, {
|
||||
'Content-Type': 'application/xml; charset=utf-8',
|
||||
'Content-Length': Buf.length,
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Hornet-Config': 'relay',
|
||||
});
|
||||
Res.end(Buf);
|
||||
}
|
||||
|
||||
const Server = http.createServer((Req, Res) => {
|
||||
const UrlPath = Req.url.split('#')[0];
|
||||
// 浏览器 → 本地落盘:控制台脚本把 dump 结果 POST 上来,写到 names/ 下
|
||||
@@ -249,20 +288,37 @@ const Server = http.createServer((Req, Res) => {
|
||||
}).end(Probe);
|
||||
return;
|
||||
}
|
||||
// /probe_chassis.js:十字军/悬浮底盘「旋转 + 移动」控制链路探针(数据 POST /__probe__?f=chassis)
|
||||
if (UrlPath.split('?')[0] === '/probe_chassis.js') {
|
||||
const ProbeFile = path.join(ROOT, 'sockets', 'probe_chassis.js');
|
||||
if (!fs.existsSync(ProbeFile)) {
|
||||
Res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' }).end('probe_chassis.js not found');
|
||||
return;
|
||||
}
|
||||
const Probe = fs.readFileSync(ProbeFile);
|
||||
Res.writeHead(200, {
|
||||
'Content-Type': 'application/javascript; charset=utf-8',
|
||||
'Content-Length': Probe.length,
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Hornet-Probe': 'local',
|
||||
}).end(Probe);
|
||||
return;
|
||||
}
|
||||
// /__probe__:页面内探针的**网络转储**入口(控制台刷屏太扰,改为 POST 落盘)
|
||||
// 探针(sockets/probe_railgun.js)把组件清单与采样分批 POST 到这里,
|
||||
// 这里追加写入 3DTank/names/probe_railgun.log,供离线分析。
|
||||
// 探针把组件清单与采样分批 POST 到这里;?f=<name> 选择落盘文件(railgun / chassis),默认 railgun。
|
||||
if (UrlPath.split('?')[0] === '/__probe__') {
|
||||
if (Req.method !== 'POST') {
|
||||
Res.writeHead(405, { 'Content-Type': 'text/plain; charset=utf-8' }).end('POST only');
|
||||
return;
|
||||
}
|
||||
const FMatch = /(?:^|&)f=([a-z0-9_]+)/i.exec(UrlPath.split('?')[1] || '');
|
||||
const LogFile = PROBE_LOGS[FMatch ? FMatch[1].toLowerCase() : 'railgun'] || PROBE_LOGS.railgun;
|
||||
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');
|
||||
fs.appendFileSync(LogFile, Body.endsWith('\n') ? Body : Body + '\n');
|
||||
} catch (Err) {
|
||||
console.warn('[probe] 落盘失败: ' + Err.message);
|
||||
}
|
||||
@@ -270,6 +326,15 @@ const Server = http.createServer((Req, Res) => {
|
||||
});
|
||||
return;
|
||||
}
|
||||
// /config.xml 中继模式:动态改写为指向本机 ws://localhost:<RELAY_PORT>/(不落盘、不依赖 local/config.xml)
|
||||
if (RELAY_PORT > 0 && UrlPath.split('?')[0] === '/config.xml') {
|
||||
ServeRewrittenConfig(Res).catch((Err) => {
|
||||
console.log('[relay-config] 生成失败: ' + Err.message);
|
||||
if (!Res.headersSent) Res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
|
||||
Res.end('relay-config error: ' + Err.message);
|
||||
});
|
||||
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);
|
||||
@@ -332,6 +397,8 @@ Server.listen(PORT, () => {
|
||||
console.log(' 站点根 : ' + WEB_DIR);
|
||||
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(' config.xml: ' + (RELAY_PORT > 0
|
||||
? ('中继改写 → ws://localhost:' + RELAY_PORT + '/(动态生成,不落盘)')
|
||||
: (!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)'));
|
||||
});
|
||||
@@ -155,9 +155,9 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 自动加载武器链路探针(默认开;localStorage.hornetProbe='0' 关闭)=====
|
||||
// ===== 自动加载链路探针(默认开;localStorage.hornetProbe='0' 关闭)=====
|
||||
// 开关只能用 localStorage:hash 会被游戏当路由(进不去战场),查询串会被当 config 参数(握手 1005)。
|
||||
// 探针数据走 POST /__probe__ 落盘到 3DTank/names/probe_railgun.log,不刷控制台。
|
||||
// 探针数据走 POST /__probe__ 落盘:武器链路 → probe_railgun.log,底盘控制 → probe_chassis.log。
|
||||
console.log('[Hornet] 探针块已执行 (localStorage.hornetProbe=' + JSON.stringify(localStorage.getItem('hornetProbe')) + ')');
|
||||
if (localStorage.getItem('hornetProbe') !== '0') {
|
||||
(async () => {
|
||||
@@ -166,15 +166,22 @@ try {
|
||||
await new Promise((R) => setTimeout(R, 1000));
|
||||
}
|
||||
if (!window.__gameMode__) { console.warn('[Hornet] 探针跳过:等不到 __gameMode__'); return; }
|
||||
try {
|
||||
const __probe_resp__ = await fetch('/probe_railgun.js', { cache: 'no-cache' });
|
||||
if (__probe_resp__.ok) {
|
||||
(0, eval)(await __probe_resp__.text());
|
||||
console.log('[Hornet] 探针已自动加载并开始转储(localStorage.hornetProbe="0" 可关闭)');
|
||||
} else {
|
||||
console.warn('[Hornet] 探针加载失败: HTTP ' + __probe_resp__.status);
|
||||
}
|
||||
} catch (__probe_err__) { console.warn('[Hornet] 探针加载异常:', __probe_err__); }
|
||||
const __probes__ = [
|
||||
['/probe_railgun.js', '武器链路探针'],
|
||||
['/probe_chassis.js', '底盘控制探针'],
|
||||
];
|
||||
for (let k = 0; k < __probes__.length; k++) {
|
||||
const __url__ = __probes__[k][0], __name__ = __probes__[k][1];
|
||||
try {
|
||||
const __probe_resp__ = await fetch(__url__, { cache: 'no-cache' });
|
||||
if (__probe_resp__.ok) {
|
||||
(0, eval)(await __probe_resp__.text());
|
||||
console.log('[Hornet] ' + __name__ + ' 已自动加载并开始转储(localStorage.hornetProbe="0" 可关闭)');
|
||||
} else {
|
||||
console.warn('[Hornet] ' + __name__ + ' 加载失败: HTTP ' + __probe_resp__.status);
|
||||
}
|
||||
} catch (__probe_err__) { console.warn('[Hornet] ' + __name__ + ' 加载异常:', __probe_err__); }
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
// 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;
|
||||
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;
|
||||
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 (Found !== Tank) { Tank = Found; LOG('玩家坦克组件数=' + Best + ' ctor=' + Ctor(Tank)); }
|
||||
Cyt = c; Zmt = z; Eyt = e; Iyt = iy; InputS = isv;
|
||||
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'));
|
||||
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));
|
||||
}
|
||||
};
|
||||
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');
|
||||
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)');
|
||||
})();
|
||||
Reference in new issue
Block a user