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

11 KiB
Raw Permalink Blame History

阶段五:把面板接到"真实数据"上(对象增删改查 / 属性读取与改写 / 命名还原)

本文记录阶段五已经做成的、实测到的数字、踩过的坑,以及还没做完的。 详细的"混淆名 ⇒ 真名"对照在 混淆名对照表.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)。


四、怎么跑(当前唯一的正确入口)

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)。