原版场景节点关联、可见性与 Room 父 ID
本批结论
新增 core_sim/NativeSceneAttachment.cs,将 CPositionableObject/Room Piece 的节点关联控制接入核心;节点创建、实际节点操作、运行时对象查询通过明确的后端接口提供。
本批不替换现有 Godot 地图节点树。对象工厂、Group 接受/拒绝、实际模型创建、完整世界分区与攻击型 proc 仍未完成,武器可构造覆盖保持 1053/1419。
三种关系不能合并
| 原字段 | 含义 | 本批字段 |
|---|---|---|
| 对象 +8/+12 | 自身运行时 ID,不是 BINLAYOUT 源 ID | RuntimeId |
| 对象 +16/+20 | 编辑器父对象的运行时 ID | ParentRuntimeId |
| 对象 +76 | 所属场景/拥有者对象 | Owner |
| 对象 +80 | 变换父对象引用 | TransformParent |
| 对象 +88 | 自身 Ogre SceneNode | Node |
| 对象 +96 | 挂接目标 SceneNode | AttachmentNode |
| 对象 +104 | 固定挂接标记;仅字节恰好为 1 时固定 | AttachmentPinned |
| 对象 +105 | 对象可见性字节,本批 API 使用规范 bool | Visible |
例如:已有 TransformParent 时,624DA0 设置新 Owner 不替换 TransformParent,但仍将 AttachmentNode 请求为新 Owner 的节点。因此计算节点派生姿态应依据实际节点树,不能直接使用 ParentRuntimeId 或 TransformParent 引用作为节点父亲。
本批夹具将此关联结果接到上一批 ResolveNodePoses,验证派生位置采用实际挂接父节点。这里只验证数据连接,不是实际 Godot 节点树验收。
624B80:可见性与挂接
当新旧可见性相同时不操作节点;否则:
- 存储新可见性。
- 有自身节点时,通过 6746C0 从实际父节点脱离。
- 如果可见,或者 TransformParent 非空,则尝试加入 AttachmentNode,再请求节点更新,参数为 true/true。
- 调用节点可见性入口,参数为新可见性和 cascade=true。
所以隐藏子节点仍可能保留在父节点下。不能用隐藏等同于脱离节点树,更不能由此推断碰撞资产已不存在。
6746C0 非空父节点路径调用两次 getParentSceneNode,再调用父节点 removeChild。本批保留调用次数和顺序;在受测后端中检查这些操作,而不将其简化成缓存的一次查询。
624D30/624EC0/624DA0
设置挂接节点:624D30
- 旧 AttachmentNode 非空且固定字节等于 1:完全不修改。
- 否则先更新固定字节,即使请求节点与旧值相同。
- 请求节点改变时先脱离自身节点,再存储新目标。
- 新目标非空时,翻转对象可见性并直接调用基类 624B80恢复原可见性,以强制传播。
- 新目标为空时清固定字节。
- 固定字节 2 不等价于 1;测试保留这个区别。
设置变换父对象:624EC0
- 先存储 TransformParent。
- 非空父对象请求挂到该对象的节点。
- 清父对象时,除非已有挂接目标且固定字节等于 1,否则脱离并清挂接目标。
- 最后翻转可见性,再经对象虚表 +56 恢复原值。
最后一步是虚调用,与 624D30 中的直接基类调用不同。
设置所属场景:624DA0
- 先存储 Owner。
- 仅当前 TransformParent 为空时,经虚表 +8 设置变换父对象为 Owner。
- Owner 非空时仍请求其节点作为挂接目标。
- Owner 为空且当前没有挂接目标时,请求 SceneManager 根节点作为目标。
此函数没有修改 ParentRuntimeId。
Room Piece 专用虚表不能漏掉
重新读取原 EXE 的主虚表:
| 槽位 | CPositionableObject:217BDDC | Room Piece:217C48C |
|---|---|---|
| +8 变换父对象 | 624EC0 | 624EC0 |
| +12 父运行时 ID | 464CF0 | 622280 |
| +20 所属场景 | 624DA0 | 624DA0 |
| +56 可见性 | 624B80 | 622330 |
此前仅查看 464CF0 不能推导 Room Piece 的完整父 ID 行为。
622330 检查 Owner+388 指向对象的 byte+16;非零则不调用基类可见性入口。本批将该值作为明确输入,不将其猜成通用“编辑器模式”。
这意味着 624EC0 先翻转可见性后,如果虚调用被 Room 覆盖阻止,翻转后的字节会保留下来。624D30 的直接基类调用不受这个覆盖阻止。该差异已由发行机器码参考验证。
622280:父 ID 与随机组祖先
基类 464CF0 只写入两个 32 位 ID 字段;Room Piece 的 622280 还执行:
- 请求父 ID 等于自身 RuntimeId 时,将父 ID 改为 -1,然后返回。
- 其他情况下先保存请求;无 Owner 或请求为 -1 时不遍历。
- 从 Owner 的运行时对象查询入口 4D6D10 获取父对象。
- 检查该对象能否转为 CRandomGroup,且其 +280 字段是否非零。
- 命中时设置 Room byte+405 为 1。
- 否则继续查该对象的父 ID;空对象或立即指回自身时终止。
byte+405 在该 setter 内只会置 1,不会清零。 更换父 ID 不应自动清掉此标记。
本批保留 +280 的原字段含义边界,不猜为“子节点数量”,也不将 byte+405 当成 621F60 的完整条件细节判断或 Group 接受结果。
原函数对超过一个节点的祖先环没有保护。移植端遇到此类畸形输入明确报错,避免挂死;这是诊断保护,不是原版会拒绝该环的声明。
节点创建与装载顺序证据
624F30 仅在自身 Node 为空时调用 SceneManager 虚表 +256 创建节点;若 +92 有已存附着对象则交给 624C30,随后翻转可见性并直接经 624B80 强制传播。
NativeSceneAttachment.EnsureSceneNode 保留这些调用边界。624C30 的附着模型处理仍由后端承担,测试使用显式 spy;没有把它声明为完整模型附着实现。
624FA0 构造路径先建立 SceneManager 关系并调用 624F30,再直接保存 SceneManager 的根节点到 +96。它不是把根节点当作已调用 AddChild 的证据。
继续核对的装载路径为:
- 4D7B30 创建对象并设置 descriptor/所属场景,处理运行时 ID 唯一性、默认快照和注册。
- 50B680 的二进制读取在对象创建后设置父运行时 ID、源 ID 和属性,将接受对象加入加载列表,再递归读取子项。
- 50BA10 处理记录的扩展数据,然后依加载列表顺序调用 descriptor 初始化槽 +76。
- Room 初始化槽为 4B9A00 → 6234B0,接前批资源刷新生命周期。
以上装载顺序是取证结果;本批没有实现全套 4D7B30/50B680/50BA10 工厂、随机组选择或扩展数据执行,也未将 Godot 的 _ready 顺序作为它们的替代。
验证与范围
- 原 EXE SHA256:
186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e。 - tools/build_native_scene_attachment_oracle.py 将原版相关代码页映射进隔离 32 位辅助进程;没有 attach 原游戏。
- 17,280 组节点设置案例覆盖节点存在/缺失、Owner/TransformParent、可见性、Room 覆盖、场景抑制、固定标记 0/1/2、不同挂接目标及当前实际父节点。
- 256 组父 ID 案例覆盖基类/Room、Owner、粘滞标记、随机组祖先、空查询、自身 ID、立即自环和多层祖先。
- 所有受测最终字段和有序节点调用与移植代码一致:17,536 组,零差异。
- Ogre 分配/节点虚调用、运行时查询和 RTTI 使用明确 spy;不宣称原 Ogre SceneManager/Godot 渲染后端已完成。
- 非规范 Visible 字节、释放/销毁、并发回调、长环原生挂起、完整对象构造和世界注册未纳入等价性声明。
- core_sim.tests:1232 条核心断言通过。
- 相关 Python 回归:149 项通过。
- Godot C# 构建零警告、零错误;coresimlint 通过。
产物:build/native_scene_attachment_oracle/results.json、build/native_scene_attachment_core.log、build/native_scene_attachment_python.log、build/native_scene_attachment_godot.log。
NativeSceneAttachmentState 目前是隔离/分阶段装载的临时数据,没有加入持久世界所有者或存档。正式运行时集成前必须明确所有权和快照/重建策略;本批不升级快照 v29。
按 godot-master 的数据/表现分层,本批只补核心关联控制,不改变正式场景画面,无新增视觉验收声明。
main 原地工作,未提交;默认 warp_autosave.json 未修改,SHA256 保持: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f。
下一批继续对象工厂/分阶段初始化与实际视觉后端,再接真实世界注册/分区和攻击型触发技能。289 个场景候选仍缺全局随机上下文,没有因此计为已解析。