6.4 KiB
6.4 KiB
ET Source Generator 分析器规则
源码位置:My project/Packages/cn.etetet.sourcegenerator/DotNet~/ET.SourceGenerator/Analyzer/
只对新写或新改的代码生效。下面按"踩坑频率从高到低"排序。
ET0004 — Hotfix 程序集禁止非 const 字段(含属性)
最爱挖坑的一条。HotfixProjectFieldDeclarationAnalyzer 把所有 属性 也当成"非const字段"报错。
// ❌ Hotfix 程序集
public abstract class RpcMessageHandler<TReq, TResp> : MessageSessionHandler<TReq, TResp>
{
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<TReq, TResp> : MessageSessionHandler<TReq, TResp>
{
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
public class BagComponent : Entity, IAwake
{
public Dictionary<long, EntityRef<ItemUnit>> ItemMap = new();
}
// ❌ 触发 ET0002
public static class BagService
{
public static bool TryUseItem(Player player, long idx)
{
var bag = player.GetComponent<BagComponent>();
bag.ItemMap.TryGetValue(...); // 错:跨类访问字段
}
}
// ✅
[FriendOf(typeof(BagComponent))]
public static class BagService
{
// ...
}
属性不算字段。ItemUnit 用 { get; set; } 属性,外部访问无需 FriendOf。
子类访问父类字段无需 FriendOf,本规则只针对跨类访问。
FriendOf 写在使用方类上,不是被使用的类。
ET0031 — 禁止 new Proto / Message 类
MessageObject(Proto 生成)走对象池,必须用工厂:
// ❌
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]:
// ❌ 在 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 — 静态类间环依赖
// ❌
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]
// ❌
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 类里
// ❌
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> 的 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/<RuleName>.cs
cn.etetet.sourcegenerator/DotNet~/ET.SourceGenerator/Config/DiagnosticIds.cs