Room 实例碰撞资源替换与共享所有权
本批交付
将原共享碰撞缓存接到 Room Piece 实例的数据宿主,不再由每个 Room 独立保存一份没有引用生命周期的几何对象。
- 原 622360 setter 与 622F40 碰撞释放段,连接原共享缓存代码,1,512 次操作对照一致。
- 核心累计 1,445 条断言、270 项相关 Python 回归通过。
- Godot C# 构建零警告、零错误,coresimlint 通过。
- Godot 实际资源探针扩展为 26 项检查,验证两份 Room 资源实例的共享/替换/最终释放。
- F2 未就绪输入继续在 staging 预检时报错,不把提取缺口当作原包不存在。
完整 CCollisionModel 构造/析构、装备实例、导航和正式世界宿主仍未据此标成完成。
622360 正常分支
两个直接 6A4650 调用均是 constant false,不应根据旧名称当成可覆盖的虚函数。代码中的另一条 collision override 路径在此 EXE 不可达。
正常路径顺序:
- 比较 request 与 cached request,完全相等直接返回。
- 调用共享缓存 release(oldModel),即使 oldModel=null 也照常调用。
- 把当前模型槽清零。
- request 为空则返回,不清空 cached request。
- 保存新 request。
- collision-enabled=false 时将 cached request 设为空。
- cached request 为空则返回。
- 查原资源是否存在。
- 存在则 acquire 原 request 并写入模型槽;不存在则记录缺失请求。
这里没有额外的动态导航刷新调用;不能为了“接线方便”把 622C20 塞进这个 setter。
必须保留的状态差异
| 操作 | 模型槽 | cached request |
|---|---|---|
| 同名请求,即使 enabled 改为 false | 原样保留 | 原样保留 |
| 不同空请求清空旧模型 | 清空并 release | 保留旧名字 |
| 不同非空请求,enabled=false | 清空并 release | 空 |
| 不同非空请求,资源不存在 | 清空并 release | 保留尝试的名字 |
| 资源存在并成功获取 | 新共享模型 | 新名字 |
因此:
- 清空后再次设置相同旧名字,不会重新 acquire。
- 某次缺文件后,重复相同名字不会自动重试,即使外部后来让资源可用了。
- 不能用“启用状态变化就重载”或“空模型就重载”的合理化规则替换原行为。
实例结束时的释放
622F40 和 622E80 中都有 release 当前碰撞模型、随后清空模型槽的步骤。原 622F40 还可能销毁 definition holder;本批只实现/验证碰撞所有权部分,没有将共享不可变 F2 catalog 当作该原 holder 的替身。
NativeRoomCollisionSlot.ReleaseForTeardown 保留 release(null) 的调用语义。F2NativeRoomResourceHost 的 IDisposable 包装是幂等的,防止 C# 调用者在结束同一个宿主时重复释放;它不声称原版重复析构合法。
实例释放只减引用,不立即 GC。由共享资源上下文决定何时调用 CollectZeroReferences。
核心与 IO 接线
NativeRoomCollisionSlot 拥有单实例:
- CachedRequest。
- 当前 NativeCollisionModelHandle。
- setter/teardown 的原顺序。
INativeRoomCollisionFiles 提供明确的文件存在判断和缺失记录边界,不能把未知可用性补成存在或不存在。
F2CollisionModelLibrary 是共享上下文:
- 持有一个 NativeCollisionModelCache。
- 使用 F2CollisionModelCacheHost 创建原几何输入。
- 按模型身份保存对应 NativeCollisionAsset,供原顶点 bounds/导航接口使用。
- 创建、回收时维护 asset 与 handle 的一致性。
- 允许已验证输入的预检和一次性预读,避免相同共享模型被重复解析。
F2NativeRoomResourceHost 构造器现在必须接收共享 library,不能悄悄为每个 Room 创建孤立缓存。已有工厂、LAYOUT 装载组合测试及 Godot 资源探针均已更新。
视觉对象依然由原视觉 backend 拥有;碰撞宿主 Dispose 不代替视觉节点 Dispose。
staging 预检与原运行时的边界
F2 数据宿主面对的是已导出并审查的输入,不是原 PAK 管理器:
- 当新请求需要装载时,先确认现有 F2 几何可读取/校验。
- 未解析、未导出或未知路径在这一步抛出输入缺失,保留旧 staging 实例及其引用。
- 预检通过后,才进入原 release-before-acquire setter 顺序。
- 原缺文件分支由核心接口表达,原生对照也覆盖;F2 层不能把“没有导出”简单返回成原文件不存在。
这是明确保留的输入准备边界,并非声称原版所有装载失败都会回滚旧模型。缓存创建中途的异常仍需处理不完整上下文,不能被预检规则掩盖。
两份实例的实际验证
Godot 探针使用真实 rock_03 碰撞资源:
- 第一份 Room 完成原定义/视觉/碰撞输入装载。
- 第二份 Room 资源宿主只接碰撞链,使用同一个共享 library。
- 两者持有相同碰撞 model handle 和 asset,引用数为 2。
- 第一份实例切换到 foliage,旧 rock 引用只减为 1;回收不能销毁 peer 持有的 rock。
- peer 结束,旧 rock 归零;回收后旧 handle 退休。
- 剩余 Room 结束后,缓存没有残留模型引用。
第二份宿主没有另外创建一套视觉节点;此验证针对碰撞共享所有权,不夸大为两套完整正式场景实例验收。
资源探针仍验证真实纹理、镂空材质、原 bounds/四叉树等既有链路。画面配置没有修改,本批新增检查不是新的像素一致性认证。
核心组合测试还检查:
- 未知 F2 请求失败前保留旧实例及 peer 引用。
- 空请求的 sticky name。
- sticky name 相同不 reacquire。
- 最后 owner Dispose 两次只释放一次。
原生执行范围
tools/native_room_slot_oracle.c 复用已验证的缓存参考基础,实际执行:
- 622360 正常 setter。
- 622F40(definition holder 为空,只观察碰撞释放段)。
- 6A4650、6488C0、68DD70 原 singleton/常量叶函数。
- 原 acquire/release/零引用收集和实际 MSVCP90 字符串操作。
明确边界:
- PackageMgr 文件存在结果。
- 缺失日志入口;不构造整个原 Ogre 日志/UI。
- 碰撞模型创建/销毁和受控分配器。
输出逐步记录两个实例的 cached name/model 身份、缓存引用和模型创建数、文件检查/缺失/重入回调。没有只比较最后 count,而漏掉中间替换顺序。
代码只在独立参考进程的映射中修补边界调用,不修改原 EXE 或 attach 游戏。
产物
- core_sim/NativeRoomCollisionSlot.cs。
- core_sim.io/F2RoomMetadata.cs。
- core_sim.io/F2CollisionModelCacheHost.cs。
- core_sim.tests/NativeRoomCollisionSlotChecks.cs。
- tools/build_native_room_slot_oracle.py、native_room_slot_oracle.c。
- tests/test_native_room_slot.py。
- torchlight-2-poc/NativeVisualBackendProbe.cs。
- build/native_room_slot_oracle/results.json。
- build/native_room_slot_mismatches.json(空)。
- build/native_room_slot_core.log、native_room_slot_python.log、native_room_slot_godot.log、native_room_slot_visual.log。
EXE SHA256: 186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e。
继续推进
后续进展:CCollisionModel构造、装载门与失败状态已补构造和装载/失败控制并接 F2 后端;完整 mesh 资源租约与单位生命周期仍需继续。
Room 碰撞槽与共享缓存的替换/释放接线不再是未知项。下一步继续原 CCollisionModel 的完整初始化、装备与角色的实例资源生命周期,再接正式世界对象集合和攻击遮挡。
武器可构造覆盖仍为 1,053/1,419;动画、攻击型 proc、缺导出资产和全武器实机验收未完成。UniverseComplete=false 未放宽。
按 godot-master 的资源归属要求,共享模型归共享 library,实例只持有引用,视觉与碰撞分别释放。main 原地工作,未提交、未改正式地图/UI,快照 v29 未变。默认存档 SHA256 保持: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f。