# ET 程序集分层与 asmref 完整规则 ## 6 个核心程序集 | 程序集 | 由谁定义 | 装哪些代码 | |---|---|---| | `ET.Core` | `cn.etetet.core/Runtime/Core/ET.Core.asmdef` | 框架基础,不可修改 | | `ET.Model` | `cn.etetet.statesync/Runtime/Model/ET.Model.asmdef` | Entity/Component 数据定义、Proto 消息类、占位/工具类(加 `[EnableClass]`) | | `ET.ModelView` | `cn.etetet.statesync/Runtime/ModelView/ET.ModelView.asmdef` | YIUI 视图组件、UIBindCDETable 派生类 | | `ET.Hotfix` | `cn.etetet.statesync/Runtime/Hotfix/ET.Hotfix.asmdef` | 业务 System / Service / Helper / Handler | | `ET.HotfixView` | `cn.etetet.statesync/Runtime/HotfixView/ET.HotfixView.asmdef` | YIUI 事件、GM 命令、客户端 RPC handler | | `ET.Loader` | `cn.etetet.loader/...` | 启动器 | > ET.Model.asmdef 的 `defineConstraints` 是 `["INITED", "IS_COMPILING || UNITY_EDITOR"]` — 项目必须先被 ET 框架"初始化"过(写入 `INITED` symbol),所有 asmref 才会激活。新克隆的工程要先走一次 ET 初始化流程。 --- ## asmref:让你的包加入框架程序集 每个 `cn.etetet.` 包**不能**自己定义 asmdef,必须用 asmref 把代码"塞"进上面 6 个程序集。 ### 标准目录布局 ``` cn.etetet.mypkg/ ├── package.json # 声明依赖 ├── Ignore.ET.MyPkg.asmdef # 占位(IGNORE constraint) ├── Ignore.ET.MyPkg.asmdef.meta ├── Proto/ # 只放 .proto │ └── MyPkgOuter_C_4000.proto └── Scripts/ ├── Model/ │ ├── Share/AssemblyReference.asmref # → ET.Model │ ├── Client/AssemblyReference.asmref # → ET.Model │ └── Server/AssemblyReference.asmref # → ET.Model ├── ModelView/ │ └── Client/AssemblyReference.asmref # → ET.ModelView ├── Hotfix/ │ ├── Share/AssemblyReference.asmref # → ET.Hotfix │ ├── Client/AssemblyReference.asmref # → ET.Hotfix │ └── Server/AssemblyReference.asmref # → ET.Hotfix └── HotfixView/ └── Client/AssemblyReference.asmref # → ET.HotfixView ``` ### AssemblyReference.asmref 内容(固定) ```json { "reference": "ET.Model" } ``` ```json { "reference": "ET.ModelView" } ``` ```json { "reference": "ET.Hotfix" } ``` ```json { "reference": "ET.HotfixView" } ``` ### Ignore..asmdef 内容(固定) ```json { "name": "Ignore.ET.MyPkg", "rootNamespace": "", "references": [], "defineConstraints": ["IGNORE"], "autoReferenced": true } ``` `defineConstraints: ["IGNORE"]` 让这个 asmdef 永远不参与编译 — 它的存在只是为了占位(避免 Unity 找不到 .meta),真正生效的是各子目录里的 asmref。 --- ## 哪个代码放哪一层(决策表) | 你想写的代码 | 放哪 | 关键约束 | |---|---|---| | `class Hero : Entity, IAwake` | `Scripts/Model/Server/Hero.cs` | 只能字段,不能写方法。需要 `[ChildOf(typeof(HeroComponent))]` | | `[ComponentOf(typeof(Player))] class HeroComponent : Entity, IAwake` | `Scripts/Model/Server/HeroComponent.cs` | 同上 | | 客户端缓存 `class HeroComponent : Entity, IAwake { Dictionary }` | `Scripts/Model/Client/HeroModels.cs` | 同名服务端类客户端可独立定义 | | `[EntitySystemOf(typeof(Hero))] partial class HeroSystem { Awake/Update }` | `Scripts/Hotfix/Server/HeroSystem.cs` | partial,函数标 `[EntitySystem]` | | `static class HeroService { TrySkillUp(player, hero) }` | `Scripts/Hotfix/Server/HeroService.cs` | 加 `[FriendOf(typeof(Hero))]` | | `RpcMessageHandler` 子类 | `Scripts/Hotfix/Server/C2G_XHandler.cs` | **不能**有非 const 字段/属性 | | `MessageHandler` 客户端 | `Scripts/Hotfix/Client/X_Handler.cs` | 同上 | | 锁对象 / 字典 / 静态状态工具类 | `Scripts/Model/Share/` + `[EnableClass]` | 因为 Hotfix 不允许静态字段 | | Proto 生成的 Message 类 | 不要自己写,跑 Proto2CS | 输出到 `cn.etetet.proto/CodeMode/` | | YIUI 视图组件 `class XXXViewComponent : Entity` | `Scripts/ModelView/Client/XXX.cs` | | | `[GM(EGMType.Common, ...)]` 命令 | `Scripts/HotfixView/Client/GM/X.cs` | 必须在 HotfixView 才被 GMAttribute 扫到 | | `AEvent` handler | `Scripts/HotfixView/Client/` | | --- ## 新建一个业务包:完整步骤 ``` Task Progress: - [ ] 1. 复制 cn.etetet.statesync 整个目录为 cn.etetet. - [ ] 2. 删除 Runtime/ 下的 ET.*.asmdef 文件(这些是核心程序集,不能在新包里) - [ ] 3. 改 package.json 的 name / displayName / dependencies - [ ] 4. 修改 Ignore.ET.Statesync.asmdef → Ignore.ET..asmdef,name 字段同步改 - [ ] 5. 确保 Scripts/**/AssemblyReference.asmref 都还在(content 不需改) - [ ] 6. 如果不需要 ModelView / HotfixView,删对应目录的 asmref(避免无意义编译) - [ ] 7. Unity Assets/Refresh,确认 Console 无报错 - [ ] 8. 写 Proto → 跑 Proto2CS → 写 Model → 写 Hotfix ``` ### package.json 依赖最小集 ```json { "name": "cn.etetet.mypkg", "displayName": "ET.MyPkg", "version": "1.0.0", "dependencies": { "cn.etetet.core": "1.0.0", "cn.etetet.login": "1.0.0", "cn.etetet.logging": "1.0.0", "cn.etetet.console": "1.0.0" } } ``` 按需补:`cn.etetet.bag` / `cn.etetet.hero` / `cn.etetet.yiuigm` 等。 --- ## 临时屏蔽 / 启用整个包 **屏蔽**:删除 `Scripts/**/AssemblyReference.asmref` 及对应 `.meta`,保留 `Ignore.*.asmdef`。包代码全部不参与编译。 **启用**:把 asmref 加回来,内容如上节模板。Unity 会自动识别。 本次会话用这招屏蔽了 `cn.etetet.gacha`: ``` 删除:cn.etetet.gacha/Scripts/Hotfix/{Client,Server}/AssemblyReference.asmref 删除:cn.etetet.gacha/Scripts/Model/{Client,Server}/AssemblyReference.asmref 保留:cn.etetet.gacha/Ignore.ET.Gacha.asmdef ``` --- ## 常见踩坑 1. **代码静默失踪**:新加的 cs 文件**所在目录上行**没有 asmref → 文件不参与任何程序集 → 调用方报"找不到类型"。检查方法: ``` Glob: cn.etetet./**/*.asmref ``` 每个有 cs 文件的子目录必须有 asmref 上溯。 2. **方向错误**:把 Helper 写到 `Scripts/Model/Server/` → ET0032。把 Entity 写到 `Scripts/Hotfix/Server/` → 各种诡异错误(Hotfix 不允许字段)。决策表见上。 3. **客户端代码引用了 UnityEngine**:`Scripts/Hotfix/Client/` 是 `ET.Hotfix` 程序集,**不能** `using UnityEngine`。需要 UnityEngine API 的代码必须放 `Scripts/HotfixView/Client/` 或 `Scripts/ModelView/Client/`。 4. **Model 程序集报 `Define` / `GlobalConfig` / `Resources` 未找到**:同样 UnityEngine 引用问题。Model 层禁止依赖 UnityEngine。需要的话挪去 ModelView 或者用反射延后调用。 --- ## 跨包引用的最佳实践 - 在 `package.json.dependencies` 显式声明依赖。 - **避免环依赖**:`login` 不要依赖 `bag/hero` 这种业务包;让业务包依赖 `login` 暴露的接口。如果非要双向,用 Invoke 契约(看 `cn.etetet.invoke` 包模式)。 - 不允许 `Hotfix/Client` 调用 `Hotfix/Server` 代码(编译会过但语义错)。共享逻辑放 `Hotfix/Share`。