# 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.` → `getPrefHeight` → 字形生成 → `glBindTexture`, 没有当前 GL 上下文就是 SIGSEGV(`libGL.dylib+0x4bc`)。离线建、大栈线程建都不行。 附带:样式若从"对话框"里的控件取,会让字体类 `s.QjK` 自递归(`` ↔ `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 # 日志:/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` 自递归)