com.wallstop-studios.dxmessaging 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/format-on-demand.yml +2 -2
- package/.github/workflows/json-format-check.yml +1 -1
- package/.github/workflows/markdown-json.yml +1 -1
- package/.github/workflows/markdownlint.yml +1 -1
- package/.github/workflows/npm-publish.yml +1 -1
- package/.github/workflows/prettier-autofix.yml +2 -2
- package/.github/workflows/yaml-format-lint.yml +1 -1
- package/Docs/Advanced.md +26 -1
- package/Docs/Comparisons.md +1229 -146
- package/Docs/Compatibility.md +27 -0
- package/Docs/DesignAndArchitecture.md +41 -34
- package/Docs/EmitShorthands.md +34 -0
- package/Docs/Helpers.md +1 -1
- package/Docs/Index.md +28 -25
- package/Docs/Install.md +29 -6
- package/Docs/Integrations/Reflex.md +292 -0
- package/Docs/Integrations/Reflex.md.meta +7 -0
- package/Docs/Integrations/VContainer.md +324 -0
- package/Docs/Integrations/VContainer.md.meta +7 -0
- package/Docs/Integrations/Zenject.md +333 -0
- package/Docs/Integrations/Zenject.md.meta +7 -0
- package/{Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.pdb.meta → Docs/Integrations.meta} +2 -1
- package/Docs/InterceptorsAndOrdering.md +371 -17
- package/Docs/ListeningPatterns.md +206 -0
- package/Docs/MessageBusProviders.md +496 -0
- package/Docs/MessageBusProviders.md.meta +7 -0
- package/Docs/MessageTypes.md +27 -0
- package/Docs/MigrationGuide.md +45 -0
- package/Docs/Patterns.md +286 -0
- package/Docs/Performance.md +9 -9
- package/Docs/QuickReference.md +31 -0
- package/Docs/RuntimeConfiguration.md +407 -0
- package/Docs/RuntimeConfiguration.md.meta +7 -0
- package/Docs/UnityIntegration.md +3 -1
- package/Docs/VisualGuide.md +206 -157
- package/Editor/CustomEditors/MessagingComponentEditor.cs +15 -6
- package/README.md +148 -26
- package/Runtime/AssemblyInfo.cs +4 -0
- package/Runtime/Core/Extensions/MessageBusExtensions.cs +253 -0
- package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +12 -0
- package/Runtime/Core/Extensions/MessageExtensions.cs +137 -89
- package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs +23 -0
- package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +11 -0
- package/Runtime/Core/MessageBus/IMessageBusProvider.cs +14 -0
- package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +11 -0
- package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs +18 -0
- package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +11 -0
- package/Runtime/Core/MessageBus/MessageBusRebindMode.cs +26 -0
- package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +11 -0
- package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs +383 -0
- package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +11 -0
- package/Runtime/Core/MessageHandler.cs +198 -27
- package/Runtime/Core/MessageRegistrationToken.cs +67 -25
- package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs +31 -0
- package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +12 -0
- package/Runtime/Unity/InitialGlobalMessageBusProvider.cs +38 -0
- package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +12 -0
- package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs +11 -0
- package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs.meta +3 -0
- package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs +73 -0
- package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +11 -0
- package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef +20 -0
- package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef.meta +7 -0
- package/Runtime/Unity/Integrations/Reflex.meta +8 -0
- package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs +11 -0
- package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs.meta +3 -0
- package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs +46 -0
- package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +11 -0
- package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef +30 -0
- package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef.meta +7 -0
- package/Runtime/Unity/Integrations/VContainer.meta +8 -0
- package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs +11 -0
- package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs.meta +3 -0
- package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef +30 -0
- package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef.meta +7 -0
- package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs +55 -0
- package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +11 -0
- package/Runtime/Unity/Integrations/Zenject.meta +8 -0
- package/Runtime/Unity/Integrations.meta +8 -0
- package/Runtime/Unity/MessageAwareComponent.cs +102 -0
- package/Runtime/Unity/MessageBusProviderHandle.cs +97 -0
- package/Runtime/Unity/MessageBusProviderHandle.cs.meta +12 -0
- package/Runtime/Unity/MessagingComponent.cs +164 -2
- package/Runtime/Unity/MessagingComponentInstaller.cs +120 -0
- package/Runtime/Unity/MessagingComponentInstaller.cs.meta +12 -0
- package/Runtime/Unity/ScriptableMessageBusProvider.cs +14 -0
- package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +12 -0
- package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab +98 -0
- package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab.meta +7 -0
- package/Samples~/DI/Prefabs.meta +8 -0
- package/Samples~/DI/Providers/GlobalMessageBusProvider.asset +14 -0
- package/Samples~/DI/Providers/GlobalMessageBusProvider.asset.meta +8 -0
- package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset +14 -0
- package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset.meta +8 -0
- package/Samples~/DI/Providers.meta +8 -0
- package/Samples~/DI/README.md +51 -0
- package/Samples~/DI/README.md.meta +7 -0
- package/Samples~/DI/Reflex/SampleInstaller.cs +75 -0
- package/Samples~/DI/Reflex/SampleInstaller.cs.meta +11 -0
- package/Samples~/DI/Reflex.meta +8 -0
- package/Samples~/DI/VContainer/SampleLifetimeScope.cs +81 -0
- package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +11 -0
- package/Samples~/DI/VContainer.meta +8 -0
- package/Samples~/DI/Zenject/SampleInstaller.cs +67 -0
- package/Samples~/DI/Zenject/SampleInstaller.cs.meta +11 -0
- package/Samples~/DI/Zenject.meta +8 -0
- package/Samples~/DI.meta +8 -0
- package/Samples~/Mini Combat/README.md +5 -7
- package/Samples~/Mini Combat/Walkthrough.md +18 -24
- package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs +12 -2
- package/Samples~/UI Buttons + Inspector/README.md.meta +7 -0
- package/Tests/Runtime/Benchmarks/BenchmarkSession.cs +444 -0
- package/Tests/Runtime/Benchmarks/BenchmarkSession.cs.meta +11 -0
- package/Tests/Runtime/Benchmarks/BenchmarkTestBase.cs +94 -0
- package/Tests/Runtime/Benchmarks/BenchmarkTestBase.cs.meta +11 -0
- package/Tests/Runtime/Benchmarks/ComparisonPerformanceTests.cs +395 -0
- package/Tests/Runtime/Benchmarks/ComparisonPerformanceTests.cs.meta +11 -0
- package/Tests/Runtime/Benchmarks/PerformanceTests.cs +77 -429
- package/Tests/Runtime/Benchmarks/ProviderResolutionBenchmarks.cs +142 -0
- package/Tests/Runtime/Benchmarks/ProviderResolutionBenchmarks.cs.meta +12 -0
- package/Tests/Runtime/Benchmarks/WallstopStudios.DxMessaging.Tests.Runtime.Benchmarks.asmdef +50 -0
- package/Tests/Runtime/Benchmarks/WallstopStudios.DxMessaging.Tests.Runtime.Benchmarks.asmdef.meta +7 -0
- package/Tests/Runtime/Core/DefaultBusFallbackTests.cs +333 -0
- package/Tests/Runtime/Core/DefaultBusFallbackTests.cs.meta +11 -0
- package/Tests/Runtime/Core/Extensions/MessageBusExtensionsTests.cs +278 -0
- package/Tests/Runtime/Core/Extensions/MessageBusExtensionsTests.cs.meta +11 -0
- package/Tests/Runtime/Core/Extensions/MessageExtensionsProviderTests.cs +289 -0
- package/Tests/Runtime/Core/Extensions/MessageExtensionsProviderTests.cs.meta +11 -0
- package/Tests/Runtime/Core/Extensions.meta +8 -0
- package/Tests/Runtime/Core/IntegrationShimSmokeTests.cs +57 -0
- package/Tests/Runtime/Core/IntegrationShimSmokeTests.cs.meta +11 -0
- package/Tests/Runtime/Core/MessageHandlerGlobalBusTests.cs +219 -0
- package/Tests/Runtime/Core/MessageHandlerGlobalBusTests.cs.meta +11 -0
- package/Tests/Runtime/Core/MessageRegistrationBuilderTests.cs +204 -0
- package/Tests/Runtime/Core/MessageRegistrationBuilderTests.cs.meta +11 -0
- package/Tests/Runtime/Core/MessagingTestBase.cs +4 -4
- package/Tests/Runtime/Core/NominalTests.cs +2 -2
- package/Tests/Runtime/Core/TypedShorthandTests.cs +2 -2
- package/Tests/Runtime/Core/UntargetedEquivalenceTests.cs +1 -1
- package/Tests/Runtime/Core/UntargetedPrefreezeTests.cs +2 -4
- package/Tests/Runtime/Integrations/Reflex/ReflexIntegrationTests.cs +162 -0
- package/Tests/Runtime/Integrations/Reflex/ReflexIntegrationTests.cs.meta +11 -0
- package/Tests/Runtime/Integrations/Reflex/Resources/ReflexSettings.asset +16 -0
- package/Tests/Runtime/Integrations/Reflex/Resources/ReflexSettings.asset.meta +8 -0
- package/Tests/Runtime/Integrations/Reflex/Resources.meta +8 -0
- package/Tests/Runtime/Integrations/Reflex/WallstopStudios.DxMessaging.Tests.Runtime.Reflex.asmdef +27 -0
- package/Tests/Runtime/Integrations/Reflex/WallstopStudios.DxMessaging.Tests.Runtime.Reflex.asmdef.meta +7 -0
- package/Tests/Runtime/Integrations/Reflex.meta +8 -0
- package/Tests/Runtime/Integrations/VContainer/VContainerIntegrationTests.cs +140 -0
- package/Tests/Runtime/Integrations/VContainer/VContainerIntegrationTests.cs.meta +11 -0
- package/Tests/Runtime/Integrations/VContainer/WallstopStudios.DxMessaging.Tests.Runtime.VContainer.asmdef +37 -0
- package/Tests/Runtime/Integrations/VContainer/WallstopStudios.DxMessaging.Tests.Runtime.VContainer.asmdef.meta +7 -0
- package/Tests/Runtime/Integrations/VContainer.meta +8 -0
- package/Tests/Runtime/Integrations/Zenject/WallstopStudios.DxMessaging.Tests.Runtime.Zenject.asmdef +37 -0
- package/Tests/Runtime/Integrations/Zenject/WallstopStudios.DxMessaging.Tests.Runtime.Zenject.asmdef.meta +7 -0
- package/Tests/Runtime/Integrations/Zenject/ZenjectIntegrationTests.cs +140 -0
- package/Tests/Runtime/Integrations/Zenject/ZenjectIntegrationTests.cs.meta +11 -0
- package/Tests/Runtime/Integrations/Zenject.meta +8 -0
- package/Tests/Runtime/Integrations.meta +8 -0
- package/Tests/Runtime/Scripts/Components/EmptyMessageAwareComponent.cs +1 -1
- package/Tests/Runtime/Scripts/Components/GenericMessageAwareComponent.cs +1 -1
- package/Tests/Runtime/Scripts/Components/SimpleMessageAwareComponent.cs +1 -1
- package/Tests/Runtime/TestUtilities/UnityFixtureBase.cs +64 -0
- package/Tests/Runtime/TestUtilities/UnityFixtureBase.cs.meta +12 -0
- package/Tests/Runtime/TestUtilities.meta +9 -0
- package/Tests/Runtime/Unity/MessageBusProviderAssetTests.cs +57 -0
- package/Tests/Runtime/Unity/MessageBusProviderAssetTests.cs.meta +11 -0
- package/Tests/Runtime/Unity/MessageBusProviderHandleTests.cs +107 -0
- package/Tests/Runtime/Unity/MessageBusProviderHandleTests.cs.meta +12 -0
- package/Tests/Runtime/Unity/MessagingComponentProviderIntegrationTests.cs +210 -0
- package/Tests/Runtime/Unity/MessagingComponentProviderIntegrationTests.cs.meta +12 -0
- package/Tests/Runtime/Unity.meta +9 -0
- package/Tests/Runtime/WallstopStudios.DxMessaging.Tests.Runtime.asmdef +3 -1
- package/package.json +1 -1
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# DxMessaging + VContainer
|
|
2
|
+
|
|
3
|
+
[← Back to Integrations Overview](../README.md#-integrations)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
**VContainer** is a fast, lightweight dependency injection framework for Unity with minimal overhead. DxMessaging integrates seamlessly with VContainer, allowing you to:
|
|
10
|
+
|
|
11
|
+
- **Inject `IMessageBus`** in any class with deterministic lifetimes
|
|
12
|
+
- **Create per-scope buses** for scene isolation (perfect for additive scenes)
|
|
13
|
+
- **Use DI for construction** + DxMessaging for events (best of both worlds)
|
|
14
|
+
- **Minimal performance overhead** — VContainer + DxMessaging = blazing fast
|
|
15
|
+
|
|
16
|
+
**Why combine DI + Messaging?** Use constructor injection for service dependencies (repositories, managers) and messaging for reactive events (damage taken, item collected). VContainer's scoped lifetimes make it perfect for per-scene message buses.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### Prerequisites
|
|
23
|
+
|
|
24
|
+
- DxMessaging installed via UPM
|
|
25
|
+
- VContainer installed (Git URL or OpenUPM)
|
|
26
|
+
|
|
27
|
+
### 1. Create a LifetimeScope with DxMessaging
|
|
28
|
+
|
|
29
|
+
```csharp
|
|
30
|
+
using DxMessaging.Core.MessageBus;
|
|
31
|
+
using UnityEngine;
|
|
32
|
+
using VContainer;
|
|
33
|
+
using VContainer.Unity;
|
|
34
|
+
|
|
35
|
+
public sealed class MessagingLifetimeScope : LifetimeScope
|
|
36
|
+
{
|
|
37
|
+
protected override void Configure(IContainerBuilder builder)
|
|
38
|
+
{
|
|
39
|
+
// Register MessageBus as both concrete and interface
|
|
40
|
+
builder.Register<MessageBus>(Lifetime.Singleton).As<IMessageBus>();
|
|
41
|
+
|
|
42
|
+
// Optional: Enable automatic IMessageRegistrationBuilder binding
|
|
43
|
+
// Requires VCONTAINER_PRESENT define (auto-added by DxMessaging when VContainer detected)
|
|
44
|
+
#if VCONTAINER_PRESENT
|
|
45
|
+
builder.RegisterMessageRegistrationBuilder();
|
|
46
|
+
#endif
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### Add to your scene
|
|
52
|
+
|
|
53
|
+
1. Create an empty GameObject in your scene
|
|
54
|
+
1. Add the `MessagingLifetimeScope` component
|
|
55
|
+
1. This creates a singleton bus for the entire scene
|
|
56
|
+
|
|
57
|
+
**Tip:** Use `Lifetime.Singleton` for project-wide buses, or `Lifetime.Scoped` for isolated scene/feature buses.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Usage Patterns
|
|
62
|
+
|
|
63
|
+
### Pattern 1: Inject into Plain Classes (Recommended for Services)
|
|
64
|
+
|
|
65
|
+
Use `IMessageRegistrationBuilder` to create message handlers in non-MonoBehaviour classes:
|
|
66
|
+
|
|
67
|
+
```csharp
|
|
68
|
+
using DxMessaging.Core.MessageBus;
|
|
69
|
+
using DxMessaging.Core.Attributes;
|
|
70
|
+
using VContainer.Unity;
|
|
71
|
+
|
|
72
|
+
// Define a message
|
|
73
|
+
[DxUntargetedMessage]
|
|
74
|
+
[DxAutoConstructor]
|
|
75
|
+
public readonly partial struct PlayerSpawned
|
|
76
|
+
{
|
|
77
|
+
public readonly int playerId;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Service that listens to messages
|
|
81
|
+
public sealed class PlayerService : IStartable, IDisposable
|
|
82
|
+
{
|
|
83
|
+
private readonly MessageRegistrationLease _lease;
|
|
84
|
+
|
|
85
|
+
// Builder is injected automatically when using the scope
|
|
86
|
+
public PlayerService(IMessageRegistrationBuilder registrationBuilder)
|
|
87
|
+
{
|
|
88
|
+
var options = new MessageRegistrationBuildOptions
|
|
89
|
+
{
|
|
90
|
+
Configure = token =>
|
|
91
|
+
{
|
|
92
|
+
_ = token.RegisterUntargeted<PlayerSpawned>(OnPlayerSpawned);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
_lease = registrationBuilder.Build(options);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public void Start()
|
|
100
|
+
{
|
|
101
|
+
_lease.Activate(); // Start listening when container starts
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public void Dispose()
|
|
105
|
+
{
|
|
106
|
+
_lease.Dispose(); // Clean up when container disposes
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private static void OnPlayerSpawned(ref PlayerSpawned message)
|
|
110
|
+
{
|
|
111
|
+
UnityEngine.Debug.Log($"Player {message.playerId} spawned!");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### Register the service in your scope
|
|
117
|
+
|
|
118
|
+
```csharp
|
|
119
|
+
protected override void Configure(IContainerBuilder builder)
|
|
120
|
+
{
|
|
121
|
+
builder.Register<MessageBus>(Lifetime.Singleton).As<IMessageBus>();
|
|
122
|
+
#if VCONTAINER_PRESENT
|
|
123
|
+
builder.RegisterMessageRegistrationBuilder();
|
|
124
|
+
#endif
|
|
125
|
+
|
|
126
|
+
// Register your service
|
|
127
|
+
builder.RegisterEntryPoint<PlayerService>();
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### Pattern 2: Configure MessagingComponents (For Existing MonoBehaviours)
|
|
134
|
+
|
|
135
|
+
```csharp
|
|
136
|
+
using DxMessaging.Core.MessageBus;
|
|
137
|
+
using DxMessaging.Unity;
|
|
138
|
+
using UnityEngine;
|
|
139
|
+
using VContainer;
|
|
140
|
+
using VContainer.Unity;
|
|
141
|
+
|
|
142
|
+
[RequireComponent(typeof(MessagingComponent))]
|
|
143
|
+
public sealed class MessagingComponentConfigurator : MonoBehaviour, IStartable
|
|
144
|
+
{
|
|
145
|
+
[Inject]
|
|
146
|
+
private readonly IMessageBus _messageBus;
|
|
147
|
+
|
|
148
|
+
private MessagingComponent _messagingComponent;
|
|
149
|
+
|
|
150
|
+
private void Awake()
|
|
151
|
+
{
|
|
152
|
+
_messagingComponent = GetComponent<MessagingComponent>();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public void Start()
|
|
156
|
+
{
|
|
157
|
+
_messagingComponent.Configure(_messageBus, MessageBusRebindMode.RebindActive);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Usage
|
|
163
|
+
|
|
164
|
+
1. Add `MessagingComponentConfigurator` alongside any `MessagingComponent` in your prefabs
|
|
165
|
+
1. VContainer will inject the bus via `IStartable.Start()` before handlers are registered
|
|
166
|
+
1. Your message handlers now use the container-managed bus
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### Pattern 3: Inject IMessageBus Directly
|
|
171
|
+
|
|
172
|
+
For simple emission without listening, inject `IMessageBus` directly:
|
|
173
|
+
|
|
174
|
+
```csharp
|
|
175
|
+
public sealed class GameInitializer : IStartable
|
|
176
|
+
{
|
|
177
|
+
private readonly IMessageBus _messageBus;
|
|
178
|
+
|
|
179
|
+
public GameInitializer(IMessageBus messageBus)
|
|
180
|
+
{
|
|
181
|
+
_messageBus = messageBus;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public void Start()
|
|
185
|
+
{
|
|
186
|
+
var message = new GameStarted();
|
|
187
|
+
_messageBus.EmitUntargeted(ref message);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Advanced: Scene Scopes and Isolation
|
|
195
|
+
|
|
196
|
+
VContainer's scoped lifetimes make it perfect for per-scene message buses. This is useful for additive scenes or isolated gameplay features:
|
|
197
|
+
|
|
198
|
+
```csharp
|
|
199
|
+
using DxMessaging.Core.MessageBus;
|
|
200
|
+
using VContainer;
|
|
201
|
+
using VContainer.Unity;
|
|
202
|
+
|
|
203
|
+
public sealed class LevelLoader
|
|
204
|
+
{
|
|
205
|
+
private readonly LifetimeScope _parentScope;
|
|
206
|
+
|
|
207
|
+
public LevelLoader(LifetimeScope parentScope)
|
|
208
|
+
{
|
|
209
|
+
_parentScope = parentScope;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
public LifetimeScope LoadLevel(GameObject lifetimeScopePrefab)
|
|
213
|
+
{
|
|
214
|
+
// Create a child scope with its own MessageBus
|
|
215
|
+
return _parentScope.CreateChildFromPrefab(lifetimeScopePrefab, builder =>
|
|
216
|
+
{
|
|
217
|
+
builder.Register<MessageBus>(Lifetime.Singleton).As<IMessageBus>();
|
|
218
|
+
#if VCONTAINER_PRESENT
|
|
219
|
+
builder.RegisterMessageRegistrationBuilder();
|
|
220
|
+
#endif
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Benefits
|
|
227
|
+
|
|
228
|
+
- Each scene gets its own isolated message bus
|
|
229
|
+
- Messages don't leak between scenes
|
|
230
|
+
- Perfect for multiplayer lobbies, mini-games, or feature-scoped events
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Testing with VContainer
|
|
235
|
+
|
|
236
|
+
### Unit Tests
|
|
237
|
+
|
|
238
|
+
```csharp
|
|
239
|
+
using DxMessaging.Core.MessageBus;
|
|
240
|
+
using VContainer;
|
|
241
|
+
using VContainer.Unity;
|
|
242
|
+
using NUnit.Framework;
|
|
243
|
+
|
|
244
|
+
[TestFixture]
|
|
245
|
+
public class GameInitializerTests
|
|
246
|
+
{
|
|
247
|
+
[Test]
|
|
248
|
+
public void Initialize_EmitsGameStarted()
|
|
249
|
+
{
|
|
250
|
+
// Arrange
|
|
251
|
+
var builder = new ContainerBuilder();
|
|
252
|
+
var bus = new MessageBus();
|
|
253
|
+
builder.RegisterInstance<IMessageBus>(bus);
|
|
254
|
+
builder.RegisterEntryPoint<GameInitializer>();
|
|
255
|
+
var container = builder.Build();
|
|
256
|
+
|
|
257
|
+
bool messageReceived = false;
|
|
258
|
+
var handler = new MessageHandler(new InstanceId(1), bus) { active = true };
|
|
259
|
+
var token = MessageRegistrationToken.Create(handler, bus);
|
|
260
|
+
_ = token.RegisterUntargeted<GameStarted>(ref msg => messageReceived = true);
|
|
261
|
+
token.Enable();
|
|
262
|
+
|
|
263
|
+
// Act
|
|
264
|
+
container.Resolve<GameInitializer>().Start();
|
|
265
|
+
|
|
266
|
+
// Assert
|
|
267
|
+
Assert.IsTrue(messageReceived);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Play-Mode Tests
|
|
273
|
+
|
|
274
|
+
For play-mode tests, create a temporary `LifetimeScope`:
|
|
275
|
+
|
|
276
|
+
```csharp
|
|
277
|
+
[UnityTest]
|
|
278
|
+
public IEnumerator PlayMode_MessageBusIsolation()
|
|
279
|
+
{
|
|
280
|
+
// Create isolated scope for this test
|
|
281
|
+
var scope = LifetimeScope.Create(builder =>
|
|
282
|
+
{
|
|
283
|
+
builder.Register<MessageBus>(Lifetime.Singleton).As<IMessageBus>();
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
var bus = scope.Container.Resolve<IMessageBus>();
|
|
287
|
+
// ... test logic ...
|
|
288
|
+
|
|
289
|
+
scope.Dispose(); // Clean up
|
|
290
|
+
yield return null;
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Checklist
|
|
297
|
+
|
|
298
|
+
### Initial Setup
|
|
299
|
+
|
|
300
|
+
- [ ] Install DxMessaging and VContainer
|
|
301
|
+
- [ ] Create `MessagingLifetimeScope` with `builder.Register<MessageBus>().As<IMessageBus>()`
|
|
302
|
+
- [ ] Add scope to your scene as a GameObject component
|
|
303
|
+
- [ ] Add `#if VCONTAINER_PRESENT` check and call `builder.RegisterMessageRegistrationBuilder()`
|
|
304
|
+
|
|
305
|
+
### Integration
|
|
306
|
+
|
|
307
|
+
- [ ] Use `IMessageRegistrationBuilder` in plain classes with `IStartable`/`IDisposable`
|
|
308
|
+
- [ ] Add `MessagingComponentConfigurator` to prefabs with `MessagingComponent`
|
|
309
|
+
- [ ] Replace `MessageHandler.MessageBus` references with injected `IMessageBus`
|
|
310
|
+
- [ ] Consider using scoped buses for scene isolation
|
|
311
|
+
|
|
312
|
+
### Testing
|
|
313
|
+
|
|
314
|
+
- [ ] Create isolated `ContainerBuilder` instances in tests
|
|
315
|
+
- [ ] Use `builder.RegisterInstance<IMessageBus>(new MessageBus())` for test buses
|
|
316
|
+
- [ ] Dispose scopes after tests to ensure clean teardown
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Next Steps
|
|
321
|
+
|
|
322
|
+
- **[Zenject Integration](Zenject.md)** — Full-featured DI with extensive Unity support
|
|
323
|
+
- **[Reflex Integration](Reflex.md)** — Minimal DI framework
|
|
324
|
+
- **[Back to Documentation Hub](../Index.md)** — Browse all docs
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
# DxMessaging + Zenject
|
|
2
|
+
|
|
3
|
+
[← Back to Integrations Overview](../README.md#-integrations)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
**Zenject** (also known as Extenject) is a powerful dependency injection framework for Unity. DxMessaging integrates seamlessly with Zenject, allowing you to:
|
|
10
|
+
|
|
11
|
+
- **Inject `IMessageBus`** as a singleton dependency in any class
|
|
12
|
+
- **Use DI for construction** + DxMessaging for events (best of both worlds)
|
|
13
|
+
- **Create per-scope message buses** for scene or gameplay isolation
|
|
14
|
+
- **Bridge to SignalBus** for gradual migration from Zenject Signals
|
|
15
|
+
|
|
16
|
+
**Why combine DI + Messaging?** Use constructor injection for service dependencies (repositories, managers) and messaging for reactive events (damage taken, item collected). This keeps your architecture clean and testable.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### Prerequisites
|
|
23
|
+
|
|
24
|
+
- DxMessaging installed via UPM
|
|
25
|
+
- Zenject/Extenject installed (source or UPM)
|
|
26
|
+
|
|
27
|
+
### 1. Create an Installer
|
|
28
|
+
|
|
29
|
+
Create a `DxMessagingInstaller` to bind the message bus to your Zenject container:
|
|
30
|
+
|
|
31
|
+
```csharp
|
|
32
|
+
using DxMessaging.Core.MessageBus;
|
|
33
|
+
using Zenject;
|
|
34
|
+
|
|
35
|
+
public sealed class DxMessagingInstaller : MonoInstaller
|
|
36
|
+
{
|
|
37
|
+
public override void InstallBindings()
|
|
38
|
+
{
|
|
39
|
+
// Bind MessageBus as a singleton and expose IMessageBus interface
|
|
40
|
+
Container.BindInterfacesAndSelfTo<MessageBus>().AsSingle();
|
|
41
|
+
|
|
42
|
+
// Optional: Enable automatic IMessageRegistrationBuilder binding
|
|
43
|
+
// Requires ZENJECT_PRESENT define (auto-added by DxMessaging when Zenject detected)
|
|
44
|
+
#if ZENJECT_PRESENT
|
|
45
|
+
Container.RegisterMessageRegistrationBuilder();
|
|
46
|
+
#endif
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### Add to your ProjectContext
|
|
52
|
+
|
|
53
|
+
1. Select (or create) your `ProjectContext` prefab
|
|
54
|
+
1. Add `DxMessagingInstaller` as a MonoInstaller
|
|
55
|
+
1. Save the prefab
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Usage Patterns
|
|
60
|
+
|
|
61
|
+
### Pattern 1: Inject into Plain Classes (Recommended for Services)
|
|
62
|
+
|
|
63
|
+
Use `IMessageRegistrationBuilder` to create message handlers in non-MonoBehaviour classes:
|
|
64
|
+
|
|
65
|
+
```csharp
|
|
66
|
+
using DxMessaging.Core.MessageBus;
|
|
67
|
+
using DxMessaging.Core.Attributes;
|
|
68
|
+
using Zenject;
|
|
69
|
+
|
|
70
|
+
// Define a message
|
|
71
|
+
[DxUntargetedMessage]
|
|
72
|
+
[DxAutoConstructor]
|
|
73
|
+
public readonly partial struct PlayerSpawned
|
|
74
|
+
{
|
|
75
|
+
public readonly int playerId;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Service that listens to messages
|
|
79
|
+
public sealed class PlayerController : IInitializable, IDisposable
|
|
80
|
+
{
|
|
81
|
+
private readonly MessageRegistrationLease _lease;
|
|
82
|
+
|
|
83
|
+
// Builder is injected automatically when using the installer
|
|
84
|
+
public PlayerController(IMessageRegistrationBuilder registrationBuilder)
|
|
85
|
+
{
|
|
86
|
+
var options = new MessageRegistrationBuildOptions
|
|
87
|
+
{
|
|
88
|
+
Configure = token =>
|
|
89
|
+
{
|
|
90
|
+
_ = token.RegisterUntargeted<PlayerSpawned>(OnPlayerSpawned);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
_lease = registrationBuilder.Build(options);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public void Initialize()
|
|
98
|
+
{
|
|
99
|
+
_lease.Activate(); // Start listening when container initializes
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public void Dispose()
|
|
103
|
+
{
|
|
104
|
+
_lease.Dispose(); // Clean up when container disposes
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private static void OnPlayerSpawned(ref PlayerSpawned message)
|
|
108
|
+
{
|
|
109
|
+
UnityEngine.Debug.Log($"Player {message.playerId} spawned!");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### Register the service in your installer
|
|
115
|
+
|
|
116
|
+
```csharp
|
|
117
|
+
public sealed class GameInstaller : MonoInstaller
|
|
118
|
+
{
|
|
119
|
+
public override void InstallBindings()
|
|
120
|
+
{
|
|
121
|
+
Container.BindInterfacesAndSelfTo<PlayerController>().AsSingle();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
### Pattern 2: Configure MessagingComponents (For Existing MonoBehaviours)
|
|
129
|
+
|
|
130
|
+
If you have existing `MessageAwareComponent` scripts, you can inject the container-managed bus into them:
|
|
131
|
+
|
|
132
|
+
```csharp
|
|
133
|
+
using DxMessaging.Core.MessageBus;
|
|
134
|
+
using DxMessaging.Unity;
|
|
135
|
+
using UnityEngine;
|
|
136
|
+
using Zenject;
|
|
137
|
+
|
|
138
|
+
[RequireComponent(typeof(MessagingComponent))]
|
|
139
|
+
public sealed class MessagingComponentConfigurator : MonoBehaviour
|
|
140
|
+
{
|
|
141
|
+
[Inject]
|
|
142
|
+
private IMessageBus _messageBus;
|
|
143
|
+
|
|
144
|
+
private void Awake()
|
|
145
|
+
{
|
|
146
|
+
MessagingComponent component = GetComponent<MessagingComponent>();
|
|
147
|
+
component.Configure(_messageBus, MessageBusRebindMode.RebindActive);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### Usage
|
|
153
|
+
|
|
154
|
+
1. Add `MessagingComponentConfigurator` alongside any `MessagingComponent` in your prefabs
|
|
155
|
+
1. Zenject will inject the bus before `RegisterMessageHandlers` is called
|
|
156
|
+
1. Your message handlers now use the container-managed bus
|
|
157
|
+
|
|
158
|
+
**Alternative approach:** Extend `MessageAwareComponent` and override `Awake`:
|
|
159
|
+
|
|
160
|
+
```csharp
|
|
161
|
+
public class ZenjectAwareComponent : MessageAwareComponent
|
|
162
|
+
{
|
|
163
|
+
[Inject]
|
|
164
|
+
private IMessageBus _messageBus;
|
|
165
|
+
|
|
166
|
+
protected override void Awake()
|
|
167
|
+
{
|
|
168
|
+
Configure(_messageBus, MessageBusRebindMode.RebindActive);
|
|
169
|
+
base.Awake();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
### Pattern 3: Inject IMessageBus Directly
|
|
177
|
+
|
|
178
|
+
For simple cases, inject `IMessageBus` and emit messages directly:
|
|
179
|
+
|
|
180
|
+
```csharp
|
|
181
|
+
public sealed class GameInitializer : IInitializable
|
|
182
|
+
{
|
|
183
|
+
private readonly IMessageBus _messageBus;
|
|
184
|
+
|
|
185
|
+
public GameInitializer(IMessageBus messageBus)
|
|
186
|
+
{
|
|
187
|
+
_messageBus = messageBus;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
public void Initialize()
|
|
191
|
+
{
|
|
192
|
+
var message = new GameStarted();
|
|
193
|
+
_messageBus.EmitUntargeted(ref message);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Advanced: Bridging to Zenject Signals
|
|
201
|
+
|
|
202
|
+
If you're gradually migrating from Zenject Signals to DxMessaging, you can create a bridge:
|
|
203
|
+
|
|
204
|
+
```csharp
|
|
205
|
+
using DxMessaging.Core;
|
|
206
|
+
using DxMessaging.Core.MessageBus;
|
|
207
|
+
using System;
|
|
208
|
+
using Zenject;
|
|
209
|
+
|
|
210
|
+
[DxUntargetedMessage]
|
|
211
|
+
[DxAutoConstructor]
|
|
212
|
+
public readonly partial struct SceneTransition
|
|
213
|
+
{
|
|
214
|
+
public readonly string sceneName;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
public sealed class DxToSignalBridge : IInitializable, IDisposable
|
|
218
|
+
{
|
|
219
|
+
private readonly IMessageBus _messageBus;
|
|
220
|
+
private readonly SignalBus _signalBus;
|
|
221
|
+
private MessageRegistrationToken _token;
|
|
222
|
+
|
|
223
|
+
public DxToSignalBridge(IMessageBus messageBus, SignalBus signalBus)
|
|
224
|
+
{
|
|
225
|
+
_messageBus = messageBus;
|
|
226
|
+
_signalBus = signalBus;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
public void Initialize()
|
|
230
|
+
{
|
|
231
|
+
// Create a handler to listen to DxMessaging events
|
|
232
|
+
var handler = new MessageHandler(new InstanceId(0), _messageBus)
|
|
233
|
+
{
|
|
234
|
+
active = true
|
|
235
|
+
};
|
|
236
|
+
_token = MessageRegistrationToken.Create(handler, _messageBus);
|
|
237
|
+
|
|
238
|
+
// Bridge DxMessaging → Zenject Signals
|
|
239
|
+
_ = _token.RegisterUntargeted<SceneTransition>(OnSceneTransition);
|
|
240
|
+
_token.Enable();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
public void Dispose()
|
|
244
|
+
{
|
|
245
|
+
_token?.Disable();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private void OnSceneTransition(ref SceneTransition message)
|
|
249
|
+
{
|
|
250
|
+
// Forward to SignalBus for legacy consumers
|
|
251
|
+
_signalBus.Fire(message);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Register the bridge in your installer
|
|
257
|
+
|
|
258
|
+
```csharp
|
|
259
|
+
public override void InstallBindings()
|
|
260
|
+
{
|
|
261
|
+
Container.BindInterfacesAndSelfTo<DxToSignalBridge>().AsSingle();
|
|
262
|
+
Container.DeclareSignal<SceneTransition>();
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Testing with Zenject
|
|
269
|
+
|
|
270
|
+
### Unit Tests
|
|
271
|
+
|
|
272
|
+
```csharp
|
|
273
|
+
using DxMessaging.Core.MessageBus;
|
|
274
|
+
using Zenject;
|
|
275
|
+
using NUnit.Framework;
|
|
276
|
+
|
|
277
|
+
[TestFixture]
|
|
278
|
+
public class GameInitializerTests : ZenjectUnitTestFixture
|
|
279
|
+
{
|
|
280
|
+
[Test]
|
|
281
|
+
public void Initialize_EmitsGameStarted()
|
|
282
|
+
{
|
|
283
|
+
// Arrange
|
|
284
|
+
var bus = new MessageBus();
|
|
285
|
+
Container.Bind<IMessageBus>().FromInstance(bus).AsSingle();
|
|
286
|
+
Container.BindInterfacesAndSelfTo<GameInitializer>().AsSingle();
|
|
287
|
+
|
|
288
|
+
bool messageReceived = false;
|
|
289
|
+
var handler = new MessageHandler(new InstanceId(1), bus) { active = true };
|
|
290
|
+
var token = MessageRegistrationToken.Create(handler, bus);
|
|
291
|
+
_ = token.RegisterUntargeted<GameStarted>(ref msg => messageReceived = true);
|
|
292
|
+
token.Enable();
|
|
293
|
+
|
|
294
|
+
// Act
|
|
295
|
+
var initializer = Container.Resolve<GameInitializer>();
|
|
296
|
+
initializer.Initialize();
|
|
297
|
+
|
|
298
|
+
// Assert
|
|
299
|
+
Assert.IsTrue(messageReceived);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Checklist
|
|
307
|
+
|
|
308
|
+
### Initial Setup
|
|
309
|
+
|
|
310
|
+
- [ ] Install DxMessaging and Zenject/Extenject
|
|
311
|
+
- [ ] Create `DxMessagingInstaller` with `Container.BindInterfacesAndSelfTo<MessageBus>()`
|
|
312
|
+
- [ ] Add installer to your `ProjectContext`
|
|
313
|
+
- [ ] Add `#if ZENJECT_PRESENT` check and call `Container.RegisterMessageRegistrationBuilder()`
|
|
314
|
+
|
|
315
|
+
### Integration
|
|
316
|
+
|
|
317
|
+
- [ ] Use `IMessageRegistrationBuilder` in plain classes with `IInitializable`/`IDisposable`
|
|
318
|
+
- [ ] Add `MessagingComponentConfigurator` to prefabs with `MessagingComponent`
|
|
319
|
+
- [ ] Replace `MessageHandler.MessageBus` references with injected `IMessageBus`
|
|
320
|
+
- [ ] Consider bridging to SignalBus if migrating from Zenject Signals
|
|
321
|
+
|
|
322
|
+
### Testing
|
|
323
|
+
|
|
324
|
+
- [ ] Inject `IMessageBus` in tests using `FromInstance(new MessageBus())`
|
|
325
|
+
- [ ] Verify messages flow through the container-provided bus
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## Next Steps
|
|
330
|
+
|
|
331
|
+
- **[VContainer Integration](VContainer.md)** — Lightweight alternative to Zenject
|
|
332
|
+
- **[Reflex Integration](Reflex.md)** — Minimal DI framework
|
|
333
|
+
- **[Back to Documentation Hub](../Index.md)** — Browse all docs
|