feat(packages): 内置 SDK 包并完善 ContextLoader 集成
- 将 ShrinkEventBus、ShrinkDataSaver 及其 EventBus 集成从 gitlink 转为仓库直接维护的完整 UPM 包,补齐运行时、编辑器工具、测试与文档 - 新增 Command 和 Network 的 App 集成组件,支持 ContextLoader 服务发布、可逆注销及 Network Loopback 生命周期管理 - 更新 Starter 与演示组合逻辑,缺失模块时可注册、已有兼容安装器时可覆盖,并补充宿主启动断言 - 升级内部包依赖与 Shared CodeGen 包定义,放宽 Integration.App 包的 Git 忽略规则 - 将独立服务器生成器改为基于已编译程序集的语义扫描,支持 partial、复杂泛型、命名冲突检测及模板 SHA-256 覆写保护 - 新增 Network 语义扫描、模板保护和 App 组件生命周期测试 - 新增真实 UPM 消费工程验证脚本,校验内部版本一致性、程序集加载及 EditMode 测试 - 重构当前架构文档并归档已完成的 Cordis 迁移与旧代码地图
This commit is contained in:
@@ -0,0 +1,330 @@
|
||||
# ShrinkSDK × Cordis 组织形式改造方案(已归档)
|
||||
|
||||
> 归档说明:阶段 0 至 5 已完成并合入当前架构。本文只保存迁移论证、过程决策和历史验收数据;当前状态以仓库根 `DESIGN.md` 为准。
|
||||
|
||||
> 生成日期:2026-08-16;独立重读与阶段 5 路线更新:2026-08-17
|
||||
> 参考文献:《Cordis: A Programming Paradigm for Spatiotemporal Composability》(北京大学 / DeepSeek-AI,88 页,`C:\Users\im\Downloads\Cordis_paper_zh-CN.pdf`)
|
||||
> 本文档回答一个问题:**如果按 Cordis 的"时空可组合性"范式重组 ShrinkSDK,目标形态是什么、差距在哪、如何分阶段走过去。**
|
||||
> 现状基线见仓库根目录 `DESIGN.md`。
|
||||
|
||||
---
|
||||
|
||||
## 1. Cordis 论文核心思想提炼
|
||||
|
||||
### 1.1 两个正交维度
|
||||
|
||||
| 维度 | 定义 | 失败症状(现状普遍存在) |
|
||||
|---|---|---|
|
||||
| **时间可组合性** | 移除组件时,其对共享环境的全部修改被**完全、有序、安全地回滚** | VSCode 式困境:禁用扩展要重启宿主;deactivate 钩子与 activate 分离,靠作者自律清理 |
|
||||
| **空间可组合性** | 组件**声明式**表达依赖,运行时**响应式**解析:依赖出现→激活,消失→停用,换提供者→重载 | 服务定位器/Spring getBean:依赖隐式分散,空检查遍布;依赖消失只能崩或靠手写适配 |
|
||||
|
||||
### 1.2 两大运行时机制
|
||||
|
||||
**可逆效应(revertible effect)**——时间维度的原语:
|
||||
- 每个上下文变换 `e : Γ → Γ × (Γ→Γ)` 在执行时**返回自己的逆函数**;
|
||||
- 运行时把逆函数累积到累加器 φ,卸载 = 执行 φ(LIFO);
|
||||
- 复合效应的逆由组合自动推导——**组件的拆卸从其加载过程推导出来,而非并列手写**;
|
||||
- 实现形态就是一个 API:`ctx.effect(callback)` 返回 `dispose` 闭包。
|
||||
|
||||
**响应式余效应(reactive coeffect)**——空间维度的原语:
|
||||
- 依赖是键值表 `Σ : K ⇀ V`,`ctx.set(key, value)` 提供依赖、`ctx.get(key)` 消费依赖;
|
||||
- 组件声明规范 `d ⊆ K`(inject),满足谓词 `σ ⊨ d` 可判定;
|
||||
- 每次上下文变化被分类为 activating / deactivating / neutral,自动驱动组件激活/停用/重载;
|
||||
- `set` 本身是效应 → 提供依赖自动可逆(卸载提供者 = 撤回绑定 = 依赖者自动停用)。
|
||||
|
||||
**统一上下文 Γ∞**:`Γ × (Γ→Γ) × Σ` 递归自相似——上下文树。父子层级由"实例化子组件是父组件的一个效应"天然形成:**卸载父级级联卸载子级**。
|
||||
|
||||
### 1.3 组件与纤程(fiber)
|
||||
|
||||
```
|
||||
组件 component = ( inject: 依赖键集合 d, provide: 供给键集合 p, apply: 效应函数 e )
|
||||
纤程 fiber = 组件的运行时实例化 ⟨d, p, e, π(父), σ, τ(退役), θ(生命周期)⟩
|
||||
```
|
||||
|
||||
- **供给不相交**:同一注册表中两个纤程的 provide 不得相交(每键唯一提供者);
|
||||
- 生命周期由 **target(目标提供者集合)+ 惯性状态机** 驱动:target 变化 → refresh → reload(执行 apply,追踪效应)或 unload(先 drain 依赖者,再 LIFO 执行逆操作);
|
||||
- 转换中途 target 再变 → 链式再转换(惯性),转换内每步迭代边界检查 target(部分回滚);
|
||||
- 提供者以 **uid(永不复用)** 标识——同值不同提供者的替换也能被观察到。
|
||||
|
||||
### 1.4 三层实现架构(这是"组织形式"的直接参照)
|
||||
|
||||
| 层 | 职责 | Cordis 内容 |
|
||||
|---|---|---|
|
||||
| **① 核心库** | 效应追踪 + 余效应解析 + fiber 生命周期 | `ctx.effect / get / set / isolate / intercept / use`、refresh/reload/unload 算法 |
|
||||
| **② 组件加载器** | 声明式配置协调 + 热模块替换 | 配置树(entry: id/url/isolate/intercept/config/disabled)、增量协调、@cordisjs/group、HMR 三阶段(模块分类→陈旧项检测→事务性重载) |
|
||||
| **③ 应用框架** | 领域词汇 | Koishi(4000+ 插件的聊天机器人框架);Web 控制台是同一模型的第二个 Cordis 应用 |
|
||||
|
||||
关键设计取向:
|
||||
- **元框架不规定领域**——只提供通用动态组合语义,领域词汇留给上层;
|
||||
- **隔离域**(isolate):同一逻辑键在不同上下文解析为不同绑定(多租户/测试/沙箱);
|
||||
- **拦截**(intercept):访问时按元数据调整依赖的使用方式(细粒度访问控制,如只读降权);
|
||||
- 依赖访问经代理介导:**未声明的访问在访问点直接拒绝**(能力安全)。
|
||||
|
||||
### 1.5 本次独立重读后的校准
|
||||
|
||||
1. **Cordis 不是“DI + Dispose”**。核心纪律是组件对环境的读写都归属于自己的上下文:写入才能被归因并自动求逆,读取才能由余效应介导。静态服务定位器只能作为系统边界或兼容门面;经它发生的隐式访问不在主保证内。
|
||||
2. **时间可组合性不等于有 cleanup 钩子**。逆操作必须和前向效应局部绑定、按应用顺序记录并 LIFO 回放。若两个组件会以任意顺序卸载,它们的效应要么在观察等价下独立/可交换,要么把顺序敏感性显式变成余效应依赖。
|
||||
3. **空间可组合性的关键是两步 Leave/Unload**:提供者先停止对外供给并排空所有消费者;消费者卸载期间仍通过 committed view 读取旧提供者;最后才执行提供者逆操作。仅在每次 `Inject` 变化后重新检查是否满足,并不足以保证安全卸载。
|
||||
4. **提供者身份不是值相等**。fiber uid 必须进入目标视图;即便新旧服务值相同,只要提供者变了,消费者也要重载。异步转换遵循惯性:已经开始的一步要落地,再根据新 target 转入后续 unload,不能在任意 await 点宣称“取消即回滚”。
|
||||
5. **失败是纤程局部事务**。apply 失败必须回滚已累积的部分效应,进入失败/非活动状态,不能自动无限重试;显式配置或依赖变化再触发下一次尝试。
|
||||
6. **加载器条目 id 是双向绑定的稳定主键**。配置变更应优先交给组件做最小 diff,而不是永久依赖统一重建。HMR 还必须是事务:模块分类、陈旧条目计算、缓存备份/失效、替换;新模块导入失败时完整恢复缓存与旧条目。只有 `dispose + use` 不能称为论文语义的 HMR。
|
||||
7. **系统边界决定“可逆”的上限**。获取 socket、注册 handler、订阅事件可以回滚;已经发出的网络字节、写入的文件/存档不能被真正撤回,只能在提交前扣留输出,或执行领域补偿。Network/DataSaver 的组件说明和阶段 4 验收必须区分这两类效应。
|
||||
8. **跨领域集成组件不是过渡瑕疵,而是论文 §6.5 的正式答案**。Command-Network、模块-EventBus 这类双向/循环关系应由一个注入双方服务键的薄组件承载,核心包保持互不依赖。真正应淘汰的是隐式反射接桥和安装器包装,不是所有 Integration 包。
|
||||
9. **字符串键仍有碰撞与接口漂移风险**。当前先使用命名空间化键;后续应把包版本约束、结构兼容检查或强类型生成键纳入加载器边界。
|
||||
|
||||
### 1.6 对 ShrinkSDK 的工程判断
|
||||
|
||||
综合论文第 3~6 节与当前实现,我对 Cordis 在 ShrinkSDK 中的定位是:
|
||||
|
||||
1. **它应当是生命周期与组合语义的唯一地基,不是新的业务框架。** EventBus、DataSaver、Network、Command、Tutorial 仍然保留自己的领域 API;Cordis 只负责回答“谁拥有这些副作用、依赖变化时谁该运行、组件退出时如何恢复”。后续不能再新增第三套启动/卸载模型。
|
||||
2. **阶段 0~4 已经覆盖论文最关键的正确性路径。** 当前 `ShrinkContextRuntime` 已具备 provider uid、committed view、惯性转换、部分回滚和 drain-before-inverse;`ShrinkModContextHost` 已把外部 DLL 的组件源、revision 缓存与旧组合恢复纳入同一事务。接下来主要是把这些保证变得可约束、可诊断、可运营,而不是重新实现 fiber。
|
||||
3. **当前最大语义缺口是“声明没有被完全强制”。** `IShrinkComponent.Provide` 目前仍主要依赖作者纪律,运行时没有拒绝组件写入未声明键;字符串键也只提供名义连接,没有类型和版本兼容保证。若不先固定访问契约,intercept 只能成为一层不可靠的包装。
|
||||
4. **当前最大工程缺口是“发生问题时看不见”。** fiber 已有状态和 `LastError`,但缺少稳定的运行时快照、事务阶段、依赖等待链和 revision 常驻统计。动态组合系统一旦发生等待、回滚或程序集常驻增长,必须能从诊断面直接回答原因。
|
||||
5. **intercept 是访问介导,不是安全沙箱。** 它适合表达“社区模组只能只读 DataSaver”“某组件不能访问某类网络能力”等宿主可控策略;但外部 DLL 仍可绕开 ctx 直接调用 CLR/Unity API。不可信代码必须另用进程、WebAssembly 或其他执行隔离,不能把语言层访问控制写成安全边界。
|
||||
6. **程序集不可卸载必须转化为容量策略。** Mono 下 revision 替换只能撤回实例和效应,旧 `Assembly` 仍然常驻。正确做法不是伪装成完全卸载,而是统计历史 revision、估算常驻量、设阈值并给出重启建议。
|
||||
|
||||
因此,阶段 5 的顺序应为:先收紧访问契约和诊断,再实现 isolate/intercept,之后补程序集常驻策略和配置/调试工具。这样后续能力建立在可验证的边界上,而不是继续扩大隐式行为。
|
||||
|
||||
---
|
||||
|
||||
## 2. 概念对照:Cordis ↔ ShrinkSDK 现状
|
||||
|
||||
| Cordis 概念 | ShrinkSDK 现有对应 | 差距评估 |
|
||||
|---|---|---|
|
||||
| `ctx`(一等上下文) | `ShrinkAppContext`(Host+Settings+Services) | 形似神不似:无效应追踪、无响应式,启动后只读 |
|
||||
| `ctx.effect(cb)` → dispose | **无统一机制**。最接近的孤例:`IShrinkEventSubscription : IDisposable`(订阅=效应,Dispose=逆) | 全仓库最大空白 |
|
||||
| fiber(组件实例+生命周期) | `IShrinkAppModuleInstaller` 实例(只有启动半程) | 现状只有 Load,没有 Unload/Reload;无 uid、无 target、无惯性 |
|
||||
| `inject`(依赖键集合) | `installer.DependsOn`(模块 ID 字符串) | 依赖对象是"模块"而非"服务键";只在启动时校验一次,不响应变化 |
|
||||
| `provide`(供给键集合) | `context.Services.Register<T>(service)` | 有键值语义(Type→实例),但无声明、无双向绑定、撤回不级联 |
|
||||
| 满足谓词 + notify/refresh | **无** | 依赖缺失时抛异常(`SortInstallers` 直接报错),而非等待/停用 |
|
||||
| 声明式配置树 + 协调器 | `ShrinkAppSettings.disabledModuleIds`(一维开关) | 无条目树、无 config 变更分发、无增量协调 |
|
||||
| 上下文树/父子级联 | 无(安装器扁平列表拓扑排序) | — |
|
||||
| isolate / intercept | 无 | 多租户测试、按组件降权等能力缺失 |
|
||||
| HMR(dispose + re-use) | ModFramework 外部 DLL **只增不减**热载 | 有"加载"半程,无替换/回滚半程 |
|
||||
| 组件加载器层 | ShrinkApp.Bootstrap(BeforeSceneLoad 一次性跑完) | 无持续协调循环 |
|
||||
| 应用框架层(Koishi 位) | EventBus/DataSaver/Network/Command/Tutorial | 这层不需要"改造",需要的是**改坐在新地基上** |
|
||||
|
||||
**结构性发现(校准后)**:`Integration.App` 的一次性安装器包装应被原生组件和组合根取代;`Integration.EventBus` / Command-Network 则应收缩为注入双方服务键的薄集成组件。范式切换消除的是隐式启动、反射发现和手写依赖编排,不要求把具有真实跨领域职责的 Integration 包全部删除。
|
||||
|
||||
---
|
||||
|
||||
## 3. Unity / C# 特有约束与对策
|
||||
|
||||
Cordis 原文(§6.4)已论证范式语言无关,并给出各语言的接入条件。针对 Unity 2022.3 / C#:
|
||||
|
||||
| 约束 | 论文对应讨论 | 对策 |
|
||||
|---|---|---|
|
||||
| **程序集不可卸载**(Mono/IL2CPP 无 collectible ALC;论文:托管运行时需模块注册表可驱逐) | §6.4 时间可组合性第二要求 | **降级语义**:可回滚的是"组件实例 + 其效应",不是"类型/程序集"。外部 DLL 模组卸载 = dispose 全部 fiber + 程序集留内存(与 ModFramework 现有边界一致,但把"实例级回滚"补全)。IL2CPP 下外部 DLL 动态加载本就不可用 |
|
||||
| **无 JS Proxy** | §6.4:无拦截原语时用运行时反射或**编译期元编程** | 已有 `ShrinkShared.CodeGen`(Cecil ILPostProcessor)正好承担"编译期元编程"角色:生成 inject/provide 声明、强类型键访问器(对应论文中 Rust 过程宏的位置) |
|
||||
| **异步模型** | create_task / Promise | `UniTask` 一一对应:`apply : Func<ShrinkCtx, UniTask>`;fiber.inertia = `UniTask` 句柄;逆操作 `Func<UniTask>` |
|
||||
| **Domain Reload / Enter Play Mode** | —(Node 单进程长生) | 上下文树挂静态根 + `[SubsystemRegistration]` 重置(沿用 `ShrinkApp.ResetStaticState` 模式) |
|
||||
| **静态门面传统**(EventBus.TriggerEvent 等静态单例) | §3.3.3 批评服务定位器;§6.7 指出库实现中上下文可被闭包误用 | 允许"默认上下文"静态门面作为过渡兼容层,但新代码一律显式 ctx;EventBus 实例成为默认上下文上的一个键 |
|
||||
| **主线程亲和**(Unity API 只能在主线程) | — | 现有 `ShrinkNetworkDispatchQueue` 模式保留;fiber 转换全部调度到主线程泵(`PlayerLoop`/`UniTask` 主线程) |
|
||||
|
||||
---
|
||||
|
||||
## 4. 目标组织形式
|
||||
|
||||
### 4.1 模块拓扑(改造后)
|
||||
|
||||
```text
|
||||
Assets/Modules/
|
||||
├── ① 元框架层(新增,Cordis 位)
|
||||
│ ├── ShrinkContext.Core/ # ctx / effect / coeffect store / fiber / 生命周期
|
||||
│ │ ├── Runtime/Context/ # ShrinkCtx、EffectAccumulator、上下文树
|
||||
│ │ ├── Runtime/Coeffects/ # get/set/isolate/intercept、notify
|
||||
│ │ ├── Runtime/Fibers/ # Fiber、refresh/reload/unload、惯性状态机
|
||||
│ │ └── Runtime/Loader/ # 声明式配置树、增量协调器、条目双向绑定
|
||||
│ ├── ShrinkContext.UnityHost/ # Unity 接地:主线程泵、Domain Reload 重置、场景桥
|
||||
│ └── ShrinkContext.CodeGen/ # 并入现有 ShrinkShared.CodeGen:inject/provide 织入、访问器生成
|
||||
│
|
||||
├── ② 加载器配置层
|
||||
│ └── (ShrinkApp.Core 演进为加载器壳 + 兼容适配;Starter 演进为配置树生成器)
|
||||
│
|
||||
├── ③ 应用框架层(现有功能模块,改造为组件)
|
||||
│ ├── ShrinkEventBus/ # 保留;另提供 ShrinkContext 适配(订阅=效应)
|
||||
│ ├── ShrinkDataSaver/ ShrinkNetwork/ ShrinkCommand/ ShrinkTutorial/
|
||||
│ │ # 各自成为组件:声明 inject/provide,apply=原初始化逻辑
|
||||
│ └── ShrinkModFramework/ # 与 Fiber 模型合流:外部 DLL=延迟组件源;热载=dispose+re-use
|
||||
│
|
||||
└── (*.Integration.* 七个桥接包 → 大部分废弃,逻辑并入对应组件或核心键适配)
|
||||
```
|
||||
|
||||
### 4.2 组件形态(C# 化的目标 API 草案)
|
||||
|
||||
```csharp
|
||||
// 组件 = (inject, provide, apply)
|
||||
[ShrinkComponent("shrink.network")] // 由 ShrinkContext.CodeGen 织入注册表
|
||||
public sealed class ShrinkNetworkComponent : IShrinkComponent
|
||||
{
|
||||
public static readonly string[] Inject = { ShrinkKeys.EventBus, ShrinkKeys.Config }; // d:依赖键
|
||||
public static readonly string[] Provide = { ShrinkKeys.NetworkService }; // p:供给键
|
||||
|
||||
public async UniTask ApplyAsync(ShrinkCtx ctx, JsonNode config) // e:效应函数
|
||||
{
|
||||
var service = new ShrinkNetworkService(...);
|
||||
ctx.Set(ShrinkKeys.NetworkService, service); // set 是效应:自动追踪、卸载自动撤回
|
||||
|
||||
await using var sub = ctx.Effect(() => // 显式可逆效应
|
||||
UniTask.FromResult(() => service.Shutdown()));
|
||||
// ... bind transport 等
|
||||
}
|
||||
}
|
||||
|
||||
// 编排 = 声明式配置树(持久化为 JSON/YAML 资产)
|
||||
// entry: { id, component, isolate, intercept, config, disabled }
|
||||
await loader.ApplyConfigAsync(configTree); // 增量协调:diff → reload/unload/原地更新
|
||||
```
|
||||
|
||||
要点:
|
||||
- `ctx.Effect(callback)` 中 callback 返回逆操作(`Func<UniTask>` 或 `IAsyncEnumerable<Func<UniTask>>` 支持分步效应);
|
||||
- `IShrinkEventSubscription` 等 `IDisposable` 天然是逆操作,写一行适配即可纳入;
|
||||
- 供给键建议 `static class ShrinkKeys`(或 CodeGen 从 `Provide` 生成常量),解决论文 §6.6 的键命名空间问题(`"shrink.network/service"` 风格);
|
||||
- 静态门面(`ShrinkNetworkRuntime.Default` 等)在过渡期指向默认上下文的键解析结果。
|
||||
|
||||
### 4.3 各现有模块的具体去向
|
||||
|
||||
| 现模块 | 去向 |
|
||||
|---|---|
|
||||
| `ShrinkApp.Core` | Host/Services 退役;`ShrinkAppSettings.disabledModuleIds` → 配置树 disabled 字段;拓扑排序 → 依赖驱动的响应式激活(不再需要启动期全排序,缺依赖的组件静默等待) |
|
||||
| `*.Integration.App` ×3 | **废弃**——其内容(把服务放进容器)= 组件的 `ctx.Set` |
|
||||
| `*.Integration.EventBus` ×3 | 收缩为一层薄适配:DataSaver 事件→EventBus 键上的发布;Network 事件桥同理。桥的"自动接入反射探测"被键解析取代 |
|
||||
| `ShrinkCommand.Integration.Network` | 保留语义,实现改为:Network 组件与 Command 组件之上的一个普通集成组件(论文 §6.5 的"集成组件"模式) |
|
||||
| `ShrinkModFramework` | 模组 = 延迟组件源(外部 DLL 扫描产出组件注册);`IShrinkMod` 四阶段生命周期映射为 apply 内部分步效应;Harmony 补丁声明为效应(PatchAll 的逆 = UnpatchAll——Harmony 本身支持,正好构成可逆对);目录监听热载 = dispose 旧 fiber + `use` 新组件 |
|
||||
| `ShrinkShared.CodeGen` | 扩展为 `ShrinkContext.CodeGen` 的宿主:新增 `[ShrinkComponent]` 注册表织入、`Inject/Provide` 校验、强类型访问器生成 |
|
||||
| `GeneratedServers` 独立服务器 | 独立宿主用自己的 `ShrinkContext` 实例(论文 §6.2 跨进程:每进程一个上下文树);服务器模块体系 `IShrinkServerModule` 与 fiber 合流 |
|
||||
| EventBus / DataSaver / Tutorial 内核 | **基本不动**——它们是被组合的领域库;改动集中在"谁调用初始化"(从 Bootstrap/Installer 改为组件 apply) |
|
||||
|
||||
---
|
||||
|
||||
## 5. 分阶段实施路线(每阶段独立可验证)
|
||||
|
||||
### 阶段 0:纸面与原型(不改生产代码)✅ 已完成(2026-08-16)
|
||||
|
||||
**产出:`Assets/Modules/ShrinkContext.Core/`(包 `com.cneicy.shrink-context-core` 0.1.0)+ 21 个 EditMode 测试全部通过。**
|
||||
|
||||
- 核心库落地:`ShrinkCtx`(效应累积/隔离派生/介导访问)、`ShrinkEffectHandle`(算法 1:armed 单次执行、LIFO、dispose 等待进行中前向)、`ShrinkContextRuntime`(算法 2-5:set 可逆绑定、notify、Use/Retire、惯性状态机 RunTransitionAsync、drain-before-inverse)、`ShrinkFiber`(uid 目标、提交视图、Retired)、`IShrinkComponent` / `IShrinkIterativeComponent`、`ShrinkContextDefaults`(Domain Reload 重置)。
|
||||
- 测试对照论文元理论:定理 7/16/20 行为(恢复初始、选择性撤销、LIFO)、算法 6 介导纪律(未声明/未激活拒绝)、定义 46 uid 语义(同值替换仍重载)、L-Unload 守卫(依赖者先排空)、4.3.3 惯性(装载中途退役永不 Active、链式卸载)、定理 64(步进边界部分回滚)、L-Raise(apply 失败部分回滚)、循环依赖静止、供给冲突、父子级联、隔离域丢弃恢复、Domain Reset。
|
||||
- **实现教训(已记入模块 README)**:Mono 的 `IAsyncEnumerable`/`ValueTask` 迭代器消费端恢复经同步上下文调度、不内联,破坏确定性语义——分步效应改用自定义 `IShrinkStepEffectEnumerator`(UniTask 步进协议)后恢复全链路内联。
|
||||
- 验证:`ShrinkContext.Core.Tests` 21/21 通过;全仓 EditMode 138 个测试中 8 个失败**全部位于既有 `ShrinkDataSaver.Tests`**(缺 `LogAssert.Expect`、`ShrinkSettings.SaveAsync` 空引用等测试侧问题),与本次纯新增改动无关(git 确认无既有文件修改)。
|
||||
|
||||
**下一步决策点**:原型语义已验证,是否进入阶段 1/2(加载器 + ShrinkApp 迁移)取决于产品侧对运行时重配(热插拔/热替换)的需求确认。
|
||||
|
||||
### 阶段 1:核心库落地
|
||||
- `ShrinkCtx`(get/set/isolate/intercept/effect/use)、`Fiber`、notify/refresh/reload/unload。
|
||||
- 验收:两个玩具组件 A(provide `k`)、B(inject `k`)——加载 A→B 激活;卸载 A→B 自动停用且上下文恢复;B 换提供者→B 重载。
|
||||
|
||||
### 阶段 2:加载器 + ShrinkApp 迁移 ✅ 完成(2026-08-16)
|
||||
|
||||
**已完成:**
|
||||
|
||||
- **声明式加载器**(`ShrinkContext.Core/Runtime/Loader/`):`ShrinkLoaderEntry`(id/component/config/disabled/isolate)+ `ShrinkComponentCatalog`(名字→工厂,不做全域反射)+ `ShrinkContextLoader.ApplyAsync` 增量协调——条目消失→退役(依赖者响应式停用但保持被管理,依赖回归自动复活);disabled→卸载/重载;组件/配置变化→重建;无变化→幂等不扰。10 个测试覆盖含隔离域条目(同键不同 realm 共存)。
|
||||
- **ShrinkApp 适配器**(新包 `ShrinkContext.AppAdapter`):`ShrinkAppInstallerComponent` 把现有 `IShrinkAppModuleInstaller` 包装为组件——`DependsOn` 映射为 `app.module.*` 注入键,**依赖缺失从"排序期抛错"变为"非活动等待"**;重复 ModuleId 从"宿主级异常"退化为"该纤程供给冲突失败";激活次序由依赖关系结构性保证。5 个测试验证行为差异。
|
||||
- **加载器宿主与模式切换**(ShrinkApp.Core 0.1.1 + 适配器):`ShrinkAppSettings.hostingMode`(ClassicHost/ContextLoader)双模式并存,经典路径零行为变化;`ShrinkAppLoaderHost` + `ShrinkAppLoaderBootstrapper` 自动引导并接回 `ShrinkApp.Services`;`SetModuleDisabledAsync` 运行中按模块开关。LoaderHost 6 个测试。
|
||||
- **PlayMode 实测**(Starter 调试台场景 ShrinkAppEntry,ContextLoader 模式):加载器宿主激活全部 3 个真实模块(shrink.command / shrink.datasaver / shrink.network),经典宿主无双启动、无错误日志;运行中 disable→enable shrink.network 增量生效,宿主与其余模块不重启。
|
||||
- 验证:全仓 EditMode **159/159 全绿**(含修复后的 DataSaver 83 个)。
|
||||
|
||||
**顺带修复的既有隐患**:`Assets/Resources/ShrinkAppSettings.asset` 的脚本链接曾为 `m_Script: fileID:0`,运行时 `Resources.Load` 会静默走默认配置。本次接手时发现问题仍存在,已通过 Unity Editor API 删除无效主对象并在同路径重建;MCP 读回确认为 `ShrinkApp.ShrinkAppSettings`,`hostingMode = ContextLoader`。
|
||||
|
||||
**阶段 2 原边界的关闭情况**:阶段 3 已补 `ShrinkAppServices.TryUnregister(instance)`,原生组件门面可逆注销且不会误删新替代者;`ShrinkApp.IsRunning` 已接入外部 LoaderHost 状态。仍保留的边界是 config 变化统一重建、编排配置仍由代码构建,持久化配置资产(ScriptableObject/JSON)与调试台 UI 开关按钮属后续打磨。
|
||||
|
||||
### 阶段 3:功能模块组件化 ✅ 默认组合主路径完成(2026-08-16)
|
||||
|
||||
**已完成:**
|
||||
|
||||
- **核心 API 补充**:`ShrinkCtx.EffectInverse(inverse)`——前向已在外部执行完毕的效应只登记逆操作,供桥接适配器把既有"注册/注销"对包装为可逆效应。
|
||||
- **EventBus 订阅=效应**(新包 `ShrinkContext.EventBusAdapter`):`EffectSubscribe<TEvent>`(`IShrinkEventSubscription.Dispose` 即逆操作,ctx 卸载自动退订)与 `EffectRegister(target)`(实例对象注册/注销对)。3 个测试:上下文回滚自动退订、手动退订选择性、实例注册随回滚注销。
|
||||
- **Network 组件化**(`ShrinkNetwork.Integration.App` 新增 `ShrinkNetworkAppComponent`):提供 `shrink.service.network` 余效应键(依赖者按键注入);loopback 传输绑定为**可逆效应**——停用时会话关闭、传输解绑(挂起 RPC 失败、会话清理由 BindTransport(null) 路径触发)、服务键撤回、依赖者停用,**全部为效应回滚而非手写卸载**。与经典 installer 二选一(同键供给冲突保护)。3 个测试:绑定+键、停用回滚+依赖者停用、重启再绑定(相对会话数断言,避免静态单例污染)。
|
||||
- **PlayMode 热插拔实测**(验收项):Use→Active(自建会话+1)→ Retire→Inactive(会话-1、服务键撤回,与 Starter 演示自身的绑定互不干扰)→ 再 Use 恢复。无错误日志。
|
||||
- 验证:全仓 EditMode **165/165 全绿**。
|
||||
|
||||
**阶段 3 第二片(同日)**:
|
||||
- `ShrinkCommandAppComponent`(`shrink.service.command` 键)与 `ShrinkDataSaverAppComponent`(`shrink.service.datasaver` 键;初始化为前向,停用补偿式收尾=触发式设置落盘,不阻塞停用链;EditMode 下 DontDestroyOnLoad 驱动按 `Application.isPlaying` 关闭)。
|
||||
- `ShrinkCommandNetworkIntegrationComponent`(论文 6.5 集成组件模式):注入双服务键(command+network),双提供者齐备才激活,任一退役即停用、回归即复活——依赖接线由响应式余效应结构性完成。已知边界:ShrinkNetworkService 无 handler 注销 API,停用不撤销 command/execute 处理器(传输解绑后不可达)。
|
||||
- 4 个集成测试(含"仅命令就绪仍等待网络"的半就绪断言)。全仓 EditMode **169/169 全绿**。
|
||||
|
||||
**阶段 3 第三片(本次接手)**:
|
||||
- **默认组合根落地**:`ShrinkAppLoaderBootstrapper.DefaultComposition` 在宿主启动前接入 Starter 装配表;`ShrinkAppBasicContextComposition` 用三个原生 App 组件覆盖旧 installer 条目,并通过 `AddModuleComponent` 加入 Command-Network 与三个 EventBus 薄适配,共 7 个声明式模块。
|
||||
- **兼容门面可逆化**:Command/DataSaver/Network 原生组件把 `ShrinkApp.Services` 注册视为效应并登记逆操作;模块退役时按实例注销。LoaderHost 同时接回 `ShrinkApp.IsRunning`,加载器模式不再出现双重状态口径。
|
||||
- **EventBus 桥收缩**:新增 Command/DataSaver/Network 三个按服务键注入的集成组件;移除 Command/Network 的程序集加载时默认注册与 Network Core 的可选程序集反射接桥;DataSaver 改为显式、可注销桥。仅安装桥包不再产生全局业务副作用。
|
||||
- **旧安装器降级为兼容面**:三个 `*.Integration.App` installer 标记 `[Obsolete]`,仅供 ClassicHost 继续发现;ContextLoader 默认路径不再执行 installer 包装。
|
||||
- **端到端测试升级**:`NativeWiringTests` 直接调用 Basic Starter 组合根,覆盖 7 模块/7 键/3 门面、Command 停用导致两个依赖集成先退出、门面撤回、重新启用恢复、Shutdown 全清退。全仓 EditMode **171/171 全绿**,Unity 编译 **0 error / 0 warning**。
|
||||
- **真实 Play Mode**:`Assets/Scenes/ShrinkAppEntry.unity`、有效 ContextLoader 设置下启动为 **7 Active / 0 Waiting**;运行中禁用 Command 后保留 4 Active、两个 Command 集成 Waiting,三个相关键与门面均撤回;重新启用恢复 **7 Active / 0 Waiting**。控制台无错误,仅有与本迁移无关的缺省 `ShrinkModFrameworkSettings` 警告。
|
||||
|
||||
**阶段 3 保留边界**:ClassicHost 与静态服务门面仍作为兼容层存在;Command-Network 的服务 handler 暂无逐项注销 API;全局静态桥在多根上下文并行时仍需引用计数或实例化。这些不阻塞单默认根主路径,但不能误写成完全隔离保证。
|
||||
|
||||
### 阶段 4:ModFramework 合流 + 热替换 ✅ 完成(2026-08-16)
|
||||
- 新增 `ShrinkModComponentSource` / `ShrinkModContextHost`:将 `IShrinkMod` 的构造、注册、初始化、Ready 生命周期纳入 Cordis apply;模组效应通过本地逆操作登记,Registry、Network handler、Harmony lease 均可随组件退役清理。
|
||||
- 事务单位固定为 `ModId + revision`:同一 revision 幂等复用;替换失败时保留 `LastError` 并重新应用上一组 source,旧模组实例、generation 与已登记内容恢复。
|
||||
- 外部 DLL 按 SHA-256 扫描当前 revision;文件内容变化创建新的组件源,旧程序集仍驻留但不会继续被发现,坏 DLL 不覆盖当前有效 revision。扫描状态随 Host 事务一起快照/恢复,失败替换不会把失败 revision 留在当前缓存中。
|
||||
- `ShrinkModLoader` 默认走 `ShrinkModContextHost`;`useContextHost=false` 保留旧加载路径作为兼容边界。Harmony 在 ContextHost 路径取得可逆 lease,旧路径仍是一次性应用。
|
||||
- `ShrinkModRuntimeDriver` 监听新增、修改、删除、重命名,并用独立 debouncer 把 burst 事件折叠为一次主线程 reload;删除文件会提交完整期望组合,依赖缺失在协调前拒绝并保留旧组合。
|
||||
- 真实 fixture 验收覆盖:编译 DLL `valid → changed(fail) → restore`、损坏字节不污染当前 revision、有效 revision 恢复、watcher burst 去抖、删除卸载、依赖 DLL 删除/恢复。`ShrinkModFramework.Tests` **9/9**,全仓 EditMode **180/180**。
|
||||
- 真实 Starter PlayMode 确认 `modCordis=true`、`modLoaded=true`、宿主 7 个模块 active 且无错误。Unity 程序集不可卸载,因此回滚单位仍是组件实例与效应,而不是类型本身。
|
||||
|
||||
### 阶段 5:生产化边界、隔离与拦截 ✅ 完成(2026-08-17)
|
||||
|
||||
#### 阶段 5A:访问契约、诊断与通知索引 ✅ 首批完成(2026-08-17)
|
||||
|
||||
- 运行时强制组件只能向 `Provide` 声明的键写入;根上下文的 ambient 绑定保留为显式系统边界。
|
||||
- 提供稳定的 runtime/fiber 诊断快照:uid、组件名、状态、target、committed provider、realm、是否退役、是否转换中、最近错误。
|
||||
- 提供依赖等待关系与加载器事务诊断,使失败能定位到条目、fiber、阶段和恢复结果。
|
||||
- 将 notify 从遍历全部 fiber 改为 `key → inject fibers` 倒排索引;realm 仍在触发时精确过滤,复杂度由 O(all fibers) 收敛为 O(affected-by-key)。
|
||||
- 强类型/版本键以新增契约逐步引入,不在本阶段强行改写全部现有字符串键调用点。
|
||||
|
||||
**验收:** 未声明供给在 apply 阶段被拒绝并完整回滚;诊断快照能解释 Active/Waiting/Failed/Unloading;索引不会遗漏不同 realm 下的合法依赖者;既有生命周期测试全部保持通过。
|
||||
|
||||
**已落地:** `Provide` 写入约束、`ShrinkKey<T>` 版本键、runtime/fiber/依赖/notify 诊断快照、loader 事务与恢复结果、`key → inject fibers` 倒排索引均已实现。target 仍是 provider uid 视图,但现在保留 inject 声明顺序和重复键,不再退化成无序 provider 集合。
|
||||
|
||||
#### 阶段 5B:isolate 隔离验证与 intercept ✅ 首批完成(2026-08-17)
|
||||
|
||||
- loader 支持隔离域条目;同一组件在多个 realm 中独立运行,替换其中一个 realm 的提供者不扰动另一个 realm。
|
||||
- 补 `ctx.intercept(key, metadata)` 的派生上下文与元数据合并;策略改变依赖的使用方式,不改变满足关系,也不因策略变化触发 fiber 重载。
|
||||
- 访问介导使用明确的服务策略/包装接口,不把 Harmony 或通用反射 AOP 当成默认实现。
|
||||
- 以“社区模组只读 DataSaver”为真实验收:读取允许,写入拒绝,核心组件仍保留完整能力。
|
||||
|
||||
**验收:** 多 realm 激活/替换/卸载互不串扰;intercept 更新不改变 provider uid 和 fiber generation;未声明访问、未激活访问与策略拒绝三类错误可以区分;文档明确其不是不可信代码沙箱。
|
||||
|
||||
**已落地:** loader 条目可携带 isolate 与 intercept;intercept 元数据按上下文链合并并在条目原位更新,不触发 fiber 重载。DataSaver 提供 `Reader / Writer` 能力拆分,社区上下文可取得只读包装,写接口与具体服务请求被策略拒绝。当前 isolate 条目变化仍通过重建该条目生效;运行中 fiber 原位迁移 realm 未实现,也不计入本批完成项。
|
||||
|
||||
#### 阶段 5C:外部 DLL 常驻 revision 策略 ✅ 首批完成(2026-08-17)
|
||||
|
||||
- 暴露当前 revision、历史程序集数量、来源路径与累计载入字节。
|
||||
- 增加软阈值和明确告警;超过阈值时建议 Domain Reload/进程重启,不尝试在 Mono 上伪造程序集卸载。
|
||||
- 长时间回归覆盖连续有效替换、失败恢复、损坏 DLL、删除/恢复与历史 revision 增长。
|
||||
|
||||
**验收:** 每次替换后当前 revision 与生效组件一致;失败 revision 不成为 current;常驻增长可查询、可告警且不影响旧组合恢复。
|
||||
|
||||
**已落地:** `ShrinkModDiagnostics.CaptureExternalAssemblies` 暴露 current/history、路径、程序集、revision、载入字节与软阈值状态;达到 `externalAssemblyRevisionSoftLimit` 时只给出 Domain Reload/进程重启建议。真实 DLL fixture 覆盖有效、失败、再有效三个 revision,确认失败程序集可以常驻但不会成为 current。
|
||||
|
||||
#### 阶段 5D:配置与调试体验 ✅ 完成(2026-08-17)
|
||||
|
||||
- 将代码构建的默认组合逐步映射到 ScriptableObject/JSON 条目,并保持代码目录作为组件工厂来源。
|
||||
- 提供 Editor/运行时调试面,查看 active/waiting/failed fiber、provider target、最近事务和常驻程序集统计。
|
||||
- 建立 notify、重载延迟、失败恢复和常驻内存的基准,避免仅以功能测试替代容量判断。
|
||||
|
||||
**已落地:** `ShrinkAppCompositionProfile / Document` 将条目启用、显式排除、isolate 与 intercept 映射为 ScriptableObject/JSON;组件工厂继续由代码组合根注册,配置不能反射实例化任意类型。Basic Starter 提交 `Resources/ShrinkAppComposition.asset`,生成器可创建或修复该资产。`ShrinkSDK/Cordis/诊断与组合` 在 Play Mode 展示 fiber、等待依赖、target、最近事务和外部 Assembly 常驻快照,并提供 Profile JSON 导入/校验/写回/导出。Editor 基准可重复测 notify/reload 与失败恢复;耗时和 GC 堆差值只作同机对比,自动测试断言结构性规模。
|
||||
|
||||
**阶段 5 明确非目标:** 不在本阶段实现不可信 DLL 沙箱;不承诺已发出的网络数据或外部文件写入可以真正撤回;不删除 ClassicHost 兼容面,除非其调用方已完成独立迁移验证。
|
||||
|
||||
**2026-08-17 验证基线:** Unity 编译 **0 error / 0 warning**;全仓 EditMode **193/193**;PlayMode Test Runner 当前没有测试项,因此另行启动真实 `Assets/Scenes/ShrinkAppEntry.unity` 验收:`hostingMode=ContextLoader`、Profile 已应用且显式列出 7 个条目、7 个模块全部 Active、0 Waiting/Failed、10 个绑定、3 个 notify 索引键,启动与退出过程无控制台 error。容量基准在 1000 个无关 fiber、100 次 provider 重载下记录 400 次 indexed candidate visit,对照全量扫描估算 440400 次;25/25 次失败替换均恢复旧组合。本机单次耗时样本约为 notify/reload 1.9 ms、失败恢复 2.8 ms,仅作后续同机对比,不作为跨机器承诺。
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险与代价(决策前必须直视)
|
||||
|
||||
1. **工程量大且不可半途而废**:阶段 2 之前核心库没有业务价值;一旦功能模块开始迁移,新旧两套启动模型并存会持续产生双重维护成本。当前通过 ContextLoader 默认主路径 + ClassicHost 兼容层控制过渡面,阶段 4 不应再新增第三套生命周期。
|
||||
2. **静态门面惯性与范式冲突**:全仓库大量 `EventBus.TriggerEvent` 式静态调用点,改显式 ctx 是长尾体力活。缓解:默认上下文门面长期保留(论文 Koishi 也保留了领域词汇层)。
|
||||
3. **Unity 生命周期摩擦**:MonoBehaviour 的 Awake/OnDestroy 与 fiber 生命周期是两套时钟(EventBus 现有 IL 织入恰好横跨两者)。桥接策略需要在阶段 1 原型中先行验证,这是最容易返工的点。
|
||||
4. **性能**:论文未给定量开销数据(§5.3 自认是存在性证据)。每键每组件的 notify 遍历在高频键上需做索引(`key → inject 它的 fiber 集`),核心库设计时就要按 O(affected) 而非 O(all fibers) 实现。
|
||||
5. **回报的位置要认清**:Cordis 的核心回报是"动态卸载/热替换/依赖响应"。ShrinkSDK 若长期只有"启动时固定组合",改造成纯支出。**先确认产品侧确实需要运行时重配(如模组热插拔、专用服运行中换后端、编辑器内重载),再启动阶段 2。**
|
||||
6. **论文未覆盖的仍需自建**:版本协商、键的版本兼容(§6.6 明说是 open problem,用键命名空间 + 包版本约束缓解——UPM 依赖恰好能承担 sibling dependency 角色)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 结论
|
||||
|
||||
- ShrinkSDK 与 Cordis 的**分层直觉一致**(核心库 / 编排层 / 领域层),差距集中在**核心库的两个原语缺失**:统一可逆效应追踪(时间)与响应式依赖解析(空间)。
|
||||
- 改造的本质不是重写功能模块,而是**把 ShrinkApp 的"一次性安装器"升级为 Cordis 的"持续协调的组件加载器"**,并让七个 Integration 桥接包退化为薄适配直至消失。
|
||||
- Unity 的"程序集不可卸载"不阻塞该范式——可回滚单位是组件实例与效应,而非类型;这与 ModFramework 既有边界声明兼容。
|
||||
- 阶段 0-4 已完成到 Basic Starter 的默认运行主路径与 ModFramework 事务性 HMR;阶段 5 已完成访问契约、诊断/notify 索引、isolate/intercept、程序集常驻策略、配置资产、调试面与容量基准。后续应以真实项目负载持续采样和收紧领域策略,不再新增第三套生命周期。
|
||||
@@ -0,0 +1,308 @@
|
||||
# ShrinkSDK 项目设计总结(2026-08-16 归档)
|
||||
|
||||
> 归档说明:本文已被仓库根 `DESIGN.md` 取代,仅供历史追溯,其中模块状态、Git 状态、测试数量和待办均可能过期。
|
||||
|
||||
> 生成日期:2026-08-16
|
||||
> 依据:仓库源码(`Assets/Modules/`、`GeneratedServers/`)、各模块 `package.json` / `README.md` / `CHANGELOG.md`、`.planning/threads/shrinksdkunity.md`、`NETWORK_PITFALLS.md`,以及 `.planning/codebase/` 既有代码库地图(其中部分内容已滞后于当前代码,本文以源码现状为准)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 项目定位
|
||||
|
||||
ShrinkSDK 是一个**以 Unity 包(UPM)为边界的 SDK 单仓库**,不是单个游戏项目。它为 Unity 游戏提供一整套可独立分发、可拼装组合的基础设施:
|
||||
|
||||
- 事件总线(ShrinkEventBus)
|
||||
- 存档与设置(ShrinkDataSaver)
|
||||
- 命令系统(ShrinkCommand)
|
||||
- 网络框架与独立服务器生成(ShrinkNetwork + GeneratedServers)
|
||||
- 模组框架(ShrinkModFramework,Forge 风格)
|
||||
- 新手引导(ShrinkTutorial)
|
||||
- 统一应用宿主与起盘 Starter(ShrinkApp.Core + ShrinkApp.Starter.Basic,最新一轮工作)
|
||||
|
||||
设计哲学贯穿全仓库:**静态门面 + Attribute 声明式范式 + 编译期注册表 + asmdef 最小抽象 + 可选桥接包**。不做重框架化(无 DI 容器、无第三方状态机),追求低侵入接入 Unity 项目。
|
||||
|
||||
## 2. 技术栈
|
||||
|
||||
| 项 | 值 |
|
||||
|---|---|
|
||||
| Unity 版本 | 2022.3(`ProjectSettings/ProjectVersion.txt`) |
|
||||
| 语言 | C#(运行时与独立 .NET 宿主共用合同) |
|
||||
| 异步 | UniTask 2.x(`com.cysharp.unitask`) |
|
||||
| 序列化 | Newtonsoft.Json(`com.unity.nuget.newtonsoft-json` 3.2.2);网络层另支持 MessagePack-CSharp 3.1.4 |
|
||||
| 编译织入 | Unity ILPostProcessing + Mono.Cecil |
|
||||
| UI | uGUI + TextMeshPro |
|
||||
| 渲染 | URP 14 |
|
||||
| 网络 | 自研传输层:Loopback / TCP(含可选 TLS)/ KCP(Kcp-CSharp.dll) |
|
||||
| 独立服务器 | .NET 8 控制台宿主(`GeneratedServers/ShrinkNetwork.ServerHost`) |
|
||||
| 测试 | Unity Test Runner(NUnit,EditMode 为主) |
|
||||
|
||||
## 3. 仓库布局
|
||||
|
||||
```text
|
||||
ShrinkSDK/
|
||||
├── Assets/Modules/ # 全部 SDK 模块(每个是独立 UPM 包 + asmdef)
|
||||
│ ├── ShrinkEventBus/ # 事件总线(Runtime/Editor/CodeGen/Tests)
|
||||
│ ├── ShrinkDataSaver/ # 存档与设置(Runtime/Editor/Tests)
|
||||
│ ├── ShrinkCommand/ # 命令系统
|
||||
│ ├── ShrinkNetwork/ # 网络框架(Core/Metadata/Routing/Serialization/Transport/Editor 脚手架)
|
||||
│ ├── ShrinkModFramework/ # 模组框架(Bootstrap/Core/Metadata/Registry/Loading/Network/Integration)
|
||||
│ ├── ShrinkTutorial/ # 新手引导(Core/Storage/Trigger/UI)
|
||||
│ ├── ShrinkApp.Core/ # 统一应用宿主层
|
||||
│ ├── ShrinkApp.Starter.Basic/ # 最小起盘 Starter
|
||||
│ ├── ShrinkShared.CodeGen/ # 共享 IL 织入管线
|
||||
│ └── *.Integration.EventBus / *.Integration.App # 模块间桥接包
|
||||
├── Assets/Scenes/ # 示例/演示场景
|
||||
├── GeneratedServers/ # 生成的独立 .NET 服务器工程(可重建,不手写业务)
|
||||
├── GeneratedModSdk/ # 导出的 Mod SDK 开发包(Libs/Templates/manifest)
|
||||
├── Servers/ # (当前为空)
|
||||
├── Packages/manifest.json # UPM 依赖
|
||||
├── ShrinkSDK.sln # 解决方案(含全部模块 csproj)
|
||||
├── NETWORK_PITFALLS.md # 联网与事件桥接踩坑记录(19 条经验)
|
||||
└── .planning/ # gsd 线程与代码库地图(部分滞后)
|
||||
```
|
||||
|
||||
## 4. 模块清单与依赖关系
|
||||
|
||||
| 包名 | 版本 | 职责 | 关键依赖 |
|
||||
|---|---|---|---|
|
||||
| `com.cneicy.shrink-eventbus` | 1.3.0 | 高性能类型安全事件总线,优先级调度与自动注册 | UniTask |
|
||||
| `com.cneicy.shrink-datasaver` | 2.2.0 | 模块化存档与设置:多槽位、链式迁移、AES 加密、双备份 | Newtonsoft, UniTask |
|
||||
| `com.cneicy.shrink-datasaver-integration-eventbus` | 2.1.0 | DataSaver 原生事件 → EventBus 桥接(9 个事件类型) | DataSaver, EventBus |
|
||||
| `com.cneicy.shrink-datasaver-integration-app` | 0.1.0 | DataSaver 初始化交给 ShrinkApp 宿主接管 | DataSaver, App.Core |
|
||||
| `com.cneicy.shrink-command` | 0.2.0 | 路径式命令系统(Minecraft/Brigadier 风格)、权限、来源判定 | UniTask |
|
||||
| `com.cneicy.shrink-command-integration-eventbus` | 0.1.1 | 事件请求执行命令 + 命令生命周期事件发布 | Command, EventBus |
|
||||
| `com.cneicy.shrink-command-integration-network` | 0.1.0 | `command/execute` RPC 远程执行命令 | Command, Network |
|
||||
| `com.cneicy.shrink-command-integration-app` | 0.1.0 | 默认命令服务纳入宿主容器 | Command, App.Core |
|
||||
| `com.cneicy.shrink-network` | 0.2.0 | 会话、消息注册、RPC、权限、TCP/KCP/Loopback 传输 | UniTask, Newtonsoft |
|
||||
| `com.cneicy.shrink-network-integration-eventbus` | 0.1.1 | EventBase 事件直接走网络同步并在远端重分发 | Network, EventBus |
|
||||
| `com.cneicy.shrink-network-integration-app` | 0.1.0 | 默认网络服务纳入宿主容器 | Network, App.Core |
|
||||
| `com.cneicy.shrink-mod-framework` | 0.1.0 | 模组发现、依赖解析、生命周期、注册表、外部 DLL 热载、Harmony | Newtonsoft |
|
||||
| `com.cneicy.shrink-tutorial` | 0.1.0 | 数据驱动互动引导:遮罩挖洞、动态锚点、条件完成 | uGUI, TMP |
|
||||
| `com.cneicy.shrink-app-core` | 0.1.0 | 统一宿主:模块安装器、服务容器、统一启动流程 | EventBus, UniTask |
|
||||
| `com.cneicy.shrink-app-starter-basic` | 0.1.0 | 最小起盘:入口场景生成 + 五模块最小闭环调试台 | App.Core, Command, DataSaver, Network 及各自 Integration.App |
|
||||
| `ShrinkShared.CodeGen`(asmdef) | — | 共享 IL 后处理器:四套编译期注册表 + EventBus 织入 | Cecil |
|
||||
|
||||
依赖层次(自底向上):
|
||||
|
||||
```text
|
||||
ShrinkShared.CodeGen(编译期,作用于 Command/Network/App 引用方程序集)
|
||||
│
|
||||
ShrinkEventBus ←── ShrinkDataSaver ←── DataSaver.Integration.EventBus
|
||||
↑ (独立桥接,不依赖 App)
|
||||
ShrinkNetwork ←── ShrinkCommand(经 Integration.Network 桥)
|
||||
↑
|
||||
ShrinkModFramework(Integration/ 胶水可选自动接入以上三者)
|
||||
↑
|
||||
ShrinkApp.Core(组合根)←── *.Integration.App(各模块安装器)
|
||||
↑
|
||||
ShrinkApp.Starter.Basic(起盘模板)
|
||||
```
|
||||
|
||||
## 5. 总体架构
|
||||
|
||||
### 5.1 分层视图
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ Starter 层:ShrinkApp.Starter.Basic(场景/配置/UI 生成) │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ 组合根:ShrinkApp.Core(Host/Services/Installer 排序) │
|
||||
├───────────────┬───────────────┬──────────────────────────┤
|
||||
│ 功能模块: │ EventBus │ DataSaver │ Tutorial │
|
||||
│ │ Command │ Network │ │
|
||||
├───────────────┴───────────────┴──────────────────────────┤
|
||||
│ 桥接层:*.Integration.EventBus / *.Integration.Network │
|
||||
│ *.Integration.App(可选包,反射自动发现) │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ 扩展面:ShrinkModFramework(模组热载 + Harmony + 上述全) │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ 编译期:ShrinkShared.CodeGen(Cecil IL 织入/注册表注入) │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ 独立宿主:GeneratedServers/ShrinkNetwork.ServerHost(.NET)│
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 5.2 四个横切设计模式(全仓库统一)
|
||||
|
||||
**模式一:Attribute 声明式范式。** 每个子系统用自己的特性声明能力,处理器/消息/命令/安装器都是 `[Xxx] + 类型` 的组合:`[EventBusSubscriber]+[EventSubscribe]`、`[ShrinkNetworkMessage(opcode, route)]`、`[ShrinkNetworkSubscriber]+[ShrinkNetworkSubscribe]`、`[ShrinkCommand("say <message...>")]`、`[ShrinkMod(modId,...)]`、`[ShrinkAppModuleInstaller]`。
|
||||
|
||||
**模式二:编译期注册表取代全域反射。** `ShrinkShared.CodeGen/Editor/ShrinkRegistryILPostProcessor.cs` 是唯一的共享 IL 后处理器,对引用了 `ShrinkCommand.Runtime` / `ShrinkNetwork.Runtime` / `ShrinkApp.Core.Runtime` 的程序集做四件事:
|
||||
|
||||
1. `InjectEventBusAutoRegister`:给带 `[EventBusSubscriber]` 的 MonoBehaviour 织入 `Awake → EventBus.AutoRegister(this)` 与 `OnDestroy → UnregisterInstance`(处理了继承基类 Awake/OnDestroy 的 base 调用)。
|
||||
2. `InjectShrinkCommandRegistry`:静态命令类型写入程序集级 `[ShrinkCommandStaticRegistry(typeof(...))]`。
|
||||
3. `InjectShrinkNetworkRegistry`:`[ShrinkNetworkMessage]` 消息类型与静态订阅者类型分别写入 `[ShrinkNetworkMessageRegistry]` / `[ShrinkNetworkStaticSubscriberRegistry]`。
|
||||
4. `InjectShrinkAppRegistry`:`[ShrinkAppModuleInstaller]` 安装器类型写入 `[ShrinkAppInstallerRegistry]`。
|
||||
|
||||
运行时 `AutoRegisterAll()` 只读程序集特性,不做 `AppDomain.GetAssemblies()+GetTypes()` 全域扫描——这是模组外部 DLL 与独立服务器共用的关键机制(外来 DLL 不经过 Unity 编译管线,仍可走实例注册路径 `RegisterHandlers(object)` / `RegisterCommands(object)` 兜底)。注意织入器刻意**不处理核心程序集自身**(否则 Cecil 写回会触发 Unity "references itself" 拒载,源码注释中已记录该教训)。
|
||||
|
||||
**模式三:静态门面 + 可实例化内核。** `EventBus`(静态门面)委托给 Builder 构建的 `IShrinkEventBus` 实例(默认 LogAndContinue + 按 phase 派发,可 CreateBus 多实例);`ShrinkNetworkRuntime.Default` / `ShrinkCommandService` 同理。使用方零成本拿到默认单例,高级场景可自建实例。
|
||||
|
||||
**模式四:可选桥接包 + 运行时反射自动发现。** 模块间集成全部做成独立小包(不污染主包依赖),并用反射探测"对方是否存在":`ShrinkNetworkService.BindTransport(...)` 自动接入 EventBus 桥(`BindTransport(null)` 解绑);`ShrinkModFramework/Runtime/Integration/ShrinkModOptionalRuntimeIntegration.cs` 在模组注册时自动尝试把模组实例接到 EventBus/Command/Network。未安装桥接包时静默跳过。
|
||||
|
||||
### 5.3 ShrinkApp 统一宿主(组合根)
|
||||
|
||||
最新引入的 `ShrinkApp.Core` 把"谁先启动、服务放哪"收口为一条统一链路:
|
||||
|
||||
1. `Shrink.Bootstrap`:`[RuntimeInitializeOnLoadMethod(BeforeSceneLoad)]` 创建 `ShrinkAppHost` GameObject(可 DontDestroyOnLoad),组装 `ShrinkAppContext = Host + Settings + Services`。
|
||||
2. **安装器发现**:`ShrinkAppGeneratedRegistry` 扫描程序集级 `[ShrinkAppInstallerRegistry]`(由共享织入器生成),实例化所有 `IShrinkAppModuleInstaller`。
|
||||
3. **拓扑排序**:按 `Order` + `DependsOn`(显式成环/缺依赖/重复 ModuleId 直接抛错),`ShrinkAppSettings.disabledModuleIds` 可整模块禁用。
|
||||
4. **两阶段启动**:先统一 `RegisterServices(context)`(向 `ShrinkAppServices` 类型字典容器注册服务),再按序 `InitializeAsync`。
|
||||
5. **事件广播**:成功发 `ShrinkAppStartedEvent`(含模块清单,等待 Unity `Start` 后才发布),失败发 `ShrinkAppStartFailedEvent`。
|
||||
|
||||
各功能模块通过 `*.Integration.App` 包提供安装器(如 `ShrinkNetworkAppInstaller`,ModuleId `shrink.network`,Order -1400),把各自的默认服务注册进容器。`ShrinkDataSaver` 2.2.0 专门新增 `ShrinkDataSaverRuntime` 把初始化入口从 MonoBehaviour Bootstrap 下沉,为的就是让宿主接管初始化顺序(旧 `ShrinkDataSaverBootstrap` 保留为兼容入口)。
|
||||
|
||||
### 5.4 生命周期入口一览
|
||||
|
||||
| 入口 | 时机 | 作用 |
|
||||
|---|---|---|
|
||||
| `ShrinkApp.Bootstrap` | BeforeSceneLoad | 创建宿主并按拓扑序启动全部安装器 |
|
||||
| `ShrinkModRuntimeBootstrap` | AfterAssembliesLoaded | 读设置、装载模组(场景内/外部 DLL)、接 Harmony |
|
||||
| `DataSaverEventBusBridge` | AfterAssembliesLoaded | DataSaver 原生事件桥到 EventBus |
|
||||
| `ShrinkTutorialRuntimeBootstrap` | 运行时初始化 | 引导系统自举 |
|
||||
| `ShrinkDataSaverBootstrap`(MonoBehaviour,兼容) | 场景 | 委托 `ShrinkDataSaverRuntime`;自动保存轮询、暂停/退出落盘 |
|
||||
| EventBus IL 织入 | 编译期 | MonoBehaviour 自动注册/反注册 |
|
||||
|
||||
## 6. 各模块设计要点
|
||||
|
||||
### 6.1 ShrinkEventBus 1.3.0
|
||||
|
||||
- **双层结构**:静态门面 `EventBus` + `IShrinkEventBus` 实例(`ShrinkEventBusBuilder` 配置异常策略、类型校验、marker 接口、phase 派发)。
|
||||
- **订阅模型**:`Action<T>` 同步与 `Func<T, UniTask>` 异步两种 handler;优先级支持枚举 `EventPriority` 与数字(数字 0 = NORMAL);`receiveCanceled` 控制取消后是否继续收;`IShrinkEventSubscription` 句柄式订阅 + 实例对象整体注册两套 API。
|
||||
- **派发机制**:监听列表按 phase 分桶 + 快照缓存(`ListenerList`),支持父事件监听子事件,跨父子按数字优先级稳定归并;事件对象持有派发时快照而非内置列表。1.3.0 对热路径做了系统性并发加固(总线内单锁、`EventId` 懒生成、`ConcurrentDictionary` 元数据缓存、`EventPool` 容量上限 128 防双重归还)。
|
||||
- **声明能力**:`[Cancelable]` / `[HasResult]` 标注事件语义;`EventResult` 承载结果。
|
||||
- **自动注册**:编译期织入(见 5.2)+ 静态注册表;1.3.0 修复"无实例订阅方法的类型被织入后在 Awake 抛异常"的问题,现改警告跳过。
|
||||
- **兜底管线**:共享织入器覆盖不到、只引用 `ShrinkEventBus.Runtime` 的程序集由模块内 `CodeGen/Editor/EventBusILPostProcessor.cs` 本地兜底。
|
||||
- **工具**:`EventBusViewerWindow` 编辑器查看器(走公开快照接口,不反射私有字段)、`EventBusBenchmark`、Tests 目录覆盖派发/优先级/注册/订阅四类行为。
|
||||
|
||||
### 6.2 ShrinkDataSaver 2.2.0
|
||||
|
||||
- **核心对象**:`ShrinkSave`(存档门面:槽位保存/加载/删除、截图存档、跨模块查询缓存)、`ShrinkSettings`(设置门面:读写、监听、防抖持久化)、`ShrinkDataSaverRuntime`(真实初始化与 autosave 驱动,2.2.0 新增)。
|
||||
- **模块化存档**:业务实现 `ISaveModule` / `ISaveModule<T>` 注册进保存流;关键模块失败终止保存,非关键失败跳过并记日志。
|
||||
- **存储抽象**:`IStorageProvider` → `LocalStorageProvider`(唯一实现)。2.1.0 起升级为异步文件流 + 主文件原子替换(`.tmp` 中转)+ `.bak1`/`.bak2` 双副本轮换备份,主文件损坏自动回退修复;`DeleteSlotAsync` 连带清理主/备/临时文件。
|
||||
- **安全**:AES-CBC + PBKDF2-SHA256(`SaveEncryptor`)。
|
||||
- **迁移**:`MigrationChain` 链式版本迁移(JObject 弱类型操作),支持缺步检测与回滚。
|
||||
- **易用性**:`GetRecentSlotIndex()` / `GetRecommendedContinueSlotAsync()` 把"继续游戏"能力下沉包内。
|
||||
- **事件桥**:`DataSaverEventBusBridge` 单向把原生事件映射为 9 个 `EventBase` 事件。
|
||||
- **测试**:仓库最扎实的测试面(保存/加载/迁移/加密/序列化/设置六类,`MockStorageProvider` 隔离文件系统)。
|
||||
|
||||
### 6.3 ShrinkCommand 0.2.0
|
||||
|
||||
- **定位**:独立于网络层的 Brigadier 风格命令系统,Unity 与纯 .NET 宿主(独立服务器)共用同一套。
|
||||
- **命令模型**:路径式定义(字面量段 + 贪婪参数段),`[ShrinkCommand("say <message...>")]`;方法参数支持 `ShrinkCommandContext` / `IShrinkCommandSource` / 路径参数按序绑定;返回值支持 void/string/Result 的同步与 Task/UniTask 变体。
|
||||
- **内建**:`help` / `?` 帮助树。
|
||||
- **注册**:静态命令走编译期注册表 `ShrinkCommandGeneratedRegistry`;实例命令 `RegisterCommands(object)` 反射方法签名(模组/运行时对象用)。
|
||||
- **集成**:`Integration.EventBus` 用 `ShrinkCommandExecuteRequestEvent` 请求执行并发布 executing/executed/failed 生命周期事件;`Integration.Network` 提供 `command/execute` RPC,远端会话可执行命令(独立服务器与控制台共用同一套命令注册)。
|
||||
|
||||
### 6.4 ShrinkNetwork 0.2.0
|
||||
|
||||
- **消息模型**:三类载荷——`IShrinkNetworkMessage`(单向)、`IShrinkNetworkRequest`(RPC 请求)、`ShrinkRpcResponseBase`(错误码+载荷)。所有消息显式 `[ShrinkNetworkMessage(opcode, route)]`,opcode/route 一一对应,冲突在生成阶段直接报错(不静默兜底)。
|
||||
- **包结构**(`ShrinkNetworkPacket`):`ProtocolVersion` / `SchemaVersion`(版本闸门)/ `Opcode` / `RequestToken`(强类型 RPC 关联,替代裸 int)/ `SessionToken` + 过期时间 / `Route` / `Kind` / `Payload`。
|
||||
- **服务内核**(`ShrinkNetworkService`):持有 `IShrinkNetworkSerializer + ShrinkNetworkMessageRegistry + ShrinkNetworkRouter` 三件套;`BindTransport` 绑定传输并自动接入可选桥接;协议版本窗口校验(越界可主动断链);`IncomingPacketValidator` 钩子做逐包鉴权;`GetDiagnosticsSnapshot()` 暴露会话/收发/RPC/协议违规/权限拒绝/处理器异常/未知 opcode/分发 miss/序列化失败等全套计数。
|
||||
- **并发模型**:会话表与挂起 RPC 并发安全,断线自动失败挂起 RPC;派发可注入 `IShrinkNetworkDispatchScheduler`——独立服务器用 `Inline`,Unity 侧用 `ShrinkNetworkDispatchQueue`(有界队列 + 拒绝/丢弃溢出策略 + 主线程 `PumpAsync` 预算泵出),避免网络线程直入 Unity API。
|
||||
- **RPC**:`RpcAsync/CallAsync` + `ShrinkRpcCallOptions`(超时、路由重载、令牌覆盖、DebugLabel)。
|
||||
- **权限**:`[ShrinkNetworkSubscribe(Authority=..., Permission=...)]` 处理器级双重约束(来源 + 权限串)。
|
||||
- **序列化**:`ShrinkJsonNetworkSerializer`(调试)/ `ShrinkMessagePackNetworkSerializer`(正式,高频实时推荐 MessagePack+KCP)。
|
||||
- **传输**:`IShrinkNetworkTransport` 抽象 + Loopback(宿主本地闭环)/ TCP 客户端·服务端(可选 `ShrinkTcpTlsOptions` TLS,读包路径统一走 `Stream` 兼容 SslStream,最大包长保护)/ KCP 客户端·服务端(`ShrinkKcpPeer`、随机 conversationId、远端地址校验)。`IShrinkNetworkSessionControlTransport` 支持服务端主动踢会话。
|
||||
- **状态同步范式**:`[ShrinkNetworkStateSync("lan", role)]` 显式声明角色(JoinRequest/JoinResponse/Command/StateDelta/LeaveNotice/Heartbeat),供独立服务器生成器产出可运行同步模块;旧 route 回退识别仍保留。
|
||||
- **EventBus 桥**(Integration.EventBus):`[ShrinkNetworkEvent]` 标注"可上网"的事件类型;扩展方法 `PublishEventAsync / BroadcastEventAsync / RequestEventAsync / UseEventBusBridge`,事件在远端重新分发回 EventBus;区分广播型/远端裁决型/增量型三类语义。
|
||||
|
||||
### 6.5 ShrinkModFramework 0.1.0
|
||||
|
||||
- **模组声明**:`[ShrinkMod(modId, displayName, version, AutoApplyHarmonyPatches=...)]` + `[ShrinkModDependency(modId, minVersion)]`;实现 `IShrinkMod` 或继承 `ShrinkModBase`。
|
||||
- **生命周期四阶段**(按依赖拓扑序):`OnConstruct → OnRegisterContent → OnInitialize → OnReady`。
|
||||
- **注册表**:`context.GetRegistry<T>("items")` 按类型+名称双重隔离,键唯一,每条记录归属模组(`ShrinkModRegistry/RegistryManager`)。
|
||||
- **装载**(`ShrinkModLoader`):程序集内模组发现 + 依赖排序;外部 DLL 增量热加载(`persistentDataPath/Mods`,目录监听 + 延迟触发,同目录依赖解析;不支持卸载、不支持 IL2CPP 动态加载)。
|
||||
- **Harmony**:检测到 `0Harmony` 时按模组自动 `Harmony("shrink.mod.<id>").PatchAll(modAssembly)`;无 Harmony 静默跳过(可选桥接,不打进框架)。
|
||||
- **网络抽象**:`IShrinkModNetworkTransport + ShrinkModNetworkManager`,只定义频道/处理器/收发语义,可接 NGO/Mirror/FishNet/自有 Socket。
|
||||
- **自动集成**:`ShrinkModOptionalRuntimeIntegration` 在模组注册时反射探测并接入 EventBus/Command/Network(含刷新 Network-EventBus 桥)。
|
||||
- **自动启动**:`[RuntimeInitializeOnLoadMethod(AfterAssembliesLoaded)]` 读 `ShrinkModFrameworkSettings` 装载;`ShrinkModBootstrap`(场景物体)仅作手动覆盖。
|
||||
- **编辑器脚手架**(`Editor/Scaffolding/`):外部 DLL 模组模板生成器、仓库内模组模板生成器(推荐路径,复用仓库 sln/asmdef 编译链)、Mod SDK 导出器(`ShrinkSDK/Mod/*` 菜单,产出 `GeneratedModSdk/`:Libs + ExternalMod 模板 + manifest)。
|
||||
|
||||
### 6.6 ShrinkTutorial 0.1.0
|
||||
|
||||
- **数据驱动**:`ShrinkTutorialData / Step / Database`(ScriptableObject)+ `ShrinkTutorialSettings`;`ShrinkTutorialManager` 单例调度(排队、前置检查、逐步执行、完成/跳过持久化)。
|
||||
- **目标定位**:静态 `Hierarchy Path` 或运行时 `AnchorId`(`ShrinkTutorialAnchor` 组件 + `AnchorRegistry`);步骤支持 `waitForTarget + waitTimeout` 等待动态目标出现。
|
||||
- **完成条件**:`ClickTarget`(EventSystem 射线真实命中才过步,防"遮罩挡住但教程已前进")/ `AnyClick` / `CustomEvent`(业务调 `CompleteStep(eventName)`)/ `Auto`;`DragToTarget` 暂按自定义事件处理。
|
||||
- **UI**:独立 Overlay Canvas + 遮罩挖洞(目标区可继续点击)+ 提示框/箭头/跳过按钮(`ShrinkTutorialMask / Dialog`)。
|
||||
- **持久化**:`IShrinkTutorialStorage` → 默认 `PlayerPrefs` 实现。
|
||||
- **本地化**:仅接口 `IShrinkTutorialLocalizationProvider`,不内置表系统。
|
||||
- **入口**:`ShrinkSDK/引导/教程编辑器`、`ShrinkSDK/引导/重置教程进度`;示例场景 `ShrinkTutorialSample.unity` 可由 `ShrinkSDK/引导/创建示例场景` 一键重建(当前示例文案为英文,规避 TMP 默认字体缺中文字形)。
|
||||
|
||||
### 6.7 ShrinkApp.Starter.Basic 0.1.0
|
||||
|
||||
- **一键起盘**:菜单 `ShrinkApp/Starter/生成 Basic Entry 场景` 生成 `ShrinkAppEntry.unity` + `ShrinkAppSettings` / `ShrinkDataSaverSettings` / `ShrinkAppBasicStarterSettings` 三份配置。
|
||||
- **最小闭环调试台**:存档槽操作、命令输入与输出、Loopback 绑定/断开、模块/服务/存档/命令/网络五块只读状态面板,演示 `App + DataSaver + Command + Network` 全链路。
|
||||
- **Starter 配置**:自动绑 loopback、默认命令、输出行数上限等。
|
||||
|
||||
## 7. 独立服务器生成链
|
||||
|
||||
**目标**:Unity 项目里的消息合同与处理器范式,能直接生成一个可编译、可运行、可继续补业务的 .NET 独立服务器工程(控制台 + 专用服,类似 Minecraft server 的形态)。
|
||||
|
||||
**生成器**:`ShrinkNetwork/Editor/Scaffolding/ShrinkDedicatedServerScaffoldGenerator.cs`,菜单 `ShrinkSDK/Network/生成完整独立服务器工程` / `刷新独立服务器 Generated 合同`,输出到 `GeneratedServers/ShrinkNetwork.ServerHost/`。
|
||||
|
||||
**生成策略**(依赖特性范式而非演示代码命名):
|
||||
|
||||
- 模板部分(`.cs.txt` 源):`Program.cs`、`Framework/`(`ShrinkDedicatedServerApp`、`ServerHostOptions/Properties`、`ServerAuthStore`、`UnityNetworkCodeScanner`、`IShrinkServerModule` 模块体系)、`AuthServerModule`、TCP/KCP 服务端传输、`server.properties`。
|
||||
- 扫描生成部分(`Generated/*.g.cs`):网络合同 DTO、按 `[ShrinkNetworkStateSync]` 角色推断同步模块、按 `[ShrinkNetworkEvent]` 三分类(广播/裁决/增量)自动生成 handler 模板并注册、`[ShrinkNetworkSubscribe]` 权限声明映射、扫描清单(`UNITY_NETWORK_SCAN.md` / `unity-network-scan.json`)。
|
||||
|
||||
**宿主运行形态**:
|
||||
|
||||
- 模块自发现:反射本程序集全部 `IShrinkServerModule` 实例化(含生成的 `UnityGeneratedServerModule` 与手写的 `AuthServerModule`)。
|
||||
- 双监听:TCP 与 KCP 同端口(默认 17777;历史演示 17001/17002)。
|
||||
- 配置体系:`ServerHostProperties.LoadOrCreate()`,优先级 **代码默认值 < server.properties < 环境变量**;支持 `SHRINK_SERVER_CONFIG_PATH` 自定义路径、`SHRINK_SERVER_AUTH_TOKEN` 等环境变量;首启缺文件自动落默认配置。
|
||||
- 鉴权与安全基线:默认**拒绝匿名登录**(`AllowAnonymousWhenAuthTokenMissing` 显式开启才放行);共享口令登录后签发内存态会话令牌(TTL + 续期窗口 + 逐包校验 + 坏令牌踢线,`server/auth/login` / `server/auth/refresh`);协议/Schema 版本窗口越界可断链;TCP 可选 TLS(服务端证书 + 客户端校验);TCP/KCP 均有最大包长与远端地址校验。
|
||||
- 可观测:`GetDiagnosticsSnapshot()` 全套指标按 `DiagnosticsLogIntervalSeconds` 周期打印,Ctrl+C 前输出最终快照。
|
||||
|
||||
**验证工具**(烟测闭环):`GeneratedServers/ShrinkCommand.RuntimeSmoke`、`ShrinkCommand.EventBusSmoke`、`ShrinkNetwork.RuntimeSmoke` 三个控制台工程,覆盖"登录拿令牌 → 业务 RPC → 令牌续期 → 篡改令牌被踢"与远程 `status` 命令链路。
|
||||
|
||||
## 8. 场景资产
|
||||
|
||||
| 场景 | 用途 |
|
||||
|---|---|
|
||||
| `SampleScene.unity` | 基础示例 |
|
||||
| `ShrinkAppEntry.unity` | Starter 一键生成的入口调试台 |
|
||||
| `ShrinkEmbeddedHostNetworkDemo.unity` | 宿主内嵌网络演示(控制器自动 RegisterService,无需手工注册) |
|
||||
| `ShrinkLanMovementDemo.unity` | LAN 平台跳跃移动同步演示(状态增量范式样板) |
|
||||
| `ShrinkTutorialSample.unity` | 引导系统示例(三类完成路径),可一键重建 |
|
||||
|
||||
## 9. 安全与健壮性设计(商业级硬化两轮成果)
|
||||
|
||||
1. **传输防线**:TCP/KCP 最大包长保护(防伪造长度触发大分配)、KCP 远端地址校验 + 随机 conversationId、TCP 并发写串行化(防包流交叉,见 NETWORK_PITFALLS #4)。
|
||||
2. **协议闸门**:包级 Protocol/Schema 版本 + 服务端窗口校验,违规可主动断链。
|
||||
3. **身份与会话**:默认拒绝匿名;共享口令 → 内存态会话令牌(TTL/续期/逐包校验/踢线)。
|
||||
4. **加密信道**:TCP 可选 TLS(证书 + SNI + 吊销检查;当前不含双向认证)。
|
||||
5. **并发安全**:服务/会话并发集合、断线自动失败挂起 RPC、EventBus 派发竞态修复、Unity 侧有界派发队列 + 溢出策略。
|
||||
6. **可观测**:全套指标快照 + 宿主周期摘要(尚无 metrics 导出/trace/告警)。
|
||||
7. **生成期报错**:opcode/route 冲突、installer 循环依赖、重复 ModuleId 均在编译/生成阶段失败,不静默兜底。
|
||||
|
||||
## 10. 测试与验证现状
|
||||
|
||||
- **包内测试**:`ShrinkDataSaver/Tests`(六类,最扎实);`ShrinkEventBus/Tests`(派发/优先级/注册/订阅四类,1.3.0 后补齐)。
|
||||
- **烟测工程**:三个 RuntimeSmoke/EventBusSmoke 控制台工程,覆盖鉴权-续期-踢线与远程命令全链路。
|
||||
- **构建验证**:`dotnet build ShrinkSDK.sln`、Assembly-CSharp(-Editor)、ServerHost.csproj 均 0 warning / 0 error(线程 2026-04-07 记录用户实测 Command/Network/独立服务器链路通过)。
|
||||
- **缺口**:Network/Command/ModFramework/Tutorial 无包内测试目录;无 CI 配置;Unity 编辑器内"重新生成完整独立服务器工程"的运行时回归未做(线程 Next Steps 首条)。
|
||||
|
||||
## 11. 已知设计限制与风险
|
||||
|
||||
来自 `.planning/codebase/CONCERNS.md`(编码问题部分已修复,如 DataSaver 中文乱码已按 UTF-8 重写)与线程 Notes/Next Steps 的仍有效项:
|
||||
|
||||
1. **网络栈生产缺口**:无 TLS 双向认证、无正式身份体系(当前是共享口令+内存令牌,无令牌轮换/外部 IdP)、协议版本是"窗口校验+断开"而非协商式、无 metrics 导出/trace/dashboard/告警、未做压测与模糊包故障演练。
|
||||
2. **EventBus 桥回传受限**:`ShrinkNetwork.Integration.EventBus` 的 `HasResult` 事件只回传 `EventResult/IsCanceled/ErrorCode/ErrorMessage`,不自动回传事件对象其它字段的最终改动。
|
||||
3. **外部 DLL 模组边界**:不支持运行时卸载程序集;IL2CPP Player 下外部 DLL 动态加载不可用。
|
||||
4. **生成器边界**:独立服务器生成以"可编译、可注册、可继续补业务"为目标,不推断完整业务逻辑;`GeneratedServers/` 产物可重建,人工业务不得写进生成文件(覆写保护行为待确认,线程 Next Steps 有记录)。
|
||||
5. **教程系统**:DragToTarget 未内建命中判定;圆形高亮视觉按外接矩形挖洞;示例文案为英文(等可用中文 TMP 字体资产后回改)。
|
||||
6. **仓库工程面**:无 CI/发布流水线;`.planning/codebase/` 地图滞后于当前模块规模(本文档即为补齐);部分公开 API 仍有 nullable 语义不一致残留(`route = null` 类签名收口未完成)。
|
||||
7. **历史已过期项**:`D:\UnityBuilds\ShrinkSDK` 曾不是 Git 仓库(现为 Git 仓库,当前处于初始提交暂存阶段);ServerHost nullable warnings 已清零(旧记录过期)。
|
||||
|
||||
## 12. 关键文件索引(推荐阅读顺序)
|
||||
|
||||
1. 统一宿主:`Assets/Modules/ShrinkApp.Core/Runtime/ShrinkApp.cs` → `ShrinkAppHost.cs` → `ShrinkAppTypes.cs`
|
||||
2. 编译期机制:`Assets/Modules/ShrinkShared.CodeGen/Editor/ShrinkRegistryILPostProcessor.cs`
|
||||
3. 事件系统:`Assets/Modules/ShrinkEventBus/Runtime/EventBus.cs` → `IShrinkEventBus.cs` → `ShrinkEventBusInstance.cs`
|
||||
4. 存档系统:`Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSave.cs` → `ShrinkDataSaverRuntime.cs` → `LocalStorageProvider.cs`
|
||||
5. 命令系统:`Assets/Modules/ShrinkCommand/Runtime/Core/ShrinkCommandService.cs` → `Metadata/ShrinkCommandAttributes.cs`
|
||||
6. 网络框架:`Assets/Modules/ShrinkNetwork/Runtime/Core/ShrinkNetworkService.cs` → `Metadata/ShrinkNetworkPacket.cs` → `Transport/Abstractions/IShrinkNetworkTransport.cs`
|
||||
7. 模组框架:`Assets/Modules/ShrinkModFramework/Runtime/Bootstrap/ShrinkModRuntimeBootstrap.cs` → `Loading/ShrinkModLoader.cs` → `Core/IShrinkMod.cs`
|
||||
8. 引导系统:`Assets/Modules/ShrinkTutorial/Runtime/Core/ShrinkTutorialManager.cs`
|
||||
9. 独立服务器:`GeneratedServers/ShrinkNetwork.ServerHost/Program.cs` → `Framework/ShrinkDedicatedServerApp.cs`
|
||||
10. 经验教训:`NETWORK_PITFALLS.md`(19 条联网/桥接/编译链踩坑,含 Kcp DLL 兼容、TCP 并发写、生成器范式化等)
|
||||
@@ -0,0 +1,10 @@
|
||||
# 文档归档
|
||||
|
||||
本目录只保存历史设计、已完成迁移方案和过期代码地图,不是当前实现依据。
|
||||
|
||||
- 当前唯一架构文档:仓库根 `DESIGN.md`
|
||||
- `CORDIS_MIGRATION.completed.md`:Cordis 阶段 0 至 5 的迁移论证和历史验收记录
|
||||
- `DESIGN-2026-08-16.md`:合并 Cordis 完成态之前的架构快照
|
||||
- `codebase-map-2026-05-23/`:2026-05-23 生成的旧代码地图
|
||||
|
||||
归档内容不随代码持续更新。发生冲突时,以源码、包清单、asmdef、自动化验证和根 `DESIGN.md` 为准。
|
||||
@@ -0,0 +1,101 @@
|
||||
# 架构
|
||||
|
||||
## 架构概览
|
||||
|
||||
- 这是一个以 Unity 包为边界的 SDK 单仓架构。
|
||||
- 主要由三个可分发模块组成:
|
||||
- `ShrinkEventBus`
|
||||
- `ShrinkDataSaver`
|
||||
- `ShrinkDataSaver.Integration.EventBus`
|
||||
- 每个模块都按 `Runtime`、`Editor`、`Tests` 或 `CodeGen` 做职责分离。
|
||||
|
||||
## 核心分层
|
||||
|
||||
- 事件总线层
|
||||
- 公开入口在 `Assets/Modules/ShrinkEventBus/Runtime/EventBus.cs`
|
||||
- 负责注册、触发、注销、调试记录和实例注册状态
|
||||
- 事件元数据与执行层
|
||||
- `Assets/Modules/ShrinkEventBus/Runtime/EventBase.cs`
|
||||
- `Assets/Modules/ShrinkEventBus/Runtime/ListenerList.cs`
|
||||
- `Assets/Modules/ShrinkEventBus/Runtime/EventHandlerInfo.cs`
|
||||
- `Assets/Modules/ShrinkEventBus/Runtime/EventPool.cs`
|
||||
- 编译织入层
|
||||
- `Assets/Modules/ShrinkEventBus/CodeGen/Editor/EventBusILPostProcessor.cs`
|
||||
- 负责给订阅者自动注入 `AutoRegister` 与 `UnregisterInstance`
|
||||
- 存档核心层
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSave.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSettings.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/SaveTypes.cs`
|
||||
- 存储与安全层
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/IStorageProvider.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/LocalStorageProvider.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/SaveEncryptor.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/DataSerializer.cs`
|
||||
- 版本迁移层
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/MigrationChain.cs`
|
||||
- Unity 入口层
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkDataSaverBootstrap.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkDataSaverSettings.cs`
|
||||
- 集成桥接层
|
||||
- `Assets/Modules/ShrinkDataSaver.Integration.EventBus/DataSaverEventBusBridge.cs`
|
||||
|
||||
## 主要数据流
|
||||
|
||||
## EventBus 数据流
|
||||
|
||||
- 注册流:
|
||||
- `[EventBusSubscriber]` + `[EventSubscribe]`
|
||||
- `EventBusILPostProcessor` 在编译期织入
|
||||
- 运行时通过 `EventBus.AutoRegister(this)` 完成注册
|
||||
- 触发流:
|
||||
- `EventBus.TriggerEvent` 或 `EventBus.TriggerEventAsync`
|
||||
- 从 `EventCache<TEvent>.List` 取监听列表
|
||||
- `ListenerList` 返回已排序快照
|
||||
- 依优先级逐个执行 handler
|
||||
|
||||
## DataSaver 数据流
|
||||
|
||||
- 保存流:
|
||||
- `ShrinkSave.SaveSlotAsync`
|
||||
- 序列化所有已注册模块
|
||||
- 可选加密
|
||||
- 经 `IStorageProvider.WriteAsync` 落盘
|
||||
- 触发保存完成或失败事件
|
||||
- 加载流:
|
||||
- `ShrinkSave.LoadSlotAsync`
|
||||
- 从 `IStorageProvider.ReadAsync` 读取
|
||||
- 可选解密
|
||||
- 必要时执行 `MigrationChain.Apply`
|
||||
- 分发给每个已注册模块反序列化
|
||||
- 缓存 `_loadedModuleData` 供查询接口使用
|
||||
|
||||
## 生命周期入口
|
||||
|
||||
- `EventBus` 通过静态构造与运行时初始化参与系统启动。
|
||||
- `ShrinkDataSaverBootstrap` 是 `MonoBehaviour` 入口,负责:
|
||||
- 初始化 `ShrinkSettings`
|
||||
- 初始化 `ShrinkSave`
|
||||
- 自动保存轮询
|
||||
- 应用暂停与退出时落盘设置
|
||||
- `DataSaverEventBusBridge` 在 `AfterAssembliesLoaded` 订阅 DataSaver 原生事件。
|
||||
|
||||
## 架构风格
|
||||
|
||||
- 偏“静态门面 + 小型基础设施”的工具库风格。
|
||||
- 对业务方暴露的是简单静态 API,而不是依赖注入容器。
|
||||
- 内部通过接口和 asmdef 边界做最小抽象,典型例子是 `IStorageProvider`。
|
||||
- 编辑器功能与运行时彻底分离,避免运行时代码依赖 UnityEditor。
|
||||
|
||||
## 入口判断
|
||||
|
||||
- 真正的运行时入口不是某个 `Main.cs`,而是 Unity 生命周期钩子和静态类。
|
||||
- 如果要阅读项目行为,优先从以下文件开始:
|
||||
- `Assets/Modules/ShrinkEventBus/Runtime/EventBus.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkDataSaverBootstrap.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSave.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver.Integration.EventBus/DataSaverEventBusBridge.cs`
|
||||
|
||||
## 结论
|
||||
|
||||
- 这套架构的重点是“低侵入接入 Unity 项目”,而不是重框架化。
|
||||
- 真正的设计价值在于 asmdef 分层、编译织入、静态门面和模块化存储抽象的组合。
|
||||
@@ -0,0 +1,66 @@
|
||||
# 风险与关注点
|
||||
|
||||
## 1. 中文编码已明显受损
|
||||
|
||||
- 多个文件在 PowerShell 读取时出现明显乱码,不适合继续作为事实来源直接复用。
|
||||
- 典型位置:
|
||||
- `Assets/Modules/ShrinkDataSaver/README.md`
|
||||
- `Assets/Modules/ShrinkDataSaver.Integration.EventBus/README.md`
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSave.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Editor/ShrinkDataSaverEditorWindow.cs`
|
||||
- 这会影响对外文档、Unity Inspector 文本、日志可读性和包元数据质量。
|
||||
|
||||
## 2. 仓库当前不在 Git 工作树内
|
||||
|
||||
- 在 `D:\UnityBuilds\ShrinkSDK` 执行 `git status --short` 返回 “not a git repository”。
|
||||
- 这意味着当前无法依赖 Git 记录变更、提交地图文档或审查历史。
|
||||
- 如果这个目录只是导出副本,需要明确真实源码仓位置。
|
||||
|
||||
## 3. EventBus 缺少自动化测试证据
|
||||
|
||||
- 当前测试基本只覆盖 `Assets/Modules/ShrinkDataSaver/Tests`。
|
||||
- `ShrinkEventBus` 的以下关键能力没有看到测试文件:
|
||||
- 优先级排序
|
||||
- 取消事件传播
|
||||
- 异步 handler 路径
|
||||
- `EventPool<T>` 复用语义
|
||||
- IL Post Processor 自动注册
|
||||
|
||||
## 4. `ShrinkSettings.Unwatch` API 设计存在可用性问题
|
||||
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSettings.cs`
|
||||
- `Watch<T>` 内部把 `Action<T>` 包成新的 `Action<object>` 存入 `_watchers`。
|
||||
- `Unwatch` 却要求外部传 `Action<object>`,调用者几乎无法用原始 `Action<T>` 精确移除。
|
||||
- 这不是理论问题,而是实际 API 不对称。
|
||||
|
||||
## 5. 截图路径可能留下纹理对象生命周期问题
|
||||
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSave.cs`
|
||||
- `CaptureScreenshotAsync` 在截图和缩放时会创建 `Texture2D`,但没有看到显式销毁临时纹理。
|
||||
- 在频繁截图存档场景下,值得确认是否会累积内存压力。
|
||||
|
||||
## 6. 集成桥接存在重复订阅风险窗口
|
||||
|
||||
- `Assets/Modules/ShrinkDataSaver.Integration.EventBus/DataSaverEventBusBridge.cs`
|
||||
- 该桥接在 `RuntimeInitializeOnLoadMethod` 里直接给静态事件追加订阅,没有看到“已初始化”防重复保护。
|
||||
- 如果遇到特定域重载配置或重复初始化路径,可能造成重复转发。
|
||||
- 这一点需要在真实 Unity 运行模式下验证,而不是仅凭源码静态阅读下结论。
|
||||
|
||||
## 7. 集成层版本描述需要复核
|
||||
|
||||
- `Assets/Modules/ShrinkDataSaver.Integration.EventBus/package.json`
|
||||
- 该包依赖中声明 `com.cneicy.shrink-eventbus` 版本 `1.0.0`,但本仓库内 `ShrinkEventBus` 包版本是 `1.1.4`。
|
||||
- 这不一定是错误,但至少说明版本关系需要人工确认。
|
||||
|
||||
## 8. 没有看到 CI 或发布流水线
|
||||
|
||||
- 当前未发现 `.github/workflows`、GitLab CI 或 Azure Pipelines 配置。
|
||||
- 对一个要分发的 Unity SDK 来说,这意味着:
|
||||
- 测试可能不自动跑
|
||||
- 文档与包版本可能靠手工维护
|
||||
- 编译织入问题更难被持续发现
|
||||
|
||||
## 结论
|
||||
|
||||
- 当前最现实的三个问题是:编码质量、EventBus 测试缺口、以及若干运行时边界没有自动化验证。
|
||||
- 如果后续要继续基于这个仓库规划工作,建议先确认真实源码仓与编码基线,再决定是否进入功能开发或补测试阶段。
|
||||
@@ -0,0 +1,76 @@
|
||||
# 代码约定
|
||||
|
||||
## 总体风格
|
||||
|
||||
- 代码整体偏 Unity 常规 C# 风格,使用 PascalCase 命名类型与公开成员,私有字段使用前导下划线。
|
||||
- 重要入口类大量使用 `public static class`,例如:
|
||||
- `Assets/Modules/ShrinkEventBus/Runtime/EventBus.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSave.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSettings.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/MigrationChain.cs`
|
||||
- 面向使用者暴露简单 API,内部状态则保留在静态字段中。
|
||||
|
||||
## 命名约定
|
||||
|
||||
- 事件类统一以 `Event` 结尾,见 `Assets/Modules/ShrinkDataSaver.Integration.EventBus/DataSaverEvents.cs`。
|
||||
- 事件参数类统一以 `EventArgs` 结尾,见 `Assets/Modules/ShrinkDataSaver/Runtime/SaveTypes.cs`。
|
||||
- 配置类统一以 `Settings`、`Options`、`Config` 结尾。
|
||||
- 编辑器窗口统一以 `EditorWindow` 结尾,见:
|
||||
- `Assets/Modules/ShrinkEventBus/Editor/EventBusViewerWindow.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Editor/ShrinkDataSaverEditorWindow.cs`
|
||||
|
||||
## 模式约定
|
||||
|
||||
- Unity 运行时与编辑器严格分目录、分 asmdef。
|
||||
- 用 Attribute 表达声明式能力:
|
||||
- `[EventBusSubscriber]`
|
||||
- `[EventSubscribe]`
|
||||
- `[Cancelable]`
|
||||
- `[HasResult]`
|
||||
- 用接口做最小抽象,而不是引入完整容器:
|
||||
- `IStorageProvider`
|
||||
- `ISaveModule`
|
||||
- `ISaveModule<T>`
|
||||
|
||||
## 空值与语法习惯
|
||||
|
||||
- 仓库同时使用普通 C# 空值判断和 Unity 对象真值判断。
|
||||
- `ShrinkEventBus` 某些文件启用了 `#nullable enable`,例如:
|
||||
- `Assets/Modules/ShrinkEventBus/Runtime/EventBase.cs`
|
||||
- `Assets/Modules/ShrinkEventBus/Runtime/ListenerList.cs`
|
||||
- 但整个仓库并没有统一启用可空引用,说明这是局部使用而非全局规范。
|
||||
|
||||
## 日志与注释
|
||||
|
||||
- 日志前缀较统一,常见前缀有:
|
||||
- `[EventBus]`
|
||||
- `[ShrinkDataSaver]`
|
||||
- `[ShrinkDataSaver.Integration]`
|
||||
- 源码注释和用户可见菜单文本原本倾向中文。
|
||||
- 但当前有多处中文字符串出现乱码,典型位置包括:
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSave.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Editor/ShrinkDataSaverEditorWindow.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/README.md`
|
||||
- `Assets/Modules/ShrinkDataSaver.Integration.EventBus/README.md`
|
||||
|
||||
## 错误处理
|
||||
|
||||
- 运行时代码多采用“抛异常 + 触发失败事件 + Debug.Log”混合策略。
|
||||
- 对非关键模块失败通常记录日志后跳过,对关键模块失败则终止保存,见 `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSave.cs`。
|
||||
- 异步 fire-and-forget 分支会用统一包装打印异常,见 `Assets/Modules/ShrinkEventBus/Runtime/EventBus.cs`。
|
||||
|
||||
## 测试约定
|
||||
|
||||
- 测试位于 `Assets/Modules/ShrinkDataSaver/Tests`。
|
||||
- 同时使用 `[Test]` 与 `[UnityTest]`。
|
||||
- 测试通过 `MockStorageProvider` 隔离文件系统,见 `Assets/Modules/ShrinkDataSaver/Tests/MockStorageProvider.cs`。
|
||||
|
||||
## 可读性与组织
|
||||
|
||||
- 单文件职责通常比较集中,尤其是 Runtime 层。
|
||||
- 但 `ShrinkSave.cs`、`ShrinkDataSaverEditorWindow.cs` 体量偏大,已经承担多个职责。
|
||||
|
||||
## 结论
|
||||
|
||||
- 当前代码约定的关键词是:静态门面、Attribute 驱动、asmdef 分层、中文日志、最小抽象。
|
||||
- 如果后续继续扩展,最需要先统一的是编码、可空规范和部分大文件拆分标准。
|
||||
@@ -0,0 +1,75 @@
|
||||
# 外部集成
|
||||
|
||||
## 总览
|
||||
|
||||
- 这个仓库外部依赖不多,主要是 Unity 运行时、Unity 编译管线、文件系统和少量第三方包。
|
||||
- 没有发现数据库、网络 API、认证服务、WebHook、云函数等后端集成代码。
|
||||
|
||||
## Unity 平台能力
|
||||
|
||||
- `Assets/Modules/ShrinkEventBus/Runtime/EventAutoRegHelper.cs`
|
||||
- 使用 `RuntimeInitializeOnLoadMethod` 参与运行时初始化。
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkDataSaverBootstrap.cs`
|
||||
- 依赖 `Application.persistentDataPath`
|
||||
- 依赖 `DontDestroyOnLoad`
|
||||
- 在 `OnApplicationQuit` 和 `OnApplicationPause` 里刷写设置
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkDataSaverSettings.cs`
|
||||
- 使用 `Resources.Load`
|
||||
- 编辑器下回退到 `UnityEditor.AssetDatabase`
|
||||
|
||||
## 第三方包
|
||||
|
||||
- `com.cysharp.unitask`
|
||||
- 用于异步事件处理与异步文件操作
|
||||
- 关键落点:`Assets/Modules/ShrinkEventBus/Runtime/EventBus.cs`
|
||||
- 关键落点:`Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSave.cs`
|
||||
- 关键落点:`Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSettings.cs`
|
||||
- `com.unity.nuget.newtonsoft-json`
|
||||
- 用于 JSON 序列化、JObject 迁移链和弱类型数据缓存
|
||||
- 关键落点:`Assets/Modules/ShrinkDataSaver/Runtime/DataSerializer.cs`
|
||||
- 关键落点:`Assets/Modules/ShrinkDataSaver/Runtime/MigrationChain.cs`
|
||||
- 关键落点:`Assets/Modules/ShrinkDataSaver/Runtime/SaveTypes.cs`
|
||||
|
||||
## 编译管线集成
|
||||
|
||||
- `Assets/Modules/ShrinkEventBus/CodeGen/Editor/EventBusILPostProcessor.cs`
|
||||
- 接入 `Unity.CompilationPipeline.Common.ILPostProcessing`
|
||||
- 使用 Mono.Cecil 改写带 `[EventBusSubscriber]` 的 `MonoBehaviour`
|
||||
- `Assets/Modules/ShrinkEventBus/CodeGen/Editor/PostProcessorAssemblyResolver.cs`
|
||||
- `Assets/Modules/ShrinkEventBus/CodeGen/Editor/PostProcessorReflectionImporter.cs`
|
||||
|
||||
## 模块间集成
|
||||
|
||||
- `Assets/Modules/ShrinkDataSaver.Integration.EventBus/DataSaverEventBusBridge.cs`
|
||||
- 将 `ShrinkDataSaver` 的原生事件桥接到 `ShrinkEventBus`
|
||||
- 桥接方向是单向的:`ShrinkDataSaver -> ShrinkEventBus`
|
||||
- `Assets/Modules/ShrinkDataSaver.Integration.EventBus/DataSaverEvents.cs`
|
||||
- 定义桥接后的 9 个事件类型
|
||||
|
||||
## 本地存储集成
|
||||
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/IStorageProvider.cs`
|
||||
- 定义存储抽象
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/LocalStorageProvider.cs`
|
||||
- 当前唯一实现
|
||||
- 使用本地文件系统读写、删除、列举文件
|
||||
- 采用 `.tmp` 文件再替换的方式降低写坏文件的概率
|
||||
|
||||
## 编辑器集成
|
||||
|
||||
- `Assets/Modules/ShrinkEventBus/Editor/EventBusViewerWindow.cs`
|
||||
- 通过 Unity 菜单打开事件查看器
|
||||
- `Assets/Modules/ShrinkDataSaver/Editor/ShrinkDataSaverEditorWindow.cs`
|
||||
- 通过 Unity 菜单操作设置与存档
|
||||
|
||||
## 未发现的外部依赖
|
||||
|
||||
- 未发现 HTTP 客户端调用。
|
||||
- 未发现数据库连接代码。
|
||||
- 未发现 OAuth、Steamworks、Firebase、PlayFab、Addressables 等 SDK 实际接入代码。
|
||||
- README 中提到 Steam Cloud 场景,但当前仓库里没有直接调用 Steam API 的实现。
|
||||
|
||||
## 结论
|
||||
|
||||
- 该仓库的“集成”重点不是远程服务,而是 Unity 生命周期、编译后处理、文件系统和模块间桥接。
|
||||
- 如果后续要扩展云存档、远程同步或遥测,这里目前只有存储接口和事件桥接两个自然扩展点。
|
||||
@@ -0,0 +1,5 @@
|
||||
# 2026-05-23 代码地图(已归档)
|
||||
|
||||
此目录原位于 `.planning/codebase/`,现整体归档。它早于 ContextLoader、Cordis 阶段 0 至 5、当前包版本和根仓库 Git 修复,其中关于仓库不是 Git、测试规模、模块布局和风险状态的描述均不再可信。
|
||||
|
||||
当前架构见仓库根 `DESIGN.md`。
|
||||
@@ -0,0 +1,74 @@
|
||||
# 技术栈
|
||||
|
||||
## 项目定位
|
||||
|
||||
- 这是一个 Unity SDK 型仓库,不是完整游戏项目。
|
||||
- 当前 Unity 版本是 `2022.3.62f3`,见 `ProjectSettings/ProjectVersion.txt`。
|
||||
- 当前仅配置了一个示例场景 `Assets/Scenes/SampleScene.unity`,见 `ProjectSettings/EditorBuildSettings.asset`。
|
||||
|
||||
## 运行时基础
|
||||
|
||||
- 核心语言是 C#,通过 Unity asmdef 拆分模块。
|
||||
- 根解决方案是 `ShrinkSDK.sln`,包含 `ShrinkEventBus`、`ShrinkDataSaver`、集成层、编辑器扩展和测试项目。
|
||||
- 运行时代码主要在:
|
||||
- `Assets/Modules/ShrinkEventBus/Runtime`
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime`
|
||||
- `Assets/Modules/ShrinkDataSaver.Integration.EventBus`
|
||||
|
||||
## Unity 包依赖
|
||||
|
||||
- `Packages/manifest.json` 中可确认以下关键依赖:
|
||||
- `com.cysharp.unitask`
|
||||
- `com.unity.nuget.newtonsoft-json`
|
||||
- `com.unity.test-framework`
|
||||
- `com.unity.ugui`
|
||||
- `com.unity.textmeshpro`
|
||||
- `com.unity.timeline`
|
||||
- `com.unity.render-pipelines.universal`
|
||||
|
||||
## 仓库内 SDK 包
|
||||
|
||||
- `Assets/Modules/ShrinkEventBus/package.json`
|
||||
- 包名 `com.cneicy.shrink-eventbus`
|
||||
- 版本 `1.1.4`
|
||||
- 依赖 `UniTask`
|
||||
- `Assets/Modules/ShrinkDataSaver/package.json`
|
||||
- 包名 `com.cneicy.shrink-datasaver`
|
||||
- 版本 `2.0.0`
|
||||
- 依赖 `UniTask` 与 `Newtonsoft.Json`
|
||||
- `Assets/Modules/ShrinkDataSaver.Integration.EventBus/package.json`
|
||||
- 包名 `com.cneicy.shrink-datasaver-integration-eventbus`
|
||||
- 版本 `2.0.0`
|
||||
- 依赖 `ShrinkDataSaver` 与 `ShrinkEventBus`
|
||||
|
||||
## 程序集拆分
|
||||
|
||||
- `Assets/Modules/ShrinkEventBus/Runtime/ShrinkEventBus.Runtime.asmdef`
|
||||
- `Assets/Modules/ShrinkEventBus/Editor/ShrinkEventBus.Editor.asmdef`
|
||||
- `Assets/Modules/ShrinkEventBus/CodeGen/Editor/Unity.ShrinkEventBus.CodeGen.asmdef`
|
||||
- `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkDataSaver.Runtime.asmdef`
|
||||
- `Assets/Modules/ShrinkDataSaver/Editor/ShrinkDataSaver.Editor.asmdef`
|
||||
- `Assets/Modules/ShrinkDataSaver/Tests/ShrinkDataSaver.Tests.asmdef`
|
||||
- `Assets/Modules/ShrinkDataSaver.Integration.EventBus/ShrinkDataSaver.Integration.EventBus.asmdef`
|
||||
|
||||
## 关键技术点
|
||||
|
||||
- 事件系统使用泛型静态缓存与对象池优化热路径,入口在 `Assets/Modules/ShrinkEventBus/Runtime/EventBus.cs`。
|
||||
- 自动注册依赖 Unity IL Post Processor 与 Mono.Cecil,入口在 `Assets/Modules/ShrinkEventBus/CodeGen/Editor/EventBusILPostProcessor.cs`。
|
||||
- 存档系统使用 `UniTask` 处理异步 IO,入口在 `Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSave.cs`。
|
||||
- 序列化基于 `Newtonsoft.Json`,入口在 `Assets/Modules/ShrinkDataSaver/Runtime/DataSerializer.cs`。
|
||||
- 加密使用 AES-CBC + PBKDF2-SHA256,入口在 `Assets/Modules/ShrinkDataSaver/Runtime/SaveEncryptor.cs`。
|
||||
- 本地存储基于 `Application.persistentDataPath` 与文件系统,入口在 `Assets/Modules/ShrinkDataSaver/Runtime/LocalStorageProvider.cs`。
|
||||
|
||||
## 编辑器与测试
|
||||
|
||||
- 编辑器工具包括:
|
||||
- `Assets/Modules/ShrinkEventBus/Editor/EventBusViewerWindow.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver/Editor/ShrinkDataSaverEditorWindow.cs`
|
||||
- 当前扫描到运行时相关文件约 `62` 个,编辑器相关文件约 `16` 个。
|
||||
- 测试主要集中在 `Assets/Modules/ShrinkDataSaver/Tests`,当前统计到 `[Test]`/`[UnityTest]` 标记共 `78` 个。
|
||||
|
||||
## 结论
|
||||
|
||||
- 这是一个围绕 Unity 包分发设计的多模块 SDK 仓库。
|
||||
- 技术选择偏向 Unity 原生能力 + `UniTask` + `Newtonsoft.Json`,没有引入重量级 DI、状态机或第三方测试平台。
|
||||
@@ -0,0 +1,119 @@
|
||||
# 目录结构
|
||||
|
||||
## 顶层结构
|
||||
|
||||
- `Assets`
|
||||
- 核心源码、编辑器扩展、测试、示例场景和包描述文件都在这里
|
||||
- `Packages`
|
||||
- Unity 包清单与锁文件
|
||||
- `ProjectSettings`
|
||||
- Unity 版本、渲染、输入、构建场景等项目配置
|
||||
- `Library`、`Temp`、`Logs`、`obj`
|
||||
- Unity 生成目录,不属于源码地图重点
|
||||
|
||||
## Assets 下的主要模块
|
||||
|
||||
## `Assets/Modules/ShrinkEventBus`
|
||||
|
||||
- `Runtime`
|
||||
- 事件总线核心实现
|
||||
- `Editor`
|
||||
- 事件查看器窗口
|
||||
- `CodeGen/Editor`
|
||||
- IL Post Processor 与 Cecil 辅助类
|
||||
- `README.md`
|
||||
- `package.json`
|
||||
- `Benchmark.txt`
|
||||
|
||||
## `Assets/Modules/ShrinkDataSaver`
|
||||
|
||||
- `Runtime`
|
||||
- 存档、设置、迁移、加密、存储抽象与配置
|
||||
- `Editor`
|
||||
- 数据查看器窗口
|
||||
- `Tests`
|
||||
- NUnit 与 Unity Test Runner 测试
|
||||
- `README.md`
|
||||
- `package.json`
|
||||
|
||||
## `Assets/Modules/ShrinkDataSaver.Integration.EventBus`
|
||||
|
||||
- `DataSaverEvents.cs`
|
||||
- `DataSaverEventBusBridge.cs`
|
||||
- `README.md`
|
||||
- `package.json`
|
||||
- `ShrinkDataSaver.Integration.EventBus.asmdef`
|
||||
|
||||
## `Assets/Modules/ShrinkModFramework`
|
||||
|
||||
- `Editor/Scaffolding`
|
||||
- Mod SDK 导出器、外部 DLL 模组模板生成器、仓库内模组模板生成器与模板文件
|
||||
- `Runtime/Bootstrap`
|
||||
- 自动启动、运行时驱动、设置资产与可选场景入口
|
||||
- `Runtime/Core`
|
||||
- `IShrinkMod`、`ShrinkModBase`、`ShrinkModContext`、`ShrinkModHandle`
|
||||
- `Runtime/Metadata`
|
||||
- 模组特性、依赖、状态、版本信息
|
||||
- `Runtime/Registry`
|
||||
- 模组注册表与注册表管理器
|
||||
- `Runtime/Loading`
|
||||
- 模组发现、依赖排序、外部 DLL 装载、Harmony 接入
|
||||
- `Runtime/Network`
|
||||
- 模组网络抽象层
|
||||
- `Runtime/Integration`
|
||||
- 对 `ShrinkEventBus`、`ShrinkCommand`、`ShrinkNetwork` 的可选自动接入胶水
|
||||
- `README.md`
|
||||
- `package.json`
|
||||
- `Runtime/ShrinkModFramework.Runtime.asmdef`
|
||||
|
||||
## 其他目录
|
||||
|
||||
- `Assets/Scenes/SampleScene.unity`
|
||||
- 当前唯一启用的示例场景
|
||||
- `Assets/Settings`
|
||||
- URP 资源和场景模板
|
||||
|
||||
## 关键文件入口
|
||||
|
||||
- 事件系统入口:`Assets/Modules/ShrinkEventBus/Runtime/EventBus.cs`
|
||||
- 事件订阅反射注册:`Assets/Modules/ShrinkEventBus/Runtime/EventBusRegHelper.cs`
|
||||
- 事件订阅编译织入:`Assets/Modules/ShrinkEventBus/CodeGen/Editor/EventBusILPostProcessor.cs`
|
||||
- 存档系统入口:`Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSave.cs`
|
||||
- 设置系统入口:`Assets/Modules/ShrinkDataSaver/Runtime/ShrinkSettings.cs`
|
||||
- Unity 启动器:`Assets/Modules/ShrinkDataSaver/Runtime/ShrinkDataSaverBootstrap.cs`
|
||||
- 模块桥接入口:`Assets/Modules/ShrinkDataSaver.Integration.EventBus/DataSaverEventBusBridge.cs`
|
||||
- 模组框架启动入口:`Assets/Modules/ShrinkModFramework/Runtime/Bootstrap/ShrinkModRuntimeBootstrap.cs`
|
||||
- 模组装载入口:`Assets/Modules/ShrinkModFramework/Runtime/Loading/ShrinkModLoader.cs`
|
||||
- 模组模板生成入口:`Assets/Modules/ShrinkModFramework/Editor/Scaffolding/ShrinkExternalModTemplateGenerator.cs`
|
||||
- 仓库内模组模板生成入口:`Assets/Modules/ShrinkModFramework/Editor/Scaffolding/ShrinkInRepoModTemplateGenerator.cs`
|
||||
- Mod SDK 导出入口:`Assets/Modules/ShrinkModFramework/Editor/Scaffolding/ShrinkModSdkExporter.cs`
|
||||
|
||||
## 命名组织规律
|
||||
|
||||
- 包名与目录名基本一一对应:`ShrinkEventBus`、`ShrinkDataSaver`、`ShrinkDataSaver.Integration.EventBus`
|
||||
- `ShrinkModFramework` 继续遵循同样规则,并在 `Runtime` 下进一步按职责拆子目录
|
||||
- 命名空间与 asmdef 名称基本一致:
|
||||
- `ShrinkEventBus`
|
||||
- `ShrinkEventBus.Editor`
|
||||
- `ShrinkEventBus.CodeGen`
|
||||
- `ShrinkDataSaver`
|
||||
- `ShrinkDataSaver.Editor`
|
||||
- `ShrinkDataSaver.Integration`
|
||||
- 编辑器专属代码全部放在 `Editor` 目录下,符合 Unity 约定。
|
||||
|
||||
## 解决方案结构
|
||||
|
||||
- `ShrinkSDK.sln` 暴露了 8 个 C# 工程:
|
||||
- `ShrinkEventBus.Runtime`
|
||||
- `ShrinkEventBus.Editor`
|
||||
- `Unity.ShrinkEventBus.CodeGen`
|
||||
- `ShrinkDataSaver.Runtime`
|
||||
- `ShrinkDataSaver.Editor`
|
||||
- `ShrinkDataSaver.Tests`
|
||||
- `ShrinkDataSaver.Integration.EventBus`
|
||||
- `Assembly-CSharp`
|
||||
|
||||
## 结论
|
||||
|
||||
- 结构上最值得注意的是:这是按“可发布 Unity 包”来组织,而不是按单一游戏功能目录来组织。
|
||||
- 阅读顺序建议先从 `Runtime`,再到 `Editor`,最后看 `Tests` 与 `README.md`。
|
||||
@@ -0,0 +1,65 @@
|
||||
# 测试情况
|
||||
|
||||
## 当前测试布局
|
||||
|
||||
- 测试集中在 `Assets/Modules/ShrinkDataSaver/Tests`。
|
||||
- 测试程序集定义见 `Assets/Modules/ShrinkDataSaver/Tests/ShrinkDataSaver.Tests.asmdef`。
|
||||
- 该 asmdef:
|
||||
- 仅在 `Editor` 平台启用
|
||||
- 依赖 `UnityEngine.TestRunner` 与 `UnityEditor.TestRunner`
|
||||
- 通过 `UNITY_INCLUDE_TESTS` 控制
|
||||
- 额外引用 `Newtonsoft.Json.dll`
|
||||
|
||||
## 已覆盖模块
|
||||
|
||||
- `Assets/Modules/ShrinkDataSaver/Tests/ShrinkSaveTests.cs`
|
||||
- 模块注册
|
||||
- 保存、加载、删除
|
||||
- 事件触发
|
||||
- 关键模块失败行为
|
||||
- 版本迁移事件
|
||||
- 查询接口
|
||||
- 自动保存间隔
|
||||
- 元数据读取
|
||||
- `LoadedSlot` 状态
|
||||
- 多模块往返
|
||||
- `Assets/Modules/ShrinkDataSaver/Tests/ShrinkSettingsTests.cs`
|
||||
- 设置读写、监听、持久化与防抖行为
|
||||
- `Assets/Modules/ShrinkDataSaver/Tests/SaveEncryptorTests.cs`
|
||||
- 加解密往返、错误密码、空输入等边界
|
||||
- `Assets/Modules/ShrinkDataSaver/Tests/MigrationChainTests.cs`
|
||||
- 注册、链式迁移、缺步、回滚
|
||||
- `Assets/Modules/ShrinkDataSaver/Tests/DataSerializerTests.cs`
|
||||
- JSON 序列化基础行为
|
||||
- `Assets/Modules/ShrinkDataSaver/Tests/SaveTypesTests.cs`
|
||||
- 类型层面的默认值与结构行为
|
||||
|
||||
## 测试风格
|
||||
|
||||
- 同时使用同步 NUnit 测试和基于 `UniTask.ToCoroutine` 的 `UnityTest`。
|
||||
- 使用 `MockStorageProvider` 模拟存储后端,避免真实磁盘依赖。
|
||||
- 通过 `ResetForTesting()`、`MigrationChain.Clear()` 等内部重置方法控制静态状态污染。
|
||||
|
||||
## 当前数量
|
||||
|
||||
- 当前扫描到 `[Test]` 与 `[UnityTest]` 标记共 `78` 个。
|
||||
- 测试密度主要集中在 `ShrinkDataSaver`,并未均匀覆盖整个仓库。
|
||||
|
||||
## 明显缺口
|
||||
|
||||
- 没有看到 `ShrinkEventBus` 对应的测试目录。
|
||||
- `EventBus` 的优先级排序、取消传播、异步处理、自动注册织入结果,没有自动化测试证据。
|
||||
- 编辑器窗口也没有独立测试。
|
||||
- 没有发现 CI 配置文件,说明测试是否自动执行未知,当前更像本地手动运行模式。
|
||||
|
||||
## 适合后续补强的位置
|
||||
|
||||
- `Assets/Modules/ShrinkEventBus/Runtime/EventBus.cs`
|
||||
- `Assets/Modules/ShrinkEventBus/Runtime/ListenerList.cs`
|
||||
- `Assets/Modules/ShrinkEventBus/CodeGen/Editor/EventBusILPostProcessor.cs`
|
||||
- `Assets/Modules/ShrinkDataSaver.Integration.EventBus/DataSaverEventBusBridge.cs`
|
||||
|
||||
## 结论
|
||||
|
||||
- 这个仓库的测试现状可以概括为:`ShrinkDataSaver` 覆盖较扎实,`ShrinkEventBus` 和集成层明显偏弱。
|
||||
- 如果后续要发包或稳定演进,优先级最高的补测点是 EventBus 核心行为与编译织入结果。
|
||||
Reference in New Issue
Block a user