6.4 KiB
Raw Permalink Blame History

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.HotfixET.HotfixViewET.Hotfix.TestET.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 类

MessageObjectProto 生成)走对象池,必须用工厂:

// ❌
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 -> BB -> 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