From 42b40f6f656e60b1ce2b91d0a2e907e5e773b534 Mon Sep 17 00:00:00 2001 From: tianmo Date: Fri, 16 Oct 2026 19:26:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E7=85=A7=E8=A1=A8=EF=BC=9A=E8=A1=A5?= =?UTF-8?q?=E4=B8=8A'=E6=93=8D=E4=BD=9C=E7=B1=BB'API=EF=BC=88=E5=8A=A0?= =?UTF-8?q?=E6=8F=92=E6=A7=BD/=E5=8A=A0=E9=99=84=E4=BB=B6/=E5=8A=A0?= =?UTF-8?q?=E9=AA=A8=E9=AA=BC/=E4=B8=8D=E9=87=8D=E5=90=8D/=E6=92=A4?= =?UTF-8?q?=E9=94=80/=E5=90=8D=E7=A7=B0=E8=BE=93=E5=85=A5=E9=9D=A2?= =?UTF-8?q?=E6=9D=BF/Actor=20=E5=87=A0=E4=BD=95=EF=BC=89=EF=BC=8C=E5=85=A8?= =?UTF-8?q?=E9=83=A8=E6=9D=A5=E8=87=AA=E7=83=AD=E6=9B=B4=E5=AE=9E=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 这次任务(不重启,纯编辑器自身 API:按钮→名称面板→按名字匹配附件→当前选中骨骼下建 15 根骨骼,每根 1 插槽 1 附件) 用到的 API 全部落表,含踩坑: - ADD_SLOT 会把模板插槽连它自带的附件一起复制('插槽上的附件变换没改'就是这个) - xl.D(dt) 不会把对话框挂进 Stage,必须再 dt.D(dl)V(Stage.addActor) - dl.b_()Z=remove 与 eX.b_()V=invalidate 同名不同描述符,按名字取会删不掉 - et(Table) 也声明 int L()/I(),按名字取 Actor 几何必须按返回类型 float 过滤 - iz 枚举顺序 normal/additive/multiply/screen 取自 clinit - un.Z()=撤销(实测栈 23->22、对象数回退) names.tsv 合并成 332 条,payload-named.zip 已重新产出(5605 处引用) --- tools/deobf/anchors.tsv | 92 +++++++++++++++++++++++++++++++++++++++++ tools/deobf/names.tsv | 35 ++++++++++++++++ 混淆名对照表.md | 62 +++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) diff --git a/tools/deobf/anchors.tsv b/tools/deobf/anchors.tsv index 076c045..bb894c2 100644 --- a/tools/deobf/anchors.tsv +++ b/tools/deobf/anchors.tsv @@ -246,3 +246,95 @@ GD D F position GD V F spacing GD S F rotateMix GD L F translateMix + +# ============================================================================ +# 【面板/新按钮用到的"操作类" API】(2026-10-16 热更实测通过:不重启、纯编辑器自身 API) +# 场景:点面板按钮 → 弹名称输入面板 → 按名字匹配附件 → 在当前选中骨骼下建 15 根骨骼, +# 每根 1 插槽 1 附件(输入的附件名),位移 xy 0~500 随机、缩放 1、旋转/斜切 0, +# 插槽 alpha 0.5~1 随机、blend normal/additive 随机、附件在插槽上变换 0/0/0/1/1。 +# 证据:`javap -p -c` 字节码 + attach 到运行进程逐条实测(每个 API 都真跑过并核对过结果)。 +# ============================================================================ + +# ---- 建对象(编辑器自己的"加"API)---- +# UH = OG 的模型编辑助手(OG.D:LUH;)。下面两个都实测建出来过: +UH D (LGU;LGp;Z;LfI;)LGU; addSlot +# ^ 加插槽:参数(插槽模板, 父骨骼, ?, 不重名用列表)。⚠️ 它会把模板插槽**整个复制**,连模板插槽上挂的附件一起复制(实测踩到过) +UH D (LGn;LGU;ZZLfI;)LGn; addAttachmentCopy +# ^ 加附件:参数(附件模板, 插槽, ?, ?, 列表);内部就是 `Gn.D(LGP;)LGn;` 复制一份模板附件,并挂到该插槽 +GP D (LGp;)V addBone +# ^ 加骨骼:字节码 `bone.B = array.size; array.add(bone)`(骨架的骨骼表) +OG D (Ljava/lang/String;LfI;LGU;)Ljava/lang/String; uniqueName +# ^ 编辑器自己的不重名生成器:`OG.D("bone", 骨骼数组, null)` 会给出 bone7 这种名字 +GU D (LGn;Z)V setAttachment +# ^ 设插槽的当前附件:第二参=是否记撤销;内部按 `Editor.V`(animate/setup)只写 `D:LGn;` 或 `B:LGn;` 其中一个 —— +# 所以要"两种模式都覆盖"就直接写两个字段(本次就是这么做的) +GU D LGn; attachmentAnimate +# ^ 与 `GU.B:LGn;` 成对:`D` 是 animate 模式用的当前附件,`B` 是 setup/非 animate 用的(JSON 导出器读的是 B) +VZ D (LGP;ZZ)V refresh +# ^ 改完模型重建层级树节点(本次每批建完都调它) +un D ()V undoBegin +un B ()V undoCommit +# ^ 撤销帧的 开始/提交(改模型前 D()、改完 B()) +un Z ()V undo +# ^ ★ 撤销一步:实测有效(`up.D:` 栈 23→22、redo 栈 0→1,场景对象数同步回退)。 +# 本次误删骨骼后就是用连点 un.Z() 一键一键撤回来的。另外 un.S()/un.C()/un.V()/un.I() 不是撤销: +# S=跳到历史某下标、C=置标志、V=按当前下标重放、I=up.D()(无效果) + +# ---- 名称输入面板(编辑器自带的那套对话框)---- +xl L (Ljava/lang/Object;)V onOkCancel +# ^ 对话框的 OK/Cancel 总回调:收到 Boolean(TRUE=OK、FALSE=Cancel)。 +# OK 时它会读名字框、去重校验,然后调下面的抽象方法;Cancel 直接 return +xl L (Ljava/lang/String;)V onNameEntered +# ^ **抽象方法**:OK 且名字合法时被调用,参数就是输入的名字。想拿名字就子类化它(本次是 ASM 现生成子类) +xl D (Ldt;)LvH; show +# ^ 显示对话框。⚠️ 实测:**光调它窗口不会挂进 Stage**(parent 一直 null、屏幕上什么都不出现), +# 必须再显式 `dt.D(Ldl;)V`(Stage.addActor)挂上去,然后自己 setPosition +xl D LKp; nameField +# ^ 对话框里的名字输入框(Kp = TextField 子类):构造时会用它填入默认名,OK 时从它读名字 +vH B (Ljava/lang/Object;)V okPressed +# ^ OK 按钮的回调:唯一会去调 `L(Object)` 的地方(javap -c vH 可见) +vH L (Ljava/lang/Object;)V onOkCancelNoop +# ^ 空实现(xl 覆盖了它) +Kp B ()Ljava/lang/String; getText +Kp B (Ljava/lang/String;)V setText +# ^ 名字框取/填文字(xl/xp 的构造与 L(Object) 里就是这么用的) + +# ---- Actor / Stage / WidgetGroup(定位、挂载、删除)---- +dt D (Ldl;)V addActor +# ^ Stage.addActor:字节码就是 `this.D:Ldo;.addActor(actor)`(root.addActor)。 +# ⚠️ 用它把对话框挂上屏幕(配合 xl.D(dt));也踩过坑:它会把 Actor 从原父级摘走(当年把面板列表搬出面板就是它) +dl D Ldo; parent +dl D Ldt; stage +# ^ Actor 上的两个同名字段:`do` 类型的是父控件,`dt` 类型的是所在 Stage。按类型取,别按名字取 +dl D ()Ldt; getStage +dl b_ ()Z remove +# ^ 把 Actor 从父级摘掉。⚠️ `eX.b_()V`(invalidate)与它同名不同描述符 —— `getMethod("b_")` 会取到 void 那个, +# 结果就是"调了删除却没删掉"(本次踩到) +eX b_ ()V invalidate +eX V (Z)V setFillParent +dl c_ ()F getX +dl d_ ()F getY +dl L ()F getWidth +dl I ()F getHeight +dl S ()F getTop +dl C ()F getRight +# ^ Actor 的几何读取(都在 dl 上用 `javap -p -c` 对出来的:c_=x、d_=y、L=width、I=height、S=y+h、C=x+w)。 +# ⚠️ `et`(Table) 自己还声明了 `int L()`/`int I()`(列数/行数),所以按名字取必须**同时按返回类型 float 过滤** +dl D (FFI)V setPosition +# ^ setPosition(x, y, alignment):**align=0 时是"以 (x,y) 为中心"**(b1 不是 left/right 就按 center 处理)。 +# 本次要把对话框摆屏幕正中就是靠这个:直接传 Stage 中心即可 + +# ---- 混合模式枚举(插槽 blend)---- +iz D Liz; normal +iz B Liz; additive +iz L Liz; multiply +iz I Liz; screen +# ^ `iz`(=BlendMode) 的常量顺序取自 `iz.`:依次 new iz("normal",0,770,1,771)、("additive",1,770,1,1)、 +# ("multiply",2,774,774,771)、("screen",3,1,1,769) —— 名字是常量池里的明文 + +# ---- 面板自己那套(本次复用的)---- +com.spineview.ObjectPanel wire (Ljava/lang/ClassLoader;Ljava/lang/Object;Ljava/lang/Runnable;)V wireButton +# ^ 把按钮接上点击(轮询 isChecked + 路由线程)——新按钮直接复用它,不用自己写监听 +com.spineview.SceneObjects listAllNodes (Ljava/lang/ClassLoader;)Ljava/util/List; listAllNodes +com.spineview.SceneActions delete (Ljava/lang/ClassLoader;Ljava/lang/Object;)Z delete +# ^ 面板自己的"罗列场景对象 / 删除对象"(走编辑器删除流程,自带撤销)——清理误建对象时用它 diff --git a/tools/deobf/names.tsv b/tools/deobf/names.tsv index 2fdc4dc..a02b865 100644 --- a/tools/deobf/names.tsv +++ b/tools/deobf/names.tsv @@ -24,11 +24,14 @@ GO I F scaleY GO L F scaleX GO S F rotation GO Z F height +GP D (LGp;)V addBone GP L (LGn;ZZ)V removeAttachment GP L LfI attachments GP Z LfI slots GU B LGn; attachment GU B Lcom/badlogic/gdx/graphics/Color; color +GU D (LGn;Z)V setAttachment +GU D LGn; attachmentAnimate GU D LGp; bone GU D Liz; blend GU D Ljava/lang/String; name @@ -99,6 +102,15 @@ KP B ()Ljava/lang/String; getSelected KP B (I)V setSelectedIndex KP B (Ljava/lang/String;)V setSelected KP D (Ldt;)V setStage +Kp B ()Ljava/lang/String; getText +Kp B (Ljava/lang/String;)V setText +OG D (Ljava/lang/String;LfI;LGU;)Ljava/lang/String; uniqueName +UH D (LGU;LGp;Z;LfI;)LGU; addSlot +UH D (LGn;LGU;ZZLfI;)LGn; addAttachmentCopy +VZ D (LGP;ZZ)V refresh +com.spineview.ObjectPanel wire (Ljava/lang/ClassLoader;Ljava/lang/Object;Ljava/lang/Runnable;)V wireButton +com.spineview.SceneActions delete (Ljava/lang/ClassLoader;Ljava/lang/Object;)Z delete +com.spineview.SceneObjects listAllNodes (Ljava/lang/ClassLoader;)Ljava/util/List; listAllNodes com/badlogic/gdx/scenes/scene2d/ui/Button B (ZZ)V setCheckedAndFire com/badlogic/gdx/scenes/scene2d/ui/Button I (Z)V setChecked com/badlogic/gdx/scenes/scene2d/ui/Button String ()Z isChecked @@ -186,8 +198,10 @@ dl B (I)F getY dl B (Lde;)Lde; stageToLocalCoordinates dl B (Ldl;Lde;)Lde; localToActorCoordinates dl B (Z)V setDebug +dl C ()F getRight dl C (FF)V setOrigin dl D ()Ldl; debug +dl D ()Ldt; getStage dl D ()Ljava/lang/Object; getUserObject dl D ()Ljava/lang/String; getName dl D ()V clearActions @@ -203,17 +217,24 @@ dl D (Ldn;)Z addListener dl D (Ljava/lang/Object;)V setUserObject dl D (Ljava/lang/String;)V setName dl D (Z)V setVisible +dl D Ldo; parent +dl D Ldt; stage dl F ()V clipEnd +dl I ()F getHeight dl I ()Z isTouchable +dl L ()F getWidth dl L ()V clear dl L ()Z hasParent dl L (Ldn;)Z addCaptureListener dl L (Ldv;)V setTouchable +dl S ()F getTop dl S (FF)V setOrigin dl S (Lde;)Lde; localToStageCoordinates dl V ()V toBack dl Z ()V toFront dl b_ ()Z remove +dl c_ ()F getX +dl d_ ()F getY do B ()Ldo; debugAll do Code ()V childrenChanged do D (F)V act @@ -234,6 +255,7 @@ do LineNumberTable ()Z isTransform do init ()Z hasChildren dt B ()F getHeight dt D ()F getWidth +dt D (Ldl;)V addActor eM C ()V expandTo eM D ()F getHeight eM D ()Z hasChildren @@ -297,3 +319,16 @@ fI D ([Ljava/lang/Object;)LfI; with fI D ([Ljava/lang/Object;)V addAll fI L ()Ljava/lang/Object; first fI L ()V reverse +iz B Liz; additive +iz D Liz; normal +iz I Liz; screen +iz L Liz; multiply +un B ()V undoCommit +un D ()V undoBegin +un Z ()V undo +vH B (Ljava/lang/Object;)V okPressed +vH L (Ljava/lang/Object;)V onOkCancelNoop +xl D (Ldt;)LvH; show +xl D LKp; nameField +xl L (Ljava/lang/Object;)V onOkCancel +xl L (Ljava/lang/String;)V onNameEntered diff --git a/混淆名对照表.md b/混淆名对照表.md index 8add26f..8a391f7 100644 --- a/混淆名对照表.md +++ b/混淆名对照表.md @@ -460,3 +460,65 @@ macOS 文件系统大小写不敏感,解到目录里会互相覆盖 —— 所 以上全部写进了 `tools/deobf/anchors.tsv`(末尾"【权威来源】JSON 导出器"一节),再合并进 `tools/deobf/names.tsv`(297 条),面板 Inspector 会按"真名(混淆名)"显示。 重命名载荷也已同步产出:`/tmp/spinework/payload-named.zip`(4176 个类,5247 处成员引用)。 + +--- + +## 十、"操作类" API(2026-10-16 热更实测通过) + +这一节是"不重启、纯用编辑器自身 API 加一个按钮:弹名称面板 → 按名字匹配附件 → 在当前选中骨骼下建 +15 根骨骼(每根 1 插槽 1 附件)"那次任务里用到的 API。**每条都真跑过并核对过结果**,不是从字节码推的纸面结论; +字节码出处也一并写上。 + +### 建对象 + +| 载荷签名 | 真名 | 依据 / 坑 | +| --- | --- | --- | +| `UH.D(LGU;LGp;Z;LfI;)LGU;` | **addSlot**(加插槽) | 实测建出插槽。⚠️ 它会把**模板插槽整个复制**,连模板插槽上挂的那份附件一起复制 —— 新插槽上于是挂着模板的附件(本次"插槽上的附件变换没改"就是这个原因) | +| `UH.D(LGn;LGU;ZZLfI;)LGn;` | **addAttachmentCopy**(按模板复制一个附件挂到插槽) | 内部 `Gn.D(LGP;)LGn;` 复制模板附件,再登记到骨架附件表 `GP.L` | +| `GP.D(LGp;)V` | **addBone** | 字节码 `bone.B = array.size; array.add(bone)` | +| `OG.D(String;LfI;LGU;)String` | **uniqueName**(编辑器自己的不重名生成) | 实测 `OG.D("bone",骨骼数组,null)` 返回 `bone7` | +| `GU.D(LGn;Z)V` | **setAttachment**(设插槽当前附件) | 字节码:第二参=是否记撤销;按 `Editor.V`(animate/setup)**只写 `D:LGn;` 或 `B:LGn;` 其中一个** → 想让两种模式都覆盖就两个字段都写 | +| `VZ.D(LGP;ZZ)V` | **refresh**(重建层级树节点) | 每批建完都调,树里立刻能看到 | +| `un.D()V` / `un.B()V` | **undoBegin / undoCommit** | 撤销帧的开始/提交 | +| `un.Z()V` | **undo(撤销一步)** | 实测:`up` 的撤销栈 23→22、redo 栈 0→1、场景对象数同步回退。误删骨骼就是靠它连点撤回来的。同族的 `un.S()/un.C()/un.V()/un.I()` **不是**撤销(分别=跳历史下标/置标志/按下标重放/无效果) | + +### 名称输入面板(编辑器自带对话框) + +| 载荷签名 | 真名 | 依据 / 坑 | +| --- | --- | --- | +| `xl`(抽象类) | **名称输入对话框基类**(标题/正文/名字框/OK/Cancel) | 结构实测:标题 + 正文 Label + `Kp` 名字框 + OK/Cancel 两个 ImageTextButton | +| `xl.L(Ljava/lang/Object;)V` | **onOkCancel** | 收到 `Boolean`:TRUE=OK(读名字、查重、调下面的抽象方法);FALSE=Cancel 直接 return | +| `xl.L(Ljava/lang/String;)V` | **onNameEntered(抽象)** | OK 且名字合法时被调用,参数=输入的名字。想拿名字就子类化它(本次用 ASM 现场生成子类) | +| `xl.D(Ldt;)LvH;` | **show** | ⚠️ **光调它窗口不会挂进 Stage**(实测 parent 一直 null、屏幕上什么都不出现),必须再显式 `dt.D(Ldl;)V`(Stage.addActor) | +| `xl.D:LKp;` | **nameField**(名字框) | `Kp` 是 TextField 子类:`B()String`=getText、`B(String)V`=setText | +| `vH.B(Ljava/lang/Object;)V` | **okPressed** | `javap -c vH` 里唯一调 `L(Object)` 的地方=OK 按钮回调 | +| `dT.D(Ldt;Ldk;)LdT;` | show 内部实现 | `vH.D(dt)` 里 `invokespecial` 到它;里面 `stage.D(this)V` 就是 Stage.addActor | + +### Actor / Stage / WidgetGroup(定位、挂载、删除) + +| 载荷签名 | 真名 | 依据 / 坑 | +| --- | --- | --- | +| `dt.D(Ldl;)V` | **Stage.addActor** | 字节码 `this.D:Ldo;.addActor(actor)`(root.addActor)。用它把对话框挂上屏幕。历史上"面板列表被搬出面板"也是它干的(把 Actor 从原父级摘走) | +| `dl.D:Ldo;` / `dl.D:Ldt;` | **parent / stage**(字段) | Actor 上两个同名字段,必须**按类型**取;`dl.D()Ldt;`=getStage | +| `dl.b_()Z` | **remove** | ⚠️ `eX.b_()V`(invalidate)同名不同描述符;`getMethod("b_")` 会取到 void 那个 → "调了删除却没删掉"(本次踩到,改成按返回类型 boolean 挑才对) | +| `eX.V(Z)V` | **setFillParent** | 与 `et.C(Z)V`=setClip、`et.B(Z)V`=setDebug 区分开 | +| `dl.c_()F` `dl.d_()F` `dl.L()F` `dl.I()F` `dl.S()F` `dl.C()F` | **getX / getY / getWidth / getHeight / getTop / getRight** | 全部按 `javap -p -c dl` 对出来(c_=x、d_=y、L=w、I=h、S=y+h、C=x+w)。⚠️ `et`(Table) 自己声明了 `int L()`/`int I()`(列数/行数)→ 按名字取要**同时按返回类型 float 过滤** | +| `dl.D(FFI)V` | **setPosition(x,y,alignment)** | **align=0 时是"以 (x,y) 为中心"**(不按 left/right 就按 center 算)→ 摆屏幕正中直接传 Stage 中心即可 | + +### 混合模式枚举 + +| 字段 | 真名 | 依据 | +| --- | --- | --- | +| `iz.D` / `iz.B` / `iz.L` / `iz.I` | **normal / additive / multiply / screen** | `iz.` 里依次 `new iz("normal",0,770,1,771)`、`("additive",1,770,1,1)`、`("multiply",2,774,774,771)`、`("screen",3,1,1,769)`,常量名是常量池明文 | + +### 面板自己的接口(复用,不重复造) + +| 方法 | 用途 | +| --- | --- | +| `com.spineview.ObjectPanel.wire(ClassLoader, Object, Runnable)` | 把新按钮接上点击(轮询 `isChecked` + 路由线程)——新按钮直接复用 | +| `com.spineview.SceneObjects.listAllNodes(ClassLoader)` / `selected(ClassLoader)` / `nameOf(Object)` | 罗列场景对象 / 当前选中 / 取名字 | +| `com.spineview.SceneActions.delete(ClassLoader, Object)` | 走编辑器删除流程(自带撤销),清理误建对象时用 | + +> 另外踩到两个**跟 API 无关但会浪费半天**的坑,记在这里: +> 1) **HotSpot 同名 agent 类只加载一次** —— 改了 agent 代码再 attach 同一个类名,跑的还是旧代码;每次改代码都要换类名。 +> 2) 编辑器字体**没有中文字形**,对话框/按钮文字写中文会显示成乱码 —— 一律用英文。