7.4 KiB
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 框架"初始化"过(写入INITEDsymbol),所有 asmref 才会激活。新克隆的工程要先走一次 ET 初始化流程。
asmref:让你的包加入框架程序集
每个 cn.etetet.<pkg> 包不能自己定义 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 内容(固定)
{ "reference": "ET.Model" }
{ "reference": "ET.ModelView" }
{ "reference": "ET.Hotfix" }
{ "reference": "ET.HotfixView" }
Ignore..asmdef 内容(固定)
{
"name": "Ignore.ET.MyPkg",
"rootNamespace": "",
"references": [],
"defineConstraints": ["IGNORE"],
"autoReferenced": true
}
defineConstraints: ["IGNORE"] 让这个 asmdef 永远不参与编译 — 它的存在只是为了占位(避免 Unity 找不到 .meta),真正生效的是各子目录里的 asmref。
哪个代码放哪一层(决策表)
| 你想写的代码 | 放哪 | 关键约束 |
|---|---|---|
class Hero : Entity, IAwake<int> |
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<long, HeroInfo> } |
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<C2G_X, R2C_X> 子类 |
Scripts/Hotfix/Server/C2G_XHandler.cs |
不能有非 const 字段/属性 |
MessageHandler<Scene, X> 客户端 |
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<Scene, YIUIEventInitializeAfter> handler |
Scripts/HotfixView/Client/ |
新建一个业务包:完整步骤
Task Progress:
- [ ] 1. 复制 cn.etetet.statesync 整个目录为 cn.etetet.<newpkg>
- [ ] 2. 删除 Runtime/ 下的 ET.*.asmdef 文件(这些是核心程序集,不能在新包里)
- [ ] 3. 改 package.json 的 name / displayName / dependencies
- [ ] 4. 修改 Ignore.ET.Statesync.asmdef → Ignore.ET.<NewPkg>.asmdef,name 字段同步改
- [ ] 5. 确保 Scripts/**/AssemblyReference.asmref 都还在(content 不需改)
- [ ] 6. 如果不需要 ModelView / HotfixView,删对应目录的 asmref(避免无意义编译)
- [ ] 7. Unity Assets/Refresh,确认 Console 无报错
- [ ] 8. 写 Proto → 跑 Proto2CS → 写 Model → 写 Hotfix
package.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
常见踩坑
-
代码静默失踪:新加的 cs 文件所在目录上行没有 asmref → 文件不参与任何程序集 → 调用方报"找不到类型"。检查方法:
Glob: cn.etetet.<pkg>/**/*.asmref每个有 cs 文件的子目录必须有 asmref 上溯。
-
方向错误:把 Helper 写到
Scripts/Model/Server/→ ET0032。把 Entity 写到Scripts/Hotfix/Server/→ 各种诡异错误(Hotfix 不允许字段)。决策表见上。 -
客户端代码引用了 UnityEngine:
Scripts/Hotfix/Client/是ET.Hotfix程序集,不能using UnityEngine。需要 UnityEngine API 的代码必须放Scripts/HotfixView/Client/或Scripts/ModelView/Client/。 -
Model 程序集报
Define/GlobalConfig/Resources未找到:同样 UnityEngine 引用问题。Model 层禁止依赖 UnityEngine。需要的话挪去 ModelView 或者用反射延后调用。
跨包引用的最佳实践
- 在
package.json.dependencies显式声明依赖。 - 避免环依赖:
login不要依赖bag/hero这种业务包;让业务包依赖login暴露的接口。如果非要双向,用 Invoke 契约(看cn.etetet.<pkg>invoke包模式)。 - 不允许
Hotfix/Client调用Hotfix/Server代码(编译会过但语义错)。共享逻辑放Hotfix/Share。