工具链(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/(含数据库口令)不进库,可用上述工具重新生成。
47 lines
2.3 KiB
JavaScript
47 lines
2.3 KiB
JavaScript
// 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 };
|