// match_methods.js - 用客户端 dump 出的 methodId 表去扫抓包帧,验证"线上哪个字段是 methodId" // 用法: node match_methods.js [--root ../packets/plain] [--table ../names/method_table.json] [--limit 40] // 原理: // names/method_table.json 是 methodId(64 位) → modelId → 模型类/codec(客户端注册表 dump)。 // 空间层的 RPC 分发是 mr.l7n(ctx, methodId, data),所以帧里应能找到 methodId 的 8 字节大端。 // 命中即说明该帧属于哪个模型(命令),从而把命令语义一个个钉死。 const fs = require('fs'); const path = require('path'); const zlib = require('zlib'); const Argv = process.argv.slice(2); const Arg = (Name, Def) => { const I = Argv.indexOf('--' + Name); return I >= 0 && Argv[I + 1] ? Argv[I + 1] : Def; }; const PROJECT = path.resolve(__dirname, '..'); const Root = path.resolve(Arg('root', path.join(PROJECT, 'packets', 'plain'))); const TablePath = path.resolve(Arg('table', path.join(PROJECT, 'names', 'method_table.json'))); const Limit = Number(Arg('limit', '40')) || 40; const Table = JSON.parse(fs.readFileSync(TablePath, 'utf8')); // methodId → 模型信息;同时准备 8 字节大端字节串 const ByHex = new Map(); for (const R of Table.Rows) { const Big = BigInt(R.MethodId); const Buf = Buffer.alloc(8); Buf.writeBigInt64BE(Big); ByHex.set(Buf.toString('hex'), R); } function Unwrap(Buf) { const B0 = Buf[0]; const HeaderSize = B0 & 0x80 ? 4 : 2; let Body = Buf.slice(HeaderSize); if (B0 & 0x40) { try { Body = zlib.inflateSync(Body); } catch (Err) { return null; } } return Body; } if (!fs.existsSync(Root)) { console.log('目录不存在: ' + Root); process.exit(1); } const Hits = []; let Scanned = 0; for (const Space of fs.readdirSync(Root).sort()) { const Dir = path.join(Root, Space); if (!fs.statSync(Dir).isDirectory()) continue; for (const F of fs.readdirSync(Dir)) { if (!/\.bin$/.test(F)) continue; const Raw = fs.readFileSync(path.join(Dir, F)); const Body = Unwrap(Raw); if (!Body || Body.length < 8) continue; Scanned++; for (let Off = 0; Off + 8 <= Body.length; Off++) { const Hex = Body.slice(Off, Off + 8).toString('hex'); const R = ByHex.get(Hex); if (R) Hits.push({ File: Space + '/' + F, Off, Size: Raw.length, Model: R.Model, MethodId: R.MethodId, Codec: R.Codec, CodecFields: R.CodecFields }); } } } console.log('扫描文件 ' + Scanned + ' 个,命中 methodId ' + Hits.length + ' 处'); const Count = {}; for (const H of Hits) Count[H.Model] = (Count[H.Model] || 0) + 1; console.log('命中模型分布: ' + JSON.stringify(Count)); console.log(''); for (const H of Hits.slice(0, Limit)) { console.log( H.File.padEnd(30) + ' @' + String(H.Off).padStart(5) + ' ' + H.Size + 'B model=' + H.Model + ' codec=' + H.Codec + ' codecFields=' + String(H.CodecFields).slice(0, 70), ); }