Zero Messenger
repository·main·Indexed 41 days ago
https://github.com/annulusgames/zeromessengerA high-performance, zero-allocation in-memory messaging library for .NET and Unity designed for fast Pub/Sub operations. Features include MessageBroker<T>, IMessagePublisher<T>, IMessageSubscriber<T>, and a middleware-style filter system via IMessageFilter<T>. Supports integration with Microsoft.Extensions.DependencyInjection, R3, and VContainer.
What's inside Zero Messenger
- Zero Messenger is a high-performance, zero-allocation, in-memory messaging library designed for .NET and Unity applications. It is optimized for extreme speed and minimal memory overhead, making it suitable for performance-critical environments like game engines.
How IMessageFilter<T> works
mainFilters allow you to intercept message processing before and after the handler executes. They follow an async decorator pattern similar to ASP.NET Core middleware. Implement
IMessageFilter<T>and use thenextdelegate to pass the message to the next step in the pipeline.public class LoggingFilter<T> : IMessageFilter<T> { public async ValueTask InvokeAsync(T message, CancellationToken cancellationToken, Func<T, CancellationToken, ValueTask> next) { Console.WriteLine("Before"); await next(message, cancellationToken); Console.WriteLine("After"); } }Integrate Zero Messenger with VContainer in Unity
mainZero Messenger provides an extension package for Unity users utilizing the VContainer dependency injection framework. This allows you to inject
IMessagePublisher<T>andIMessageSubscriber<T>directly into your classes.Installation
- Open the Unity Package Manager (
Window > Package Manager). - Click the
[+]button and selectAdd package from git URL. - Enter the following URL:
https://github.com/AnnulusGames/ZeroMessenger.git?path=src/ZeroMessenger.Unity/Assets/ZeroMessenger.VContainer
Configuration
Use the
AddZeroMessenger()extension method on yourIContainerBuilderwithin aLifetimeScopeto register the messenger services.NOTE AddZeroMessenger()uses Open Generics. This may cause issues with IL2CPP on Unity versions older than 2022.1.using VContainer; using VContainer.Unity; using ZeroMessenger.VContainer; public class ExampleLifetimeScope : LifetimeScope { protected override void Configure(IContainerBuilder builder) { // Add Zero Messenger to the DI container builder.AddZeroMessenger(); } }- Open the Unity Package Manager (
Install Zero Messenger in Unity
mainTo use Zero Messenger in Unity, you must install the NuGet packages using the NugetForUnity tool.
Requirements
- Unity 2021.3 or later
Installation Steps
- Install NugetForUnity.
- In Unity, navigate to
NuGet > Manage NuGet Packages. - Search for the
ZeroMessengerpackage and install it.
Integrate Zero Messenger with R3
mainBy adding the
ZeroMessenger.R3package, you can convert between Zero Messenger interfaces and R3 Observables. This allows you to use R3 operators on message streams or pipe Observable data back into a publisher.// Install package: dotnet add package ZeroMessenger.R3 // Convert IMessageSubscriber<T> to Observable<T> subscriber.ToObservable() .Subscribe(x => { }); // Connect Observable<T> to IMessagePublisher<T> observable.SubscribeToPublish(publisher); // Connect Observable<T> to IMessagePublisher<T>.PublishAsync() observable.SubscribeAwaitToPublish(publisher, AwaitOperation.Sequential, AsyncPublishStrategy.Parallel);Integrate Zero Messenger with Dependency Injection
mainTo use Zero Messenger with
Microsoft.Extensions.DependencyInjection, install theZeroMessenger.DependencyInjectionpackage. Useservices.AddZeroMessenger()to register the library in yourIServiceCollection.# .NET CLI dotnet add package ZeroMessenger.DependencyInjection # Package Manager Install-Package ZeroMessenger.DependencyInjectionHost.CreateDefaultBuilder() .ConfigureServices((context, services) => { // Add Zero Messenger services.AddZeroMessenger(); services.AddSingleton<ServiceA>(); services.AddSingleton<ServiceB>(); }) .Build() .Run();Add filters to the message pipeline
mainYou can add filters in three ways:
- Directly to a
MessageBroker<T>: UseAddFilter<T>(). Filters are applied in the order they are added. - Globally via Dependency Injection: Configure filters within
AddZeroMessenger()during service registration. - Per-subscription: Use the
WithFilter<T>()orWithFilters()extension methods on anIMessageSubscriber<T>.
// 1. To a broker var broker = new MessageBroker<int>(); broker.AddFilter<LoggingFilter<int>>(); // 2. Globally in DI services.AddZeroMessenger(messenger => { messenger.AddFilter<LoggingFilter<Message>>(); messenger.AddFilter(typeof(LoggingFilter<>)); }); // 3. Per subscription subscriber .WithFilter<LoggingFilter<Message>()} .Subscribe(x => { });- Directly to a
Quick Start with MessageBroker<T>
mainYou can use
MessageBroker<T>.Defaultfor a global Pub/Sub implementation, or instantiate a localMessageBroker<T>to act like aneventor an RxSubject<T>.using System; using ZeroMessenger; // Subscribe to messages var subscription = MessageBroker<Message>.Default.Subscribe(x => { Console.WriteLine(x.Text); }); // Publish a message MessageBroker<Message>.Default.Publish(new Message("Hello!")); // Unsubscribe subscription.Dispose(); // Type used for the message public record struct Message(string Text) { }Install Zero Messenger
mainZero Messenger requires .NET Standard 2.1 or later. You can install it via NuGet using the .NET CLI or the Package Manager.
# .NET CLI dotnet add package ZeroMessenger # Package Manager Install-Package ZeroMessengerUse PredicateFilter for conditional message handling
mainZero Messenger provides
PredicateFilter<T>. If you pass aPredicate<T>toAddFilter<T>()orWithFilter<T>(), aPredicateFilter<T>is automatically created. This allows you to exclude messages that do not meet specific criteria.public record struct FooMessage(int Value); IMessageSubscriber<FooMessage> subscriber; // Exclude values less than 0 subscriber .WithFilter(x => x.Value >= 0) .Subscribe(x => { });Use IMessageSubscriber<T> for subscribing to messages
mainThe
IMessageSubscriber<T>interface provides methods to listen for messages. UseSubscribe()for synchronous handlers (returning anIDisposableto unsubscribe) orSubscribeAwait()for asynchronous handlers.IMessageSubscriber<Message> subscriber; // Subscribe to messages var subscription = subscriber.Subscribe(x => { Console.WriteLine(x.Text); }); // Unsubscribe subscription.Dispose(); // Asynchronous processing var asyncSubscription = subscriber.SubscribeAwait(async (x, ct) => { await FooAsync(x, ct); }, AsyncSubscribeStrategy.Sequential);Use IMessagePublisher<T> for publishing messages
mainThe
IMessagePublisher<T>interface allows you to send messages. You can usePublish()for fire-and-forget operations orPublishAsync()to wait for all subscribers to finish processing.IMessagePublisher<Message> publisher; // Publish a message (Fire-and-forget) publisher.Publish(new Message("Foo!")); // Publish a message and wait for all subscribers to finish processing await publisher.PublishAsync(new Message("Bar!"), AsyncPublishStrategy.Parallel, cancellationToken);