Packet Registry Generator

Read time 2 minutes Package: Nalix.Analyzers
On this page

PacketRegistryGenerator (analyzers/Nalix.Analyzers.Generators/PacketRegistryGenerator.cs) is an incremental Roslyn source generator that discovers all concrete packet classes in a compilation, checks for duplicate StaticOpCode values (NALIX068), and emits a [ModuleInitializer] that registers each packet’s Deserialize delegate with PacketRegistry.

Source Mapping

  • analyzers/Nalix.Analyzers.Generators/PacketRegistryGenerator.cs
  • analyzers/Nalix.Analyzers.Generators/Internal/GeneratorDiagnosticDescriptors.cs
  • src/Nalix.Codec/DataFrames/PacketRegistry.cs

How It Works

  1. Packet Discovery:
    • Scans the compilation for non-abstract classes implementing IPacket and IPacketDeserializer<TSelf> (such as PacketBase<TSelf> subclasses) that have [Packet] (AutoRegister != false) or implement IPacketStaticOpcode.
  2. Compile-Time Opcode Collision Check (NALIX068):
    • Extracts StaticOpCode values across all discovered packet classes in the compilation. If two packet classes declare the same StaticOpCode, reports error NALIX068.
  3. Module Initializer Registration:
    • Emits PacketRegistryGenerated.Initialize() ([ModuleInitializer]) in PacketRegistryGenerated.g.cs, calling PacketRegistry.RegisterGenerated<TPacket>("Namespace.TPacket", static raw => TPacket.Deserialize(raw)) for each discovered packet type.

Generated Code Structure

// <auto-generated/>
#nullable enable
using System.Runtime.CompilerServices;
using Nalix.Codec.DataFrames;

namespace Nalix.Generated
{
    internal static class PacketRegistryGenerated
    {
        [ModuleInitializer]
        internal static void Initialize()
        {
            PacketRegistry.RegisterGenerated<global::HelloWorld.Contracts.HelloRequestPacket>(
                "HelloWorld.Contracts.HelloRequestPacket",
                static raw => global::HelloWorld.Contracts.HelloRequestPacket.Deserialize(raw));

            PacketRegistry.RegisterGenerated<global::HelloWorld.Contracts.HelloResponsePacket>(
                "HelloWorld.Contracts.HelloResponsePacket",
                static raw => global::HelloWorld.Contracts.HelloResponsePacket.Deserialize(raw));
        }
    }
}

Runtime Activation (PacketRegistry.Build())

[ModuleInitializer] stages every assembly’s generated packet entries at module load time. Calling PacketRegistry.Build() (invoked automatically by NetworkApplicationBuilder.Build() and TransportSession, or manually in standalone tests) compiles the staged registrations into the lock-free PacketDeserializer?[65536] lookup table:

using Nalix.Codec.DataFrames;

// Finalizes the O(1) 65,536-entry opcode lookup table
PacketRegistry.Build();

Diagnostic Emitted

IDTitleSeverityCause
NALIX068Duplicate packet StaticOpCodeErrorTwo or more packet classes in the compilation define the same StaticOpCode value.