Files
Spine_lab/阶段五-面板与实际数据联动.md
T

129 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 阶段五:把面板接到"真实数据"上(对象增删改查 / 属性读取与改写 / 命名还原)
本文记录阶段五**已经做成的**、**实测到的数字**、**踩过的坑**,以及**还没做完的**。
详细的"混淆名 ⇒ 真名"对照在 [`混淆名对照表.md`](混淆名对照表.md),交互链路在 [`编辑器交互与数据API.md`](编辑器交互与数据API.md)。
---
## 一、阶段五做成了什么
### 1. 面板(Spine View)长这样(三段式)
| 段 | 内容 | 高度 |
| --- | --- | --- |
| 上 | `Refresh / New Bone / New Slot / New BBox / New Path / Delete / Rename / Inspect / Set Value` + 名字输入框 | 由 `spineview-fit` 线程设置(当前按 600 的比例) |
| 中 | 列表 / Inspector(对象清单 或 选中对象的字段+方法) | 同上(1400 的比例) |
| 下 | 日志(最近 6 条按钮动作提示) | 同上(400 的比例) |
面板文字**一律英文**(编辑器只装了拉丁字形,中文渲染成方块)。
### 2. 能做的事(都已实测跑通)
| 能力 | 实现 | 证据 |
| --- | --- | --- |
| **列出**场景对象 | 走层级树的节点;`SceneObjects.listAllNodes()`(树里 28 个对象:skeleton/bone/slot/attachment + Constraints/Draw Order/… 固定根) | 面板列表与编辑器 Tree 一致 |
| **选中**对象 | `VZ.D(Tree.Node, boolean)`(`SceneObjects.select`) | 编辑器 Tree 里对应行被高亮(截图已确认) |
| **读**当前选择 | `VZ.B()` = `Selection.getLastSelected().object` | 面板 "selected: bone \"bone3\"" 与编辑器一致 |
| **增**对象 | 先选中父级 → 跑编辑器命令 `New Bone` / `New Slot` / `New BBox` / `New Path` | 新建出 `bone7`、`bone8`(骨骼自动编号)等 |
| **删**对象 | `un.D()` → `对象.L()` → `un.B()` → `VZ.D(骨架,true,true)` 重建树 | 删除后树与面板同步(父骨骼删除会连子骨骼一起删) |
| **改**名字 | 同上,`对象.D(新名字,true)` + 重建树 | `bone3` → `panel_test` 生效 |
| **改**属性(测试用) | Inspector 里点字段行 → 值读进输入框 → `Set Value` 直接写模型字段(带撤销) | 写回后值与编辑器属性面板同步 |
| **输入框自动化** | 有些 `New X` 会弹"输入名字"对话框:找到"带 TextField 且有 `L(Object)V` 回调"的对话框对象 → 填名字 → 直接调 `L(true)`(等于点 OK),不弹给用户 | `[crud] 已自动填写对话框并点 OK` |
| **跟随编辑器选择** | 后台线程 600ms 轮询 `VZ.B()`,变了就自动 Inspect | 日志 `[panel] Inspected slot "mega_dg6": 48 entries` |
| **鼠标滚轮** | 点面板按钮后把 Stage 的 scroll focus 抢到列表(`grabScrollFocus`) | 用户实测:"拖动/点列表后滚动条出现、滚轮可用" |
### 3. 命名还原(把 `D/B/L` 变成有意义的名字)
| 产物 | 内容 |
| --- | --- |
| `tools/deobf/Renamer.java` | ASM `Remapper`:按映射表重命名**成员**(不改类名 —— 载荷的 indy 靠加密串里的原名反射查找) |
| `tools/deobf/anchors.tsv` | 手动/实测锚点(含依据注释) |
| `tools/deobf/anchors-libgdx.tsv` | `MapMethods` 对上游 **gdx-1.11.0** 的**唯一命中**(`= `)条目 |
| `tools/deobf/anchors-libgdx-unconfirmed.tsv` | 只给了候选(`~n/m`)的条目 —— **不可信,别用** |
| `tools/deobf/names.tsv` | 合并结果(当前 **232 条**):确定 + 手动/实测 |
| `~/Desktop/spine398/src-named/` | 改名后的反编译源码(每个类单独反编译、`class_<载荷名>.java` 命名) |
| `tools/decrypt/DumpSites.java` | 逐 invokedynamic 站点解出"真实类.方法 **+ 描述符**"(带描述符是关键) |
**面板 Inspector 现在会按映射表显示真名**,格式 `真名(混淆名)`,例如:
```
float scaleX(L) = 1.0 float width(C) = 166.0
float rotation(S) = 0.0 float height(Z) = 87.0
```
### 4. 本轮实测出来的字段语义(都写进了 `anchors.tsv`)
| 对象 | 字段 | 含义 | 依据 |
| --- | --- | --- | --- |
| `Gp`(骨骼) | `Z/V` | 当前 x/y | 面板 Translate(-168,-116) ↔ Z=-168.00009/V=-116.0 |
| | `F` / `Code` / `SourceFile` | 当前 rotation / scaleX / scaleY | 面板 Rotate 341.456 ↔ F=33.676704;Scale(0.8799,0.572) ↔ Code/SourceFile |
| | `StackMapTable` / `LineNumberTable` | 当前 shearX / shearY | 面板 Shear(-52.221,-28.63) |
| | `D/B/L/I/S/C` | 静止姿态(setup) 的 x/y/rotation/scaleX/scaleY/shear(推断) | 默认值 0,0,0,1,1,0;未改动时与上面那组相等 |
| | `Class` / `Ljava` / `java` | length / 世界缩放 X / 世界缩放 Y | 面板 Length=0.0 ↔ Class=0.0;Ljava/java 与 Code/SourceFile 相等 |
| `GO`(附件) | `S/L/I/C/Z` | rotation / scaleX / scaleY / width / height | 面板 Region 49°/1.997/1.335/157/154 一一对应 |
| `GU`(插槽) | `String D` / `Gp D` / `Gn D` / `Color D` | 名字 / 所属骨骼 / 附件 / 颜色 | 面板 Slot 只有 Name+Color;树节点名一致 |
| 通用 | `Table` 上 `D(Ldl;)LdR;`=`add`、`I()LdR;`=`row`、`SourceFile()`=`clearChildren` | | 指纹 + 目录铺满/换行实测 |
---
## 二、踩过的坑(按"代价从大到小")
1. **`VZ.D(Object)` 不是"选中"** —— 它内部是 `Selection.L()`(去掉选中项)。真正"设成选中"的是 `VZ.D(Tree.Node, boolean)`。这让我前面所有增删改都"执行了但没生效"。
2. **动模型的代码必须在编辑器渲染线程** —— `OG.I()` 有线程断言,非渲染线程直接抛 `RuntimeException`;要用编辑器的 `OG.B(Runnable)` 切过去。
3. **不能在渲染线程里 sleep** —— 编辑器有些创建是**下一帧**做的;占着渲染线程睡 300ms,那一帧永远不来,对象就建不出来。
4. **`MapMethods` 的候选(`~n/m`)不是结论** —— 我把它们混进 `names.tsv` 当确定名字用,结果"拿的方法不对"。现在拆成 `anchors-libgdx.tsv`(只放 `=` 唯一命中)与 `anchors-libgdx-unconfirmed.tsv`。
5. **同名不同描述符的坑就在我们自己代码里** —— `Cell.fill()` 的载荷名是 `D`,但 Cell 上有多个无参 `D()`,`getMethod("D")` 会取错;`fillY="L"` 是"按形状猜"的,没验证过(很可能取错)。
6. **`ScrollPane.B(ZZ)V` 是 `setScrollingDisabled`,不是 `setForceScroll`** —— 我传了 `(false,true)`,等于**把纵向滚动关掉了**(现象:列表滚不动)。`setForceScroll` 是 `I(ZZ)V`。
7. **滚轮不生效的根因是焦点** —— 给列表 ScrollPane 挂事件监听后,滚轮事件收到 **0 条**:焦点在画布上。解法:点面板后 `Stage.setScrollFocus(面板)`;找这个方法必须用 `getMethods()`(它声明在接口 `LPt9` 里,`getDeclaredMethods` 扫不到)。
8. **`run.sh` 漏了 `import os`** —— python 那句报 NameError,**编辑器根本起不来**;而我用 `>/dev/null` 把错误吞了,白排查了好几轮。
9. **`nohup … &` 启动会被会话回收** —— 必须 `subprocess.Popen(..., start_new_session=True)`,否则我这边一次工具调用结束,编辑器就被收掉(你几次看到"进程没了"就是这个)。
10. **面板根表格的高度被父容器限制** —— `fillParent` 是"复制父容器尺寸",而父容器又按我们的内容算 → 互相依赖;结果 panel 永远等于自己算出来的高度(实测 root≈1360,停靠框 1944)→ 底部固定留一条空带(你说的"区域1")。**从面板内部无解**,只能用 `expand` 或把停靠栏拖大。
11. **热更(换容器重建面板)把面板搞成空白** —— 那版已 `git revert`;要做得换一种容器(能让 dock 正确布局的 WidgetGroup)。
12. **macOS 文件系统大小写不敏感** —— 一次反编译整包时 `GP.java`/`Gp.java`、`Yr.java`/`yR.java` 互相覆盖,导致"有些类像没改名"。现在改成每个类单独反编译 + `class_<载荷名>.java`。
13. **`Table.setDebug(true)` 的红/绿线是调试描边**,不是布局错误;它给每层表格的每个 cell 各画一圈(红=根表格三段,绿=内部格)。
14. **我自作主张加过"分页"**(你没要求)—— 已回滚,并按你说的把纪律写成项目级技能 `.codex/skills/scope-discipline`。
---
## 三、API 规模统计(载荷里的"面")
| 分组 | 类 | 方法 | public 方法 |
| --- | --- | --- | --- |
| LWJGL(保名) | 629 | 9564 | 5444 |
| Spine 编辑器(保名,`editor/**`) | 57 | 312 | 184 |
| libGDX(保名那部分) | 52 | 545 | 462 |
| jglfw + libimagequant | 7 | 169 | 150 |
| reflectasm + 其它第三方 | 13 | 98 | 55 |
| **默认包(类名被混淆)** | **3418** | **15782** | **9918** |
| 合计 | 4176 | 26470 | 16213 |
默认包那 3418 个是**混合体**:用"方法描述符多重集 + 上游 gdx-1.11.0 指纹"能较有把握认出 **~55 类 / ~907 方法**属于 libGDX(你举的"设置文字/颜色"在这层),**其余 ~3363 类 / ~14875 方法**是编辑器自有业务代码(Tree 视图、模型、命令系统、属性面板、导出…)。这只是"正例识别",真实环境自带会比 907 略多。
另外:invokedynamic 调用点 **55637** 个(`~/Desktop/spine398/sites.tsv`,已解出真实目标+描述符);编辑器注册的菜单命令 100 多条(`hotkeys.txt`)。
---
## 四、怎么跑(当前唯一的正确入口)
```bash
cd "/Users/tianmokeji/Desktop/临时/spine加功能"
JAVA_HOME=/tmp/jdk-17.0.20.1+1/Contents/Home ./viewpanel/build.sh # 编译
./viewpanel/run.sh # 先杀旧实例再启动(约 20s)
# 或双击 viewpanel/重启编辑器.command / spine-lab/启动实验室Spine.command(后者已修好,内部同样调 run.sh)
tail -f /tmp/spineview.log # 日志([panel]/[view]/[crud] 前缀)
```
调试开关:`-Dspine.probe=objects|panels|select|crud|matrix|panel`(探针)、`-Dspine.runCommand="New Bone"`(跑一条编辑器命令)、`-Dpanel.h=<像素>`(写死面板高度,默认 auto=读 Stage)。
---
## 五、还没做完 / 没验证的(别当成已完成)
1. **"列表控件铺满红框"**:要裁剪+滚动就不能铺满(ScrollPane 里 widget 按自己内容大小);要铺满就得去掉 ScrollPane(会溢出)。两条路都试过,**取舍未定**。
2. **新确认的 `fillY="L"`、`CELL_FILL="D"`**:`D` 在映射表里是唯一命中的 `fill`,`L` 从没验证过 —— 列表没被纵向拉伸很可能就是这里取错了方法。
3. **编辑器自有类(那 3363 个)的名字还原**:只做了 libGDX 层与少数手工锚点;业务层(`VZ`/`GP`/命令系统…)还没命名。
4. **热更**:回滚了;要做就得换容器方案。
5. **`GO/GU` 里还有一批未确认字段**(`boolean D/B/L/I/S/C/Z/V/F/Code`、`int D`、`Color B/L/I`…)。
6. **调试描边常开**(`ObjectPanel` 里搜 `TABLE_SET_DEBUG` 可关),还没做开关。
7. **导出链路**:现阶段未动(`Export` 菜单动作可用,见 `编辑器交互与数据API.md` §6)。