Files
TermiNexus b6f2cb71ee feat(3dtank): 协议逆向工程与自研服务器入库(工具链 + 协议表 + 空间回放 + MySQL)
工具链(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/(含数据库口令)不进库,可用上述工具重新生成。
2026-09-17 20:00:41 +08:00

47 lines
2.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// modelservice.js - 模型/对象服务协议(module_29554)的语义层
//
// 出处:src/module_29554.js。客户端 `U.q7i(methodId, reader)` 按 methodId 分发 4 条 s2c:
// _loadDependencies → K{ callbackId i32, resources list }
// _loadObjectsData → W{ modelsData list<list<X>>, objects list<list<h>> }
// _setModelCommandAlias → alias + 两个 i64(告诉客户端「某 gameType 由某 methodId 承载」)
// _unloadObjects → objects
// 客户端 → 服务端只有一条:`G.q1wd`(见下),负载实测 4 字节 i32。
//
// 实测结论(命名/语义对应,标定过):
// 这 4 条命令的 methodId 就是 names/method_table.json 里 4 条 `_t` / codec=`lt` 的行,
// 而它们的**真实负载布局是容器的 zt 形状**(method_table 的 `Codec` 列在这几行上是错的,见 container.js 头注)。
// 空间 100045260 认证后那 3 帧,前两帧正好是 _loadDependencies 与 _loadObjectsData。
'use strict';
const fs = require('fs');
const path = require('path');
// 语义表:methodId(十进制字符串)→ 名字 + 方向
const METHOD = {
'3216143066888387731': { Name: '_loadDependencies', Dir: 's2c' },
'7640916300855664666': { Name: '_loadObjectsData', Dir: 's2c' },
'8179162314198150518': { Name: '_setModelCommandAlias', Dir: 's2c' },
'9038756763225879447': { Name: '_unloadObjects', Dir: 's2c' },
'1816792453857564692': { Name: 'G.q1wd(客户端请求对象数据)', Dir: 'c2s' },
};
function Name(MethodId) {
const M = METHOD[String(MethodId)];
return M ? M.Name : null;
}
// G.q1wd 的请求负载:实测 4 字节大端 i32(老注释里写的 ObjectsData 结构对不上字节,故按实测来)
function DescribeRequest(Payload) {
if (Payload.length === 4) return { Kind: 'i32', Value: Payload.readInt32BE(0) };
return { Kind: '未知', Hex: Payload.toString('hex').slice(0, 64) };
}
// 从 method_table.json 里把这 4 行的模型名/ModelId 也读出来,便于日志与后续建表
function LoadRows(NamesDir) {
const P = path.join(NamesDir, 'method_table.json');
if (!fs.existsSync(P)) return [];
const J = JSON.parse(fs.readFileSync(P, 'utf8'));
return (J.Rows || []).filter((R) => METHOD[String(R.MethodId)]);
}
module.exports = { METHOD, Name, DescribeRequest, LoadRows };