Tài liệu

Packet Handler Generator

Thời gian đọc 4 phút Gói: Nalix.Analyzers
Trên trang này

PacketHandlerGenerator (analyzers/Nalix.Analyzers.Generators/PacketHandlerGenerator.cs) là một incremental Roslyn source generator, biên dịch các lớp được đánh dấu [PacketHandler] thành các implementation IPacketHandlerCompiler không dùng reflection, được đăng ký qua [ModuleInitializer].

Ánh xạ mã nguồn

  • analyzers/Nalix.Analyzers.Generators/PacketHandlerGenerator.cs
  • analyzers/Nalix.Analyzers.Generators/Internal/GeneratorDiagnosticDescriptors.cs

Việc generator làm lúc biên dịch

  1. Tìm controller: Tìm mọi lớp cụ thể được đánh dấu [PacketHandler] và kiểm tra các method [PacketOpcode] của chúng.
  2. Nối constructor và [Inject]:
    • Chọn constructor có thể truy cập (public hoặc internal) với nhiều tham số nhất để khởi tạo controller. Tham số ConfigurationLoader được lấy qua ConfigurationManager.Instance.Get<TParam>(), còn tham số service được lấy qua InstanceManager.Instance.GetExistingInstance<TParam>() (nếu không có thì dùng giá trị mặc định của tham số, hoặc ném InvalidOperationException nếu tham số là bắt buộc).
    • Nếu controller khai báo field hoặc property có đánh dấu [Inject] (yêu cầu lớp controller được khai báo partial), generator sinh ra <Controller>_Inject.g.cs chứa private void InitializeDependencies() và gọi nó bên trong factory được sinh ra.
  3. Biên dịch metadata: Đọc [PacketOpcode], [PacketTimeout], [PacketPermission], [PacketEncryption], [PacketRateLimit] và [PacketConcurrencyLimit] trên mỗi method handler (với giá trị dự phòng ở mức controller), rồi tạo một descriptor PacketMetadata qua PacketMetadataBuilder.
  4. Biên dịch delegate: Sinh ra một lambda invoker có kiểu mạnh, ép IPacketContext<TPacket> sang IPacketContext<TConcretePacket>, lấy các tham số [FromScope] qua ctx.Scope.GetRequiredService<TParam>() (hoặc ctx.Scope.GetService<TParam>() khi kiểu tham số là TParam? nullable), rồi gọi handler mà không dùng reflection hay boxing.
  5. Đăng ký Module Initializer: Sinh ra PacketHandlersGenerated.Initialize() ([ModuleInitializer]) gọi PacketHandlerRegistry.Register(typeof(TController), new TController_PacketHandlerCompiler()).

Chữ ký handler được hỗ trợ

Mọi method [PacketOpcode] phải tuân theo hợp đồng tham số sau:

Vị trí tham sốKiểu / Attribute bắt buộcCách cung cấp
Tham số 0 (bắt buộc)IPacketContext<TPacket> (với TPacket : IPacket)Được truyền trực tiếp từ context điều phối pooled lúc chạy (context.Packet, context.Connection, context.Sender, context.Scope, context.CancellationToken).
Tham số 1..N (tùy chọn)Kiểu tham chiếu TService (hoặc TService?) được đánh dấu [FromScope]TService không nullable được lấy qua context.Scope.GetRequiredService<TService>(); TService? nullable được lấy qua context.Scope.GetService<TService>().

Kiểu trả về được hỗ trợ

  • void
  • Task
  • ValueTask
  • TResult (ví dụ trả về một IPacket phản hồi, string hoặc byte[])
  • Task<TResult>
  • ValueTask<TResult>
  • IAsyncEnumerable<TElement> (phản hồi streaming từ server tới client)

Nội bộ, generator phân loại kiểu trả về của mỗi handler thành enum công khai Nalix.Analyzers.Generators.HandlerReturnKind (Void, NonGenericValueTask, GenericValueTask, NonGenericTask, GenericTask, AsyncEnumerable, SyncValue), hiển thị qua HandlerMethodModel.ReturnKind, và sinh ra một thân điều phối khác cho mỗi loại. Đây là chi tiết cài đặt nội bộ của generator, không phải API để bạn gọi.

Ví dụ

using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Nalix.Abstractions.Injection;
using Nalix.Abstractions.Networking.Packets;
using Nalix.Abstractions.Security;

[PacketHandler("AuthController")]
public sealed partial class AuthController
{
    [Inject]
    private ILogger? _logger;

    [PacketOpcode(0x1001)]
    [PacketPermission(PermissionLevel.NONE)]
    [PacketEncryption(true)]
    public ValueTask HandleLoginAsync(
        IPacketContext<LoginRequest> context,
        [FromScope] IUserSessionTracker tracker)
    {
        _logger?.LogInformation("Login from {Id}", context.Connection.ConnectionId);
        tracker.Track(context.Connection.ConnectionId);
        return context.Sender.ReplyAsync(new LoginResponse { Success = true });
    }
}

Chẩn đoán được phát ra

IDTiêu đềMức độNguyên nhân
NALIX003Method handler của controller gói tin có chữ ký không được hỗ trợCảnh báoTham số 0 bị thiếu hoặc không phải IPacketContext<TPacket>.
NALIX067Method handler bị loại khỏi bảng điều phốiLỗiTham số ở chỉ số >= 1 bị thiếu [FromScope] hoặc không phải kiểu tham chiếu.
NALIX104Field [Inject] không được là readonlyLỗiField được đánh dấu [Inject] được khai báo readonly.

API liên quan