# ET Source Generator 分析器规则 源码位置:`My project/Packages/cn.etetet.sourcegenerator/DotNet~/ET.SourceGenerator/Analyzer/` 只对**新写或新改**的代码生效。下面按"踩坑频率从高到低"排序。 --- ## ET0004 — Hotfix 程序集禁止非 const 字段(含属性) **最爱挖坑的一条**。HotfixProjectFieldDeclarationAnalyzer 把所有 **属性** 也当成"非const字段"报错。 ```csharp // ❌ Hotfix 程序集 public abstract class RpcMessageHandler : MessageSessionHandler { protected virtual string Module => "Net"; // ET0004: 字段: Module protected virtual string Action => typeof(TReq).Name; // ET0004: 字段: Action } // ❌ Hotfix 程序集 public static class LogExtensions { private static readonly object AuditLock = new(); // ET0004 + ET0015 } // ✅ 修复 1:完全去掉属性,在 Run 里 inline public abstract class RpcMessageHandler : MessageSessionHandler { protected sealed override async ETTask Run(Session session, TReq request, TResp response) { string action = typeof(TReq).Name; Log.Info($"[Net] {action} in"); await OnRun(session, request, response); } protected abstract ETTask OnRun(Session session, TReq req, TResp resp); } // ✅ 修复 2:挪到 Model + [EnableClass],Model 程序集不被本规则约束 // 文件路径从 Scripts/Hotfix/Share/ 移到 Scripts/Model/Share/ namespace ET { [EnableClass] public static class LogExtensions { private static readonly object AuditLock = new(); // ✅ Model 允许 } } ``` 适用程序集:`ET.Hotfix`、`ET.HotfixView`、`ET.Hotfix.Test`、`ET.HotfixView.Test`。 --- ## ET0002 — Entity 字段访问需 FriendOf ```csharp public class BagComponent : Entity, IAwake { public Dictionary> ItemMap = new(); } // ❌ 触发 ET0002 public static class BagService { public static bool TryUseItem(Player player, long idx) { var bag = player.GetComponent(); bag.ItemMap.TryGetValue(...); // 错:跨类访问字段 } } // ✅ [FriendOf(typeof(BagComponent))] public static class BagService { // ... } ``` **属性不算字段**。`ItemUnit` 用 `{ get; set; }` 属性,外部访问无需 FriendOf。 **子类访问父类**字段无需 FriendOf,本规则只针对**跨类访问**。 **FriendOf 写在使用方类上**,不是被使用的类。 --- ## ET0031 — 禁止 new Proto / Message 类 `MessageObject`(Proto 生成)走对象池,必须用工厂: ```csharp // ❌ HeroInfo hero = new() { heroId = 1 }; Hero_HeroInfo notice = new() { hero = info }; // ✅ HeroInfo hero = HeroInfo.Create(); hero.heroId = 1; Hero_HeroInfo notice = Hero_HeroInfo.Create(); notice.hero = info; ``` --- ## ET0032 — Model/ModelView 禁止非 Entity 类 Model 程序集理论上只放 Entity / Component / Proto / `struct`。普通工具类/DTO 要么挪走,要么加 `[EnableClass]`: ```csharp // ❌ 在 Scripts/Model/Share/ 下 namespace ET.Server { public sealed class MongoDBComponent : IDBComponent { ... } // ET0032 } // ✅ namespace ET.Server { [EnableClass] public sealed class MongoDBComponent : IDBComponent { ... } } ``` 实战上经常用 Model 层放 `static` 工具:因为 Hotfix 不允许静态字段,需要持有锁/字典之类的静态状态时**只能**放在 Model + `[EnableClass]`。 --- ## ET0013 — 静态类间环依赖 ```csharp // ❌ public static class BagComponentSystem { public static void AddItem(this BagComponent self, ...) { BagNoticeHelper.NotifyItemChange(...); } } public static class BagNoticeHelper { public static void NotifyItemList(BagComponent bag) { bag.GetAllItems(); // 调到 BagComponentSystem,形成环 } } // ✅ 让 BagNoticeHelper 内联自己的小逻辑,不再调上层 public static class BagNoticeHelper { public static void NotifyItemList(BagComponent bag) { foreach (var unitRef in bag.ItemMap.Values) { ItemUnit unit = unitRef; list.items.Add(BagMapper.ToProto(unit)); } } } ``` 排查:报错信息会列出所有 `A -> B` 和 `B -> A` 的调用点。 --- ## ET0015 — static 字段必须 `[StaticField]` ```csharp // ❌ namespace ET { public class Foo { private static int counter; // ET0015 } } // ✅ namespace ET { public class Foo { [StaticField] private static int counter; } } ``` 在 Hotfix 程序集里 ET0015 + ET0004 会同时触发,唯一出路是**挪到 Model**。 --- ## ET0024 / ET0025 — EntitySystem 必须在 partial 类里 ```csharp // ❌ public static class HeroSystem { private static void Awake(this Hero self) { ... } // ET0024 } // ✅ [EntitySystemOf(typeof(Hero))] [FriendOf(typeof(Hero))] public static partial class HeroSystem { [EntitySystem] private static void Awake(this Hero self, int configId) { self.ConfigId = configId; } } ``` ET 用源码生成器在 partial 的另一半生成 `HeroAwakeSystem` 类。**忘记 `partial`** 是常见错误。 --- ## 其他规则(出错概率低,简要列出) | ID | 含义 | |---|---| | ET0001 | `AddChild` 的 T 必须在 `[ChildOf]` 声明里 | | ET0003 | Entity 子类不能写构造函数 | | ET0005 | Hotfix 类必须 static | | ET0006 | Entity 类不能写实例方法(要写成扩展方法在 System 里) | | ET0007 | Component 必须有 `[ComponentOf]` 声明 | | ET0008 | 同步方法里不能调 `await ETTask` | | ET0009 | async 方法里不能丢弃 ETTask | | ET0010 | 不允许 delegate 字段 | | ET0011 | UniqueId 范围检查 | | ET0012 | UniqueId 重复 | | ET0014 | Entity 内不能用 child component | | ET0016~0019 | ETCancellationToken 链路检查 | | ET0020 | Entity 字段声明位置 | | ET0021 | async 方法返回类型必须 ETTask | | ET0022 | Client 类不能在 Server 程序集 | | ET0023 | LSEntity float 字段检查(确定性帧同步) | | ET0026 | FiberLog 使用规则 | | ET0027 | EntityHashCode 检查 | | ET0028 | EntityComponentChild 检查 | | ET0029 | Entity 不能声明泛型类型 | | ET0030 | NetMessage 必须从 Proto 生成 | | ET1001 | ETSystem 方法必须在 static partial 类 | 具体规则触发时去看对应分析器源码: ``` cn.etetet.sourcegenerator/DotNet~/ET.SourceGenerator/Analyzer/.cs cn.etetet.sourcegenerator/DotNet~/ET.SourceGenerator/Config/DiagnosticIds.cs ```