对照表:补上'操作类'API(加插槽/加附件/加骨骼/不重名/撤销/名称输入面板/Actor 几何),全部来自热更实测
这次任务(不重启,纯编辑器自身 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 处引用)
This commit is contained in:
1 parent
b8e84c7aa5
commit
42b40f6f65
3 files changed
+189
No files matched your search
@@ -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.<clinit>` 里依次 `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) 编辑器字体**没有中文字形**,对话框/按钮文字写中文会显示成乱码 —— 一律用英文。
|
||||
Reference in new issue
Block a user