11 KiB
11 KiB
阶段五:把面板接到"真实数据"上(对象增删改查 / 属性读取与改写 / 命名还原)
本文记录阶段五已经做成的、实测到的数字、踩过的坑,以及还没做完的。
详细的"混淆名 ⇒ 真名"对照在 混淆名对照表.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 |
指纹 + 目录铺满/换行实测 |
二、踩过的坑(按"代价从大到小")
VZ.D(Object)不是"选中" —— 它内部是Selection.L()(去掉选中项)。真正"设成选中"的是VZ.D(Tree.Node, boolean)。这让我前面所有增删改都"执行了但没生效"。- 动模型的代码必须在编辑器渲染线程 ——
OG.I()有线程断言,非渲染线程直接抛RuntimeException;要用编辑器的OG.B(Runnable)切过去。 - 不能在渲染线程里 sleep —— 编辑器有些创建是下一帧做的;占着渲染线程睡 300ms,那一帧永远不来,对象就建不出来。
MapMethods的候选(~n/m)不是结论 —— 我把它们混进names.tsv当确定名字用,结果"拿的方法不对"。现在拆成anchors-libgdx.tsv(只放=唯一命中)与anchors-libgdx-unconfirmed.tsv。- 同名不同描述符的坑就在我们自己代码里 ——
Cell.fill()的载荷名是D,但 Cell 上有多个无参D(),getMethod("D")会取错;fillY="L"是"按形状猜"的,没验证过(很可能取错)。 ScrollPane.B(ZZ)V是setScrollingDisabled,不是setForceScroll—— 我传了(false,true),等于把纵向滚动关掉了(现象:列表滚不动)。setForceScroll是I(ZZ)V。- 滚轮不生效的根因是焦点 —— 给列表 ScrollPane 挂事件监听后,滚轮事件收到 0 条:焦点在画布上。解法:点面板后
Stage.setScrollFocus(面板);找这个方法必须用getMethods()(它声明在接口LPt9里,getDeclaredMethods扫不到)。 run.sh漏了import os—— python 那句报 NameError,编辑器根本起不来;而我用>/dev/null把错误吞了,白排查了好几轮。nohup … &启动会被会话回收 —— 必须subprocess.Popen(..., start_new_session=True),否则我这边一次工具调用结束,编辑器就被收掉(你几次看到"进程没了"就是这个)。- 面板根表格的高度被父容器限制 ——
fillParent是"复制父容器尺寸",而父容器又按我们的内容算 → 互相依赖;结果 panel 永远等于自己算出来的高度(实测 root≈1360,停靠框 1944)→ 底部固定留一条空带(你说的"区域1")。从面板内部无解,只能用expand或把停靠栏拖大。 - 热更(换容器重建面板)把面板搞成空白 —— 那版已
git revert;要做得换一种容器(能让 dock 正确布局的 WidgetGroup)。 - macOS 文件系统大小写不敏感 —— 一次反编译整包时
GP.java/Gp.java、Yr.java/yR.java互相覆盖,导致"有些类像没改名"。现在改成每个类单独反编译 +class_<载荷名>.java。 Table.setDebug(true)的红/绿线是调试描边,不是布局错误;它给每层表格的每个 cell 各画一圈(红=根表格三段,绿=内部格)。- 我自作主张加过"分页"(你没要求)—— 已回滚,并按你说的把纪律写成项目级技能
.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)。
四、怎么跑(当前唯一的正确入口)
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)。
五、还没做完 / 没验证的(别当成已完成)
- "列表控件铺满红框":要裁剪+滚动就不能铺满(ScrollPane 里 widget 按自己内容大小);要铺满就得去掉 ScrollPane(会溢出)。两条路都试过,取舍未定。
- 新确认的
fillY="L"、CELL_FILL="D":D在映射表里是唯一命中的fill,L从没验证过 —— 列表没被纵向拉伸很可能就是这里取错了方法。 - 编辑器自有类(那 3363 个)的名字还原:只做了 libGDX 层与少数手工锚点;业务层(
VZ/GP/命令系统…)还没命名。 - 热更:回滚了;要做就得换容器方案。
GO/GU里还有一批未确认字段(boolean D/B/L/I/S/C/Z/V/F/Code、int D、Color B/L/I…)。- 调试描边常开(
ObjectPanel里搜TABLE_SET_DEBUG可关),还没做开关。 - 导出链路:现阶段未动(
Export菜单动作可用,见编辑器交互与数据API.md§6)。