# justanyproject 代码框架组织审计 > 审计日期:2026-08-24 > 源项目:`D:\UnityBuilds\justanyproject` > 源基线:`main` / `b336715`,工作树干净,Unity `2022.3.62f3` > 证据范围:`AGENTS.md`、`Assets/Scripts`、`Assets/Tests`、asmdef、`Packages/manifest.json` 和相关模块文档。本文是源码与工程结构审计,不代表重新执行过源项目的 Unity 编译、EditMode、PlayMode 或运行时验收。 ## 1. 当前组织概况 源项目的第一方运行时代码主要位于 `Assets/Scripts`:共 564 个 C# 文件,按 24 个业务目录组织。规模最大的三个目录是 `EndlessMode`(205)、`CombatRuntime`(82)和 `Bootstrap`(41)。 项目共有 12 个第一方 asmdef,其中大部分基础模块已有独立程序集;但 `SeedsKey.Game` 仍引用 28 个程序集,并承载大量未拆分目录。测试分为 EditMode 与 PlayMode 两个程序集,二者都直接引用多个生产程序集。 当前框架可以概括为: ```text Entry / AppBootstrapper | Bootstrap + AppCompositionRoot | 全局 Manager / 领域系统 / Scene Composition Root | CombatRuntime / EndlessMode / StrategicMap / 其它业务模块 | ShrinkEventBus / ShrinkDataSaver / ShrinkNetwork / ShrinkModFramework ``` 目录层面的领域划分已经清楚,但编译边界、对象所有权和运行时入口仍处于新旧模式并存状态:显式组合根、窄接口和结构化 facade 已出现,业务单例、静态入口和事件协调仍然大量存在。 ## 2. 值得迁移的经验 ### 2.1 显式 Composition Root `AppCompositionRoot` 和 `EndlessBattleCompositionRoot` 把关键依赖集中在可检查的绑定边界。后者通过序列化字段表达场景依赖,并提供 `ValidateRequiredBindings()` 汇总缺失项。这比在业务对象内部临时查找单例、场景对象或 prefab 子节点更容易审计。 `AppScope`、`SessionScope`、`SceneScope` 目前只是借用关系和已释放状态的标记:它们会拒绝失效父作用域,但不持有资源集合,也不会级联或反向释放对象。因此可迁移的是“显式作用域边界”,不是现有实现本身。 ShrinkSDK 的承载方式: - App 级组合继续由 `ShrinkAppLoaderBootstrapper`、Composition Profile 与 `ShrinkAppLoaderHost` 表达。 - Session/Scene/Mod 生命周期继续映射为 `ShrinkContext` fiber、派生 context 和可逆 effect,不再增加第二套 Scope 容器。 - 场景组件仍应使用显式序列化引用和独立校验结果,不允许运行时静默补建或猜路径。 ### 2.2 命令、查询和事实事件分离 源项目正在把跨模块调用收敛为三类: - 查询依赖窄的只读接口,例如 `ISquadReadOnly`、`IInventorySlotReadOnly`。 - 改变状态的操作经过 command facade,并返回 `Succeeded / Failed / Rejected` 等结构化结果。 - EventBus 只广播已经发生的事实;请求事件仅作为有清理计划的兼容桥。 `GameSessionFlowFacade` 和 `SceneTransitionFlowFacade` 还处理了重复请求拒绝、异常转结果以及 `finally` 恢复运行状态。这些行为使调用方不必从日志或事件顺序猜测执行结果。 ShrinkSDK 的承载方式: - `ShrinkCommandExecutionResult`、Network RPC 结果、Context 事务诊断继续作为状态变更的明确结果面。 - Context key 和 App service 应优先发布消费者真正需要的窄契约;只读消费者不应获得完整可写服务对象。 - EventBus bridge 只传递事实、通知或明确的异步请求合同,不用事件绕开 Command/Network 的返回值、权限和失败语义。 ### 2.3 Owner-aware 内容注册表 `ContentRegistry` 对内容 id、owner、覆盖优先级和冻结阶段有明确规则: - 新增内容必须使用 owner namespace。 - 同目标、同优先级覆盖直接报冲突。 - 覆盖目标必须存在。 - 启动完成后冻结,运行阶段只读。 这套规则适合“启动时装载、运行时固定”的游戏内容。ShrinkModFramework 现已把其中可兼容热替换的部分落地:基础项强制使用 `:`,覆盖目标和优先级显式声明,同目标同优先级直接拒绝,owner 卸载时自动恢复下一个覆盖或基础项。 没有复制冻结模型。ShrinkModFramework 支持 revision 热替换和失败回滚,注册表必须随组件 effect 撤回;全局冻结会与此生命周期冲突。模组通过 `RegisterContent` / `OverrideContent` 写入,查询只暴露 `IReadOnlyShrinkModRegistry`,owner 由当前 `ShrinkModContext` 决定。 ### 2.4 把架构约束写成门禁 源项目的 EditMode architecture gate 会检查: - 只读接口没有混入写操作。 - 已迁移调用方没有重新引入 Service Locator。 - command facade 返回结构化结果。 - 事实事件只在状态变更成功后发布。 - 兼容请求事件受 allowlist 约束。 方向正确,但部分测试通过读取 C# 源文本并匹配字符串实现,重命名、格式化或等价重构都可能导致误报。ShrinkSDK 应优先使用编译期类型检查、Cecil/程序集元数据、package.json/asmdef 图检查和真实 UPM 消费测试;只有无法从语义层验证的临时迁移约束才使用源码文本门禁。 ### 2.5 文档按所有权分层 源项目使用总索引说明模块所有权和主流程,再由目录 README 说明模块内部细节,同时把高代价故障沉淀到 `KNOWN_PITFALLS.md`。这种结构比一份不断膨胀的全局文档更容易维护。 ShrinkSDK 继续保持: - `DESIGN.md` 只描述当前架构和跨包约束。 - 包级用法、API 和限制放到各包 README。 - 已完成迁移与旧地图进入 `Docs/Archive`。 - 经验文档必须区分源码事实、工具验证和真实运行时验证。 ## 3. 不应迁移的部分 ### 3.1 大总程序集 `SeedsKey.Game` 的 28 个程序集引用和大量目录归属,使很多“模块”只有文件夹边界,没有独立编译边界。它适合渐进式游戏工程重构,不适合作为 SDK 包组织模板。 ShrinkSDK 必须继续以 UPM 包为发布边界、asmdef 为编译边界,主包不得反向依赖集成包。 ### 3.2 业务单例与全局初始化入口 源项目仍有 32 个包含 `BaseManager` 的文件、10 个运行时自动初始化文件,以及大量静态 `Instance`/EventBus 调用。它们说明迁移需要兼容面,不说明 SDK 应复制这种全局所有权模型。 ShrinkSDK 的静态入口只能是默认实例 facade;真实生命周期由实例、Context fiber 和 effect 拥有。 ### 3.3 字符串场景名和补救式 Bootstrap 源项目的主流程依赖 `Entry -> MainMenu -> ...`、字符串场景名和 `DevBooter` 补齐孤立场景依赖。它们是具体游戏的流程合同,不属于通用 SDK。 SDK 可以提供组合、诊断和校验能力,但不应内置游戏场景名、玩法 ready 顺序或自动修复缺失场景对象。 ### 3.4 游戏内容类型 存档槽、库存、法杖、战斗、房间、无尽模式和 Steam Workshop 的具体状态都应留在游戏层。SDK 只提供生命周期、注册、存储、传输和扩展机制。 ## 4. 对 ShrinkSDK 的实际结论 这次审计没有发现需要从源项目复制的新容器或通用框架。ShrinkSDK 当前在包边界、可逆生命周期、热替换回滚、生成式注册和真实 UPM 消费验证上已经更严格。有效经验应表现为边界规则,而不是移植源项目实现: 1. 在组合入口集中显式绑定并一次性报告缺失项。 2. 查询依赖窄的只读能力;状态变更使用 command/RPC 并返回结构化结果。 3. 事件表达事实,不作为无结果的跨模块命令通道。 4. 可扩展注册表必须记录 owner,冲突要显式拒绝,卸载必须可逆。 5. 架构规则进入自动化门禁,但优先验证程序集和包语义,避免长期依赖源码字符串测试。 6. 文档中的数量、测试结果和运行时状态必须标注快照日期与证据来源。 上述规则已同步进入 `DESIGN.md`,并完成两项实现: - `ShrinkModFramework 0.2.0` 使用只读查询接口与 owner-bound 写命令,支持 namespace、优先级覆盖、冲突拒绝和卸载恢复。 - UPM Consumer Validation 新增内部包循环和主包反向依赖 Integration 包的门禁,并支持 `-GraphOnly` 快速检查。 EventBus 2.0、Context、ModFramework 和 UPM Consumer Validation 继续作为这些规则的主要承载面,没有新增一套并行容器或 Service Locator。 ## 5. 当前验证边界 - 已读取并对照源项目当前 git 基线、目录、asmdef、Composition、Modding 和 architecture gate 源码。 - 已读取并对照 ShrinkSDK 当前 App、Context、Mod registry、包清单与 UPM 消费验证脚本。 - 本次没有修改 Unity 序列化资产,也没有修改源项目 `justanyproject`。 - ShrinkSDK Unity Editor 已确认 `productName=ShrinkSDK`,重新编译为 0 error / 0 warning。 - `ShrinkModFramework.Tests` EditMode 为 13/13,通过 namespace、覆盖优先级、冲突、只读 API 边界和真实外部 DLL 热替换回滚。 - `dotnet build ShrinkSDK.sln /m:1` 为 0 warning / 0 error。 - UPM Consumer Validation 的 `-GraphOnly` 通过;仓库外临时 Unity 工程成功加载 20 个包和 42 个 asmdef。本次该轮使用 `-SkipTests`,包内 EditMode 覆盖由上述定向 Test Runner 承担。 - 没有重新运行 `justanyproject` 测试、ShrinkSDK PlayMode、Player Build 或运行时场景;附录 A 的历史结果仍不是本次验证。 ## 附录 A:既有 EventBus 2.0 落地记录 以下内容保留此前审计推动的 EventBus 2.0 设计与验证记录。它描述 ShrinkSDK 当前工作树中的既有改造,不是本次重新执行的结果。 ### A.1 统一事件模型 ShrinkSDK 使用一个事件协议和多个命名 Bus 实例: ```text game -> Unity 主线程 scene:id -> Scene/Context 生命周期 mod:id -> 模组专属线程或宿主指定调度 server -> Inline 或专属线程 world:id -> ECS playback 后进入对应 Bus ``` 公共契约为 `IShrinkEvent`、`IShrinkCancelableEvent`、`IShrinkResultEvent` 和 `IShrinkEventBus.Post/PostAsync/Attach`。订阅通过 `[ShrinkEventSubscriber]`、`[ShrinkSubscribe]` 声明,实例生命周期由 App、Context、Mod 或 Mono scope 接管。 ### A.2 调度与生成 `ShrinkBusOptions` 固定 Bus 创建时的调度策略: - `Inline`:调用线程直接执行。 - `MainThread`:Unity 环境切换到 UniTask PlayerLoop。 - `DedicatedThread`:单线程串行状态机。 - `TaskPool`:适合并行计算和 IO。 跨线程 `Post` 表示投递;需要等待完成、取消或结果时使用 `PostAsync`。队列容量、关闭排空、取消和异常策略由 options 明确配置。 生产注册由 Unity ILPostProcessor 或纯 .NET Roslyn Generator 生成强类型调用,不使用 `MethodInfo.Invoke` 或 `Delegate.DynamicInvoke`。没有生成合同的外部 DLL 不自动进入反射扫描。ECS/Burst 通过 `ShrinkEcsEventQueue` 的 NativeQueue writer/playback 接入,Job 不直接调用托管 delegate 或 UniTask。 ### A.3 参考边界 NeoForge 的 owner/lifecycle、稳定优先级、取消事件和并行事件约束可用于定义行为;MessagePipe 的专用 broker、稳定 handler 快照和低分配发布路径可用于实现参考。ShrinkSDK 不复制 NeoForge 的双协议模型,也不引入 MessagePipe 的 DI 或第二套 Publisher/Subscriber API。 ### A.4 历史验证快照 此前工作记录的结果为:Unity EditMode `206/206`、EventBus PlayMode `1/1`,纯 .NET smoke 覆盖 Inline、DedicatedThread、实例与静态生成绑定;UI Toolkit 调试窗口完成结构读回和截图检查;Windows x64 Development IL2CPP Player 构建成功。 此前记录的 Play Mode benchmark 三轮中位数为:8 handler class `25.32M publish/s`、8 handler struct `35.02M publish/s`、30 handler `10.82M publish/s`,同步 struct Post 为 `0 B / 10,000,000` 次。真实 Burst Job 性能、其它平台 AOT 和设备侧吞吐仍属于发布前门禁;这些历史数字本次未重新运行确认。