From 1c5dc3afb9ac365becf130542e1b6b2aa975d609 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 21 Oct 2026 03:29:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(protocol):=20=E9=87=87=E9=9B=86=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E6=9E=9A=E4=B8=BE=E8=AF=BB=E5=8F=96=E6=89=93?= =?UTF-8?q?=E7=82=B9=E5=B9=B6=E5=8A=A0=E5=88=86=E6=9E=90=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3DTank/tools/analyze_enum_trace.py | 71 +++++++++++++++++++ .../play/static/js/unpacked/module_24635.js | 34 +++++++++ 2 files changed, 105 insertions(+) create mode 100644 3DTank/tools/analyze_enum_trace.py diff --git a/3DTank/tools/analyze_enum_trace.py b/3DTank/tools/analyze_enum_trace.py new file mode 100644 index 0000000..b250f13 --- /dev/null +++ b/3DTank/tools/analyze_enum_trace.py @@ -0,0 +1,71 @@ +# analyze_enum_trace.py - 分析客户端「枚举读取」打点:确认读 enum 时字节游标是否推进 +# +# 数据来源:镜像站 module_24635.js 的 Kt.t7h 补丁(枚举读器)。它记录 +# {enum: 枚举 kClass 名, pos0/pos1: 读前/读后的字节游标, bit: nullmap 位游标} +# 节流 POST 到 serve.js 的 /__dump__/enum_trace.json。 +# +# 判读: +# pos1 - pos0 == 0 → 该枚举不占字节(与源码 EnumCodecInfo/Kt 空实现一致); +# pos1 - pos0 != 0 → 客户端确实为这个枚举读了字节,说明宽度不是常量。 +# +# 用法: python tools/analyze_enum_trace.py [--names names] +import json +import os +import sys +from collections import Counter, defaultdict + +Root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +def Arg(Name, Def): + if '--' + Name in sys.argv: + I = sys.argv.index('--' + Name) + if I + 1 < len(sys.argv): + return sys.argv[I + 1] + return Def + + +NamesDir = os.path.abspath(Arg('names', os.path.join(Root, 'names'))) +TracePath = os.path.join(NamesDir, 'enum_trace.json') +FieldsPath = os.path.join(NamesDir, 'codec_fields.json') + + +def Main(): + if not os.path.exists(TracePath): + print('缺少 ' + TracePath) + print('请先用镜像站进一局战斗(Kt.t7h 已打枚举打点,会自动 POST)。') + return 1 + with open(TracePath, 'r', encoding='utf-8') as F: + Data = json.load(F) + Rows = Data.get('trace') or [] + print('枚举打点:%d 条(采集于 %s)' % (len(Rows), Data.get('at', '?'))) + + with open(FieldsPath, 'r', encoding='utf-8') as F: + Codecs = json.load(F).get('Codecs') or {} + # 枚举类型名 → 用到它的 (codec, 字段) + Users = defaultdict(list) + for Key, C in Codecs.items(): + for F in C.get('Fields') or []: + T = str(F.get('Type') or '') + if T.startswith('enum<') and T.endswith('>'): + Users[T[5:-1]].append((Key, F.get('Name'))) + + Delta = Counter() + ByEnum = defaultdict(Counter) + for R in Rows: + D = (R.get('pos1', -1) - R.get('pos0', -1)) if R.get('pos0', -1) >= 0 else -1 + Delta[D] += 1 + ByEnum[R.get('enum') or '?'][D] += 1 + + print(' 字节推进量分布(pos1-pos0):%s' % dict(Delta)) + print(' 按枚举类型:') + for Name, Cnt in sorted(ByEnum.items(), key=lambda X: -sum(X[1].values()))[:25]: + Where = Users.get(Name) or [] + Sample = ('%s.%s' % Where[0]) if Where else '?' + print(' %-4s 次数=%-5d 推进=%s 使用处=%s%s' + % (Name or '?', sum(Cnt.values()), dict(Cnt), Sample, (' 等 %d 处' % len(Where)) if len(Where) > 1 else '')) + return 0 + + +if __name__ == '__main__': + sys.exit(Main()) diff --git a/3dtank.com/play/static/js/unpacked/module_24635.js b/3dtank.com/play/static/js/unpacked/module_24635.js index 4489366..ef9cb69 100644 --- a/3dtank.com/play/static/js/unpacked/module_24635.js +++ b/3dtank.com/play/static/js/unpacked/module_24635.js @@ -510,6 +510,40 @@ (z(Vt).u7h = function (t) {}), (z(Kt).u7h = function (t) {}), (z(Kt).t7h = function (t) { + // [Hornet 调试] 枚举读取打点:记录「枚举类型名 + 读前/读后的字节游标」。 + // 枚举本身按源码不占字节(这里恒返回 null),打点只为确认客户端在真实数据里 + // 读 enum 时字节游标到底动没动,以及是哪个枚举类型(y7h_1 = kClass)。 + try { + if (typeof window !== "undefined") { + var S = t.e7h_1; + var P0 = S && typeof S.g2t === "function" ? S.g2t() : -1; + var E = (window.__HornetEnumTrace__ = window.__HornetEnumTrace__ || { + rows: [], timer: 0, + push: function (Row) { + if (this.rows.length >= 3000) return; + this.rows.push(Row); + if (!this.timer) { + var Me = this; + this.timer = setTimeout(function () { + Me.timer = 0; + try { + fetch("/__dump__/enum_trace.json", { + method: "POST", + body: JSON.stringify({ at: new Date().toISOString(), count: Me.rows.length, trace: Me.rows }), + }); + } catch (E2) {} + }, 1500); + } + }, + }); + E.push({ + enum: (this.y7h_1 && this.y7h_1.name) || "", + pos0: P0, + pos1: S && typeof S.g2t === "function" ? S.g2t() : -1, + bit: (t.f7h_1 && typeof t.f7h_1.y7g_1 === "number") ? t.f7h_1.y7g_1 : -1, + }); + } + } catch (E4) {} return null; }), (z(Kt).s7h = function (t, n) {}),