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

86 lines
5.4 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.
# 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 根不被直接绘制。
## 五、复现方式
```zsh
./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` 自递归)