Files
Spine_lab/viewpanel/进展与结论.md
T
tianmo 2e7ab7c33c 阶段四:Spine 3.8.99 编辑器里挂上自建视图面板
目标:在 Spine 编辑器窗口内部加一个自己的视图面板(按钮 + 文字显示),
点击读取场景对象,并像原生面板一样出现在 Views 菜单、可停靠可折叠。

本轮打通:
1. 编辑器在我们自己的进程里完整启动
   - 类加载器必须是 URLClassLoader 本身(自检要求)
   - Editor.main 必须在 AWT EDT 上调用
   - jvm.cfg 第 21 行要以两个空格结尾 + -DSpineJava
   - Editor.D:I 版本号、fonts/、/tzax 资源、Java8 版 launcher.Launcher 替身
2. 3.8.99 全量解密 → 可读源码
   - 字符串 17251/17461(伪造同名调用帧对齐 key)
   - 混淆动态派发还原 53871 处(调引导方法取 MemberName)
3. 自建视图
   - agent 在内存里给 us 枚举加 spineView 常量(不改磁盘,避开完整性自检)
   - 面板 Actor 由外部反射填进 us.spineView.D
   - 复用已有图标做别名(不新建图片资源)
   最终 "Spine View" 出现在 Views 菜单,可与 Tree 并排停靠、折叠展开

另外:
- ViewPatcher/ViewAgent:视图枚举与面板挂载的字节码插桩
- DecAll/ObMap/rewrite_source:字符串解密、派发还原、可读源码生成
- 阶段四-视图面板挂载.md:完整记录做法与踩过的坑

不纳入版本管理:rea/(独立仓库)、tools 下的 JDK/Ghidra 工具链、
Spine.app 与 spine-lab 副本、从载荷派生的批量产物。
2026-10-16 13:34:18 +08:00

5.4 KiB
Raw Blame History

ViewPanel 进展与结论(2026-10-15)

目标:在 Spine 4.2.43 编辑器窗口内新增一个面板(一个按钮 + 一块文字),点击后读取编辑器里的对象。

一、已经打通的部分(都有实测证据)

环节 状态 关键证据
注入编辑器进程 ✅ DYLD_INSERT_LIBRARIES + _dlsym 重绑定 + 拦 JNI_CreateJavaVM
把我们的类定义进编辑器的类加载器 ✅ LPt4.mc(2370 条类名)→ defineIntoLoader → class com.spineview.SceneScan
在编辑器命名空间里按名字找类 ✅ s.AmK / s.Xgs / s.nHS / s.heZ / s.fSy / Editor / TextButton 全部找到
找到编辑器真实 UI 根 ✅ s.Xgs 子树含 s.XDB "Editor"/"Preview"/"Weights"/"Tree"、s.nHS "mainMenu"
确认是 Scene2D + Stage(不是 Swing) ✅ scene2d 类 33 个;swing/awt 应用类 0;AWT 窗口 0
拿到渲染线程执行权 ✅ 编辑器自己的 Actions.run(Runnable),Action 每帧在渲染线程跑
在渲染线程上构造出编辑器原生控件 ✅ *** 编辑器侧(渲染线程):按钮+文字已挂上

二、两个曾经卡了很久的坑(已定位并修掉)

  1. 编辑器和启动器是两套类加载器。同名类不是同一个 Class: Class.forName("s.nHS") 会抛 ClassNotFoundException,而对象的 getClass().getName() 就是 s.nHS。 → 必须把我们的类 DefineClass 进编辑器的加载器,或至少保证"控件类与样式取自同一个节点"。

  2. 控件必须在渲染线程上构造。Label.<init> → getPrefHeight → 字形生成 → glBindTexture, 没有当前 GL 上下文就是 SIGSEGV(libGL.dylib+0x4bc)。离线建、大栈线程建都不行。

    附带:样式若从"对话框"里的控件取,会让字体类 s.QjK 自递归(<init> ↔ I 无限循环)。 正确做法是从主界面正在渲染的控件上取样式。

三、当前唯一剩下的问题

控件已经建好、也调用了 addActor 挂到编辑器 Stage 根(s.Xgs),但紧接着进程就退出, 而且 spine.log 里没有崩溃报告(只有正常启动输出),说明更像是渲染线程抛异常后被应用自身的错误处理结束掉。

已经排除的:

  • 构造阶段(已移到渲染线程,不再 SIGSEGV)
  • 挂载点选错(自动扫描挂过 40 个容器,都不显示)

四、下一步的两个方向

  1. 隔离挂在哪个动作上:只构造不 addActor → 再只 addActor 不设文字 → 再设文字,逐步定位是 "绘制我们的控件"还是"改 Stage 结构"导致的退出。
  2. 换宿主:不挂 Stage 根,改挂到 s.azP(工具条容器)或 s.KJW(面板容器), 看是否是 Stage 根不被直接绘制。

五、复现方式

./viewpanel/build.sh
./viewpanel/run.sh /path/to/project.spine     # 或 open -n --env ... Spine.app --args -u 4.2.43 <project>
# 日志:/tmp/spine-viewpanel.log

关键代码:

  • viewpanel/native/inject.c — 注入器;VPC_defineIntoLoader 是"定义进编辑器加载器"的入口
  • viewpanel/src/com/spineview/SceneScan.java — 探针、命名空间识别、渲染线程装配

六、最终实验结果(项目在此停止)

实验 做法 结果
A 渲染线程上构造按钮+文字,不挂载 ✅ 进程存活 —— 构造阶段完全没问题
C 把界面里已有的 s.nHS 重新 addActor 到同一处 ❌ 进程退出 —— 不是我们造的控件的问题,是"加子节点"这个动作本身
B 换宿主 s.azP / s.KJW / s.nHS ⚠️ 这些类只有类名没有实例,不在可达对象里
D 从 Editor 实例(com.esotericsoftware.spine.editor.Editor._)出发找 Stage ❌ 死路:Editor 直接继承 Object,0 个实例字段、只有 3 个静态字段(一个 long、一个 null 的 Object[]、它自己),是空壳

结论:注入链路、类命名空间、渲染线程、原生控件构造全部打通;唯一失败的是 「往 s.Xgs 加子节点」这个动作(连挂已有控件也会退出),而 Editor 实例这条路又因为它是空壳而断掉。

还没试过的两条路(留给后续)

  1. 找到编辑器每帧真正绘制的那棵 UI 根:s.Xgs 很可能只是骨架容器,不是被绘制的那层。 需要继续在 2370 个类名的类表里找"持有 s.azP / s.KJW 实例的类",从它的静态字段里拿实例。
  2. 接管一个已有面板:不新增节点,改为把某个现有面板的内容替换成我们的按钮+文字 (阶段一验证过"改现有控件的文字"是会渲染的)。

已确认可用的能力(后续可直接复用)

  • viewpanel/native/inject.c 的 VPC_defineIntoLoader:把任意类定义进编辑器加载器 LPt4
  • 类表:s.LPt4.mc(ConcurrentHashMap,约 2370 个类名),用它能按名字解析出编辑器全部 2500 个类
  • 编辑器侧命名空间里可解析:s.AmK(Actor) / s.Xgs(Group) / s.nHS / s.heZ(Stage) / com.esotericsoftware.spine.editor.Editor / com.badlogic.gdx.scenes.scene2d.ui.TextButton 等
  • 渲染线程入口:编辑器自己的 Actions.run(Runnable)
  • 控件构造:必须在渲染线程上;样式必须取自主界面正在渲染的控件(否则字体类 s.QjK 自递归)