工具链(3DTank/tools/):extract(模块提取)、mine_evidence/apply_names(命名证据与别名回填)、extract_codec_fields(字段表 + 全库槽位类型 + PrimClassWire/ResourceClasses)、mine_downlink(下行字段表)、extract_space_init/rebase_space_init/audit_space_init(空间模板与记录切分审计)、extract_space_session(把真实会话切成按上行触发的回放模板)、pair_flow(请求→应答配对)、probe_containers、ingest/decrypt_frames(抓包入库与解密)、serve/fetch_assets(本地站点镜像)、import_db(抓包对象仓库导入 MySQL)。 服务器(3DTank/tools/server/):link/entry/codec/container/dispatch/world/space/semantic(协议层与 lt/pt/zt 三个容器布局)、session(会话回放,顺序推进)、db + schema.sql(MySQL 连接层与对象仓库表结构)、store/responder(查库组帧与定向应答;接管范围默认只开 ba.lcr)。 同时修 pair_flow 的时间轴偏移:timeline_c2s 与 timeline_s2c 的 0 点相差 144~4453ms,改前配对整体偏约 2 秒(实测 space-100073070 偏 2084ms),改后 22.4% 的触发者被纠正。 src/、src_named/、packets/、3DTank/local/(含数据库口令)不进库,可用上述工具重新生成。
249 lines
8.6 KiB
JavaScript
249 lines
8.6 KiB
JavaScript
// client_registry_dump.js - dump 协议注册表(空间层命令语义的正解)
|
||
// 前置:本地镜像的 module_61860.js 已加 3 行 [Hornet 调试] 暴露句柄(见下),
|
||
// 所以这里不用再"猜着找",直接读 window 上挂好的实例。
|
||
// window.__HornetModelReg__ = mr RPC 分发器 : v7n_1(methodId→modelId) / u7n_1(modelId→模型)
|
||
// / w7n_1(modelId→codec) / t7n_1 / s7n_1
|
||
// window.__HornetSpaceReg__ = or 空间命令注册表 : x7m_1 / y7m_1(id→codec)
|
||
// window.__HornetConn__ = ur 连接实例 : o7m_1(懒容器,含 "spaceRegistry") / p7m_1(分发器)
|
||
// 用法:进游戏(大厅/车库都行)→ 控制台整段粘贴 → copy(JSON.stringify(window.__regDump__))
|
||
(function () {
|
||
// ---------- 自检:确认浏览器跑的是"带钩子"的那份 module_61860 ==========
|
||
var M = window.__modules__ && window.__modules__[61860];
|
||
var SelfCheck = {
|
||
url: location.href,
|
||
frames: window.frames.length,
|
||
modules: window.__modules__ ? Object.keys(window.__modules__).length : 0,
|
||
mod61860Loaded: !!M,
|
||
mod61860HasHook: M ? String(M).indexOf('__Hornet') >= 0 : null,
|
||
wpRequire: typeof window.__wp_require__,
|
||
gameMode: typeof window.__gameMode__,
|
||
handleModelReg: typeof window.__HornetModelReg__,
|
||
handleSpaceReg: typeof window.__HornetSpaceReg__,
|
||
handleConn: typeof window.__HornetConn__,
|
||
};
|
||
console.log('[RegDump/自检] ' + JSON.stringify(SelfCheck));
|
||
if (SelfCheck.mod61860Loaded && SelfCheck.mod61860HasHook === false) {
|
||
console.warn('[RegDump/自检] 加载的是旧模块(无 __Hornet 钩子)→ 请硬刷新(Ctrl+Shift+R)或勾上 DevTools 的 Disable cache 再刷新');
|
||
}
|
||
if (SelfCheck.mod61860HasHook === true && SelfCheck.handleConn === 'undefined') {
|
||
console.warn('[RegDump/自检] 钩子已加载但没执行过 → 说明该连接类/引导函数在此流程里没被调用,需要换挂载点');
|
||
}
|
||
|
||
function CtorHead(V) {
|
||
try {
|
||
if (typeof V === 'function') return String(V).replace(/\s+/g, ' ').slice(0, 130);
|
||
var C = V && V.constructor;
|
||
return C ? String(C).replace(/\s+/g, ' ').slice(0, 130) : '';
|
||
} catch (E) {
|
||
return '';
|
||
}
|
||
}
|
||
function CtorName(V) {
|
||
try {
|
||
return (V && V.constructor && V.constructor.name) || typeof V;
|
||
} catch (E) {
|
||
return '?';
|
||
}
|
||
}
|
||
function Keys(V) {
|
||
try {
|
||
return Object.keys(V).slice(0, 50);
|
||
} catch (E) {
|
||
return [];
|
||
}
|
||
}
|
||
|
||
// 这些注册表字段是 Kotlin 的 Map 包装(.g3=put / .z2=get / .a3=keys),不是原生 Map
|
||
function IsKollection(V) {
|
||
return !!V && typeof V === 'object' && (typeof V.a3 === 'function' || typeof V.t === 'function');
|
||
}
|
||
// Kotlin 迭代器协议:t() 取迭代器,u() 有下一个,v() 取值
|
||
function Iterate(C) {
|
||
var Out = [];
|
||
var It = null;
|
||
try {
|
||
It = C.t ? C.t() : null;
|
||
} catch (E) {
|
||
It = null;
|
||
}
|
||
if (It && typeof It.u === 'function' && typeof It.v === 'function') {
|
||
var Guard = 0;
|
||
try {
|
||
while (It.u() && Guard++ < 5000) Out.push(It.v());
|
||
} catch (E2) {}
|
||
return Out;
|
||
}
|
||
try {
|
||
return Array.from(C);
|
||
} catch (E3) {
|
||
return Out;
|
||
}
|
||
}
|
||
// 统一取「键值对」:原生 Map / Kotlin Map 都吃
|
||
function Entries(M, Cap) {
|
||
var Out = [];
|
||
if (M instanceof Map) {
|
||
var N = 0;
|
||
M.forEach(function (Val, Key) {
|
||
if (N++ < Cap) Out.push({ key: Key, value: Val });
|
||
});
|
||
return Out;
|
||
}
|
||
if (IsKollection(M)) {
|
||
var Ks = [];
|
||
try {
|
||
Ks = typeof M.a3 === 'function' ? Iterate(M.a3()) : Iterate(M);
|
||
} catch (E) {
|
||
Ks = [];
|
||
}
|
||
for (var I = 0; I < Ks.length && I < Cap; I++) {
|
||
var K = Ks[I];
|
||
var V = K;
|
||
try {
|
||
if (typeof M.z2 === 'function') V = M.z2(K);
|
||
} catch (E2) {}
|
||
Out.push({ key: K, value: V });
|
||
}
|
||
return Out;
|
||
}
|
||
return Out;
|
||
}
|
||
function SizeOf(M) {
|
||
try {
|
||
if (typeof M.e1 === 'function') return M.e1();
|
||
} catch (E) {}
|
||
if (M instanceof Map) return M.size;
|
||
return -1;
|
||
}
|
||
|
||
// 键/值的可读渲染:这些是 Kotlin 对象,String() 常常是 "[object Object]"
|
||
// 优先 p57()(协议 id),其次 toString()(数据类都实现了,如 UserLabelData(userId=…)),
|
||
// 最后退化成「类名 + 前几个标量字段」
|
||
function KeyLabel(K) {
|
||
if (K === null || K === undefined) return String(K);
|
||
var T = typeof K;
|
||
if (T !== 'object' && T !== 'function') return String(K);
|
||
if (T === 'function') {
|
||
// 类当键(t7n_1 = 类→模型):直接用类名
|
||
try {
|
||
return 'class:' + (K.name || '?');
|
||
} catch (E0) {
|
||
return 'class:?';
|
||
}
|
||
}
|
||
try {
|
||
if (typeof K.p57 === 'function') {
|
||
var Id = K.p57();
|
||
if (Id !== null && Id !== undefined && typeof Id !== 'object') return 'id:' + String(Id);
|
||
}
|
||
} catch (E) {}
|
||
try {
|
||
var S = String(K);
|
||
if (S && S !== '[object Object]' && S !== '[object Function]') return S;
|
||
} catch (E2) {}
|
||
var Own = [];
|
||
var Ks = Object.keys(K).slice(0, 6);
|
||
for (var I = 0; I < Ks.length; I++) {
|
||
var V;
|
||
try {
|
||
V = K[Ks[I]];
|
||
} catch (E3) {
|
||
continue;
|
||
}
|
||
var R;
|
||
if (V === null || V === undefined) R = String(V);
|
||
else if (typeof V !== 'object') R = String(V);
|
||
else if (typeof V.p57 === 'function') {
|
||
try {
|
||
R = 'id:' + String(V.p57());
|
||
} catch (E4) {
|
||
R = '{obj}';
|
||
}
|
||
} else R = '{obj}';
|
||
Own.push(Ks[I] + '=' + R);
|
||
}
|
||
return CtorName(K) + '(' + Own.join(',') + ')';
|
||
}
|
||
|
||
function DumpMap(M, Cap) {
|
||
if (!(M instanceof Map) && !IsKollection(M)) return { kind: typeof M, size: -1, head: CtorHead(M) };
|
||
var Out = [];
|
||
var Ent = Entries(M, Cap);
|
||
for (var I = 0; I < Ent.length; I++) {
|
||
var Val = Ent[I].value;
|
||
var Key = Ent[I].key;
|
||
var T = typeof Val;
|
||
var IsPrim = Val === null || T === 'number' || T === 'string' || T === 'boolean' || T === 'bigint';
|
||
Out.push({
|
||
key: KeyLabel(Key),
|
||
raw: IsPrim ? String(Val) : undefined, // 保留原始标量(v7n_1 的值=模型哈希,join 要用)
|
||
ctor: CtorName(Val),
|
||
keys: Keys(Val),
|
||
head: CtorHead(Val),
|
||
text: (function () {
|
||
try {
|
||
var S = String(Val);
|
||
return S && S !== '[object Object]' ? S.slice(0, 300) : '';
|
||
} catch (E) {
|
||
return '';
|
||
}
|
||
})(),
|
||
});
|
||
}
|
||
return { kind: 'MapLike', size: SizeOf(M), entries: Out };
|
||
}
|
||
|
||
var Sources = {
|
||
ModelReg: window.__HornetModelReg__,
|
||
SpaceReg: window.__HornetSpaceReg__,
|
||
Conn: window.__HornetConn__,
|
||
};
|
||
var Dump = { handles: {}, maps: {} };
|
||
for (var Name in Sources) {
|
||
var H = Sources[Name];
|
||
Dump.handles[Name] = H ? { ctor: CtorName(H), keys: Keys(H) } : null;
|
||
if (!H) continue;
|
||
var Ks = Keys(H);
|
||
for (var I = 0; I < Ks.length; I++) {
|
||
var V;
|
||
try {
|
||
V = H[Ks[I]];
|
||
} catch (E) {
|
||
continue;
|
||
}
|
||
if (V instanceof Map || IsKollection(V)) Dump.maps[Name + '.' + Ks[I]] = DumpMap(V, 800);
|
||
else if (V && typeof V === 'object' && !Array.isArray(V)) {
|
||
// 懒容器(如 Conn.o7m_1)里可能嵌着 Map/集合,再挖一层
|
||
var K2 = Keys(V);
|
||
for (var J = 0; J < K2.length; J++) {
|
||
try {
|
||
var V2 = V[K2[J]];
|
||
if (V2 instanceof Map || IsKollection(V2)) Dump.maps[Name + '.' + Ks[I] + '.' + K2[J]] = DumpMap(V2, 400);
|
||
} catch (E2) {}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
window.__regDump__ = Dump;
|
||
console.log('[RegDump] 句柄: ' + Object.keys(Dump.handles).map(function (N) {
|
||
return N + '=' + (Dump.handles[N] ? Dump.handles[N].keys.length + '字段' : '缺失');
|
||
}).join(' '));
|
||
Object.keys(Dump.maps).forEach(function (P) {
|
||
console.log(' ' + P + ' size=' + Dump.maps[P].size);
|
||
});
|
||
|
||
// 直接上传到本地服务器落盘(serve.js 的 /__dump__/ 端点,写到 3DTank/names/ 下)
|
||
try {
|
||
var Body = JSON.stringify(Dump);
|
||
fetch('/__dump__/regdump.json', { method: 'POST', body: Body }).then(function (R) {
|
||
console.log('[RegDump] 已上传 /__dump__/regdump.json → HTTP ' + R.status + '(' + Body.length + ' 字节)');
|
||
}).catch(function (E) {
|
||
console.warn('[RegDump] 上传失败,请手动 copy(JSON.stringify(window.__regDump__)):' + E.message);
|
||
});
|
||
} catch (E5) {
|
||
console.warn('[RegDump] 上传异常:' + E5.message);
|
||
}
|
||
console.log('[RegDump] 备用导出:copy(JSON.stringify(window.__regDump__))');
|
||
})();
|